/* Charte Paddock : catalogue ivoire. Papier de catalogue d'encheres plein
   jour, encre brune, filets typographiques. Anti-generique : aucun degrade,
   aucune ombre diffuse, aucune carte, coins nets, laiton par petites touches.
   Les planches blueprint restent marines : planches contrecollees du livre. */

/* Les polices de la maison (spec Maison 2.3), servies par le site : la
   politique de securite de contenu n'admet que `font-src 'self'`. Memes
   faces et memes fichiers que la vitrine. Cormorant Garamond declare la
   plage de graisses de l'axe de ses deux fichiers, 300 a 700 : declaree
   plus etroite, une graisse demandee au-dela se rabattait sur la borne. */
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/cormorant-garamond-latin.c47ff12887a3.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/cormorant-garamond-italique-latin.3e2c4175a96d.woff2") format("woff2");
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/manrope-latin.938c6e8019b6.woff2") format("woff2");
}

:root {
  color-scheme: light;
  --ground: #F6F1E7;
  --plate: #1E2A38;
  --hair: #B8AC94;
  --hair-2: #DCD2BC;
  --hair-in: #E7DFCC;
  --ink: #2A251C;
  --ink-2: #5C5545;
  --ink-faint: #6F6450;
  --accent: #8F6D2C;
  --accent-dim: #B8965A;
  --accent-texte: #866629;
  --danger: #A44E3E;
  --ok: #5F7F4B;
  --plate-ink: #8FB8CE;
  --r: 3px;
  /* La mesure de la page (decision Vincent du 28/08, qui AMENDE la pleine
     largeur du 27/08) : toute la page se pose sur une colonne centree. La
     gouttiere se resserre au repli, ce qui recale les trois bandeaux d'un
     coup au lieu de trois paddings a tenir en phase a la main. */
  --mesure: 1240px;
  --mesure-courte: 40rem;
  --gouttiere: 32px;
  --serif: Georgia, "Iowan Old Style", "Times New Roman", serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;
  --sans: "Manrope", system-ui, sans-serif;
  /* La famille d'apparat : navigation et tableau de bord la prennent aux
     endroits que la maquette designe. Les titres des ecrans restent dans
     la famille de texte (dashboard.css ramene --serif a --sans). */
  --apparat: "Cormorant Garamond", Georgia, "Iowan Old Style", "Times New Roman", serif;
}

* { box-sizing: border-box; }
body {
  font-family: var(--sans);
  background: var(--ground);
  color: var(--ink);
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  /* Cesure de dernier recours : une URL dans un message d'erreur ou les
     sources d'un rapport IA sont insecables et elargissaient la page de
     ~95px a 393px (recette telephone du 31/08 au soir). `break-word` et
     PAS `anywhere` : seul `anywhere` entre dans le calcul de la largeur
     min-content, ce qui laisse flexbox ecraser un item jusqu'a une lettre
     (le "Assuran/ce" de la recette du matin). `break-word` ne coupe qu'un
     mot qui ne tient pas SEUL sur sa ligne, le vocabulaire de l'app est
     donc intact. */
  overflow-wrap: break-word;
  /* Fond « traces de circuits » : contexte d'empilement pour le calque
     body::before en z-index negatif (regles en fin de fichier). `isolation`
     seul : `position: relative` ferait de body le bloc conteneur des
     descendants absolus sans ancetre positionne (sprite SVG, sr-only). */
  isolation: isolate;
}
/* Liens editoriaux : soulignement fin laiton terni decolle du texte, jamais
   le souligne epais par defaut du navigateur qui fait "HTML brut". */
a {
  color: var(--ink);
  text-decoration-color: var(--accent-dim);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color .15s ease, text-decoration-color .15s ease;
}
a:hover { color: var(--accent-texte); text-decoration-color: var(--accent); }

/* Trois voix typographiques : serif pour noms de modeles et titres, mono
   pour les chiffres, sans pour les micro-labels (capitales espacees). */
h1, h2, h3 { font-family: var(--serif); font-weight: 600; letter-spacing: .01em; }
h1 { font-size: 2rem; line-height: 1.15; }
/* Reinitialisation durable : .num et .muted vivent souvent imbriques dans un
   .micro (synthese, entry-spec) et heritaient sinon sa voix capitales
   espacees. */
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; text-transform: none; letter-spacing: normal; }
/* font-family EXPLICITE et pas herite du body : depuis le lot depenser la
   couleur, un micro-label vit aussi DANS un titre (le compte du titre de section,
   .section-title .compte), et il y heritait le serif de h1,h2,h3. Un
   micro-label en Georgia capitales espacees n'est plus la voix sans de la
   charte : c'est le meme piege d'heritage que .num et .muted ci-dessus. */
.micro {
  font-family: var(--sans);
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .18em;
  color: var(--ink-faint);
}

/* Oreilles du journal : date a gauche, recherche et compte a droite. */
/* UN SEUL AXE POUR TOUTE LA PAGE. Les oreilles, le masthead et le contenu se
   posent sur la meme colonne centree. Avant, chacun gerait sa largeur dans
   son coin : le wordmark se centrait sur la fenetre pendant que le contenu
   restait colle a gauche, deux axes differents et un tiers d'ecran vide a
   droite (constat Vincent du 28/08). Regle GROUPEE a dessein : trois regles
   separees rederiveraient au premier lot suivant. Les paddings horizontaux
   des trois bandeaux sont a zero, la gouttiere est portee par cette largeur,
   sinon l'encre du contenu ne s'aligne pas sur le filet du masthead. */
.ears, .masthead, .content {
  width: min(var(--mesure), 100% - var(--gouttiere) * 2);
  margin-inline: auto;
}
.ears {
  display: flex; justify-content: space-between; align-items: center;
  gap: 8px 18px; flex-wrap: wrap; padding: 10px 0 4px;
}
.ears-right { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ears form { display: inline; margin: 0; }
.ears input[type=search] {
  width: 15rem; max-width: 40vw; background: transparent;
  border: 0; border-bottom: 1px solid var(--hair-2); border-radius: 0;
  padding: .25rem .1rem;
}
.ears button { border: 0; padding: .25rem 0; }
/* Un seul controle de compte (maquette Respirer, spec 2.1) : le monogramme
   sur un details natif (Maison a retire le nom et le chevron de l'ecran, le
   nom se lit dans l'intitule du bouton) ; Preferences et Deconnexion vivent
   dans son menu, sous le controle, alignes a droite. */
.ears details.compte { position: relative; }
.ears details.compte summary { cursor: pointer; display: inline-flex; align-items: center; gap: 8px; }
.ears details.compte .monogram { width: 26px; height: 26px; font-family: var(--sans); font-size: 10.5px; letter-spacing: .06em; }
.compte-menu {
  position: absolute; right: 0; top: 34px; z-index: 10; min-width: 11rem;
  background: var(--ground-2); border: 1px solid var(--frame); border-radius: var(--r);
  padding: 10px 14px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
}
/* La recherche compacte : le champ et ses propositions s'effacent derriere
   l'icone quand le script a pose la classe. L'icone elle-meme se cache plus
   bas, a la suite de `.btn-icone` dont elle herite : meme poids (0,1,0), la
   source tranche, et posee ici elle perdrait. */
.search.compacte input[type=search], .search.compacte .suggest, .search.compacte .loupe { display: none; }

/* Recherche a propositions (spec 5) : le deroulant se pose sous le champ,
   au dessus du contenu mais SOUS le tiroir de la visionneuse (z-index 20).
   Il s'ancre a l'enveloppe du champ, qui porte aussi la loupe (spec
   Finitions 4, RF-02). Sa hauteur est bornee sur la fenetre et il defile
   jusqu'au dernier element ; static/recherche.js la reduit encore a
   l'espace reellement libre sous le champ. `overscroll-behavior` garde le
   defilement dans le panneau arrive en bout de liste, au doigt comme a la
   molette. */
.search { position: relative; }
/* La croix native d'effacement des champs de recherche est retirée (spec
   Finitions 6 bis, point 6) : composant brut du navigateur, sans pendant
   dans la charte ; Échap et la sélection effacent la saisie. */
input[type=search]::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }
input[type=search]::-webkit-search-decoration { -webkit-appearance: none; display: none; }
.champ-recherche, .champ-loupe { position: relative; }
.champ-loupe { display: block; }
.champ-loupe .loupe {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; pointer-events: none;
}
.suggest {
  position: absolute; top: calc(100% + 6px); right: 0; width: min(26rem, 90vw); z-index: 10; text-align: left;
  max-height: min(60vh, 28rem); overflow-y: auto; overscroll-behavior: contain;
  background: var(--ground-2); border: 1px solid var(--frame); border-radius: var(--r);
}
.suggest:empty { display: none; }
/* Posee par static/recherche.js quand l'espace sous le champ ne tient
   pas quelques propositions et que le dessus en offre davantage. */
.suggest.suggest-dessus { top: auto; bottom: calc(100% + 6px); }
/* Le cadre est celui du panneau qui defile : porte par la boite defilee,
   son bord bas passait sous le pli et la ligne coupee se confondait avec la
   page. */
.suggest-boite {
  padding: 10px 12px; display: flex; flex-direction: column; gap: 8px;
}
.suggest-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.suggest-liste { display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--hair-in); padding-top: 8px; }
/* Le filet separe la liste des chips. Sans chips elle ouvre la boite, dont
   la bordure fait deja la separation : deux filets colles se lisent comme
   un defaut d'impression. */
.suggest-boite > .suggest-liste:first-child { border-top: 0; padding-top: 0; }
.suggest-liste a { text-decoration: none; display: flex; gap: 10px; align-items: baseline; }
.suggest-liste .micro { min-width: 5.5rem; }

/* Masthead catalogue (spec section 5) : filet double, embleme du jour,
   wordmark serif espace, devise, navigation en capitales espacees. */
.masthead { text-align: center; border-top: 3px double var(--ink); }
.masthead .embleme { height: 64px; margin: 16px auto 6px; display: block; }
.wordmark {
  display: inline-block; font-family: var(--serif); font-size: 2rem;
  letter-spacing: .35em; padding-left: .35em; color: var(--ink);
  text-decoration: none;
}
.masthead .devise { margin: 6px 0 12px; }
.mast-nav {
  display: flex; justify-content: center; gap: 26px; padding: 8px 2px;
  border-top: 1px solid var(--hair); border-bottom: 1px solid var(--hair);
  overflow-x: auto; white-space: nowrap;
}
.mast-nav a {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .16em;
  text-decoration: none; color: var(--ink-2); padding: 2px 0;
  border-bottom: 2px solid transparent;
}
.mast-nav a:hover { color: var(--ink); }
/* L'entrée allumée se dit par l'encre et la typographie (spec Maison 1.3) :
   aucun trait de couleur sur un bord. L'espace connecté l'habille dans
   interface.css. */
.mast-nav a[aria-current="page"] { color: var(--ink); }
/* Le compte des analyses a relire, dans l'entree du menu : un chiffre en
   accent a la suite du libelle, pas une pastille. L'espace qui les separe
   est dans le balisage (nom accessible « Analyses 1 »), pas une marge. */
.mast-nav .badge { font-size: .68rem; color: var(--accent-texte); letter-spacing: 0; }

/* La colonne est une seule balise sur tous les ecrans : le mode compact
   des pages denses, qui retrecissait les entrees et decalait la colonne
   sur Ajouter, la fiche et la synthese des couts, est retire (spec
   Finitions 2.5). */

/* Largeur du contenu : la colonne partagee ci-dessus (--mesure). La decision
   du 27/08 bornait a 1100px puis a rien du tout ; celle du 28/08 tranche a
   1240px centres, ce qui laisse la page de lot et les tables d'entretien
   s'etaler sans coller aux bords. Padding HORIZONTAL A ZERO : la gouttiere
   est portee par la largeur partagee, sinon l'encre du contenu se decale du
   filet du masthead. La lisibilite du texte long est rendue par la mesure de
   prose ci-dessous, pas par un plafond de plus. */
.content { padding: 22px 0; min-width: 0; }
/* Ecrans courts (saisie, confirmation, connexion, petits formulaires) :
   colonne etroite et texte centre. C'est le traitement de la couverture de
   connexion du lot K5, generalise plutot que redecline. */
.content.court {
  width: min(var(--mesure-courte), 100% - var(--gouttiere) * 2);
  text-align: center;
}
/* Le texte SAISI reste au fer a gauche : un champ dont le contenu se centre
   pendant la frappe est illisible. Les champs eux-memes se centrent dans la
   colonne par l'alignement du parent, ce sont des boites en ligne. */
.content.court input, .content.court select, .content.court textarea { text-align: left; }
/* La prose garde sa mesure de lecture mais son bloc se recentre, sinon un
   paragraphe plus court que la colonne se colle a gauche. */
.content.court > p { margin-inline: auto; }
/* Mesure de lecture des paragraphes poses a la racine de la page (notes d'un
   vehicule, chapeaux d'ecran) : une ligne de prose de 1800px ne se lit pas.
   Enfant DIRECT seulement : dans la marge reglee ou un panneau, la largeur
   est deja contrainte par le conteneur et un plafond en plus decalerait le
   texte de son cadre. */
.content > p { max-width: 78ch; }
/* Couverture de connexion : la premiere page du catalogue. */
.cover { max-width: 26rem; margin: 9vh auto 0; text-align: center; }
.cover .logo-couverture { width: 132px; height: auto; margin: 0 auto 10px; display: block; }
.cover .devise { margin-top: 6px; }
.cover .logo-couverture.marque-maison { width: auto; max-width: 100%; }
/* Le formulaire de connexion et le message de verrou tiennent la meme
   place sous la devise : filet et respiration communs, le formulaire seul
   au fer a gauche (le message reste centre). */
.cover form, .cover .verrou {
  margin-top: 24px;
  border-top: 1px solid var(--hair); padding-top: 16px;
}
.cover form { text-align: left; }
.cover h1 { font-size: 1.1rem; margin: 0 0 .6rem; }

/* Message flash : laiton, dans la nuance lisible sur papier (annonce vocale,
   role=status pose au HTML). Combine avec .muted (base.html rend "muted
   flash") : sans la combinaison, .muted plus tardif ecrasait la couleur et
   le correctif K2 restait inerte. */
.flash, .muted.flash { color: var(--accent-texte); }

/* Aucun repere natif de <details> : chaque repli porte le chevron de la
   charte (spec Finitions 2.3). Regle generale, les replis nommes ajoutent
   leur mise en page. */
summary { list-style: none; }
summary::-webkit-details-marker { display: none; }

/* Rangee d'actions d'un formulaire (spec Finitions 2.4) : le bouton
   principal, puis « Annuler », lien secondaire vers l'ecran d'origine. */
.form-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-top: 18px; }
/* Une rangee d'actions dans une autre (l'intervenant parmi les gestes d'une
   reponse de l'assistant) : elle s'aligne sur ses voisins, sans marge. */
.form-actions .form-actions { margin-top: 0; }

/* Filets fins plutot que cartes ombrees. */
.hairline { border: 0; border-top: 1px solid var(--hair); margin: 14px 0; }
.panel { background: transparent; border: 1px solid var(--hair-2); border-radius: var(--r); padding: 14px 18px; }
/* Panneau image : le fond porte la grille blueprint que la spec (section 9)
   confie a l'app ("l'app dessine elle-meme tout le chrome : cadre fin,
   grille"). Hairlines a peine visibles, pas un degrade. Regle groupee :
   les TROIS planches marines partagent le meme fond, chacune gardant chez
   elle bordure et mise en page. La planche de la fiche et l'entree
   editoriale sont des boites ; la troisieme est une IMAGE, la diapositive
   `.photo-piste img.planche` qui prend au telephone la place de la planche
   de la fiche (spec Respirer, 4). Elle a REJOINT ce groupe plutot que de
   recopier ses deux declarations : c'est la meme planche deplacee, et une
   copie aurait laisse la grille derriere le jour ou ce fond change. C'est
   aussi ce qui oblige son bloc de repli a ne poser aucun `background`, un
   raccourci y remettrait `background-image: none`.
   Le trait de grille est `--plate-line` et pas un rgba en dur : le jeton
   existe au contrat avec une valeur par theme (le vert de l'Alpine, le jaune
   de Ferrari, le lime de Weissach), et l'ancien litteral etait exactement
   celui d'ivoire, donc les sept autres planches se dessinaient au cyan du
   theme temoin. Meme reparation que le cadre de la planche (`--plate-frame`)
   dans ce lot : un hexadecimal en dur dans app.css est un theme qui ne sort
   jamais. */
.plate, .entry-media, .photo-piste img.planche {
  background-color: var(--plate);
  background-image:
    repeating-linear-gradient(0deg, var(--plate-line) 0 1px, transparent 1px 26px),
    repeating-linear-gradient(90deg, var(--plate-line) 0 1px, transparent 1px 26px);
}

/* Icones : trait encre par defaut (lisible sur papier), sans remplissage,
   18 a 20 px. */
.icon { width: 18px; height: 18px; stroke: var(--ink-2); fill: none; stroke-width: 1.4; flex: 0 0 auto; }
/* Sur les planches marines, le trait redevient blueprint. */
.plate .icon, .entry-media .icon { stroke: var(--plate-ink); }
/* Gravures (spec gravures, section 3) : un pictogramme grave par TYPE
   (echeance, piece, entretien, cout, source d'analyse), pose en tete de
   rangee a la place des icones du sprite, qui ne gardent que les gestes.
   Masque alpha peint a l'encre secondaire du theme : un fichier, huit
   themes. Une classe par fichier et jamais d'url() en style en ligne, le
   stockage manifest reecrit les url() de la feuille seulement. */
.gravure {
  display: inline-block; width: 22px; height: 22px; flex: none; background: var(--ink-2);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center center; mask-position: center center;
  -webkit-mask-size: contain; mask-size: contain;
}
.gravure-huile { -webkit-mask-image: url("img/pictos/huile.530f5951a9bb.png"); mask-image: url("img/pictos/huile.530f5951a9bb.png"); }
.gravure-cle { -webkit-mask-image: url("img/pictos/cle.64387ea2dd8b.png"); mask-image: url("img/pictos/cle.64387ea2dd8b.png"); }
.gravure-pneu { -webkit-mask-image: url("img/pictos/pneu.5d5ffbe6cee0.png"); mask-image: url("img/pictos/pneu.5d5ffbe6cee0.png"); }
.gravure-compteur { -webkit-mask-image: url("img/pictos/compteur.dce53824a2d6.png"); mask-image: url("img/pictos/compteur.dce53824a2d6.png"); }
.gravure-carburant { -webkit-mask-image: url("img/pictos/carburant.ca413b891da1.png"); mask-image: url("img/pictos/carburant.ca413b891da1.png"); }
.gravure-garage { -webkit-mask-image: url("img/pictos/garage.e0a710b7a1ff.png"); mask-image: url("img/pictos/garage.e0a710b7a1ff.png"); }
.gravure-carnet { -webkit-mask-image: url("img/pictos/carnet.dedd42363323.png"); mask-image: url("img/pictos/carnet.dedd42363323.png"); }
.gravure-sablier { -webkit-mask-image: url("img/pictos/sablier.220bc2c7d8c5.png"); mask-image: url("img/pictos/sablier.220bc2c7d8c5.png"); }
.gravure-facture { -webkit-mask-image: url("img/pictos/facture.c091e77c1057.png"); mask-image: url("img/pictos/facture.c091e77c1057.png"); }
.gravure-carte-grise { -webkit-mask-image: url("img/pictos/carte-grise.4b3138b2265e.png"); mask-image: url("img/pictos/carte-grise.4b3138b2265e.png"); }
.gravure-controle-technique { -webkit-mask-image: url("img/pictos/controle-technique.28c18da97e0a.png"); mask-image: url("img/pictos/controle-technique.28c18da97e0a.png"); }
.gravure-expertise { -webkit-mask-image: url("img/pictos/expertise.f6997b514ba1.png"); mask-image: url("img/pictos/expertise.f6997b514ba1.png"); }
.gravure-assurance { -webkit-mask-image: url("img/pictos/assurance.b449cf2c35ed.png"); mask-image: url("img/pictos/assurance.b449cf2c35ed.png"); }
.gravure-document { -webkit-mask-image: url("img/pictos/document.ab1189c4df6b.png"); mask-image: url("img/pictos/document.ab1189c4df6b.png"); }
.gravure-photo { -webkit-mask-image: url("img/pictos/photo.8a5f65ca0a50.png"); mask-image: url("img/pictos/photo.8a5f65ca0a50.png"); }
.gravure-cachet { -webkit-mask-image: url("img/pictos/cachet.82867ef60c14.png"); mask-image: url("img/pictos/cachet.82867ef60c14.png"); }
.filtres .chip .gravure { width: 18px; height: 18px; }
/* La croix de la puce qui retire un filtre suit la couleur de la puce. */
.filtres .chip .icon { width: 12px; height: 12px; stroke: currentColor; }
/* Etats vides (spec gravures, section 4) : la gravure du bloc au-dessus de
   la phrase, a l'encre pleine et un peu voilee ; la marge de la fiche, plus
   etroite, la reduit. Boite CARREE : le sablier et le jerrican sont en
   portrait, une boite 120 x 84 les rendait aux deux tiers des autres. */
.vide { display: flex; flex-direction: column; align-items: center; gap: 6px; margin: 0; padding: 14px 0 8px; text-align: center; }
.vide .gravure { width: 120px; height: 120px; background: var(--ink); opacity: .82; }
.lot-aside .vide .gravure { width: 84px; height: 84px; }
/* Coffre (spec coffre 3.1, 6) : le clair d'un secret revele prend `.num`,
   qui porte deja la chasse fixe. La mention « Chiffre dans votre
   navigateur » et l'avis « demande JavaScript » s'effacent des que html.js
   est pose, avant que coffre.js (defer) ne les cache pour de bon. L'inverse
   ne se fait pas en CSS : `.dashboard-app [hidden]` (dashboard.css) porte
   un !important, c'est coffre.js qui retire `hidden` des `.js-seulement`. */
html.js .coffre .js-absent { display: none; }
/* Le dialogue de la phrase du coffre (spec coffre 3.3) : le formulaire de
   la couverture de connexion, sans la couverture. */
.coffre-dialogue { border: 1px solid var(--hair); border-radius: var(--r); background: var(--ground); color: var(--ink); padding: 1.25rem 1.5rem; width: min(100% - 2rem, 24rem); }
.coffre-dialogue::backdrop { background: rgba(42, 37, 28, .35); }
.coffre-dialogue form { text-align: left; }
/* La marge tient le champ hors de l'anneau de focus (3 px + 3 px de
   decalage sous .dashboard-app), qui mordait l'etiquette. */
.coffre-dialogue input { width: 100%; margin-top: .4rem; }
/* Le champ secret et son bouton « Afficher » sur une ligne (spec 3.5). */
.coffre-form .champ-secret { display: flex; gap: .5rem; align-items: stretch; }
.coffre-form .champ-secret input { flex: 1 1 auto; min-width: 0; }
.coffre-dialogue .actions { display: flex; gap: .5rem; justify-content: flex-end; margin: 0; }
/* Le champ de la confirmation est autofocalise : l'anneau de focus de
   `.dashboard-app` (3 px, decale de 3 px) mordait l'etiquette juste
   au-dessus (capture iPhone du 11/09), comme sur le dialogue. */
.coffre-confirmer input { margin-top: .4rem; }
/* Au repli, le titre d'une entree prend le reste de sa ligne (base 45 % :
   il entre dans le calcul du passage a la ligne assez petit pour laisser
   « Reveler » a sa droite, meme a 320 px, puis il grandit), et le chevron
   d'une note se range au fer de droite pendant que la note descend en
   `.sub`. Sans cela (capture iPhone du 11/09) le chevron collait au titre,
   et un titre suivi du nom du vehicule prenait sa ligne seul, chevron ou
   bouton rejetes en ligne 3. */
.coffre .filet-row .chev { margin-left: auto; }
/* Les radios de nature et de portee, rendues par as_p en pile : la pile
   s'aligne a gauche dans le bloc centre, sinon chaque puce flotte. Le
   groupe est enfant direct du formulaire : un div ferme le p qui l'ouvre. */
.coffre-form > div:not(.form-actions) { display: inline-grid; text-align: left; }
@media (max-width: 720px) {
  .coffre .filet-row .titre { flex: 1 1 45%; min-width: 0; }
}
/* Le repli de la note est indexe sur la FENETRE (720 px) alors que c'est la
   colonne qui plie : sur l'iPad en portrait (768 px, menu lateral deploye)
   la colonne du coffre fait 523 px, la note y tient encore sur sa base de
   16rem et c'est le chevron qui passe seul en ligne 2 (capture du 11/09,
   coffre du vehicule). Le coffre se mesure donc lui-meme : sous 40rem de
   colonne, la note descend sous le titre comme au telephone, et le chevron
   reste au fer de droite de la premiere ligne. Premiere requete de
   conteneur de la feuille : le `@media 720` ci-dessus reste le repli des
   navigateurs qui ne la comprennent pas. Elle bat les regles de base de
   `.filet-row .sub` par la specificite du prefixe `.coffre`, pas par
   l'ordre source. */
.coffre { container-type: inline-size; }
@container (max-width: 40rem) {
  .coffre .filet-row .titre { flex: 1 1 45%; min-width: 0; }
  .coffre .filet-row .sub { order: 9; flex: 1 0 100%; margin-left: 0; }
  .coffre .filet-row .sub.grow:not(:empty) { flex: 1 0 100%; }
}
.gravure-vide-carnet { -webkit-mask-image: url("img/gravures/vide-carnet.7d7e3d4346e4.png"); mask-image: url("img/gravures/vide-carnet.7d7e3d4346e4.png"); }
.gravure-vide-chemise { -webkit-mask-image: url("img/gravures/vide-chemise.2684258019fa.png"); mask-image: url("img/gravures/vide-chemise.2684258019fa.png"); }
.gravure-vide-jerrican { -webkit-mask-image: url("img/gravures/vide-jerrican.381c0a322078.png"); mask-image: url("img/gravures/vide-jerrican.381c0a322078.png"); }
.gravure-vide-marteau { -webkit-mask-image: url("img/gravures/vide-marteau.e6f8bbac1df0.png"); mask-image: url("img/gravures/vide-marteau.e6f8bbac1df0.png"); }
.gravure-vide-cle { -webkit-mask-image: url("img/gravures/vide-cle.185873762a27.png"); mask-image: url("img/gravures/vide-cle.185873762a27.png"); }
.gravure-vide-sablier { -webkit-mask-image: url("img/gravures/vide-sablier.1539e17397f4.png"); mask-image: url("img/gravures/vide-sablier.1539e17397f4.png"); }

/* Signaux. */
.danger { color: var(--danger); }
/* Un message d'erreur ramene a l'ecran par un script garde de l'air au bord
   (preferences, echec d'un enregistrement au clic). */
[role="alert"] { scroll-margin-block: 16px; }
/* Une rubrique d'onglet visee par une ancre (fiche.js) : un peu d'air
   au-dessus de son titre, colle sinon au bord de la fenetre. */
[data-onglet] [id^="rubrique-"] { scroll-margin-top: 16px; }
.ok { color: var(--ok); }
/* Voix reprises des ecrans de depot (31/08). Ils etaient restes a la charte
   d'avant le renversement ivoire, avec vingt-quatre couleurs en dur posees
   en style en ligne : l'ancien laiton #a06000, l'ancien rouge #b00020 et le
   filet gris #ddd. Un style en ligne echappe a toute reprise de charte, d'ou
   ces deux classes et la garde qui bannit les litteraux. L'avertissement et
   l'erreur, eux, reprennent .flash et .danger qui existaient deja. */
.encart { margin-bottom: 1.2rem; }
.encart > p { margin: 0; }
/* La note jointe au fichier (retours testeur du 01/10, B1), sur la carte du
   lot photo comme sur l'attente et la confirmation d'un depot : le meme
   bloc partout, l'etiquette serree sur la note, un blanc avant et apres.
   Sur la carte, `.encart > p` mettait l'etiquette, la note et la ligne
   d'identification bord a bord (0 px, recette du 02/10). */
.note-jointe { margin: 1rem 0; }
.note-jointe > p { margin: 0; }
.note-jointe > .micro { margin-bottom: .3rem; }
.encart .encart-total { font-size: 1.4rem; margin-top: .2rem; }
.encart .encart-total .muted { font-size: .85rem; }
.encart.etroit { max-width: 22rem; }
/* Apercu de piece (carte grise, facture, controle technique) : le document
   se lit en pleine largeur de sa colonne, dans un filet de charte. */
.apercu { width: 100%; height: 70vh; border: 1px solid var(--hair-2); border-radius: var(--r); }

/* Pastille d'etat d'un vehicule. Le LIBELLE reste a l'encre : mesure du
   01/09, en texte de 9,5 px les couleurs d'etat ne tiennent pas 4,5:1 sur
   leur papier (l'ocre d'Atelier est a 3,51, l'ambre de Fiche d'atelier a
   2,56). La couleur porte le filet et la puce, qui sont des signaux non
   textuels et relevent du seuil de 3:1 : c'est ce qui lui permet de rester
   de l'ocre au lieu de devenir un brun. */
.etat {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink); border: 1px solid currentColor; border-radius: 2px;
  /* Le filet prend la teinte de l'etat, l'encre du libelle reste l'encre.
     Declare ICI, apres le raccourci `border` qu'il corrige, et non dans un
     second bloc `.etat` plus bas : un selecteur en double se lit mal et se
     garde mal (voir `_regle` de test_charte.py). */
  border-color: var(--teinte, var(--hair-2));
  padding: 2px 7px; white-space: nowrap;
}
.etat::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--teinte, var(--ink-2));
}
/* La teinte d'un etat se pose par la meme classe sur la pastille ET sur
   l'entree de vehicule, qui en fait son lisere (spec depenser la couleur,
   section 1). Une seule table de correspondance, pas deux a tenir en phase. */
.etat.collection, .entry.collection { --teinte: var(--statut-collection); }
.etat.restauration, .entry.restauration { --teinte: var(--statut-restauration); }
.etat.vente, .entry.vente { --teinte: var(--statut-vente); }
.etat.cede, .entry.cede { --teinte: var(--statut-cede); }
/* Les themes a aplats groupent par surface plutot que par filet : fond
   teinte tres pale, obtenu par transparence de la teinte sur le papier, donc
   sans jeton supplementaire a tenir au contrat. */
/* color-mix demande Safari 16.2, Chrome 111, Firefox 113. Plus etroit
   que le reste du fichier, d'ou cette note : un navigateur qui ne le
   connait pas ignore la declaration et la pastille garde son filet, sa
   puce et son libelle. Elle perd l'aplat, pas sa lisibilite. */
[data-etats="aplat"] .etat {
  background: color-mix(in srgb, var(--teinte, var(--hair-2)) 14%, var(--ground));
}
/* Grille des themes de l'ecran de preferences. Nommee .themes et non
   .vignettes : les vignettes du parc (tableau de bord, planche Accueil du
   02/09) ont leur propre regle plus bas, un nom partage l'ecraserait. */
/* Le fieldset porte le nom du groupe pour les lecteurs d'ecran, pas un
   cadre : celui du navigateur est le signal « site pas fini » que la
   charte combat. */
.themes-groupe { border: 0; padding: 0; margin: 0; min-width: 0; }
.themes-groupe > legend { padding: 0; }
.themes {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px; margin: 18px 0;
}
/* Chaque theme porte son propre data-theme, donc ses propres variables :
   il se peint lui-meme, et un theme nouveau n'ajoute pas une ligne de CSS. */
.theme { display: block; cursor: pointer; }
.theme input[type=radio] { position: absolute; opacity: 0; }
.theme-papier {
  display: block; background: var(--ground); color: var(--ink);
  border: 1px solid var(--hair); border-radius: var(--r); padding: 12px 14px;
}
.theme-encre { font-family: var(--serif); font-size: 1.05rem; }
.theme-teintes { display: flex; gap: 5px; margin-top: 10px; }
.theme-teintes .t { width: 22px; height: 12px; border-radius: 2px; }
.theme-teintes .accent { background: var(--accent); }
.theme-teintes .collection { background: var(--statut-collection); }
.theme-teintes .restauration { background: var(--statut-restauration); }
.theme-teintes .vente { background: var(--statut-vente); }
.theme-teintes .danger { background: var(--danger); }
/* Le champ etant masque, son anneau de focus part avec lui : le theme le
   reprend, sinon le clavier navigue a l'aveugle. Meme motif que .fichier. */
.theme input:focus-visible + .theme-papier { outline: 2px solid var(--accent); }
/* Le theme choisi se lit d'un coup d'oeil (RF-09) : un anneau dans l'accent
   de la vignette, dessine a l'interieur de son filet. Les vignettes gardent
   ainsi les bords de la colonne, alignes sur la legende et les cases. */
.theme input:checked + .theme-papier { outline: 2px solid var(--accent); outline-offset: -5px; }
.traces-choix { display: flex; flex-direction: column; gap: 4px; margin: 0 0 18px; }
/* La case et son libelle, sans le vide de la rangee : un clic a cote ne
   bascule rien. */
.traces-choix > label { align-self: flex-start; }

/* Tables editoriales, filets internes. */
table { border-collapse: collapse; width: 100%; }
/* Conteneur de toute table : sur un telephone, une table plus large que sa
   colonne defile ICI, sinon elle pousse la page entiere et tout le document
   part de cote (fiche a 390px : 587px de large pour 485px de fenetre,
   recette du 28/08). Le conteneur plutot qu'un display: block sur la table :
   ce dernier contient aussi le debordement mais retire a la table sa nature
   de table dans l'arbre d'accessibilite. */
/* `position: relative` : un descendant pose en absolu (l'etiquette `.sr-only`
   d'un en-tete) prend le cadre pour bloc conteneur et reste coupe par lui.
   Sans elle il echappait au defilement du cadre et elargissait la fenetre
   de mise en page du telephone (suivi IA a 655 px pour 393, recette du
   29/09). */
.table-scroll { overflow-x: auto; position: relative; }
/* Le montant saisi dans une cellule de nombres (budget du suivi IA) : la
   largeur de dix chiffres, au fer droit de sa colonne. Sans elle le champ
   prenait la place que la colonne lui laissait : « 2,00 » se lisait
   « 2,C » au telephone, et le champ s'etirait sur 330 px en plein ecran. */
input.champ-montant { width: calc(10ch + 26px); text-align: right; font-variant-numeric: tabular-nums; }
/* Son erreur, sous lui : calee sur son bord droit, sans puce, sur une
   largeur de lecture. Dans la seule largeur du champ elle coulait sur cinq
   lignes, sa puce restee seule a gauche (recette du 29/09). */
td.nombre .errorlist { list-style: none; padding-left: 0; min-width: 14rem; }
/* Les gestes d'une ligne de tableau (suivi IA) : cote a cote, ecartes,
   chacun sur une ligne de texte. */
.gestes-ligne { display: flex; gap: .5rem; }
.gestes-ligne form { margin: 0; }
.gestes-ligne button { white-space: nowrap; }
/* Registre des vendus : la table s'assoit sur l'aplat neutre, et chaque
   ligne signe son P&L par un lisere sur sa premiere cellule. Le CHIFFRE
   reste a l'encre : un nombre est du texte, et ni --ok ni --danger ne sont
   garantis a 4,5:1 (spec depenser la couleur, section 4). Sur la cellule et
   pas sur la ligne : un tr sans border-collapse ne dessine pas de bord. */
/* Composants, lot C : le registre se pose sur la page, et le signe du P&L
   passe du lisere de la premiere cellule a la couleur du montant
   (interface.css). */
th, td { text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--hair-in); }
/* En-tetes de colonnes dans la voix micro-label : l'instrument etiquette
   en capitales espacees, jamais en gras de tableur. */
th {
  color: var(--ink-faint); font-weight: 400; font-size: .68rem;
  text-transform: uppercase; letter-spacing: .14em;
}
/* Colonne de nombres : au bord droit, en-tete compris, pour que les chiffres
   se lisent en colonne (lecture de l'annee des Sorties). Aucune regle de la
   charte ne l'avait : les tables du bilan et du registre gardent leurs
   montants au fer gauche. (0,1,1) : bat `th, td` juste au-dessus. */
th.nombre, td.nombre { text-align: right; }
/* Lecture de l'annee des Sorties : plancher de la colonne des circuits. Entre
   721 et 779 px, quatre colonnes de nombres et des noms de voiture longs
   la laissaient a 48 px, et un nom a trait d'union suivi de son chrono
   (« Dijon-Prenois 1:37.642 ») coulait sur trois lignes. Au telephone la
   cellule empilee prend toute la rangee : le plancher n'y pese rien. */
#lecture td[data-label="Circuits"] { min-width: 7rem; }

/* Formulaires. */
form p { margin: .6rem 0; }
/* `.etiquette-lue` : l'etiquette d'une ligne en lecture dans un formulaire
   (le Lieu du formulaire Modifier), dans la voix des autres champs. */
label, .etiquette-lue { display: block; font-size: .85rem; color: var(--ink-2); }
/* `font: inherit` : sans lui le navigateur rend un champ en Arial et une
   zone de texte en monospace, a cote du texte courant (spec Finitions 2.1).
   Le placeholder suit la police du champ. */
input, select, textarea {
  font: inherit;
  padding: .45rem .55rem; width: 100%; max-width: 26rem; background: transparent;
  color: var(--ink); border: 1px solid var(--hair); border-radius: var(--r);
}
input:focus, select:focus, textarea:focus { outline: 1px solid var(--accent-dim); }
/* Boutons en controles d'instrument : capitales espacees, fond transparent,
   filet ; le laiton n'arrive qu'au survol ou sur l'action primaire. */
/* Un lien .btn a la geometrie d'un bouton (la vedette en porte trois) : sans
   cette regle il rendait comme un lien nu. */
button, .btn {
  padding: .55rem 1.1rem; cursor: pointer; background: transparent;
  color: var(--ink-2); border: 1px solid var(--hair-2); border-radius: var(--r);
  font-family: var(--sans); font-size: .72rem; text-transform: uppercase;
  letter-spacing: .12em; display: inline-flex; align-items: center; text-decoration: none;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
button:hover, .btn:hover { border-color: var(--accent-dim); color: var(--accent-texte); }
/* `display: inline-flex` ci-dessus est une regle d'AUTEUR : elle bat le
   `[hidden] { display: none }` de la feuille du NAVIGATEUR, pour tous les
   boutons de l'application. Un bouton servi cache l'est toujours a dessein
   (il ne ferait rien sans son script) : les fleches de la bande photo de la
   fiche, les deux boutons de la banniere d'installation. Sans cette ligne,
   ils s'affichent tous, et clignotent avant le `defer` quand le script
   arrive. Elle vit ICI, avec la regle qu'elle repare, et pas scopee a un
   composant : le prochain bouton servi cache ne s'en souviendra pas. */
button[hidden], .btn[hidden] { display: none; }

/* Le selecteur natif de fichier suit les boutons (sinon chip claire du
   navigateur, premier signal "site pas fini"). */
input[type=file] { border: 1px dashed var(--hair-2); padding: .6rem; }
input[type=file]::file-selector-button {
  padding: .4rem .8rem; margin-right: .7rem; cursor: pointer;
  background: transparent; color: var(--ink-2);
  border: 1px solid var(--hair-2); border-radius: var(--r);
  font-size: .72rem; text-transform: uppercase; letter-spacing: .12em;
}
/* Habillage du selecteur de fichier. Le "Choose file / No file chosen" du
   champ natif est rendu par le NAVIGATEUR, dans SA langue : ni traduisible
   ni habillable, et sur un outil francais il se lit comme un ecran pas fini.
   Le script de base.html enveloppe donc chaque champ et pose nos propres
   commandes. Les deux regles ci-dessus restent le repli sans JS : elles sont
   ce qu'on voit si le script ne tourne pas. */
.fichier {
  display: inline-flex; align-items: center; gap: 12px;
  border: 1px dashed var(--hair-2); border-radius: var(--r); padding: .5rem .6rem;
  text-align: left;
}
/* Lu par le lecteur d'ecran, masque a l'oeil : ce qu'un aplat de couleur
   dit seul (WCAG 1.4.1) se dit aussi en toutes lettres. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
/* Masque a l'oeil mais TOUJOURS focalisable : display: none le sortirait de
   l'ordre de tabulation et le selecteur deviendrait inatteignable au
   clavier. Le champ garde son etiquette, c'est lui qui ouvre le dialogue. */
.fichier input[type=file] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  border: 0; overflow: hidden; clip-path: inset(50%);
}
.fichier-bouton {
  display: inline-block; padding: .4rem .8rem; cursor: pointer; flex: none;
  color: var(--ink-2); border: 1px solid var(--hair-2); border-radius: var(--r);
  font-size: .72rem; text-transform: uppercase; letter-spacing: .12em;
  transition: color .15s ease, border-color .15s ease;
}
.fichier-bouton:hover { border-color: var(--accent-dim); color: var(--accent-texte); }
/* Le champ etant masque, son anneau de focus part avec lui : le bouton
   habille le reprend, sinon le clavier navigue a l'aveugle. */
.fichier input[type=file]:focus-visible + .fichier-bouton {
  outline: 1px solid var(--accent-dim); color: var(--accent-texte);
}
/* L'etat porte un NOM DE FICHIER : pas la voix micro, qui capitalise et
   descend a 9,5px. Un nom de fichier se rend tel qu'il est. */
.fichier-etat {
  font-size: .8rem; color: var(--ink-faint); text-transform: none;
  overflow-wrap: anywhere;
}
/* Relecture d'une date saisie, en toutes lettres francaises. Le format d'un
   <input type=date> est impose par la LOCALE DU NAVIGATEUR : un client dont
   le navigateur est en anglais lit mm/dd/yyyy sur un outil francais et peut
   inverser jour et mois. Plutot que de remplacer le champ natif (on perdrait
   le calendrier et le clavier date du telephone), l'app relit ce qu'elle a
   compris. Vide tant qu'aucune date n'est saisie : rien ne bouge a l'ecran
   avant que l'utilisateur n'ait quelque chose a verifier. */
.date-lue {
  display: block; font-size: .8rem; color: var(--ink-faint);
  text-transform: none; margin-top: .2rem;
}
/* Une case a cocher n'est pas un champ de saisie : la regle groupee
   `input, select, textarea` lui posait une largeur de 26rem, un padding et
   un filet, et le navigateur dessinait alors la coche CENTREE dans une
   boite pleine largeur, l'etiquette tombant dessous (constate au telephone
   sur /points-a-surveiller/, 01/09). Deux ecrans de saisie l'avaient
   rattrape localement, ce qui masquait que la cause etait globale : elle
   touchait aussi la relecture d'extraction et le dossier de vente. La
   neutralisation est ici, pas chez l'appelant. */
input[type=checkbox], input[type=radio] {
  width: auto; max-width: none; padding: 0; border: 0;
  /* margin-left a 0 : la feuille navigateur en pose 4px, et la case de
     Preferences sortait decalee du bord de colonne (revue du 05/09). */
  margin-left: 0; margin-right: .4rem; vertical-align: -.1rem;
  accent-color: var(--accent);
  /* Taille de la charte (spec Finitions 2.3) : la case native de 13 px se
     perdait a cote d'un champ de 44 px. */
  width: 18px;
  height: 18px;
  flex: none;
}
/* Une case rendue par `as_p` suit son libelle, pose en bloc au-dessus : la
   case tombait seule sous le libelle. La rangee la remet devant lui, sur la
   meme ligne ; l'aide et l'erreur passent a la ligne. */
p:has(> input[type=checkbox]) { display: flex; align-items: center; flex-wrap: wrap; gap: 0 .5rem; }
p:has(> input[type=checkbox]) > input[type=checkbox] { order: -1; margin: 0; }
p:has(> input[type=checkbox]) > label { display: inline; }
p:has(> input[type=checkbox]) > .helptext, p:has(> input[type=checkbox]) > .errorlist { flex-basis: 100%; }
/* Laiton sur l'action (spec sections 2 et 7) : le bouton primaire porte
   l'accent laiton au repos, en petite touche (bord + texte, pas d'aplat). */
.btn-primary { border-color: var(--accent-dim); color: var(--accent-texte); }
.btn-primary:hover { background: var(--hair-in); }

.muted { color: var(--ink-2); font-size: .85rem; text-transform: none; letter-spacing: normal; }
.errorlist { color: var(--danger); margin: 0; padding-left: 1rem; font-size: .85rem; }
/* La periodicite d'une echeance (spec echeances recurrentes, 5.1) : le
   nombre et l'unite sur UNE ligne, sous un seul libelle. La regle groupee
   `input, select, textarea`, plus haut, donne a chaque champ toute la
   largeur, donc une ligne chacun : ici le nombre tient en trois chiffres et
   l'unite prend le reste, les deux bords de la ligne sont ceux des champs
   voisins. */
.periodicite { display: flex; gap: .5rem; }
.periodicite input { flex: none; width: 6rem; }
.periodicite select { flex: 1 1 auto; min-width: 0; }

/* Spinner du JS maison : conserve tel quel, recolore charte. */
form.busy button[type=submit]::after {
  content: ""; display: inline-block; width: .7rem; height: .7rem;
  margin-left: .5rem; vertical-align: -.05rem;
  border: 2px solid var(--ink-faint); border-top-color: transparent; border-radius: 50%;
  animation: busy-spin .8s linear infinite;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }

/* Responsive iPhone d'abord : masthead et oreilles se resserrent, la grille
   repasse en une colonne. */
@media (max-width: 720px) {
  /* Une seule valeur a bouger : les trois bandeaux se resserrent ensemble. */
  :root { --gouttiere: 14px; }
  .ears { padding: 8px 0 0; }
  /* Oreilles conservees au repli (decision Vincent 31/08 : elles portent la
     seule recherche et la seule deconnexion), seule la date decorative se
     masque pour rendre une demi-ligne. */
  .ears .ears-date { display: none; }
  /* Maquette Respirer (spec 2.1) : la recherche se replie derriere son
     icone. */
  /* L'icone ne s'affiche que revelee par le script : `hidden` garde le
     dernier mot. Sans `:not([hidden])` la regle peserait (0,2,0), plus que
     `button[hidden] { display: none }`, et le bouton servi cache
     s'afficherait sans script, sans rien faire. */
  .ears .recherche-icone:not([hidden]) { display: inline-flex; padding: .3rem .45rem; }
  /* Le champ ouvert prend sa propre ligne sous les oreilles. */
  .ears .search:not(.compacte) { flex: 1 0 100%; order: 9; }
  .ears .search:not(.compacte) input[type=search] { width: 100%; max-width: none; }
  /* La date masquee, le bloc de droite est seul dans les oreilles : il prend
     la mesure (le champ ouvert la prend avec lui) et range l'icone et le
     monogramme a droite, ou le menu du compte, ancre a droite, tient dans
     l'ecran ; a gauche il sortirait par le bord. */
  .ears-right { flex: 1; justify-content: flex-end; }
  /* Au repli le champ ouvert prend sa propre ligne, toute la mesure, sous
     l'icone et le monogramme : le deroulant s'ancre a gauche et la prend. */
  .suggest { left: 0; right: auto; width: calc(100vw - var(--gouttiere) * 2); }
  /* Recette 390px, deux mesures : la colonne de 5.5rem tombe, mais sans
     `flex: none` le micro-label se brisait en « ECH / EAN / CE », et avec le
     seul `nowrap` il debordait sous le texte de la ligne. Il prend donc sa
     largeur naturelle, rien de plus. */
  .suggest-liste .micro { min-width: 0; flex: none; white-space: nowrap; }
  .wordmark { font-size: 1.5rem; }
  /* Au telephone la gravure et la devise s'effacent : la voiture ouvre
     l'ecran (maquette Respirer, spec 2.2). Le bureau et la connexion les
     gardent (la couverture pose `.cover .embleme`, pas `.masthead`). Le
     wordmark reprend les marges qu'elles portaient, sinon il colle au filet
     double ; le masthead COMPACT (fiche, saisie, rapports) n'a rien perdu et
     ne reprend rien : ecrites sur `.masthead .wordmark`, ces marges le
     faisaient passer de 89 a 107 px, plus haut que le complet allege. */
  .masthead .embleme, .masthead .devise { display: none; }
  .masthead:not(.compact) .wordmark { margin: 10px 0 8px; }
  /* La barre de menus tient sur UNE SEULE LIGNE, sans defilement et sans
     repli. Elle a eu les deux defauts l'un apres l'autre le 03/09 : d'abord
     une zone qui glissait sous le doigt (« on peut scroll horizontalement
     sur la barre des menus alors qu'on ne devrait pas »), puis un passage a
     la ligne (« il faut que tout tienne sur une seule ligne ! le depot est
     a la ligne et ce n'est pas bon »).

     Le levier est la TYPOGRAPHIE, pas la mise en page : aucun libelle n'est
     abrege, aucun ne disparait. L'espacement des capitales est ce qui coute
     le plus (.16em sur 33 caracteres, c'est 61 px a lui seul), puis la
     gouttiere, puis le corps. Valeurs MESUREES au navigateur le 05/09, pour
     une colonne de 292 px a 320 px de fenetre, badge compris :

       compte client (4 entrees), sans badge  216,6 px  (+75,4 de reste)
       compte client, badge a trois chiffres  239,2 px  (+52,8)
       compte staff  (5 entrees), sans badge  264,3 px  (+27,7)
       compte staff,  badge a trois chiffres  286,9 px   (+5,1)

     Le BADGE est ce que la mesure du 03/09 ne comptait pas : elle ne pesait
     que des libelles, et l'ancienne note (« 22 px de reste a 320 px pour un
     compte client ») etait fausse des le premier chiffre affiche. A .05em et
     9 px de gouttiere, la barre du staff mesurait 284,4 px sans badge (+7,6)
     mais 294,1 avec un chiffre (-2,1), 300,6 avec deux (-8,6) et 307,2 avec
     trois (-15,2) : « SUIVI IA » sortait de la colonne, puis de l'ecran, ou
     le filet de la racine le coupait en silence. La gouttiere passe donc de
     9 a 6 px et l'espacement de .05 a .03em, ce qui rend 20,3 px et fait
     tenir le compte a relire jusqu'a trois chiffres. A QUATRE chiffres la
     barre du staff deborde de 1,5 px : ecart assume, un millier d'analyses
     en attente est un autre probleme que celui de cette regle. Les mots
     restent separes, c'est la gouttiere qui les separe : a .03em l'ecart
     entre deux lettres vaut 0,34 px pour 6,3 px entre deux entrees.

     `flex-wrap: nowrap` et `overflow-x: visible` vont ensemble, et l'une
     sans l'autre ramene un des deux defauts. Contrepartie assumee : une
     septieme entree deborderait en silence, le filet anti-defilement de la
     racine la clippant ; ce sont les marges mesurees ci-dessus qui tiennent
     lieu de garde. `white-space` n'est plus redeclare, le `nowrap` de la
     regle de base est exactement ce qu'il faut pour qu'« A VALIDER » ne se
     casse pas en deux.

     `justify-content: safe center` et non `center` nu : la barre se recentre
     sous le wordmark comme le reste de la page TANT QU'ELLE TIENT, et repart
     au fer des qu'elle deborde. Sans le mot-cle, un centrage partage le
     debordement entre les deux bords : a 320 px pour un compte staff, « Parc »
     commencait a -2 px et « Suivi IA » finissait a 322, les DEUX bouts
     coupes. Avec lui, « Parc » commence a 16 px, intact, et seule la queue
     depasse. Un navigateur qui ignore `safe` jette la declaration entiere et
     retombe sur le `center` de la regle de base, c'est-a-dire sur le
     comportement d'avant. */
  .mast-nav {
    gap: 6px; padding: 6px 2px; justify-content: safe center;
    flex-wrap: nowrap; overflow-x: visible;
  }
  /* Une seule ligne se vise quand meme au pouce : l'item garde sa bande. */
  .mast-nav a { padding: 7px 0; letter-spacing: .03em; font-size: .70rem; }
  .content { padding: 14px 0; }
  /* Filet anti-defilement horizontal (decision Vincent 31/08 au soir : au
     telephone la page ne doit ABSOLUMENT pas defiler de cote). Il double les
     corrections de cause, il ne les remplace pas : la contrepartie assumee
     est qu'un debordement futur sera masque au lieu d'etre visible.
     `clip` et pas `hidden` : `hidden` ferait de la racine un conteneur de
     defilement et casserait `position: sticky`. La ligne `hidden` reste
     declaree AVANT pour les navigateurs anterieurs a Safari 16, qui
     ignorent la ligne `clip` qui suit et gardent la premiere. */
  html { overflow-x: hidden; overflow-x: clip; }
}

/* Titre de section en rubrique centree (spec titres de section du 05/09) :
   capitales espacees a l'encre pleine, centrees sur la colonne entre deux
   filets pointilles, comme le separateur de rubrique du Financial Times.
   C'est la voix des micro-labels de la marge, trois crans plus grande. Sur
   TOUS les themes : c'est de la structure, pas un signal. Le ruban teinte a
   barre d'accent qui vivait ici (spec depenser la couleur, section 2) etait
   le motif « card header » de tous les gabarits d'interface ; il est parti
   avec son debord negatif, et `--band` n'a plus d'emploi dans la feuille.
   `position: relative` : le compte se pose en absolu, le titre se centre sur
   la colonne entiere et pas sur ce que le compte lui laisse. Les filets sont
   a l'encre des micro-labels et pas a `--hair` : un pointille ne peint qu'un
   pixel sur deux, et un filet deja pale a moitie peint disparaissait sur
   l'ivoire (recette du 05/09 a 1440). Le filet et la voix partagent
   ainsi la meme encre. */
.section-title {
  position: relative; margin-top: 26px; padding: 8px 0;
  border-top: 1px dotted var(--ink-faint); border-bottom: 1px dotted var(--ink-faint);
  text-align: center; color: var(--ink);
  font-family: var(--sans); font-size: 13px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .2em;
}
/* Le compte a droite du titre, en micro-label (`.micro` dit lui-meme sa
   fonte, sa taille et son encre : rien de la voix du titre ne fuit dedans),
   pose en absolu sur la ligne du titre. Le souligne editorial des liens
   RESTE : c'est le seul signe qui dit que le compte se clique. */
.section-title .compte { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
/* La fiche reste en deux colonnes jusqu'a 721 px et sa colonne principale y
   descend a 377 px : entre 721 et 915 px le compte le plus long de la fiche
   (« + ajouter · 12 observations ouvertes ») recouvrait « Carnet » centre
   (revue qualite du 05/09, mesure a 768 px). Sous 920 px le compte de la
   fiche passe sous le titre, comme tous les comptes le font sous 720 px. */
@media (max-width: 920px) {
  .lot-main .section-title .compte { position: static; transform: none; display: block; margin-top: 2px; }
}
/* Le bloc de chiffres d'une synthese : une bande entre deux filets, sans
   aplat (Composants, lot C ; ses filets viennent d'interface.css). */
.synthese {
  display: flex; flex-wrap: wrap; gap: 8px 28px; align-items: baseline;
  justify-content: space-between;
  padding: 10px 0;
}
.synthese .num { font-size: 1.05rem; color: var(--ink); margin-left: .4em; }
.synthese .num.call { color: var(--accent-texte); }
/* La cote de marche a son jeton : un nombre, donc un jeton de texte, garde
   a 4,5:1 depuis le lot depenser la couleur. PLACEE ICI a dessein, apres
   `.synthese .num` : les deux selecteurs pesent (0,2,0), seul l'ordre source
   les departage, et remontee au bloc des signaux la cote repasserait a
   l'encre des qu'elle entre dans une synthese. */
.num.marche { color: var(--marche); }
.deadline-list { padding: 4px 2px; }
/* `flex-wrap: wrap` sur la regle de BASE, et pas dans le repli du telephone
   (mesure du 10/09, navigateur en demi-ecran) : de 721 a 1300 px la rangee
   tenait encore sur une ligne, mais en ecrasant la note d'echeance a ZERO
   pixel de large pour 836 de haut, une lettre par ligne. Une rangee qui
   replie partout n'a plus de bande ou elle ecrase. Le repli du telephone
   n'est plus une exception, c'est le meme mecanisme plus tot.
   Interligne de 2 px (`gap: 2px 12px`) : c'est un retour a la ligne dans une
   rangee, pas une rangee de plus. Une carte qui prend la main sur ses rangees
   peut en decider autrement (le tableau des echeances du dashboard passe a
   8 px au telephone, dashboard.css). */
.filet-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; padding: 9px 0; border-bottom: 1px solid var(--hair-in); }
.filet-row:last-child { border-bottom: 0; }
/* Aplat teinte sur une rangee qui a quelque chose a dire, et sur elle
   seule : echue (danger) ou proche (warn). Meme transparence que la pastille
   des themes a aplats, meme note de compatibilite color-mix : un navigateur
   qui l'ignore garde la rangee nue, la date echue reste en danger.

   Le papier est celui du CONTENANT (`--papier`), plus celui de la page :
   depuis le dashboard la rangee vit dans une carte blanche, et le melange
   sur `--ground` y virait au gris sale (mesure du 10/09 sur l'accueil : un
   echu a rgb(237,226,231) dans une carte a rgb(255,255,255)).

   Le debord de 10 px est parti avec le rayon de 2 px. Il donnait de l'air a
   l'aplat hors du fer du texte quand la rangee etait posee a meme la page ;
   dans une carte a 20 px de marge il ne mene nulle part et s'arrete 11 px
   avant le bord, angle vif contre coin arrondi (« la couleur du cadre ne fit
   pas dans le tableau », Vincent, 10/09). L'aplat tient donc la rangee, au
   fer de ses filets ; c'est la CARTE qui donne sa largeur a ses rangees
   quand elle est le tableau (`--debord`, dashboard.css). */
/* Composants, lot C (spec du 09/10/2026, section 1) : l'aplat est retire.
   Un etat se dit par la couleur du texte : la rangee echue ou proche pose
   sa teinte (`--etat`, melee a l'encre pour tenir 4,5) sur son titre et sur
   sa mention de droite ; la cellule d'etat d'un tableau (suivi IA : IA
   coupee, budget atteint) la pose sur son texte. */
.filet-row.echu { --etat: color-mix(in srgb, var(--danger) 82%, var(--ink)); }
.filet-row.proche { --etat: color-mix(in srgb, var(--warn) 55%, var(--ink)); }
.filet-row:is(.echu, .proche) > :is(.titre, .end, .grow:not(.muted)),
.filet-row:is(.echu, .proche) > .end.muted { color: var(--etat); }
td.avertissement { color: color-mix(in srgb, var(--warn) 55%, var(--ink)); font-weight: 600; }
/* Rangees a filet en remplacement des tables denses (spec finitions 31/08) :
   .grow prend la place libre, .end s'aligne a droite, .sub porte
   l'information secondaire et prend sa propre ligne au repli.
   overflow-wrap : un titre de document a underscores (les titres sont des
   noms de fichiers) ou une note insecable deborderait de la rangee a 390px
   et repousserait la page entiere, le defaut fondateur du lot. La cesure
   vit sur .sub et .grow (les contenus utilisateur) et JAMAIS sur la rangee
   entiere : posee sur la regle de base, elle coupait le vocabulaire de
   l'app en plein mot dans les echeances de la fiche a 390px (Assuran/ce,
   Control/e techniq/ue, recette du 31/08). */
.filet-row .grow { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.filet-row .end { margin-left: auto; }
.filet-row .sub { min-width: 0; overflow-wrap: anywhere; }
/* Le plancher de lecture, et c'est LUI qui fait plier la rangee. `flex: 1`
   pose une base de zero : un item a base nulle n'entre jamais dans le calcul
   du passage a la ligne, il se contente de prendre ce qui reste, fut-ce dix
   pixels. Avec `anywhere` juste au-dessus, sa largeur min-content vaut UNE
   lettre, et rien ne l'arrete avant.
   `min(100%, 8rem)` et pas `8rem` : dans une colonne plus etroite que le
   plancher (le parc de la fiche a 240 px, le telephone a 320) un plancher
   ferme ferait deborder la rangee, c'est-a-dire remplacer ce defaut par le
   defaut fondateur du lot.
   `:not(:empty)` : la rangee sans note porte un `<span class="grow">` VIDE
   qui ne sert que de ressort. Planche a 8rem, ce vide pousserait le compte a
   rebours et le chevron a la ligne pour ne rien montrer. */
.filet-row .grow:not(:empty) { min-width: min(100%, 8rem); }
/* La note d'une rangee (echeance, entretien, cout) demande 16rem AVANT de
   plier : 8rem ne l'ecrase plus mais ne la lit pas non plus, « Contre-visite
   a programmer, echeance depassee » y tenait sur cinq lignes de deux mots.
   C'est la BASE qui compte, pas la croissance : seule la base entre dans le
   calcul du passage a la ligne.
   `:not(:empty)` pour la meme raison que le plancher, et mesuree : la note
   d'un entretien est TOUJOURS rendue, vide quand l'entretien n'a ni ligne ni
   kilometrage. Base 16rem, ce vide reservait une ligne entiere pour ne rien
   montrer, rangee de 42 a 66 px, chevron rejete en ligne 2. */
.filet-row .sub.grow:not(:empty) { flex: 1 1 16rem; }
/* Un `form` du bloc d'actions reste DANS la ligne : il est la parce qu'un
   `form` n'est pas du contenu de phrase et ne tient pas dans un `span`, pas
   parce qu'on veut un paragraphe. Bloc par defaut, il jetait son bouton sur
   une ligne a lui et laissait le lien precedent suspendu au-dessus de la
   rangee ; les gabarits le rattrapaient par un `style` en dur, repete. */
.filet-row .sub.end form { display: inline; }
/* Rangee d'historique d'hivernage (spec 2.4) : « Annuler » ne vaut que pour
   la derniere coche de chaque geste, et son absence sur les autres rangees
   variait leur hauteur (le bouton, avec son fond et sa bordure, est plus
   haut qu'une ligne de texte nue). Le formulaire reste dans le DOM sur
   chaque rangee : la reserve de hauteur est la meme partout, et la classe
   `invisible` retire le bouton du clavier et du lecteur d'ecran quand la
   coche n'est pas annulable. */
.filet-row .sub.end form.invisible { visibility: hidden; }
/* Un geste libre porte un libelle de longueur quelconque (spec 2.2) : sans
   plancher, il enjambait sur deux lignes quand les autres rangees
   d'historique tenaient sur une seule (16/08 vs 17/08 sur la meme fiche,
   revue de contre-verification du lot B), et la rangee grandissait seule.
   Revue de qualite du 26/09 : l'ellipse coupait le libelle au telephone et
   a l'iPad, ou `title` ne sert a rien au doigt. Le libelle s'affiche donc
   ENTIER, retour a la ligne permis ; la colonne des dates reste alignee
   (`.deadline-date`, largeur fixe, non touchee ici). */
.historique .filet-row > .deadline-kind {
  flex: 1 1 0; min-width: 0; overflow-wrap: break-word;
}
/* La mention « faite le JJ/MM/AAAA » d'une echeance faite et son chevron
   passent a la ligne ENSEMBLE. Revue du lot, fiche a 960 px : dans une
   colonne de 411 px la note (base 16rem) et la mention tenaient sur la
   ligne, le chevron tombait seul sur une troisieme ligne, au fer a gauche ;
   un compte a rebours, deux fois plus court, tenait. La mention RESERVE la
   place du chevron dans le calcul du passage a la ligne (l'ecart de 12 px
   de la rangee plus les 18 px de `.icon`), et le chevron rentre dans cette
   reserve : les deux marges s'annulent, il se dessine au meme pixel, mais la
   coupure se decide sur la mention. Scope a `.faite` : aucune rangee
   ouverte ne bouge. APRES `.filet-row .end`, plus haut. */
.filet-row .end.faite { margin-right: 30px; }
.filet-row .end.faite + .chev { margin-left: -30px; }
/* Meme reserve sur la rangee d'une sortie sans fin de ligne (ni chrono, ni
   statut) : au demi-ecran, un titre long et sa ligne de voiture et de lieu
   (base 16rem) tenaient sur la ligne, le chevron tombait seul dessous, au
   fer gauche (recette B4 des Sorties, 960 px). La ligne de voiture reserve
   la place du chevron ; si elle plie, il plie avec elle et reste au bord
   droit. Au telephone la ligne de voiture passe en fin de rangee et le
   chevron reste sur celle du titre : la reserve y est levee. Meme reserve
   sur la rangee d'une session (fiche d'une sortie, correctif B-10) : le
   libelle et « tours · conditions », avec ou sans temps, tenaient sur la
   ligne et le chevron tombait seul dessous. La reserve y est portee par
   l'enfant qui precede le chevron, ligne de tours ou temps. */
.rangee-sortie > .sub.grow:not(:empty):has(+ .chev),
.rangee-session > :has(+ .chev) { margin-right: 30px; }
.rangee-sortie > .sub.grow:not(:empty) + .chev,
.rangee-session > * + .chev { margin-left: -30px; }
/* Le retrait suspendu du telephone vaut aussi au-dela : la ligne de
   voiture repliee partait sous l'icone, a 34 px du fer de la date. */
.rangee-sortie:has(> .gravure:first-child) { padding-left: calc(var(--debord, 0px) + 22px + var(--ecart, 12px)); }
.rangee-sortie > .gravure:first-child { margin-left: calc(-22px - var(--ecart, 12px)); }
@media (max-width: 720px) {
  .rangee-sortie > .sub.grow:not(:empty):has(+ .chev),
  .rangee-session > :has(+ .chev) { margin-right: 0; }
  .rangee-sortie > .sub.grow:not(:empty) + .chev,
  .rangee-session > * + .chev { margin-left: 0; }
}
/* Rangee d'un record de l'onglet Sorties de la fiche (revue du lot D,
   DQ-2) : sa sous-ligne est une date, et la base de 16rem d'une note la
   gardait sur la ligne du nom du circuit. A 960 px le temps tombait seul
   sur une seconde ligne, et d'une rangee a l'autre les temps changeaient de
   ligne a 1 100 et 1 280 px. La date prend sa propre ligne, sous le nom, a
   toutes les largeurs ; le nom, le temps et le chevron tiennent la premiere.
   Meme poids que `.filet-row .sub.grow:not(:empty)`, plus bas : elle
   l'emporte. Les rangees de session de la fiche d'une sortie ne bougent pas. */
.rangee-record > .sub.grow:not(:empty) { order: 9; flex: 1 0 100%; margin-right: 0; }
@media (max-width: 720px) {
  .filet-row .sub { order: 9; flex: 1 0 100%; margin-left: 0; }
  /* Meme valeur, meme poids que la base : `.filet-row .sub.grow:not(:empty)`
     (0,4,0) battrait la ligne au-dessus (0,2,0) et rendrait a la note un item
     de 16rem au telephone, la ou elle doit prendre sa ligne. */
  .filet-row .sub.grow:not(:empty) { flex: 1 0 100%; }
}
/* Rangées de liste au téléphone (spec Finitions 6 bis, point 7). Mesuré le
   24/09 à 393 px sur le parc de démonstration : le chevron tombait sous le
   titre (échéances, intervenants, entretiens) et la description passait
   sous l'icône (x 18 contre 52 pour le titre). Trois gestes, pour toute
   rangée à chevron :
   - la première ligne porte l'icône, le titre, ce qui le complète
     (montant, compte à rebours) et le chevron ; le reste passe dessous,
     après un saut forcé (`::after`, ou `.saut` des échéances) ;
   - le titre prend la place libre sur une base nulle : il se replie
     lui-même et le chevron reste sur sa ligne ;
   - retrait suspendu : la rangée se retire de la largeur de l'icône et
     de l'écart, l'icône revient dans ce retrait, et chaque ligne suivante
     part au fer du titre. `--ecart` suit l'écart de la rangée (8 px dans
     la carte des échéances). Ce retrait vit sous le bloc, en règle de
     base. */
@media (max-width: 720px) {
  .filet-row:has(> .chev) > * { order: 5; }
  .filet-row:has(> .chev) > :first-child { order: 0; }
  .filet-row:has(> .chev) > :first-child + *,
  .filet-row:has(> .chev) > .titre,
  .filet-row:has(> .chev) > .deadline-kind { order: 1; }
  .filet-row:has(> .chev) > .titre { flex: 1 1 0; min-width: 0; overflow-wrap: break-word; }
  .filet-row:has(> .chev) > .deadline-kind { flex: 1 1 0; min-width: min-content; }
  /* La rangée d'une sortie ouvre sur sa date. Une période (« du 18 au
     22/04/2026 ») prend sa propre ligne : sur celle du titre, elle lui
     laissait de 1 à 63 px à 320 px, et ses mots s'y coupaient ; un
     plancher `min-content` sur le titre laissait à son tour le chevron
     seul sur sa ligne (contre-vérification du lot D, 28/09). Titre et
     chevron partagent la ligne suivante. */
  .filet-row:has(> .chev).rangee-sortie > .periode { flex: 1 0 100%; }
  /* La colonne de dates de 6,5rem aligne les rangées au PC ; au téléphone
     elle prenait 104 px pour 72 de date et poussait le chevron à la ligne. */
  .filet-row:has(> .chev) > .deadline-date { min-width: 0; }
  /* L'entretien garde sa date et son montant en tête, l'atelier passe
     après le saut : à 393 px, entre les deux, il ne lui restait que 70 px
     et son nom se coupait. */
  .filet-row:has(> .chev) > .titre + .num,
  .filet-row:has(> .chev) > .atelier + .num,
  .filet-row:has(> .chev) > .end { order: 2; margin-left: auto; }
  /* « faite le JJ/MM/AAAA » est trois fois plus long qu'un compte à
     rebours : il ouvre la ligne suivante, et la réserve du chevron
     (`.end.faite` plus haut) n'a plus d'objet. */
  .filet-row:has(> .chev) > .end.faite { order: 5; margin-left: 0; margin-right: 0; }
  .filet-row:has(> .chev) > .end.faite + .chev { margin-left: 0; }
  /* La rangée d'un prêt (rubrique de la fiche, « Hors les murs ») porte un
     titre, une sous-ligne et la mention du retour : la mention se lit après
     la sous-ligne (order 9), dans l'ordre du gabarit. À 393 px « retour
     attendu depuis le » remontait avant « Exposition · depuis le » (revue
     de qualité du lot B des prêts). La rangée d'une sortie garde son ordre.
     APRÈS la règle de `.end.faite` ci-dessus, qu'elle reprend. */
  .filet-row:has(> .chev):not(.rangee-sortie) > .titre ~ .end.faite { order: 10; }
  .filet-row:has(> .chev) > .gravure:not(:first-child),
  .filet-row:has(> .chev) > .chev { order: 3; }
  .filet-row:has(> .chev) > .sub { order: 9; }
  .filet-row:has(> .chev)::after { content: ""; order: 4; flex: 1 0 100%; height: 0; }
}
/* Le retrait suspendu des rangées à chevron vaut à toutes les largeurs
   (correctifs du 04/10, spec 4) : de 721 à 900 px la note de 16rem plie
   elle aussi, et repartait sous l'icône (inventaire des pièces à 860 px,
   x 210 contre 244 pour le titre). Sur une ligne unique, le retrait et la
   marge négative de l'icône s'annulent. Le poids (0,4,0) l'emporte sur le
   débord des rangées du tableau de bord (0,3,0, dashboard.css), d'où
   `--debord` dans le calcul. */
.filet-row:has(> .chev):has(> :is(.gravure, .monogram):first-child) {
  padding-left: calc(var(--debord, 0px) + 22px + var(--ecart, 12px));
}
.filet-row:has(> .chev) > :is(.gravure, .monogram):first-child { margin-left: calc(-22px - var(--ecart, 12px)); }
/* Au petit téléphone, la date d'une sortie d'un seul jour prend elle aussi
   sa ligne, comme la période : dans l'onglet de la fiche à 320 px, elle ne
   laissait au titre que 90 px, et « Touristenfahrten » ou
   « Unterschleißheim » s'y coupaient en milieu de mot. La règle vaut
   jusqu'au palier du petit téléphone (374 px) : mesuré le 28/09 en Chrome,
   un seuil à 355 px laissait 126 px au titre à 356 px, sous les 128 px
   d'un mot de seize lettres. */
@media (max-width: 374px) {
  .filet-row:has(> .chev).rangee-sortie > .gravure:first-child + .num { flex: 1 0 100%; }
}
/* Les rangées sans chevron des fiches (intervenant : entretiens, frais,
   observations ; lieu : documents, places, historique) prennent le même
   retrait suspendu : la description passait sous l'icône, et un nom de
   véhicule long quittait la ligne de son icône (revue de qualité du lot
   E2, x 18 contre 52 pour le titre à 320 px). L'icône est l'enfant de la
   rangée, ou celui de l'ancre qui l'ouvre (document d'un lieu). Seul un
   titre sans voisin extensible prend la place libre sur une base nulle :
   à côté d'un nom de locataire, il garde sa largeur.
   Le retrait vaut à toutes les largeurs (correctifs du 04/10) : à 768 et
   860 px la suite repliée d'un entretien repartait sous l'icône. */
.lot-main .filet-row:not(:has(> .chev)):has(> :is(.gravure, .monogram):first-child, > .row-lien:first-child > :is(.gravure, .monogram):first-child) {
  padding-left: calc(var(--debord, 0px) + 22px + var(--ecart, 12px));
}
.lot-main .filet-row:not(:has(> .chev)) > :is(.gravure, .monogram):first-child,
.lot-main .filet-row:not(:has(> .chev)) > .row-lien:first-child > :is(.gravure, .monogram):first-child { margin-left: calc(-22px - var(--ecart, 12px)); }
@media (max-width: 720px) {
  .lot-main .filet-row:not(:has(> .chev)):not(:has(> .grow:not(.sub))) > .titre { flex: 1 1 0; min-width: 0; overflow-wrap: break-word; }
}
/* Les rangées de la routine d'hivernage (spec 2.4, revue de qualité) : même
   retrait suspendu, hors fiche cette fois (`hivernage/routine.html` n'est
   pas dans `.lot-main`). L'icône restait seule à 320 px, le titre passait
   dessous, et la colonne de date n'existait au PC sur aucune des trois
   rubriques (`.num muted` sans largeur fixe). `.rangee-hivernage` porte
   cette structure, posée par `_geste_rangee.html` et l'historique : la
   portée est restreinte à cette classe, quand `.filet-row.row` seul reste
   partagé par d'autres rangées de l'app (le parc de la fiche, entre autres).
   Le titre d'un geste (`.deadline-kind.titre`) prend seul la place libre,
   contre-vérification du 26/09 (un `.grow` vide la lui disputait, titre à
   73 px et mots coupés) ; sa date peut se réduire pour lui laisser de la
   place. L'historique garde sa colonne de date en largeur fixe : son
   alignement tient par construction, et son titre sans `.titre` (le
   `.deadline-kind` seul de l'historique) prend déjà la place libre par la
   règle `.historique .filet-row > .deadline-kind` plus haut.
   Le retrait vaut à toutes les largeurs (correctifs du 04/10) : à 768 et
   860 px le libellé long d'un geste daté repartait sous l'icône.
   Le retrait et la marge négative de l'icône vont sous le même `:has()` :
   un navigateur qui l'ignore rejette les deux règles ensemble, et l'icône
   reste dans sa rangée. */
.rangee-hivernage:has(> :is(.gravure, .monogram):first-child) {
  padding-left: calc(var(--debord, 0px) + 22px + var(--ecart, 12px));
}
.rangee-hivernage:has(> :is(.gravure, .monogram):first-child) > :is(.gravure, .monogram):first-child {
  margin-left: calc(-22px - var(--ecart, 12px));
}
@media (max-width: 720px) {
  .rangee-hivernage > .deadline-date { min-width: 0; }
  .historique .rangee-hivernage > .deadline-date { min-width: 6.5rem; }
  .rangee-hivernage > .deadline-kind.titre { flex: 1 1 0; min-width: 0; overflow-wrap: break-word; }
}
/* Sous 375 px le compte à rebours d'une échéance et le montant d'un
   entretien quittent la première ligne, qu'ils auraient fait passer à la
   ligne avec le chevron : le compte à rebours rejoint la ligne du véhicule
   (ou ouvre la suivante sur la fiche), le montant précède l'atelier. */
@media (max-width: 374px) {
  .filet-row:has(> .chev) > .end,
  .filet-row:has(> .chev) > .atelier + .num { order: 5; }
  .filet-row:has(> .chev) > .atelier + .num,
  .filet-row:has(> .chev) > .end { margin-left: 0; }
  /* Le montant parti, le chevron prend sa marge automatique et reste au
     fer droit (68 px de vide avant, entretien à 320 px). */
  .filet-row:has(> .chev):has(> .atelier + .num, > .end) > .chev { margin-left: auto; }
  /* Maison, lot A : en Manrope la date d'un geste mesure 86 px (82 dans la
     police systeme) et le titre tombait a 156 px a 320 px, sous les 60 % de
     la rangee que la routine lui garde. L'ecart de la rangee se resserre. */
  .rangee-hivernage { column-gap: 8px; }
}
.deadline-date { color: var(--ink-2); min-width: 6.5rem; }
/* L'echu garde sa couleur malgre l'ordre source : la combinaison bat la
   regle de base par specificite, pas par position. */
.deadline-date.danger { color: var(--danger); }
.deadline-kind { min-width: 9rem; }
.entries { margin-top: 4px; }
.entry {
  display: flex; gap: 22px; align-items: center; padding: 18px 2px 18px 12px;
  border-bottom: 1px solid var(--hair-2); text-decoration: none; color: var(--ink);
}
.entry:hover { color: var(--ink); }
.entry:hover .entry-name { color: var(--accent-texte); }
.entry-media {
  width: 180px; height: 96px; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.entry-media img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Pictogramme agrandi plein cadre : trait fin, sinon boudin. */
.entry-media svg { width: 60%; height: 60%; stroke-width: .7; }
.entry-body { display: flex; flex-direction: column; gap: 2px; }
.entry-name { font-family: var(--serif); font-size: 1.35rem; }
.entry-figures { margin-left: auto; text-align: right; flex: none; }
.entry-figures > * { display: block; }

/* Surcharges mobiles des entrees editoriales : PLACEES APRES les classes
   qu'elles surchargent (meme specificite, l'ordre source departage) sinon
   elles resteraient inertes derriere les regles de base .entry*. */
@media (max-width: 720px) {
  .entry { flex-wrap: wrap; gap: 10px 22px; }
  .entry-media { width: 100%; height: 120px; }
  .entry-figures { margin-left: 0; text-align: left; }
}

/* Cartes-propositions de la saisie (spec section 7) : style panneau, cibles
   tactiles conservees pour le pouce sur telephone tenu d'une main. */
/* .capture-card se pose sur .panel (fond/filet/rayon) et ne surcharge que
   son padding serre et sa marge basse. */
.capture-card { padding: .2rem .8rem .6rem; margin: 0 0 1rem; }
.capture-card legend { font-size: 9.5px; text-transform: uppercase; letter-spacing: .18em; color: var(--ink-faint); }
.capture-keep { font-size: 1rem; color: var(--ink); display: inline-block; padding: .5rem .6rem .5rem 0; }
.capture-keep input[type=checkbox] { width: 1.4rem; height: 1.4rem; vertical-align: -.25rem; }

/* Encart carnet a lisere d'accent (spec section 6). Nomme par son ROLE et
   pas par sa couleur : le laiton n'est celui que du theme ivoire, un theme
   Ferrari y met du Rosso Corsa. */

/* Page de lot (spec section 7) : colonne principale + marge reglee a droite. */
/* Le cartouche (spec depenser la couleur, section 4) : la fiche s'annonce.
   Aplat plein a l'accent de la livree, ou du theme du compte pour une marque
   sans livree ; texte en on-accent, le couple que le contrat garantit
   lisible ensemble. Pas d'opacite sur le texte, la garde mesure a pleine
   opacite. Le nom est du grand texte (seuil 3:1), la sous-ligne du petit
   (seuil 4,5:1) : c'est elle qui commande la garde. */
.lot-head {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  background: var(--accent); color: var(--on-accent);
  padding: 14px 18px; margin-bottom: 10px;
}
.lot-head .vname { font-family: var(--serif); font-size: 1.6rem; color: var(--on-accent); margin: 0; }
.lot-head .vsub { font-family: var(--mono); color: var(--on-accent); font-size: .85rem; }
.lot-head .micro { color: var(--on-accent); margin-left: auto; }
/* Les outils vivent SOUS le cartouche : une action n'est pas une identite. */
.lot-tools { display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap; margin: 0 0 8px; }
.lot-tools select { width: auto; max-width: 14rem; }
.lot { display: grid; grid-template-columns: minmax(0, 2.2fr) minmax(250px, 1fr); gap: 0 30px; margin-top: 8px; }
.lot-main { min-width: 0; }
/* Relectures de facture et de contrôle (spec Finitions 6 bis, point 5) :
   le document à gauche, la saisie à droite, puis l'un sous l'autre au
   téléphone, où deux colonnes de 170 px ne laissaient lire ni l'aperçu ni
   les champs. */
.relecture { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 32px; align-items: start; }
.lot-main table { margin: .4rem 0; }
/* Les intertitres discrets de la colonne sont des blocs a eux seuls, et ce
   sont les SEULS micro-labels de la colonne a etre des `div` : les autres
   (le compte d'un titre de section, la legende de la bande photo, les libelles des
   releves, la pastille « depassee » d'une echeance) sont des `span`, items
   de rangees en flex, que `margin-top: 10px` decalait de dix pixels vers le
   bas, hors de l'alignement de leur ligne.

   Ecrite `.lot-main .micro`, la regle les attrapait tous et il fallait la
   defaire par une liste d'exceptions qu'aucun auteur ne pense a rallonger :
   « depassee » n'y figurait pas et tombait. Viser l'element ferme la
   liste. */
.lot-main div.micro { display: block; margin-top: 10px; }
/* La marge des chiffres se pose sur la page (Composants, lot C) : ni
   aplat, ni cadre. */
.lot-aside { overflow-wrap: anywhere; }
.lot-aside .fig { padding: 6px 0 10px; border-bottom: 1px solid var(--hair-in); }
.lot-aside .fig:last-child { border-bottom: 0; }
.lot-aside .fig > * { display: block; }
.lot-aside .fig .num { font-size: 1.15rem; }
/* Le changement de statut d'une sortie : le bouton touchait le selecteur
   (recette C3). */
.lot-aside form.fig button { margin-top: 8px; }
/* La planche : fond marine et grille cyan, une planche contrecollee. */
.plate { border: 1px solid var(--plate-frame); padding: 10px; margin-bottom: 6px; }
.plate img { width: 100%; display: block; }
.plate .plate-fallback { width: 100%; height: 150px; stroke: var(--plate-ink); stroke-width: .7; }
.comparable-prix { color: var(--ink); min-width: 5.5rem; }

/* Fiche recomposee (spec 02/09) : monogramme, photos, onglets, rangees
   cliquables, releves, marge en groupes. Aucun jeton nouveau. */
.lot-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.monogram {
  width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--ink-2);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--serif); font-size: 11px; color: var(--ink-2);
  background: var(--ground-2); flex: none;
}
.lot-head .monogram {
  width: 40px; height: 40px; font-size: 18px; background: transparent;
  color: var(--on-accent); border-color: color-mix(in srgb, var(--on-accent) 70%, transparent);
}
.photo-piste {
  flex: 1; display: flex; gap: 10px; overflow-x: auto;
  scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.photo-piste::-webkit-scrollbar { display: none; }
.photo-piste img {
  width: 62%; height: 300px; object-fit: cover; border-radius: var(--r);
  flex: none; scroll-snap-align: start;
}
/* Les photos d'une identification (spec Finitions 3.6) : la meme bande,
   en vignettes de hauteur fixe, a toute largeur. Deux classes, pour passer
   devant le repli du telephone de `.photo-piste img`. */
.photo-piste.lot-photos { margin-bottom: 12px; }
.photo-piste.lot-photos img { width: auto; height: 120px; }
.photo-nav { padding: .4rem .5rem; }
.tabs {
  display: flex; gap: 22px; border-bottom: 1px solid var(--hair); margin-top: 18px;
  overflow-x: auto; white-space: nowrap;
}
.tabs a {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .16em;
  text-decoration: none; color: var(--ink-2); padding: 8px 0 6px;
  border-bottom: 2px solid transparent; display: inline-flex; gap: 6px; align-items: baseline;
  /* L'onglet quitte perd l'accent a l'instant ou le nouveau le prend : le
     fondu de couleur des liens les laissait allumes ensemble (audit du 23/09). */
  transition: none;
}
.tabs a .num { font-size: .7rem; color: var(--ink-faint); }
.tabs a[aria-current] { color: var(--ink); }
/* Le fondu revient au survol seul : l'onglet vise fond vers l'accent sous
   le pointeur ; l'onglet quitte, hors du pointeur, s'eteint au clic. */
.tabs a:hover { color: var(--ink); transition: color .15s ease; }
/* Rangee cliquable : ancre entiere (.row) ou ancre dans la rangee (.row-lien).
   Le survol se dit en encre sur le papier de la rangee. Il peignait
   `--ground-2`, plus 10 px d'ombre portee de chaque cote pour ne pas bouger
   le texte : sur une carte deja `--ground-2` il ne peignait RIEN (mesure du
   10/09 : rgb(255,255,255) sur rgb(255,255,255)), et son ombre s'ajoutait au
   debord de l'aplat d'etat. Un melange sur le papier se voit sur les deux
   surfaces et ne demande aucune ombre. */
a.filet-row.row, .row-lien { text-decoration: none; color: inherit; }
/* L'ancre imbriquee de la rangee de document est une rangee elle aussi : sans
   repli, ses contenus s'ecrasent A L'INTERIEUR pendant que la rangee qui la
   porte, elle, plie proprement (titre de document a 53 px de large pour 135
   de haut, mesure du 10/09 a 1024 px). C'est son repli a elle qui les sauve,
   pas le plancher : sous cette ancre le `100%` du plancher se resout sur la
   largeur de l'ancre et non sur celle de la rangee, il vaut donc moins que
   les 8rem annonces (106 px mesures dans une colonne de 200). */
.row-lien { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; flex: 1; min-width: 0; }
/* Composants, lot C : sans panneau la rangee part du fer, et un aplat de
   survol collerait a son texte. Le survol souligne le titre de la rangee
   et le passe a l'accent lisible ; aucun fond, aucun trait lateral. Les
   rangees du tableau de bord (`.a-venir`, Maison) gardent leur survol.
   Revue du lot C : UNE regle pour toute rangee cliquable, au survol et au
   focus clavier. L'intitule est le titre de la rangee (`.titre`,
   `.deadline-kind`), l'atelier d'un entretien (`.atelier`), ou a defaut
   son texte principal (`.grow` hors sous-ligne) : la rangee d'entretien
   de la fiche et l'observation du carnet n'avaient plus aucun signal. */
a.filet-row.row:where(:not(.a-venir a)):is(:hover, :focus-visible) > :is(.titre, .deadline-kind, .atelier),
a.filet-row.row:where(:not(.a-venir a)):is(:hover, :focus-visible) > .grow:not(.sub):not(:is(.titre, .deadline-kind, .atelier) ~ .grow) {
  color: var(--accent-texte); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px;
}
/* Deux regles au meme corps et PAS une liste de selecteurs : `:has()` est la
   seule pseudo-classe du fichier qu'un navigateur puisse ne pas connaitre
   (Safari 15.4, Chrome 105, Firefox 121), et un selecteur invalide dans une
   liste invalide la regle ENTIERE. Groupees, les rangees-ancres perdraient
   leur survol chez qui ne connait pas `:has()` ; separees, seule la rangee a
   ancre imbriquee le perd, et elle garde son lien. Meme discipline que la
   note de compatibilite de color-mix plus haut. */
.row-lien:is(:hover, :focus-visible) > :is(.titre, .deadline-kind),
.row-lien:is(:hover, :focus-visible) > .grow:not(.sub):not(:is(.titre, .deadline-kind) ~ .grow) {
  color: var(--accent-texte); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px;
}
/* Le signal survit au survol : le titre d'une rangee echue ou proche garde
   la teinte de son etat sous le pointeur, souligne comme les autres. La
   regle se qualifie depuis `a.filet-row.row` et gagne par sa SPECIFICITE
   seule (0,5,1 contre 0,4,1), ou qu'elle soit ecrite. */
a.filet-row.row:is(.echu, .proche):is(:hover, :focus-visible) > .titre { color: var(--etat); }
.row .chev { stroke: var(--accent-dim); }
/* Le carnet joint TOUTES ses etiquettes de structuration dans une seule
   pastille (systeme, conditions, gravite, kilometrage) : a 393px elle
   mesurait 450px et poussait la page de 83px (recette du 02/09). Seule la
   pastille du carnet leve le `white-space: nowrap` de `.etat` : elle porte
   une phrase, pas un mot d'etat. Sans place, elle se coupe ; avec de la
   place, rien ne change. */
.obs-ligne .etat { white-space: normal; }
/* La colonne de l'atelier d'un entretien. `min-width` SEULE la laissait
   grandir sans fin : un nom de carnet de 57 caracteres (« Carrosserie
   Peinture Bellanger et Fils - Levallois-Perret ») prenait toute la rangee et
   renvoyait le montant a la ligne suivante, au ras gauche, pendant que la
   gravure de facture et le chevron restaient sur la premiere (mesure du
   18/09 : montant a x=285 au lieu de 623). La colonne des montants n'existait
   plus pour cette rangee, marqueur « bords de cellules non alignes » de la
   charte. Le mecanisme preexistait pour un `issuer` long ; le carnet le rend
   courant, un nom de fiche etant plus long et plus complet qu'un texte lu.

   Meme vocabulaire que `.grow` et `.sub` juste au-dessus : le nom PLIE dans
   sa propre colonne (`overflow-wrap: anywhere`) au lieu de pousser ses
   voisins, avec le meme plancher de lecture `min(100%, …)` qui evite de
   deborder dans une colonne plus etroite que lui. */
.atelier { flex: 1 1 11rem; min-width: min(100%, 11rem); overflow-wrap: anywhere; }
/* Le rattachement d'un entretien a son intervenant (spec intervenants 4.5) :
   une ligne discrete SOUS la rangee, jamais dedans (un <form> place dans
   l'ancre de la facture verrait ses controles captures par le lien). Elle
   plie comme les rangees, meme mecanique et meme raison qu'elles : sans
   `flex-wrap`, la liste et le bouton ecrasaient l'etiquette a une lettre par
   ligne sous 400px. Le retrait la rattache visuellement a la rangee qu'elle
   suit, et le filet de la rangee reste au-dessus des deux. */
.ligne-rattachement { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 0 0 9px 28px; border-bottom: 1px solid var(--hair-in); }
/* Le filet ferme le GROUPE, pas la rangee : une rangee suivie de sa liste
   renonce au sien, sans quoi il tombe ENTRE l'entretien et son propre
   formulaire, et rien ne separe ce formulaire de l'entretien suivant. Mesure
   du 18/09 : rangee 801-867 filetee, formulaire 867-920 sans filet, rangee
   suivante 920-986. Par proximite, chaque liste se lisait comme appartenant
   a la rangee du dessous. */
.filet-row.sans-filet { border-bottom: 0; }
.ligne-rattachement:last-child { border-bottom: 0; }
.ligne-rattachement label { color: var(--ink-2); font-size: .85rem; }
/* `width: auto` dimensionne la liste sur son option la plus longue, et la
   `max-width` heritee vaut 100% : a 1280 px, ou la colonne de contenu est
   PLUS ETROITE qu'a 900 px (l'aside), un nom d'atelier de 57 caracteres
   poussait la ligne sur deux rangs et le bouton sous l'etiquette. La borne
   laisse le navigateur tronquer proprement l'option affichee ; le menu
   deroulant, lui, rend le nom entier.

   La borne elle-meme vit dans `dashboard.css` et non ici : c'est la que
   `.dashboard-app select { max-width: 100% }` la contredit, avec une
   specificite superieure ET une position plus tardive dans l'ordre source.
   Posee ici, elle etait purement et simplement ignoree (style calcule
   `max-width: 100%`, liste mesuree a 389 px alors que la regle disait
   20rem). C'est la cinquieme fois que l'ordre source CSS mord ce projet. */
.ligne-rattachement select { width: auto; min-width: min(100%, 12rem); }
@media (max-width: 700px) { .ligne-rattachement { padding-left: 0; } }
/* Les cases d'un ecran centre (dossier de vente) : un bloc unique qui se
   centre comme un tout, ferre a gauche en dedans. Paragraphe par paragraphe,
   chaque case suivait la longueur de son libelle et aucune ne s'alignait sur
   les autres (584, 561 puis 599 px mesures au 18/09). */
.choix-dossier { display: inline-block; text-align: left; }
.releves { display: flex; gap: 8px 28px; align-items: baseline; padding: 8px 0; flex-wrap: wrap; }
.releves .num { font-size: 1.05rem; }
.lot-aside .aside-groupe + .aside-groupe { border-top: 1px solid var(--hair); margin-top: 12px; padding-top: 8px; }
.lot-aside .fig.ligne { display: flex; flex-direction: row; align-items: baseline; gap: 10px; }
.lot-aside .fig .end { margin-left: auto; }
.lot-aside .fig .sub { font-size: .85rem; }
.lot-aside .aside-pied { padding-top: 8px; }

@media (max-width: 720px) {
  .lot { grid-template-columns: 1fr; }
  .relecture { grid-template-columns: minmax(0, 1fr); }
  /* Au repli, la planche et les onglets ouvrent l'ecran ; la marge (documents,
     chiffres, parc) suit la colonne principale (plan fiche recomposee, ecart 1). */
  .lot-aside { margin-top: 22px; }
  .photo-piste img { width: 86%; height: 220px; }
  .atelier { min-width: 0; }
  /* Les neuf onglets replient sur autant de rangees que necessaire au lieu
     de defiler de cote : une barre qui defile ne dit pas qu'elle continue.
     Interligne serre (2px) et gouttiere large (18px) pour que plusieurs
     rangees se lisent comme une seule barre. La regle active sur la fiche
     vit dans dashboard.css (`.dashboard-app .tabs`, plus specifique) ;
     celle-ci reste le secours pour un `.tabs` rendu hors de `.dashboard-app`. */
  .tabs { flex-wrap: wrap; overflow: visible; white-space: normal; gap: 2px 18px; }
}

/* Planche Accueil (spec 02/09) : la voiture du jour, valeur repliee,
   filtres, vignettes. Aucun jeton nouveau : la vedette peint --plate et
   --plate-ink comme la planche de la fiche, et comme elle a les coins nets
   (pas de rayon, ni sur le bandeau ni sur son selecteur). */
.vedette {
  position: relative; height: 420px; margin-top: 22px; overflow: hidden;
  background: var(--plate); border: 1px solid var(--plate-frame);
  width: 100%;
}
/* La piste (geste tactile du 03/09) : une diapositive par vehicule, defilee
   par le NAVIGATEUR au doigt, a la molette et au pave tactile. Meme idiome
   que `.photo-piste` de la fiche, et c'est la que vit tout le repli sans
   JavaScript. Elle defile DANS elle-meme : `overscroll-behavior-x: contain`
   empeche un geste pousse en butee de se propager a la page, qui ne defile
   jamais de cote au telephone. Barre de defilement masquee : le cran et les
   deux fleches disent deja ou l'on en est. */
.vedette-piste {
  display: flex; height: 100%; overflow-x: auto;
  scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.vedette-piste::-webkit-scrollbar { display: none; }
/* Un cran vaut une voiture : la diapositive prend toute la largeur de la
   vedette, et c'est elle qui porte le reperage du voile et de la silhouette,
   position: relative de son cote. */
.vedette-diapo {
  position: relative; flex: none; width: 100%; height: 100%;
  scroll-snap-align: start;
}
.vedette-image { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
.vedette-image.contain { object-fit: contain; padding: 30px 60px 90px; box-sizing: border-box; }
.vedette-fallback { position: absolute; inset: 0; margin: auto; width: 40%; height: 40%; stroke: var(--plate-ink); stroke-width: .6; }
/* Le voile ne pose plus d'aplat sur la photo (Vincent, 03/09 : « le degrade
   cache la voiture, il faut que ce soit quasi transparent »). Il ne fait
   plus qu'assombrir le bas de l'image d'un souffle, et c'est le HALO porte
   par le texte qui tient la lisibilite : text-shadow en --plate, la couleur
   que le contrat de themes garantit sous --plate-ink. Un halo suit les
   lettres, la ou un aplat couvrait la voiture. Le text-shadow s'herite, donc
   les boutons et le monogramme en profitent sans regle de plus. */
.vedette-voile {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 22px 28px;
  min-height: 40%; box-sizing: border-box;
  background: linear-gradient(to top, color-mix(in srgb, var(--plate) 58%, transparent) 0%, color-mix(in srgb, var(--plate) 26%, transparent) 55%, transparent 100%);
  color: var(--plate-ink); text-shadow: 0 0 2px var(--plate), 0 0 4px var(--plate), 0 0 9px var(--plate), 0 0 18px var(--plate);
  display: flex; align-items: flex-end; gap: 22px;
}
.vedette-voile .micro, .vedette-voile .num { color: var(--plate-ink); }
.vedette-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Le nom EST le lien vers la fiche : il n'y a plus de bouton a cote. */
.vedette-nom { font-family: var(--serif); font-size: 1.7rem; color: inherit; text-decoration: none; }
.vedette-nom:hover { color: var(--plate-ink); text-decoration: underline; text-underline-offset: 4px; }
.vedette-sub { font-size: .85rem; }
/* Coins nets jusqu'aux boutons : la regle de base `.btn` apporte un rayon.
   UN SEUL bloc pour ces boutons : il y en avait deux, et le second posait un
   `display: inline-flex` qui pesait (0,2,0), autant que `button[hidden],
   .btn[hidden] { display: none }`, mais arrivait APRES dans la source. Les
   deux fleches servies cachees s'affichaient donc sans JavaScript, sans rien
   faire (mesure au navigateur du 03/09 : `display: flex`, boite de 34 px).
   Le `display` etait de surcroit redondant, `button, .btn` le pose deja.
   Deux blocs pour un meme selecteur, c'est aussi une garde qui certifie le
   premier pendant que la cascade applique le second : voir `_regle` de
   test_charte.py. */
.vedette-nav .btn {
  color: var(--plate-ink); border-color: color-mix(in srgb, var(--plate-ink) 50%, transparent);
  border-radius: 0; padding: .35rem .45rem;
}
.vedette-nav .btn:hover { background: color-mix(in srgb, var(--plate-ink) 12%, transparent); }
.vedette-nav {
  position: absolute; top: 16px; right: 16px; display: flex; gap: 8px; align-items: center;
  background: color-mix(in srgb, var(--plate) 85%, transparent); padding: 6px 10px;
}
.vedette-nav .icon { stroke: var(--plate-ink); }
.vedette-nav .num { color: var(--plate-ink); font-size: .8rem; }
/* La valeur du parc derriere un details : le summary lit comme les autres
   micro-labels, sans marqueur natif. SANS `.synthese ` devant : depuis la
   maquette Respirer (spec 3) le details vit sur la liste du parc et PORTE
   lui-meme la classe `synthese` pour l'aplat de la bande. Un element n'etant
   pas son propre descendant, `.synthese details.valeur` n'aurait plus rien
   vise : ni marqueur eteint, ni chevron. */
details.valeur summary { cursor: pointer; display: inline-flex; gap: 6px; align-items: center; }
/* Meme langue que le repli d'acquisition de la fiche : un chevron qui tourne,
   et non un mot qui disparaissait a l'ouverture en laissant le resume sans
   aucune commande visible pour refermer. Taille reduite : la synthese est une
   bande dense de micro-labels, le chevron de 18 px y pesait deux fois la
   hauteur du texte. */
details.valeur summary .chev { width: 13px; height: 13px; stroke: var(--accent-dim); transition: transform .15s; }
details.valeur[open] summary .chev { transform: rotate(90deg); }
/* Une BANDE sous le titre, pas une ligne de releves : le details prend la
   largeur (block bat le `display: flex` de `.synthese`, par specificite) et
   respire entre le h1 et la premiere entree. Ses trois releves sont ranges
   par le `.releves` du gabarit, PAS par un flex pose ici : Chrome enveloppe
   le contenu d'un details dans une boite anonyme (::details-content) qui ne
   fait qu'UN article, et les trois releves y coulaient colles, le montant
   de l'un contre le libelle du suivant (4 px mesures au lieu de 28). */
details.valeur.synthese { display: block; margin: 8px 0 14px; }
/* Le repli ordinaire, celui d'un historique : resume en voix micro, marqueur
   natif eteint, chevron qui tourne. Meme geste que le repli de la valeur du
   parc, sa propre classe : `valeur` porte EN PLUS la bande de la synthese
   (`details.valeur.synthese` juste au-dessus), et les deux selecteurs ne
   sont pas groupes parce que les gardes de charte lisent un selecteur AU
   FER ; dans un groupe, la regle ne serait plus mesuree du tout. */
details.repli summary { cursor: pointer; display: inline-flex; gap: 6px; align-items: center; }
details.repli summary .chev { width: 13px; height: 13px; stroke: var(--accent-dim); transition: transform .15s; }
details.repli[open] summary .chev { transform: rotate(90deg); }
/* Trois releves au fer (maquette Respirer, spec 3) : plus rien a repartir
   entre les deux bords. */
.synthese.trois { justify-content: flex-start; gap: 8px 44px; }
/* QUATRE releves (fiche d'un lieu) : `space-between` en jetait un SEUL sur une
   deuxieme rangee. Mesure du 12/09 a 1920 px : la colonne offre 871 px utiles,
   les quatre releves et leurs trois espaces en demandent 923, et « Reste a
   votre charge » tombait donc seul sous les trois autres, aligne a gauche.
   Une grille a colonnes EGALES : deux par deux au repli, quatre de front des
   que la colonne a la place, et jamais d'orphelin quelle que soit la longueur
   des montants. Pas `repeat(auto-fit, minmax(...))` : la colonne de la fiche
   n'est pas monotone en largeur de fenetre (l'aside apparait, la page est
   bornee a 1240 px), et entre les deux paliers l'auto-fit rendait TROIS
   pistes, donc de nouveau un orphelin. */
.synthese.quatre {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  justify-content: start; gap: 8px 20px;
}
/* Le seul palier ou la colonne depasse 800 px utiles (mesure du 12/09 : 784 px
   a 1600, 871 a 1920 et au-dela). En dessous, quatre colonnes tomberaient sous
   180 px et le libelle le plus long passerait sur trois lignes. */
@media (min-width: 1600px) {
  .synthese.quatre { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.filtres { display: flex; gap: 8px; align-items: center; padding: 12px 0 6px; flex-wrap: wrap; }
.filtres .micro { margin-right: 6px; }
.filtres .tri { margin-left: auto; }
/* Le separateur d'une rangee n'existe qu'au repli (rangee d'une recette). */
.saut { display: none; }
.vignettes { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; padding-top: 14px; }
/* Les deux constats d'un pret (etat au depart, etat au retour) : cote a cote
   quand la colonne porte deux demi-mesures lisibles, l'un sous l'autre en
   dessous. A 768 px, cote a cote, chaque carte de photo tombait a 100 px et
   sa legende coulait sur quatre lignes (capture du 05/10). Dans une
   demi-colonne, deux cartes par rangee : la regle porte `.dashboard-app`
   pour peser plus que la grille a trois cartes de dashboard.css. */
.constats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 32px; align-items: start; }
.dashboard-app .constats .vignettes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 1280px) {
  .constats { grid-template-columns: minmax(0, 1fr); }
  .dashboard-app .constats .vignettes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .dashboard-app .constats .vignettes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Un pret sans serie de retour : le constat du depart est seul dans la
   grille, que le gabarit marque `seul`. A deux colonnes, la troisieme photo
   tombait seule a cote d'une demi-colonne vide (capture a 1 440 px, revue de
   qualite du lot B). Une colonne, trois cartes par rangee, comme sous
   1 280 px. Bornee par `min-width` : plus lourde que les deux replis
   au-dessus, la regle des vignettes leur prendrait le telephone. */
@media (min-width: 1281px) {
  .constats.seul { grid-template-columns: minmax(0, 1fr); }
  .dashboard-app .constats.seul .vignettes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.vignette { text-decoration: none; color: var(--ink); display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.vignette .plate { padding: 6px; margin: 0; display: flex; align-items: center; justify-content: center; aspect-ratio: 2 / 1; }
.vignette .plate img { max-height: 100%; object-fit: contain; }
.vignette .plate .plate-fallback { height: 60%; }
.vignette-nom { display: flex; align-items: center; gap: 8px; min-width: 0; }
.vignette-nom .nom { font-family: var(--serif); font-size: 1.05rem; overflow-wrap: anywhere; }
.vignette:hover .nom { color: var(--accent-texte); }
.vignette-spec { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
@media (max-width: 720px) {
  .vedette { height: 240px; }
  /* Recette iPhone du 02/09 : a 240 px le micro-label se posait sur la photo
     et la ligne mono passait sur trois lignes. Le voile monte a 70 %, opaque
     sur 60 % ; la prochaine echeance (deja dans la synthese et la liste)
     se masque. */
  .vedette-voile { padding: 14px 16px; gap: 12px; min-height: 70%; background: linear-gradient(to top, color-mix(in srgb, var(--plate) 62%, transparent) 0%, color-mix(in srgb, var(--plate) 28%, transparent) 50%, transparent 100%); }
  .vedette-echeance { display: none; }
  .vedette-nom { font-size: 1.2rem; }
  .vedette-sub { font-size: .75rem; }
  .synthese.trois { gap: 6px 24px; }
  .filtres .tri { margin-left: 0; flex: 1 0 100%; }
  /* Les puces de la tete de resultats (famille active, tailles) sont des
     cibles du doigt : 40 px de haut au telephone. */
  .resultats-tete .chip { min-height: 40px; }
  /* Recette iPhone du 02/09 : « Le parc » se cassait en « Le / parc », ses
     trois liens .compte prenaient la place du titre. Depuis la rubrique
     centree (spec titres de section du 05/09) le compte quitte la ligne au
     repli, pour TOUS les comptes : en bloc sous le titre, centre comme lui,
     entre les deux filets. Un compte long chevauchait un titre centre a
     390 px, et un compte court qui resterait a droite ferait lire la
     rubrique autrement d'une section a l'autre. */
  .section-title .compte { position: static; transform: none; display: block; margin-top: 2px; }
  .vignettes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
  /* Recette iPhone du 21/09, rubrique « Recettes » de l'onglet Couts : une
     nature (« Indemnite d'assurance ») est trois fois plus longue qu'un type
     de cout (« Stockage »). Avec le montant elle ne laissait plus la place du
     bouton en ligne 1, et « Supprimer » tombait seul sur une ligne a lui,
     entre la nature et sa note. La rangee se coupe donc apres le montant
     (le saut, comme la rangee d'echeance ci-dessus), la note rend sa pleine
     ligne et le geste se range a sa droite : la facture de la rangee d'un
     cout au demi-ecran. APRES `.saut`, `.filet-row .sub` et
     `.filet-row .sub.grow:not(:empty)` du premier repli, qu'elles battent.
     Scope a `.recette` : aucune rangee de cout ne bouge. */
  .filet-row.recette .saut { display: block; flex: 1 0 100%; height: 0; order: 4; }
  .filet-row.recette .sub.grow:not(:empty) { flex: 1 1 0; }
  .filet-row.recette .end { order: 10; }
}

.encart-ia {
  display: grid; grid-template-columns: 22px 1fr; gap: 6px 12px; padding: 12px 14px;
  background: var(--ground-2); border: 1px solid var(--frame); border-radius: var(--r); margin-top: 4px;
}
.encart-ia .icon { stroke: var(--accent); }
.encart-ia h3 { font-size: 1.05rem; margin: 0; }
.encart-ia > .micro, .encart-ia > p { grid-column: 2; }
.encart-ia p { margin: 0; }

/* Visionneuse : un tiroir fixe a droite, plein ecran au repli. Le corps est
   teinte a l'encre par transparence (pas de gris en dur). */
.visionneuse {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(620px, 100%);
  background: var(--ground-2); border-left: 1px solid var(--hair);
  display: flex; flex-direction: column; z-index: 20;
}
.visionneuse[hidden] { display: none; }
.visionneuse-tete { display: flex; align-items: center; gap: 12px; padding: 14px 20px; border-bottom: 1px solid var(--hair-2); }
.visionneuse-titre { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.visionneuse-titre .v-nom { font-family: var(--serif); font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn-icone { padding: .45rem .6rem; border: 1px solid var(--hair-2); border-radius: var(--r); display: inline-flex; background: transparent; color: var(--ink-2); text-decoration: none; font-size: .72rem; }
.btn-icone:hover { border-color: var(--accent-dim); color: var(--accent); }
/* L'icone de recherche des oreilles n'existe qu'au repli du telephone (voir
   la media query) : au bureau elle reste cachee, script ou pas. */
.recherche-icone { display: none; }
.visionneuse-corps { flex: 1; min-height: 0; background: color-mix(in srgb, var(--ink) 6%, var(--ground)); padding: 22px; display: flex; }
.visionneuse-corps iframe { flex: 1; border: 1px solid var(--hair-2); background: var(--ground-2); }
.visionneuse-corps iframe[hidden] { display: none; }
.visionneuse-pied { display: flex; align-items: center; gap: 10px; padding: 12px 20px; border-top: 1px solid var(--hair-2); flex-wrap: wrap; }
/* Le verrou se pose sur la RACINE et pas sur body : au repli,
   `html { overflow-x: clip }` fait que l'overflow de body ne se propage plus
   au viewport, et la page defilait sous le tiroir (recette du 02/09). */
html.visionneuse-ouverte, html.visionneuse-ouverte body { overflow: hidden; }
/* Chips : capitales espacees sur filet, coins nets comme la pastille d'etat. */
.chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .12em; color: var(--ink-2);
  border: 1px solid var(--hair-2); border-radius: 2px; padding: 4px 11px; text-decoration: none;
}
.chip.on, .chip:hover { color: var(--accent-texte); border-color: var(--accent-dim); }
/* Nature et conditions d'une sortie en pastilles (spec du 29/09, 2.5) :
   des radios, lisibles au clavier et sans script. Le groupe porte son nom
   par fieldset et legend, sans le cadre du navigateur, comme le groupe des
   cases ; la legend prend la voix du libelle de formulaire. La radio est
   masquee a l'oeil et reste focalisable : sa pastille prend l'allure de
   `.chip.on` quand elle est cochee, et l'anneau quand elle a le focus.
   `.pastilles label.chip` (0,2,1) bat `.dashboard-app label` (0,1,1), qui
   poserait sinon la taille d'un libelle sur la pastille. */
fieldset.pastilles { border: 0; padding: 0; margin: .6rem 0; min-width: 0; }
fieldset.pastilles > legend { display: block; padding: 0; font-size: .85rem; color: var(--ink-2); }
.pastilles-choix { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.pastilles label.chip { position: relative; font-size: .72rem; color: var(--ink-2); cursor: pointer; margin: 0; }
.pastilles label.chip > input[type=radio] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  border: 0; overflow: hidden; clip-path: inset(50%); opacity: 0;
}
/* Deux regles au meme corps et PAS une liste : `:has()` y ferait tomber le
   survol chez qui ne le connait pas (voir la note de `.filet-row:has`). */
.pastilles label.chip:hover { color: var(--accent-texte); border-color: var(--accent-dim); }
.pastilles label.chip:has(input:checked) { color: var(--accent-texte); border-color: var(--accent-dim); }
.pastilles label.chip:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
/* L'ajout rapide au carnet sous l'organisateur et le transporteur d'une
   sortie (spec du 29/09, 2.3) : le lien sans script et le bouton que
   sorties.js montre, puis le bloc replie, retire sous son champ. Le bloc ne
   pose pas de `display` : son `hidden` reste celui du navigateur. */
.ajout-rapide-gestes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: -.3rem; }
.ajout-rapide { border-left: 1px solid var(--hair-2); padding-left: 14px; margin: .6rem 0; }
.ajout-rapide-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
/* Les raccourcis du formulaire d'une sortie (spec du 29/09, 2.2 et 2.4),
   poses par sorties.js entre le libelle et son champ : des boutons a
   l'allure de `.chip`. `.dashboard-app button` (0,1,1, dashboard.css, lu
   apres) poserait sinon sa taille, son fond, ses 43 px et sa casse :
   `.dashboard-app button.chip` (0,2,1) l'emporte, `button.chip` sert hors
   de l'application. Presse, le bouton prend l'allure de `.chip.on`. */
.raccourcis { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 8px; }
button.chip, .dashboard-app button.chip {
  font: inherit; font-family: var(--sans); font-size: .72rem; text-transform: uppercase; letter-spacing: .12em;
  background: none; color: var(--ink-2); border: 1px solid var(--hair-2); border-radius: 2px;
  padding: 4px 11px; min-height: 0; cursor: pointer;
}
button.chip[aria-pressed="true"], button.chip:hover:enabled,
.dashboard-app button.chip[aria-pressed="true"], .dashboard-app button.chip:hover:enabled {
  color: var(--accent-texte); border-color: var(--accent-dim);
}
button.chip:disabled, .dashboard-app button.chip:disabled { color: var(--ink-faint); cursor: default; }
button.chip:focus-visible, .dashboard-app button.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Le lien d'un pilote (« épouse ») en petit sous le prenom. */
button.chip:has(> .micro), .dashboard-app button.chip:has(> .micro) { flex-direction: column; gap: 2px; }
/* Au doigt, sous 720 px, les pastilles et raccourcis offrent 44 px de haut
   (doctrine de `.btn-tactile`, dashboard.css). */
@media (max-width: 720px) {
  button.chip, .dashboard-app button.chip, .pastilles label.chip { min-height: 44px; }
}
/* La ligne sous un champ : le releve de reference du compteur (2.1), la
   reprise d'une sortie recurrente (2.7). */
.ligne-champ { display: block; margin-top: 4px; font-size: .78rem; }
.ligne-champ[hidden] { display: none; }
/* Cases a cocher nombreuses (marques d'un intervenant) : une grille lue de
   gauche a droite, et non une colonne centree dont chaque case tombe a une
   abscisse differente selon la longueur de son libelle (recette du 16/09). */
div.cases { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 4px 16px; text-align: left; }
/* Un libelle long (le nom d'une voiture, recette des pieces du 26/09) passe
   a la ligne au fer de son texte : en ligne, sa seconde ligne revenait sous
   la case, au bord de la colonne. En rangee flexible, le texte forme son
   propre bloc (l'espace qui suit la case tombe en debut de ligne) ; la case
   se centre sur la premiere ligne de ce bloc. L'espace tombee, l'ecart
   case-libelle la retrouve par `column-gap` : une espace de la police du
   libelle, l'ecart d'avant la rangee flexible (revue de qualite du lot B
   des pieces). En Manrope l'espace chasse 0,207 em, soit 9 px avec la marge
   de la case (0,26 em et 9,8 px dans la police systeme). */
div.cases label { display: flex; align-items: flex-start; column-gap: .207em; }
div.cases label > input { margin-top: calc((1lh - 18px) / 2); }
/* Le groupe porte son nom par fieldset et legend, et non par un libelle
   flottant : sans lui un lecteur d'ecran annonce « Lancia, case a cocher, 3
   sur 30 » sans jamais dire ce que ces cases choisissent. Le cadre du
   navigateur part, comme sur les themes de Preferences : c'est le signal
   « site pas fini » que la charte combat. La legend prend la voix du
   libelle de formulaire, pour que le groupe s'aligne sur les champs qui
   l'entourent, et le meme rythme vertical que `form p`. */
fieldset.cases-groupe { border: 0; padding: 0; margin: .6rem 0; min-width: 0; }
fieldset.cases-groupe > legend { display: block; padding: 0; font-size: .85rem; color: var(--ink-2); }
/* Premières échéances : chaque groupe est une proposition entière, que sa
   légende annonce à la graisse des petits titres de groupe, à l'encre
   pleine, au-dessus de ses libellés. Une
   règle encore vide (aucune date d'appui) reste en place pour le script
   sans creuser la marge d'un paragraphe. */
form.premieres-echeances fieldset.cases-groupe > legend { font-weight: 600; color: var(--ink); }
form.premieres-echeances .micro:empty { margin: 0; }
/* La date passe sous sa case et son libellé : l'écart des champs d'appui,
   que leur libellé tient par sa marge basse. Porté par la date seule : un
   `row-gap` s'ajoutait aussi au-dessus de la relecture en toutes lettres,
   plus loin de sa date ici que sous les champs d'appui (recette du 01/10). */
form.premieres-echeances p.echeance-proposee > input[type="date"] { margin-top: 8px; }
/* Montage d'une piece (spec 2.4) : l'entretien prerempli se replie quand
   la case « Ajouter au carnet d'entretien » est decochee, par la feuille
   seule, sans script ; montage.js le desactive alors et le formulaire
   ecarte ses champs. Le cadre du navigateur part, comme sur le groupe des
   cases. */
fieldset.montage-entretien { border: 0; padding: 0; margin: 0; min-width: 0; }
form:has(input[name="ajouter_au_carnet"]:not(:checked)) fieldset.montage-entretien { display: none; }
/* Fin de la rangee d'un montage (recette B6, 26/09) : quantite, montant et
   « Annuler » passent a la ligne d'un seul tenant, au bord droit. Separes,
   le bouton restait seul sur sa ligne, a gauche, entre la voiture et la
   quantite au telephone (`.filet-row .sub` y passe en fin de rangee) et
   sous la date au demi-ecran. Meme fin pour la rangee d'un frais de
   sortie : montant et « Retirer ». */
.rangee-montage .montage-fin,
.rangee-frais .frais-fin,
.rangee-piece .piece-fin { display: inline-flex; align-items: center; gap: 12px; }
/* Le compte de la rubrique Frais porte la rangee « + ajouter : » d'une
   nature par lien (spec du 29/09, 2.10) : trop long pour une ligne, il passe
   a la ligne au fer droit, et le total qui le clot garde le bord droit de la
   rubrique, celui des montants des rangees. Seul sur sa ligne au
   telephone, il y reste a droite. */
#frais .section-title .compte { text-align: right; margin-left: auto; }

/* Rubrique « Traces » de la fiche d'une sortie (spec du 29/09, 3.3) : la fin
   de rangee tient « Telecharger » et « Retirer » d'un seul tenant, comme
   celle d'un frais ou d'une piece ; le report des kilometres et le depot
   suivent les rangees, leurs commandes alignees. */
.rangee-trace .trace-fin { display: inline-flex; align-items: center; gap: 12px; }
.report-traces,
.depot-traces { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 12px; }

/* La carte d'une sortie et celle du journal (spec du 29/09, 3.1, 3.4 et
   3.5), dessinees par static/carte.js sur Leaflet. La boite tient la largeur
   de la colonne et 60 % de l'ecran au plus. Servie `hidden`, elle ne se
   montre que sous le script. `leaflet.css` est lue APRES cette feuille : les
   reprises passent par `.carte` en tete (0,2,x) pour battre ses regles a une
   classe. Sans script ni lieu, la rubrique de la fiche n'a rien a dire ; sans
   script, le repli « Carte » du journal non plus. */
.carte { height: min(420px, 60vh); border: 1px solid var(--hair-2); margin: 4px 0 8px; }
.carte[hidden] { display: none; }
/* Deux regles et PAS une liste, pour la meme raison : sans `:has()` le
   repli du journal reste retire, seule la carte vide d'une sortie se montre. */
html:not(.js) #carte-journal { display: none; }
html:not(.js) #carte:not(:has(.compte)) { display: none; }
.carte.leaflet-container { background: var(--ground-2); font-family: var(--sans); font-size: .8rem; }
.carte.carte-pose { cursor: crosshair; }
/* La mire de l'epingle armee, au centre : Entree la pose la (carte.js). */
.carte .carte-mire {
  display: none; position: absolute; left: 50%; top: 50%; z-index: 450;
  width: 22px; height: 22px; margin: -11px 0 0 -11px; pointer-events: none;
  background: linear-gradient(var(--accent), var(--accent)) center / 2px 100% no-repeat,
              linear-gradient(var(--accent), var(--accent)) center / 100% 2px no-repeat;
  filter: drop-shadow(0 0 1px var(--ground-2));
}
.carte.carte-pose .carte-mire { display: block; }
/* Les tuiles teintees vers le papier ; les themes sombres les renversent.
   Un theme se dit sombre par son schema dans garage/themes.py et
   themes.css ne pose aucun autre attribut : un selecteur par theme sombre,
   la liste gardee par test_vues_carte. */
.carte .leaflet-tile-pane { filter: grayscale(.55) sepia(.18) contrast(.95); }
[data-theme="nuit-atelier"] .carte .leaflet-tile-pane,
[data-theme="weissach"] .carte .leaflet-tile-pane { filter: invert(.92) hue-rotate(180deg) grayscale(.5) brightness(.9) contrast(.9); }
/* Attribution, zoom et bulles au papier et a l'encre du theme : lisibles
   dans les deux schemas, hors du filtre des tuiles. `.leaflet-container`
   et `.leaflet-touch` y posent des regles a deux classes : la reprise en
   prend trois. */
.carte.leaflet-container .leaflet-control-attribution { background: var(--ground-2); color: var(--ink-2); padding: 1px 6px; }
.carte .leaflet-control-attribution a { color: var(--accent-texte); }
.carte .leaflet-bar a { background: var(--ground-2); color: var(--ink); border-bottom-color: var(--hair-2); }
.carte .leaflet-bar a:hover,
.carte .leaflet-bar a:focus { background: var(--ground); color: var(--accent-texte); }
.carte .leaflet-bar a.leaflet-disabled { background: var(--ground); color: var(--ink-faint); }
.carte.leaflet-container .leaflet-bar { border: 1px solid var(--hair); box-shadow: none; }
.carte .leaflet-popup-content-wrapper,
.carte .leaflet-popup-tip { background: var(--ground-2); color: var(--ink); box-shadow: 0 1px 4px rgba(0, 0, 0, .25); }
.carte .leaflet-popup-content { margin: 10px 14px; }
.carte .leaflet-tooltip { background: var(--ground-2); color: var(--ink); border-color: var(--hair-2); box-shadow: 0 1px 3px rgba(0, 0, 0, .25); }
.carte .leaflet-tooltip-top::before { border-top-color: var(--ground-2); }
.carte .leaflet-tooltip-bottom::before { border-bottom-color: var(--ground-2); }
.carte .leaflet-tooltip-left::before { border-left-color: var(--ground-2); }
.carte .leaflet-tooltip-right::before { border-right-color: var(--ground-2); }
.carte .bulle { display: flex; flex-direction: column; gap: 2px; }
.carte .bulle a { color: var(--accent-texte); font-weight: 600; }
/* L'epingle : une goutte a l'accent, pointe en bas sur le lieu. */
.carte .epingle span {
  display: block; width: 16px; height: 16px; box-sizing: border-box;
  background: var(--accent); border: 2px solid var(--ground-2);
  border-radius: 50% 50% 50% 0; transform: rotate(-45deg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
/* Le depart d'une trace en cercle, son arrivee en disque, a sa couleur. */
.carte .trace-borne span {
  display: block; width: 10px; height: 10px; box-sizing: border-box; border-radius: 50%;
  border: 2px solid var(--teinte); background: var(--ground-2);
}
.carte .trace-arrivee span { background: var(--teinte); }
.carte-gestes { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.carte-gestes[hidden] { display: none; }
.carte-gestes button[hidden],
.carte-erreur[hidden] { display: none; }
#carte-journal .carte { margin-top: 10px; }

/* Ecran Analyses (maquette Respirer, spec 6) : trois bandeaux d'etat, des
   rangees a filet, plus de table. Les rangees reprennent `.filet-row` et ses
   replis ; ne vit ici que ce qui est propre a cet ecran. */
/* Le type au fer, pour que les libelles s'alignent d'une rangee a l'autre :
   sans borne, chaque ligne commencait son titre a un endroit different. */
.analyses .filet-row .kind { min-width: 8.5rem; }
.analyses .filet-row .titre { font-family: var(--serif); font-size: 1.02rem; }
/* Une classee est close : son titre passe en encre secondaire, et ce qui
   attend encore une main garde l'encre pleine. */
.analyses .filet-row.classee .titre { color: var(--ink-2); }
.analyses .en-cours { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
/* La place du chevron sur une rangee a filet qui n'en a pas (Analyses :
   ligne en cours, identification classee ; suivi de l'inscription d'une
   sortie : ligne de statut) : meme largeur que `.icon`, et la gouttiere de
   la rangee fait le reste. Sans elle, le verdict ou le compte d'une rangee
   sans ancre se posait 30 px a droite de celui de sa voisine, dans la meme
   colonne et sous les memes yeux. Un element prend la gouttiere tout seul ;
   une marge calculee de 30 px deriverait au premier changement d'ecart. */
.filet-row > .chev-vide { width: 18px; flex: none; }
/* Tourniquet du geste « Analyse en cours ». Declarations propres et non une
   regle groupee avec le spinner des formulaires : leur mise en page differe
   (l'un est en ligne dans un bouton, l'autre est un article flex) et fondre
   les deux redeclarerait un selecteur du premier niveau. La CADENCE, elle,
   est la meme a dessein : l'application n'a qu'un rythme d'attente.
   L'attente d'une identification (lot A-bis des retours testeur, C6.1)
   prend le meme tourniquet ; sa phrase porte une duree et peut aller a la
   ligne, d'ou sa propre mise en page, sans le `nowrap` de la rangee. */
.identifications .en-cours { display: flex; align-items: center; gap: 8px; }
.analyses .en-cours::before,
.identifications .en-cours::before {
  content: ""; width: .7rem; height: .7rem; flex: none;
  border: 2px solid var(--ink-faint); border-top-color: transparent; border-radius: 50%;
  animation: busy-spin .8s linear infinite;
}
/* Trois lignes par rangee au telephone (spec 6) : gravure, type et geste sur
   la premiere, le titre sur la deuxieme, la sous-ligne sur la troisieme. Son
   propre bloc, POSE APRES les regles de base ci-dessus : media query et
   regle de base pesent le meme poids (0,3,0), seul l'ordre source les
   departage, et loge dans un bloc de repli anterieur le `min-width: 0`
   aurait perdu contre les 8,5 rem du bureau. La sous-ligne n'est pas
   reprise : `.filet-row .sub` lui donne deja order 9 et la pleine largeur au
   repli, pour toutes les rangees a filet de l'application. */
@media (max-width: 720px) {
  .analyses .filet-row .kind { min-width: 0; flex: 1; order: 1; }
  .analyses .filet-row .end, .analyses .filet-row .chev, .analyses .filet-row .chev-vide { order: 2; }
  .analyses .filet-row .titre { flex: 1 0 100%; order: 3; }
  /* Le compte de la rangee d'Acces, apres l'objet : sans rang il se
     posait sur la premiere ligne, entre la date et le type, et `.kind`
     (flex: 1, min-width: 0) se faisait ecraser par lui. La recette du
     07/09 a photographie « PHOTOS » rendu sur deux lignes des que le
     compte disait « Conciergerie pour demo ». L'ecran Analyses n'a pas
     cette cellule : la declaration ne lui coute rien. */
  .analyses .filet-row .compte { order: 4; }
}

/* Pagination partagée (spec Finitions 6 bis, point 10) : deux boutons de la
   charte autour de la position, au fer de la liste. */
.pagination { display: flex; align-items: center; gap: 12px; padding: 16px 0 4px; }
.pagination .num { color: var(--ink-2); }
/* Le geste absent garde sa place, éteint : ni survol ni clic. */
.pagination .btn[aria-disabled="true"] { color: var(--ink-faint); border-color: var(--hair-2); pointer-events: none; }

/* Page de rapport « Analyse IA » (spec planches, section 4). Elle vit sur la
   mesure courte, qui centre ses ecrans (formulaires) : .rapport reprend le
   fer a gauche pour tout le rapport, plutot qu'une regle sur `h1` qui
   decentrerait aussi la saisie et la connexion. */
.rapport { text-align: left; }
.rapport-tete { display: flex; align-items: center; gap: 10px; }
.rapport-tete .icon { stroke: var(--accent); }
.rapport-tete .end { margin-left: auto; }
.rapport-titre { text-align: left; max-width: 30ch; margin-top: 10px; font-size: 1.6rem; }
.rapport-info { display: flex; gap: 22px; margin-top: 8px; align-items: baseline; flex-wrap: wrap; }
.rapport-description { max-width: 70ch; margin: 12px 0 0; }
.rapport-releves { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 16px; padding: 12px 0 4px; }
.releve { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.releve .num { font-size: 1.25rem; }
.releve .serif { font-family: var(--serif); font-size: 1.05rem; }
.releve .sous { font-size: .85rem; }
/* Le filet d'une rangée est la bordure de ses cellules : la gouttière est
   un retrait intérieur, un écart de colonne le trouait (spec Finitions
   6 bis, point 9). */
.rapport-postes { display: grid; grid-template-columns: 2fr 1fr 2.2fr; gap: 0; padding-top: 6px; }
.rapport-postes > * { padding: 9px 18px 9px 0; border-bottom: 1px solid var(--hair-in); min-width: 0; overflow-wrap: anywhere; }
.rapport-postes > .tete { padding-top: 6px; padding-bottom: 6px; border-bottom: 1px solid var(--hair); }
.rapport-postes > :nth-child(3n) { padding-right: 0; }
.rapport-suite { margin-top: 4px; }
.rapport-suite summary { cursor: pointer; font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: var(--accent-texte); padding: 8px 0; }
/* Le <summary> reste VISIBLE une fois ouvert : c'etait le seul controle du
   repli, le masquer rendait l'ouverture irreversible et sortait le controle
   de l'ordre de tabulation. Le libelle alterne, sans JavaScript. */
.rapport-suite .suite-fermer { display: none; }
.rapport-suite[open] .suite-ouvrir { display: none; }
.rapport-suite[open] .suite-fermer { display: inline; }
.rapport-reserves { margin: 8px 0 0; padding-left: 18px; max-width: 70ch; display: flex; flex-direction: column; gap: 4px; }
.rapport-sources { display: flex; gap: 8px; flex-wrap: wrap; padding-top: 10px; }
.rapport-actions { display: flex; gap: 10px; margin-top: 26px; align-items: center; }
.rapport-actions form { margin: 0; }

@media (max-width: 720px) {
  /* Deux colonnes : la note passe sous le poste, sur toute la largeur, et
     l'en-tete de sa colonne disparait (elle ne titre plus rien).
     Par le RANG et jamais par :last-child / :last-of-type : la grille est
     plate et tous ses enfants sont des <span>, « le dernier » y designe la
     derniere cellule du tableau. Recette iPhone du 02/09 : c'etait la note
     du dernier poste qui disparaissait, et la grille entiere se decalait
     d'une cellule. */
  .rapport-postes { grid-template-columns: 1fr 1fr; }
  .rapport-postes > .tete:nth-child(3) { display: none; }
  .rapport-postes > .muted:nth-child(3n) { grid-column: 1 / -1; padding-top: 0; }
  /* Un seul filet par poste, sous sa description : posé aussi sous le
     poste et sa fourchette, il les séparait de leur propre description et
     un poste se lisait comme deux rangées (revue de qualité du lot E2). */
  .rapport-postes > :is(:nth-child(3n+1), :nth-child(3n+2)):not(.tete) { border-bottom: 0; }
  .rapport-info { gap: 6px 14px; }
  /* La date du rapport prend sa propre ligne : a 393 px les deux moities de
     l'en-tete se cassaient chacune en plein milieu (recette iPhone). */
  .rapport-tete { flex-wrap: wrap; }
  /* Le libelle prend le reste de la ligne 1 et se coupe dans sa boite :
     sans flex-basis 0 il ne tient pas a cote de l'icone et passe en bloc,
     laissant le sparkle seul sur sa ligne. */
  .rapport-tete .micro { flex: 1 1 0; min-width: 0; }
  .rapport-tete .end { margin-left: 0; flex: 1 0 100%; }
}

/* Banniere d'installation (spec planches 5.1) : proposer l'ajout a l'ecran
   d'accueil la ou le geste existe. Deux conditions, toutes deux de la
   feuille de style et non du script : au repli seulement (au bureau
   « ajouter a l'ecran d'accueil » ne veut rien dire), et jamais une fois
   l'application installee (display-mode: standalone). */
.installer { display: none; }
@media (max-width: 720px) {
  /* Une seule ligne (maquette Respirer, spec 2.3) : icone, titre avec le
     geste dessous, boutons a droite. text-align au fer a gauche :
     `.content.court` CENTRE son texte, et la banniere est incluse par la
     saisie (courte) comme par le tableau de bord (large). Sans cette ligne,
     le meme composant se lit centre sur un ecran et au fer sur l'autre.
     Meme raison que sur `.entree`. */
  .installer { display: flex; align-items: center; gap: 10px; text-align: left; background: var(--ground-2); border: 1px solid var(--frame); border-radius: var(--r); padding: 7px 10px; margin-bottom: 10px; }
  .installer[hidden] { display: none; }
  .installer img { width: 28px; height: 28px; border-radius: 6px; flex: none; }
  /* Le texte prend la mesure entre l'icone et les boutons : c'est lui qui
     pousse les boutons a droite, plus aucune marge automatique. */
  .installer-texte { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
  .installer-titre { font-family: var(--serif); font-size: .9rem; }
  /* Le geste vit DANS la banniere, sous le titre. Il ne se REPLIE pas :
     l'icone et le texte sont deux articles flex, et avec `wrap` le texte
     (article anonyme) passait a la ligne ENTIER a 320 px, laissant l'icone
     seule sur la sienne (89 px de banniere contre 56 a 375, revue de
     qualite). L'icone s'aligne en haut, le texte se casse DANS sa boite et
     sa deuxieme ligne pend en retrait sous la premiere ; rien ne deborde.
     Le script masque le geste sur Android, ou le bouton « Ajouter » le
     remplace : une regle d'auteur bat le `[hidden]` de la feuille du
     navigateur, d'ou la redite (meme raison que `.installer[hidden]`). */
  .installer .geste { display: flex; align-items: flex-start; gap: 4px; font-size: .72rem; color: var(--ink-faint); flex-wrap: nowrap; }
  .installer .geste[hidden] { display: none; }
  /* 2 px du haut : l'icone s'aligne sur la premiere ligne du texte, et
     non sur le bord de la boite ou elle flotterait au-dessus. */
  .installer .geste .icon { width: 12px; height: 12px; margin-top: 2px; }
  /* Le libelle du bouton ne se casse PAS : en capitales espacees,
     « AJOU / TER » sur deux lignes se lit comme un ecran pas fini. C'est au
     titre serif de passer a la ligne s'il le faut (recette iPhone). */
  .installer .btn { padding: .5rem .7rem; white-space: nowrap; }
  /* La croix n'a pas de cadre : dans une banniere deja encadree, un second
     filet autour d'un trait se lit comme un bouton de plus. Elle se vise au
     pouce : 36 px de cible, mesures pour tenir sans grandir la banniere. */
  .installer .btn-icone { padding: .3rem .4rem; border-color: transparent; min-width: 36px; min-height: 36px; justify-content: center; }
}
@media (display-mode: standalone) {
  .installer { display: none; }
}

/* Quatre entrees de saisie (spec planches 5.2) : tuiles sur aplat neutre,
   deux colonnes au telephone, quatre au bureau. La mesure est celle de
   l'ecran court, portee par .content.court : la grille ne repose pas de
   plafond a elle. Servie cachee, revelee par static/saisie.js. */
.entrees { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-top: 16px; margin-bottom: 24px; }
.entrees[hidden] { display: none; }
/* Les trois chemins de l'ecran « Ajouter un vehicule » (retours testeur du
   01/10) : trois colonnes au bureau, une pile au telephone, ou deux colonnes
   laisseraient la troisieme tuile seule sur sa rangee. */
.entrees.trois { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 720px) { .entrees.trois { grid-template-columns: 1fr; } }
.entree {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px; text-align: left;
  /* 96px logeaient une icone, un titre sur deux lignes ET un sous-libelle,
     et rendaient un tiers de vide quand les sous-libelles sont partis
     (03/09). Ils sont revenus (spec Respirer, 5) et 76px restent : la tuile
     grandit d'elle-meme sous son texte, et les tuiles d'une meme RANGEE se
     mettent a la hauteur de la plus haute (quatre au bureau, deux au repli,
     ou les deux rangees peuvent donc differer). Une hauteur figee plus haut
     ne servirait qu'aux ecrans ou tous les sous-libelles tiennent en une
     ligne.
     Le filet est celui des boutons de l'espace connecte (`--hair-2`) : la
     tuile-lien et la tuile-bouton portaient deux jetons, egaux dans le
     Studio clair seulement (Maison, lot A). */
  border: 1px solid var(--hair-2); border-radius: var(--r); padding: 14px 12px; min-height: 76px;
  background: var(--ground-2); color: var(--ink); text-transform: none; letter-spacing: normal;
  font-size: 1rem; line-height: 1.35;
}
.entree:hover, .entree.on { border-color: var(--accent-dim); }
.entree .icon { width: 22px; height: 22px; stroke: var(--accent); }
.entree-nom { font-family: var(--serif); }
.entree.on .entree-nom { color: var(--accent-texte); }
/* Le sous-libelle dit ce que l'analyse fait de l'entree : voix secondaire,
   sous le titre et sans capitales (`.entree` pose deja text-transform:
   none, rien ne le rallume ici). */
.entree .entree-sous { font-size: .8rem; color: var(--ink-faint); line-height: 1.3; }
/* La rangee de cloture de l'ecran Ajouter (Finitions 3.4) : le libelle en
   .micro, puis les trois facons de creer un vehicule en boutons secondaires
   alignes sur trois colonnes egales, libelles insecables. Au telephone le
   retrait se resserre pour que « Par carte grise » tienne dans son tiers ;
   sous 360 px les trois boutons s'empilent sur toute la largeur. */
/* `.ajouter-rangee` : les rangees de cloture, « Un nouveau vehicule » et,
   dessous, « Un sinistre » (retours testeur du 01/10, B4), sur la meme
   grille. Le bouton seul de la seconde tient le premier tiers, au droit de
   « Par photo ». */
.ajouter-rangee { margin-top: 24px; }
.ajouter-rangee .form-actions { margin-top: 8px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ajouter-rangee .form-actions .btn { justify-content: center; white-space: nowrap; }
@media (max-width: 720px) { .ajouter-rangee .form-actions .btn { padding-inline: 8px; } }
@media (max-width: 359px) { .ajouter-rangee .form-actions { grid-template-columns: 1fr; } }
/* Les tuiles prennent la place du bouton natif : l'etat (nom du fichier)
   reste seul sous la grille et le champ garde sa place au clavier. Le
   bouton habille REPARAIT des qu'il prend le focus : le champ etant masque
   par clip-path, c'est lui qui porte l'anneau, et sans ce retour le clavier
   naviguerait a l'aveugle sur un selecteur invisible. */
.fichier-tuiles { border-color: transparent; padding-inline: 0; }
/* Meme masquage pour l'etiquette du champ (« Deposez une photo ou un
   document ») : avec les tuiles, elle redit ce qu'elles font et pointe un
   bouton masque. Elle sort de l'OEIL, jamais du DOM ni de l'arbre
   d'accessibilite : c'est elle qui nomme le champ, et sans JavaScript la
   classe n'existe pas, l'etiquette reste la seule voie vers le selecteur. */
/* La ligne entiere aussi, tant qu'elle n'a rien a montrer : sans fichier
   choisi ni focus, elle tenait 26 px vides entre le champ et « Analyser ».
   Le nom d'un fichier choisi et le bouton rendu au focus la font revenir. */
.fichier-tuiles .fichier-bouton,
.champ-tuiles > label {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  border: 0; overflow: hidden; clip-path: inset(50%);
}
/* Sa regle a elle et PAS un membre de la liste ci-dessus : sans `:has()` le
   champ reste visible sous les tuiles, et le bouton natif reste masque. */
.champ-tuiles:has(.fichier-etat:empty):not(:focus-within) {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  border: 0; overflow: hidden; clip-path: inset(50%);
}
.fichier-tuiles input[type=file]:focus-visible + .fichier-bouton {
  position: static; width: auto; height: auto; padding: .4rem .8rem; margin: 0;
  border: 1px solid var(--accent-dim); overflow: visible; clip-path: none;
}
@media (max-width: 720px) {
  .entrees { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Le bandeau de la visionneuse porte le titre et cinq controles sur une
     rangee. Sur une facture ou un controle technique, « Analyser » s'y ajoute
     et il ne reste plus rien pour le titre : `min-width: 0` le laissait
     tomber a UN pixel de large et la sous-ligne descendait en colonne d'une
     lettre par ligne (mesure a 393px : titre 1px, sous-ligne 371px de haut,
     contre 117px et 43px sur une carte grise, qui n'a pas ce bouton). Le
     titre prend sa ligne, les controles passent dessous. Au bureau le tiroir
     fait 620px et la rangee tient : rien ne change. */
  .visionneuse-tete { flex-wrap: wrap; }
  .visionneuse-titre { flex-basis: 100%; }
}

/* Fonds « traces de circuits » (spec 2026-09-05). Un pseudo-element fixe
   sous tout le contenu porte un trace de circuit en `mask-image` peint a
   l'encre du theme : le meme PNG (alpha seul, couleur inerte) prend la
   teinte de chaque theme, ivoire comme nuit. `isolation: isolate` sur body
   (regle body en tete de fichier) ouvre le contexte d'empilement qui place
   le z-index negatif ENTRE le fond de body et son contenu ; sans lui le
   calque passerait sous le fond et disparaitrait. Le calque n'occupe PAS
   tout l'ecran : `inset: 15%` (pourcentage du viewport, bloc conteneur du
   fixe, exact sur iOS la ou vh/vw comptent le grand viewport) et le masque
   tient dedans en `contain`, c'est ce qui garantit qu'un trace ne touche
   jamais un bord (exigence Vincent, 05/09), quel que soit le format. Fixe
   et borne, il n'entre dans aucun calcul de largeur : le contrat « zero
   defilement horizontal » tient. Les `url()` sont posees sur le calque
   lui-meme, pas dans une variable : une url dans une propriete
   personnalisee se resout par rapport a la feuille sur les navigateurs
   recents seulement, et le stockage manifest n'a rien a deviner. Les deux
   prefixes -webkit- restent necessaires a Safari iOS. Positions a deux
   mots-cles ou quatre valeurs, jamais trois : `center bottom -2vw` est
   invalide et renvoie le masque en haut a gauche (vu en maquette). */
body::before {
  content: ""; position: fixed; inset: 15%; z-index: -1; pointer-events: none;
  background-color: var(--ink); opacity: .08;
  -webkit-mask-image: url("img/circuits/nordschleife-2.41730bb595a6.png"); mask-image: url("img/circuits/nordschleife-2.41730bb595a6.png");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: right bottom; mask-position: right bottom;
}
/* Couverture de connexion : trace centre sous le formulaire, un cran plus present. */
body:has(> main > .cover)::before {
  opacity: .1; -webkit-mask-position: center bottom; mask-position: center bottom;
}
/* Telephone : la boite a 15 % fait 273 px de large a 390 ; un trace plus
   carre (Monaco, ratio 1,8) y garde 154 px de haut la ou Spa (2,9) tombait
   a 94 px et ne se devinait plus (revue de conformite du 05/09). */
@media (max-width: 720px) {
  body::before {
    -webkit-mask-image: url("img/circuits/monaco-1.96f2c588f0bc.png"); mask-image: url("img/circuits/monaco-1.96f2c588f0bc.png");
    -webkit-mask-position: center bottom; mask-position: center bottom;
  }
  body:has(> main > .cover)::before {
    -webkit-mask-image: url("img/circuits/nordschleife-3.f831c6e4c9e8.png"); mask-image: url("img/circuits/nordschleife-3.f831c6e4c9e8.png");
  }
}
/* Preferences (06/09) : un circuit choisi, ou servi par une rotation, se
   pose partout, bureau comme telephone, par l'attribut que base.html ecrit
   depuis garage/circuits.py. L'attribut (0,1,1) bat le repli telephone
   (0,0,1) quel que soit l'ordre source ; la couverture de connexion n'a pas
   de compte, elle garde ses traces. Une regle par fichier, jamais d'url
   hors de la feuille : le stockage manifest ne reecrit que celle-ci. */
body[data-circuit="nordschleife-2"]::before { -webkit-mask-image: url("img/circuits/nordschleife-2.41730bb595a6.png"); mask-image: url("img/circuits/nordschleife-2.41730bb595a6.png"); }
body[data-circuit="monaco-1"]::before { -webkit-mask-image: url("img/circuits/monaco-1.96f2c588f0bc.png"); mask-image: url("img/circuits/monaco-1.96f2c588f0bc.png"); }
body[data-circuit="spa-1"]::before { -webkit-mask-image: url("img/circuits/spa-1.c3118a32f6fa.png"); mask-image: url("img/circuits/spa-1.c3118a32f6fa.png"); }
body[data-circuit="spa-2"]::before { -webkit-mask-image: url("img/circuits/spa-2.56d5b7334c75.png"); mask-image: url("img/circuits/spa-2.56d5b7334c75.png"); }
body[data-circuit="lemans-1"]::before { -webkit-mask-image: url("img/circuits/lemans-1.49ef93152846.png"); mask-image: url("img/circuits/lemans-1.49ef93152846.png"); }
body[data-circuit="reims-1"]::before { -webkit-mask-image: url("img/circuits/reims-1.5a484fa564de.png"); mask-image: url("img/circuits/reims-1.5a484fa564de.png"); }
body[data-circuit="nordschleife-1"]::before { -webkit-mask-image: url("img/circuits/nordschleife-1.9b70f8ef2e08.png"); mask-image: url("img/circuits/nordschleife-1.9b70f8ef2e08.png"); }
body[data-circuit="nordschleife-3"]::before { -webkit-mask-image: url("img/circuits/nordschleife-3.f831c6e4c9e8.png"); mask-image: url("img/circuits/nordschleife-3.f831c6e4c9e8.png"); }
/* Preferences : le compte a coupe les traces, base.html marque le corps. */
body[data-traces="non"]::before { display: none; }
/* Impression : un pseudo-element fixe se repete sur chaque page (Chrome) et
   un masque s'imprime mal selon les moteurs ; le dossier de vente PDF est
   bati par ReportLab et n'est pas concerne. */
@media print {
  body::before { display: none; }
}
/* Le dossier d'un prêt s'imprime sans ses gestes (revue de qualité du lot B
   des prêts) : la barre « Modifier », « Corriger le retour », « Annuler le
   retour », « Supprimer », les liens de titre « + ajouter une photo » et
   « + rattacher une pièce », les « Retirer » (formulaires) et les gestes du
   revenu dans la marge ; sans revenu, le groupe ne porte que son geste
   (`geste-seul`) et part entier, filet compris. `main` porte `dossier-pret` ; aucun autre écran ne
   bouge. Le préfixe `.dashboard-app` donne le poids qui bat les `display`
   d'interface.css et de dashboard.css, chargées après cette feuille ; dans
   celle-ci, la règle vient APRÈS toutes celles qui affichent ces éléments
   (`.form-actions`, `.section-title .compte` et ses replis, `.lot-tools`). */
@media print {
  .dashboard-app .dossier-pret .lot-tools,
  .dashboard-app .dossier-pret .section-title .compte,
  .dashboard-app .dossier-pret form,
  .dashboard-app .dossier-pret .lot-aside .form-actions,
  .dashboard-app .dossier-pret .lot-aside .aside-groupe.geste-seul { display: none; }
}

.plaque-pied { margin-top: 22px; }
.plaque-legal { text-align: left; }
.plaque-legal h2 { font-size: 1rem; margin: 22px 0 4px; }

/* Pages d'erreur (10/10/2026) : adresse introuvable, accès refusé, session
   expirée. Un écran court dans l'habit commun : le titre, enfant direct du
   contenu, prend la forme d'un titre de formulaire dans l'espace connecté ;
   ces règles le composent aussi sans session, où le corps ne porte pas
   l'espace connecté. */
.erreur-titre { margin: 48px 0 16px; font-family: var(--apparat); font-variant-numeric: lining-nums; font-size: 29px; font-weight: 500; line-height: 1.15; letter-spacing: 0; }
.erreur-titre em { color: var(--accent-texte); font-weight: 400; }
.erreur-phrase { margin: 0 0 12px; color: var(--ink-2); }
a.erreur-lien { display: inline-flex; align-items: center; min-height: 44px; color: var(--accent-texte); font-size: 12px; font-weight: 600; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
a.erreur-lien:hover { color: var(--ink); text-decoration-thickness: 2px; }
