/* LE HAUT DE LA FENETRE EST OCCUPE : la topbar est collante. Sans cette
   marge de defilement, tout scrollIntoView (le navigateur au Tab, Playwright
   au clic) posait sa cible SOUS la topbar, qui interceptait alors les clics
   sans fin : les specs mobiles l'ont montre le 27.08, un bouton « visible et
   stable » que rien ne pouvait presser. Declare ici une fois, il vaut pour
   tout le cockpit. */
html {
  scroll-padding-top: calc(var(--topbar-h) + var(--space-3));
}

/* =========================================================================
   SWISS LINGUA - SUPPLÉMENT DE LA CONSOLE D'ADMINISTRATION
   Ce fichier porte ce qui n'existe QUE dans le cockpit admin, pour que
   public/design-system.css reste rigoureusement identique (md5) dans les
   quatre cockpits : c'est la seule garantie qu'une correction visuelle profite
   partout. Charge apres le design system, dans src/entry-server.tsx.
   ========================================================================= */

/* =========================================================================
   RECHERCHE GLOBALE (palette Cmd+K / Ctrl+K)
   Declencheur topbar (.cmdk-trigger) + dialogue plein ecran (.cmdk-overlay,
   porte hors du flux via <Portal> pour ne jamais etre coupe par un ancetre en
   overflow). Cible tactile 44px conservee sous 768px (libelle + raccourci
   masques, seule l'icone reste).
   ========================================================================= */
.cmdk-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  min-width: 44px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast)
    var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.cmdk-trigger:hover {
  background: var(--greige);
  border-color: var(--navy);
  color: var(--navy);
}
.cmdk-trigger:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.cmdk-trigger svg {
  width: 20px;
  height: 20px;
  flex: none;
}
.cmdk-trigger__label {
  font-size: var(--text-sm);
  white-space: nowrap;
}
.cmdk-trigger__kbd {
  display: inline-flex;
  gap: 2px;
  margin-left: var(--space-1);
}
.cmdk-trigger__kbd kbd {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  line-height: 1;
  padding: 3px 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--greige);
  color: var(--muted);
}
/* 1180 px et non 768 : le declencheur de recherche pese 185 pixels dans une
   barre ou le titre de la page tombait a zero des 1100 (releve du 11.08). Le
   raccourci clavier continue de fonctionner, et l'icone garde son nom
   accessible : c'est un libelle qui cesse d'etre peint, pas une commande qui
   disparait. */
@media (max-width: 1180px) {
  .cmdk-trigger {
    width: 44px;
    padding: 0;
    justify-content: center;
  }
  .cmdk-trigger__label,
  .cmdk-trigger__kbd {
    display: none;
  }
}

.cmdk-overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh var(--space-4) var(--space-6);
  background: rgba(20, 20, 15, 0.5);
}
.cmdk-panel {
  width: min(640px, 100%);
  max-height: min(70vh, 560px);
  background: var(--surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}
.cmdk-panel__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line);
  flex: none;
}
.cmdk-panel__icon {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--muted);
}
.cmdk-input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  color: var(--ink);
  background: transparent;
}
.cmdk-input::placeholder {
  color: var(--muted);
}
.cmdk-input:focus {
  outline: none;
  box-shadow: none;
}
.cmdk-panel__close {
  flex: none;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.cmdk-panel__close:hover {
  background: var(--greige);
  color: var(--navy);
}
.cmdk-panel__close:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.cmdk-panel__close svg {
  width: 18px;
  height: 18px;
}

.cmdk-results {
  overflow-y: auto;
  padding: var(--space-3);
  min-height: 0;
}
.cmdk-status {
  padding: var(--space-2) var(--space-3);
}
.cmdk-group + .cmdk-group {
  margin-top: var(--space-3);
}
.cmdk-group__label {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--muted);
  font-weight: 600;
}
.cmdk-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 0;
}
.cmdk-item:hover,
.cmdk-item:focus-visible {
  background: var(--salmon-soft);
  outline: none;
}
.cmdk-item:hover .cmdk-item__icon,
.cmdk-item:focus-visible .cmdk-item__icon {
  color: var(--coral-700);
}
.cmdk-item__icon {
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--muted);
}
.cmdk-item__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}
.cmdk-item__title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cmdk-item__subtitle {
  font-size: var(--text-xs);
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmdk-panel__foot {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--line);
  background: var(--greige);
  flex: none;
}
.cmdk-hint {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--muted);
}
.cmdk-hint kbd {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
}
@media (max-width: 600px) {
  .cmdk-overlay {
    padding: var(--space-4) var(--space-3);
  }
  .cmdk-panel__foot {
    display: none;
  }
}

/* Accueil des réglages : les capacités existantes sont regroupées par fonction.
   Le lien documentaire reste volontairement plus discret que la configuration
   des offres pour distinguer contenu éditorial et paramètres persistés. */
.settings-page,
.settings-detail-page,
.settings-group {
  display: grid;
  min-width: 0;
}
.settings-page,
.settings-detail-page {
  gap: var(--space-4);
}
.settings-page__header {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
}
.settings-group {
  gap: var(--space-3);
}
.settings-group__title {
  margin: 0;
  color: var(--navy);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.settings-destinations {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: var(--space-3);
  min-width: 0;
}
.settings-destinations--commercial {
  grid-template-columns: minmax(0, 2fr) minmax(min(100%, 15rem), 1fr);
  align-items: stretch;
}
.settings-destinations > .settings-destination {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-auto-flow: row;
  align-content: start;
  align-items: start;
  justify-items: start;
  gap: var(--space-2);
  min-width: 0;
  min-height: 13rem;
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-out), transform var(--dur-fast)
    var(--ease-out);
}
.settings-destinations > .settings-destination > * {
  min-width: 0;
  max-width: 100%;
}
.settings-page .settings-destinations > .settings-destination {
  grid-template-rows: auto auto auto minmax(0, 1fr) auto;
}
.settings-destination:hover {
  border-color: var(--navy);
  transform: translateY(-2px);
}
.settings-destination:focus-visible,
.settings-secondary-link:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.settings-destination__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  color: var(--navy);
}
.settings-destination__title {
  margin: 0;
  color: var(--navy);
  font-family: var(--font-serif);
  font-size: var(--text-h3);
  line-height: 1.15;
}
.settings-destination__description {
  width: 100%;
  margin: 0;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.settings-destination__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-block-start: auto;
  padding-block-start: var(--space-3);
  color: var(--navy);
  font-weight: 700;
  justify-self: start;
}
.settings-default-notice {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  color: var(--ink-soft);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.settings-offer-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: end;
  gap: var(--space-4);
  min-width: 0;
}
.settings-offer-grid > *,
.settings-offer-grid .field,
.settings-offer-grid .input {
  width: 100%;
  min-width: 0;
}
.settings-offer-grid > .field {
  margin: 0;
}
.settings-offer-grid > .field > label,
.settings-field-label {
  min-height: 2rem;
  align-items: center;
}
.settings-offer-grid > .field > label {
  display: flex;
}
.settings-field-label > label {
  min-width: 0;
}
.settings-language-picker {
  min-width: 0;
  margin: var(--space-4) 0 var(--space-3);
  padding: 0;
  border: 0;
}
.settings-language-picker > legend {
  padding: 0;
}
.settings-language-picker__choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-start: var(--space-2);
}
.settings-language-choice {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  cursor: pointer;
}
.settings-language-choice:has(input:checked) {
  border-color: var(--navy);
  background: var(--surface-alt);
}
.settings-language-choice:has(input:focus-visible) {
  box-shadow: var(--shadow-focus);
}
.settings-document-sections {
  display: grid;
  gap: var(--space-3);
  min-width: 0;
}
.settings-document-section {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  overflow: clip;
}
.settings-document-section[open] {
  border-color: var(--line-strong);
}
.settings-document-section__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-3) var(--space-4);
  color: var(--navy);
  font-weight: 700;
  cursor: pointer;
}
.settings-document-section__summary::after {
  content: "";
  flex: 0 0 auto;
  width: 0.55rem;
  height: 0.55rem;
  border-inline-end: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--dur-fast) var(--ease-out);
}
.settings-document-section[open] > .settings-document-section__summary::after {
  transform: rotate(225deg);
}
.settings-document-section__summary > span:first-child {
  min-width: 0;
  overflow-wrap: anywhere;
}
.settings-document-section__summary:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.settings-document-section__body {
  min-width: 0;
  padding: 0 var(--space-4) var(--space-4);
}
.settings-document-section__body .input {
  width: 100%;
  min-width: 0;
  resize: vertical;
}
.settings-secondary-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-alt);
  color: var(--navy);
  text-decoration: none;
}
.settings-secondary-link:hover {
  border-color: var(--navy);
}
.settings-secondary-link span,
.settings-secondary-link small {
  display: block;
  min-width: 0;
}
.settings-secondary-link small {
  margin-block-start: var(--space-1);
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: 400;
}
.settings-legacy > .muted {
  max-width: 46rem;
}
.settings-identity {
  display: grid;
  gap: var(--space-4);
  min-width: 0;
  margin-block-end: var(--space-5);
  padding-block-end: var(--space-4);
  border-block-end: 1px solid var(--line);
}
.settings-identity__title {
  margin: 0;
  color: var(--navy);
}
.settings-identity-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  min-width: 0;
}
.settings-identity-grid > * {
  min-width: 0;
}
.settings-identity-sites {
  display: grid;
  gap: var(--space-3);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.settings-identity-sites > legend {
  padding: 0;
}
.settings-identity-site {
  display: grid;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.settings-identity-site__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
}
.settings-identity-site__legend {
  color: var(--navy);
  font-weight: 700;
}
.settings-identity-site__fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  min-width: 0;
}
.settings-identity-site__lines {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
}
.settings-identity-line {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  min-width: 0;
}
.settings-identity-line > .field {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.settings-identity-preview {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.settings-identity-preview__body {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--space-4);
  row-gap: var(--space-6);
  min-width: 0;
}
.settings-identity-preview__brand img {
  display: block;
  width: min(100%, 150px);
  height: auto;
}
.settings-identity-preview__issuer,
.settings-identity-preview__baseline {
  display: block;
  margin: var(--space-1) 0 0;
  color: var(--navy);
  font-size: var(--text-sm);
  font-weight: 700;
}
.settings-identity-preview__contact-name {
  font-weight: 700;
}
.settings-identity-preview__contact p,
.settings-identity-preview__site p,
.settings-identity-preview__recipient p {
  margin: 0 0 var(--space-1);
  color: var(--navy);
  font-size: var(--text-sm);
}
.settings-identity-preview__sites {
  display: grid;
  grid-column: 2 / 4;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  min-width: 0;
}
.settings-identity-preview__site {
  text-align: center;
}
.settings-identity-preview__recipient {
  grid-column: 3;
  color: var(--muted);
  font-style: italic;
}
.settings-identity-preview__site-title {
  margin: 0 0 var(--space-1);
  color: var(--navy);
  font-weight: 700;
  font-size: var(--text-sm);
}
@media (max-width: 720px) {
  .settings-identity-grid,
  .settings-identity-site__fields,
  .settings-identity-preview__body,
  .settings-identity-preview__sites {
    grid-template-columns: minmax(0, 1fr);
  }
  .settings-identity-preview__sites,
  .settings-identity-preview__recipient {
    grid-column: auto;
  }
}
@media (max-width: 1199px) {
  .settings-offer-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 720px) {
  .settings-destinations--commercial {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 600px) {
  .settings-page,
  .settings-detail-page {
    gap: var(--space-3);
  }
  .settings-page__header,
  .settings-destination,
  .settings-legacy {
    padding: var(--space-4);
  }
  .settings-destination {
    min-height: 0;
  }
  .settings-offer-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* =========================================================================
   NAVIGATION EN PARITE BUREAU (tranche NAV0)
   Separateur discret dans la sidebar navy : detache les entrees sans
   equivalent legacy (CRM, Leads, Suivi, Comptes, Audit, Notifications,
   Prets) de la fin de leur groupe, et introduit les entrees seules du menu
   legacy (Flux email). Aligne sur le padding horizontal des .nav-item.
   ========================================================================= */
.sidebar__sep {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  margin: var(--space-2) var(--space-3);
}

/* Grille de la fiche 360 : le contenu prend tout ce qui reste, le fil de vie
   garde une largeur BORNEE.

   Le reglage precedent donnait au fil une FRACTION (1 part sur 2,8), ce qui
   produit les deux defauts a la fois. Sur un ecran de 1280, la colonne de
   contenu tombait a 590 pixels, et les onglets qui la recoupaient en deux y
   posaient des cartes de 288 pixels : mesure du banc, le 11.08, sur l'onglet
   commercial de l'entreprise. Sur un ecran de 1920, le meme reglage donnait au
   fil pres de 580 pixels pour afficher des titres courts, largeur prise au
   contenu sans que personne en profite.

   Une largeur bornee corrige les deux : le fil ne descend jamais sous 20rem
   (il reste lisible) et ne depasse jamais 24rem (il ne prend plus ce dont il
   n'a pas l'usage). Tout le reste est au contenu.

   L'empilement se declenche sous 1400 pixels de fenetre, et non plus sous 900.
   La raison est mesuree, pas esthetique : a 1280, une fois la barre laterale
   du bureau deduite, la fiche ne dispose que de 950 pixels. Les partager
   laisserait 600 pixels au contenu, soit exactement le defaut qu'on corrige.
   En dessous du seuil, le fil passe donc sous l'onglet courant et le contenu
   occupe toute la largeur. */
.fiche-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr) clamp(20rem, 24vw, 24rem);
  align-items: start;
}
@media (max-width: 1400px) {
  .fiche-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* =========================================================================
   LISTE-TABLEAU DU BUREAU (etape 3 de la refonte, releve du 11.08)
   =========================================================================
   CE QUE CE BLOC CORRIGE. Le bureau liste des milliers de lignes sur une
   quarantaine d'ecrans, et son tableau etait regle comme une page de lecture :
   ligne de 78 pixels, en-tete de carte de 279, soit 419 pixels preleves avant
   la premiere donnee. Six lignes entieres pour 116 clients a 1440 x 900, huit
   sur 240 factures, une seule sur le planning. Un tableau de gestion se juge a
   ce qu'il montre d'un coup d'oeil : la mesure est le sujet, pas le style.

   POURQUOI ICI ET NON DANS LA FEUILLE PARTAGEE. `public/design-system.css`
   vit en cinq copies identiques sous contrat d'empreinte, et sert aussi les
   espaces de LECTURE (apprenant, entreprise), ou une ligne de 44 pixels n'a
   aucun sens : on y consulte cinq factures, pas deux cent quarante. La densite
   est une propriete du poste de travail, pas du composant.

   L'UNITE EST 44 PIXELS, et elle est choisie, pas heritee : c'est la cible
   tactile minimale que le depot s'impose deja (feuille de presence, tiroir de
   navigation), donc la ligne reste cliquable au doigt sans regle a part. La
   hauteur est portee par `height` et non par une marge interieure : une
   cellule qui recoit une pastille, un bouton ou deux elements cote a cote
   garde alors la meme hauteur que sa voisine, la ou une marge s'ajoutait a la
   hauteur du contenu et donnait les 171 pixels de ligne releves sur les
   salles. */
/* TOUT CE BLOC NE VAUT QU'AU-DESSUS DE 768 PIXELS.
   En dessous, la liste cesse d'etre un tableau : elle devient une suite de
   fiches, ou une valeur doit pouvoir s'enrouler sur deux lignes et ou la
   hauteur n'est plus la ressource rare. Laisser `nowrap` et une hauteur imposee
   y descendre faisait deborder les cartes hors de l'ecran : mesure du banc, le
   12.08, sur quatre ecrans du bureau au format telephone. */
@media (min-width: 769px) {
  .table tbody td {
    height: 44px;
    padding: 0 var(--space-4);
    /* UNE SEULE LIGNE DE TEXTE. C'est ce qui tient la hauteur : sans cette
       regle, une adresse longue s'enroule et la ligne double, quelle que soit sa
       hauteur declaree. Ce que la cellule ne peut pas montrer, elle le tronque ;
       la valeur entiere reste dans l'infobulle native et dans la fiche. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Une borne par defaut, pour qu'une colonne sans largeur declaree ne prenne
       pas a elle seule la largeur du tableau. Les colonnes qui ont besoin d'une
       autre borne la declarent (`.col--large`, `.col--etroite`). */
    max-width: 20rem;
  }
  .table thead th {
    /* 8 pixels de marge verticale, et pas moins : la garde de la grille horaire
       exige au minimum cette valeur sur les en-tetes du planning, qui partagent
       cette feuille. L'en-tete tombe ainsi de 44 a 29 pixels sans entrer en
       conflit avec un controle deja pose. */
    /* 8 pixels de marge verticale, et pas moins : la garde de la grille horaire
       (defauts-design.spec.ts) mesure les en-tetes du premier tableau de
       /planning, qui porte cette classe, et exige au minimum cette valeur.
       L'en-tete tombe donc de 44 a 33 pixels, et non a 25 : quatre pixels
       achetes au prix d'une garde affaiblie ne valent pas leur prix. */
    padding: var(--space-2) var(--space-4);
    white-space: nowrap;
  }
  /* Un lien de cellule ne doit pas rendre la ligne plus haute qu'elle : il est
     deja passe en `inline-block` par le socle (une cible, pas du texte courant),
     ce qui lui donne la hauteur de sa propre boite. */
  .table tbody td a {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
  }
  /* Les commandes d'une ligne restent dans la hauteur de la ligne. Sans cette
     borne, un bouton de 36 pixels et une marge de 12 donnaient une ligne de 60,
     et la liste deroulante de qualification des leads, a 35 pixels, imposait sa
     hauteur a toute la colonne.

     28 pixels, et non 24 : le minimum WCAG 2.2 AA (critere 2.5.8) est de 24, et
     la marge de quatre pixels evite qu'un arrondi de rendu passe dessous. La
     borne ne vaut QUE sur le format bureau : sous 768 pixels, la liste devient
     une suite de fiches ou les memes commandes se prennent au doigt, et elles y
     gardent leur taille pleine. */
  .table tbody td .btn,
  .table tbody td .pill,
  .table tbody td .select,
  .table tbody td .input {
    height: 28px;
    min-height: 28px;
    padding-block: 0;
  }
  .table tbody td .btn,
  .table tbody td .pill {
    padding-inline: var(--space-2);
  }
  /* UN GROUPE DANS UNE CELLULE NE S'ENROULE PAS. `.form-inline` porte
     `flex-wrap: wrap` par le socle, ce qui a du sens dans un formulaire et aucun
     dans une cellule : sur les salles, cinq jours de fermeture s'empilaient sur
     trois rangs et donnaient a TOUT le tableau une ligne de 109 pixels, une
     cellule imposant sa hauteur a ses voisines. */
  .table tbody td .form-inline {
    flex-wrap: nowrap;
  }
  .table tbody td .badge {
    vertical-align: middle;
  }
}

/* --- La cellule d'identite : le nom et son rappel sur UNE ligne -------------
   Le nom se lit, l'adresse de courriel sert a distinguer deux homonymes : ce
   sont deux rangs d'une meme information, pas deux colonnes. Les separer
   coutait une colonne entiere de largeur ; les empiler coutait une deuxieme
   ligne de texte, donc 34 pixels par ligne du tableau. Le rappel suit donc le
   nom sur la meme ligne, en gris et en 12 px, et c'est lui qui se tronque en
   premier : il est le seul des deux dont l'amputation ne coute rien. */
.cell-id {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}
.cell-id__name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-id__meta {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-sm);
  color: var(--muted);
}
/* Le point median qui separe les deux rangs. Il est pose par le style et non
   par le balisage : un lecteur d'ecran annoncerait sinon un caractere isole
   entre le nom et l'adresse. */
.cell-id__meta::before {
  content: "\00b7";
  margin-right: var(--space-2);
  color: var(--line-strong);
}

/* La devise d'une colonne de montants : presente, mais d'un rang en dessous.
   Elle est identique sur toute la colonne et ne se lit qu'une fois ; en corps
   plein, elle prenait autant de largeur que les centimes. `nowrap` est deja
   pose par le socle sur `.amount` : un montant ne se coupe jamais de sa
   devise. */
.amount__cur {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--muted);
  margin-left: 3px;
}

@media (min-width: 769px) {
  /* Jauge du score de qualite : 96 pixels au bureau, ou la colonne les a ; en
   fiche, elle prend ce qui reste apres le chiffre et la bande. */
  .progress--score {
    width: 96px;
    min-width: 96px;
  }
  @media (max-width: 768px) {
    .progress--score {
      width: auto;
      min-width: 3rem;
      flex: 1 1 auto;
    }
  }

  /* Cellule de score : une jauge, sa valeur et sa bande, sur un rang.
   Au bureau, les trois restent insecables ; en fiche, ou la largeur est la
   ressource rare, ils passent a la ligne. Sans cela le tableau du pilotage
   demandait 331 pixels dans une boite de 317. */
  .cell-score {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
  }
  @media (min-width: 769px) {
    .cell-score {
      flex-wrap: nowrap;
      white-space: nowrap;
    }
  }

  /* --- Largeurs de colonnes ---------------------------------------------------
     Une largeur MINIMALE declaree evite qu'une colonne se retrouve a 40 pixels
     parce que sa voisine porte un texte long, et une largeur MAXIMALE evite
     qu'un intitule de cours prenne la moitie du tableau. Trois classes suffisent
     aux quarante ecrans : l'etroite pour une date, un compte ou un statut, la
     large pour un intitule, la moyenne par defaut.

     `width: 1%` N'EST PAS UNE LARGEUR, C'EST UNE INSTRUCTION DE REPARTITION.
     Un tableau en disposition automatique ignore largement `max-width` sur une
     cellule : il distribue la place restante au prorata des largeurs demandees,
     et une colonne d'etat se retrouvait a 250 pixels pour porter une pastille
     de 46, laissant un vide entre elle et le nom. Une largeur derisoire declaree
     dit au moteur de ne rien lui donner de plus que son contenu, tout le reste
     allant aux colonnes qui portent du texte. */
  .table .col--etroite,
  .table .col--statut,
  .table .col--montant {
    width: 1%;
    white-space: nowrap;
  }
  .table .col--etroite {
    min-width: 6rem;
  }
  .table .col--large {
    min-width: 14rem;
    max-width: 28rem;
  }
  /* Colonne d'etat : elle porte une pastille, parfois une liste deroulante de
     qualification (les leads). Plus etroite que la moyenne, plus large que la
     colonne de date. */
  .table .col--statut {
    min-width: 8rem;
  }
  .table .col--montant {
    min-width: 7rem;
    text-align: right;
  }
}

/* --- Colonnes secondaires et depliant de ligne ------------------------------
   Le pilotage du personnel demande 1559 pixels de colonnes, les salles 1436.
   Aucune largeur d'ecran ne les montre toutes : au-dela de trois colonnes
   prioritaires, les suivantes sont marquees secondaires et cessent d'etre
   peintes sous 1200 pixels, largeur en dessous de laquelle la barre laterale
   du bureau ne laisse plus de quoi les lire. Elles ne disparaissent pas : la
   ligne porte un depliant qui les rend en paires libelle / valeur.

   `visibility` ne suffirait pas (la colonne garderait sa largeur) et
   `display: none` sur une cellule de tableau retire la cellule de sa ligne :
   c'est bien ce qu'on veut ici, en-tete compris, pour que la grille se
   recompose sur les colonnes restantes. */
@media (max-width: 1200px) {
  .table .col--secondaire {
    display: none;
  }
}
@media (min-width: 1201px) {
  /* Au-dela du seuil, le depliant n'a plus rien a montrer : sa commande
     disparait plutot que d'ouvrir un panneau vide. */
  .table .row-more,
  .table .row-detail {
    display: none;
  }
}
/* La colonne de commande occupe une largeur fixe DANS LE TABLEAU seulement. En
   fiche, la même largeur réservée poussait le tableau du pilotage à 338 pixels
   dans une boîte de 317 : elle y prend la place de son bouton, pas plus. */
@media (min-width: 769px) {
  .table .row-more {
    width: 2.5rem;
    min-width: 2.5rem;
    padding-inline: var(--space-2);
  }
}
@media (max-width: 768px) {
  .table .row-more {
    width: auto;
    min-width: 0;
    padding: 0;
  }
}
.row-more__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
}
.row-more__btn:hover {
  border-color: var(--navy);
  color: var(--navy);
}
.row-more__btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.row-more__btn svg {
  width: 16px;
  height: 16px;
  transition: transform var(--dur-fast) var(--ease-out);
}
.row-more__btn[aria-expanded="true"] svg {
  transform: rotate(180deg);
}
/* La ligne depliee n'est pas une ligne de donnees : elle explique celle du
   dessus, donc elle en reprend le fond et s'en distingue par sa marge. */
.table tbody tr.row-detail > td {
  height: auto;
  white-space: normal;
  max-width: none;
  padding: var(--space-3) var(--space-4);
  background: var(--greige);
}
.row-detail__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-3) var(--space-5);
  margin: 0;
}
/* Meme hierarchie que .deflist du socle : etiquette navy compacte, valeur en
   encre reguliere. Les deux motifs rendent la MEME chose (une paire etiquette
   et valeur) et doivent se ressembler trait pour trait, sinon la fiche change
   de visage d'un bloc a l'autre. */
.row-detail__list dt {
  /* Trait pour trait sur .deflist dt : juste milieu du 22.08, capitales de
     12px qui guident sans crier. */
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--navy);
  margin-bottom: 4px;
}
.row-detail__list dd {
  margin: 0;
  /* 15px comme .deflist dd : la valeur se lit, l'etiquette guide (22.08). */
  font-size: 0.9375rem;
  color: var(--ink);
}

/* --- Le dossier se lit par sections -----------------------------------------
   Vingt-deux paires a la suite forment un mur : l'oeil cherche l'adresse au
   milieu des dates de contrat, et rien ne dit ou une idee finit (retour
   Patrick du 22.08 : « avec des cadres, un tableau ou une ligne, pour que ce
   soit lisible et comprehensible »).

   Le cadre est LEGER, et c'est deliberé : un filet d'un pixel et un fond a
   peine plus clair que la carte suffisent a poser une frontiere. Des cadres
   marques auraient decoupe la fiche en six boites concurrentes, la ou on veut
   un seul document qui respire. Le titre de section porte les memes marques
   que les etiquettes de paires (petites capitales, navy) : la fiche garde une
   seule voix, et la HIERARCHIE vient de la taille et de l'espace, pas d'une
   nouvelle couleur. */
.fiche-groupe {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: var(--space-3) var(--space-4) var(--space-4);
  margin-top: var(--space-4);
}
.fiche-groupe__titre {
  margin: 0 0 var(--space-3);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--navy);
  /* Le filet sous le titre tient la section ensemble : sans lui, le titre
     flotte au-dessus de sa grille et pourrait appartenir a la section
     precedente. */
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
}
@media (max-width: 600px) {
  /* Au telephone, les cadres coutent seize pixels de largeur utile par
     section pour une frontiere que l'empilement rend deja evidente : le
     filet du titre suffit. */
  .fiche-groupe {
    border: 0;
    padding-inline: 0;
    background: transparent;
  }
}

/* --- La commande de liste, plus nette -------------------------------------
   Trois retouches demandees le 22.08 : la loupe DANS le champ de recherche
   (un champ nu au milieu de selects ne se lit pas comme une recherche), un
   filtre qui FILTRE se distingue de son etat de repos, et la liste ne
   clignote plus pendant qu'elle interroge le serveur : la derniere reponse
   reste peinte sous un leger voile. */
.champ-loupe {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.champ-loupe__icone {
  position: absolute;
  left: 9px;
  width: 14px;
  height: 14px;
  color: var(--muted);
  pointer-events: none;
}
.champ-loupe .input {
  padding-left: 30px;
  width: 12rem;
}
.select--actif {
  border-color: var(--navy);
  color: var(--navy);
  font-weight: 600;
}
.liste-voilee {
  opacity: 0.55;
  transition: opacity var(--dur-fast) var(--ease-out);
}

/* --- La fenetre d'envoi, a l'usage d'un client de messagerie ----------------
   Trois manques que la zone de texte nue laissait (demande du 23.08, « la
   meme experience qu'Outlook ») : on n'y voyait pas A QUI on ecrit, on ne
   pouvait pas mettre un mot en gras, et on ne voyait pas ce que le
   destinataire recevrait. */
.message-destinataires {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-2);
  /* Trente destinataires ne poussent pas le bouton d'envoi hors de la
     fenetre : la liste defile dans sa propre boite. */
  max-height: 7.5rem;
  overflow-y: auto;
}
.message-destinataires .pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.message-destinataires__retirer {
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--muted);
  /* Vingt-quatre pixels : plus petit qu'une cible de doigt, mais ce geste
     vit dans une fenetre de bureau, a la souris, et une croix de 44 pixels
     dans une pastille de 24 serait absurde. */
  width: 20px;
  height: 20px;
  line-height: 1;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
}
.message-destinataires__retirer:hover {
  background: var(--line);
  color: var(--ink);
}
.message-barre {
  display: flex;
  gap: 2px;
  margin: var(--space-2) 0 var(--space-1);
}
.message-barre .btn {
  /* Carre : ces commandes sont des pictogrammes, pas des libelles. */
  min-width: 32px;
  padding-inline: var(--space-2);
}
.message-apercu {
  margin-top: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  /* Le fond blanc et la serif du corps rapprochent l'apercu de ce que le
     client de messagerie affichera, plutot que du formulaire qui l'entoure. */
  color: var(--ink);
  font-size: var(--text-sm);
  max-height: 12rem;
  overflow-y: auto;
}
.message-apercu p {
  margin: 0 0 var(--space-2);
}
.message-apercu p:last-child,
.message-apercu ul:last-child {
  margin-bottom: 0;
}
.message-apercu ul {
  margin: 0 0 var(--space-2);
  padding-left: var(--space-5);
  /* Le socle retire les puces de TOUTES les listes (les menus et les listes
     de donnees n'en veulent pas) : l'apercu d'un email en a besoin, lui, car
     c'est ainsi que le destinataire les verra. */
  list-style: disc;
}

/* --- L'annuaire se serre ----------------------------------------------------
   Neuf colonnes (l'ordre du legacy, seance du 21.08) ne tiennent pas un ecran
   de 1280 avec les marges internes standard (--space-5 de chaque cote de
   chaque cellule : plus de 300 pixels de vide sur une ligne). La variante
   rapproche les colonnes sans toucher a la hauteur des lignes, et rend a
   l'en-tete long (« Debut-Fin de collaboration ») le droit de s'enrouler sur
   deux lignes plutot que d'imposer sa largeur a toute la colonne. Le but est
   mesure, pas estime : a 1280, le tableau ne defile plus dans son ScrollX
   (spec annuaire-sans-scroll). */
.table--serree thead th {
  padding-inline: 6px;
}
/* Le plancher commun des colonnes etroites (6rem, pose pour les numeros de
   telephone) donnait 96 pixels a une case a cocher : ici chaque colonne
   etroite vaut son contenu, rien de plus. */
.table--serree .col--etroite {
  min-width: 0;
}
/* La colonne large aussi rend sa largeur DECLAREE : le nom est deja borne
   par sa propre ellipse, une consigne de 14rem par-dessus gardait 224
   pixels quoi qu'il porte. */
.table--serree .col--large {
  width: auto;
  min-width: 0;
}
.table--serree tbody td {
  /* 6 pixels et non --space-2 : mesure, pas gout. A 8, les neuf colonnes
     demandaient 898 pixels pour 886 de boite a 1280 (spec sans-scroll), et
     la ligne creusait 49 pour 48 de budget (densite-liste). */
  padding: 6px;
}
.table--serree thead th.th--enroulable {
  white-space: normal;
}
/* Le nom aussi se borne : en table auto, chaque colonne prend son contenu le
   plus large, et un seul nom long ecarte la colonne pour toute la liste. Le
   nom entier vit sur la fiche, a un clic. */
.table--serree .cell-id__name {
  display: inline-block;
  max-width: 8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
/* La periode tient SA ligne (doctrine densite-liste : une ligne de texte
   par cellule) : c'est sa taille qui cede, pas son enroulement. */
.table--serree td.td--periode {
  white-space: nowrap;
  font-size: 0.6875rem;
}
/* Les etiquettes se montrent, elles ne s'etalent pas : la cellule est bornee,
   l'infobulle et la fiche portent la liste entiere. */
.table--serree .annuaire__tags {
  /* 7rem depuis que la ligne porte quatre gestes : les etiquettes cedent la
     place aux commandes, et l'infobulle rend la liste entiere. */
  max-width: 7rem;
  overflow: hidden;
}
/* UNE LIGNE PAR CELLULE : c'est la doctrine de densite-liste (le Range du
   texte ne doit pas depasser 1,9 interligne), une pastille enroulee la
   viole. La pastille tient sa ligne en 11px, et l'ellipse borne le cas
   pathologique. */
.table--serree .pill {
  font-size: 0.6875rem;
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
  padding: 2px 8px;
}
/* Les deux gestes de la ligne sont des boutons-icones compacts, comme dans
   le legacy : deux mots cote a cote valaient une colonne entiere, et
   empiles ils crevaient le budget de hauteur de la ligne. */
.table--serree .annuaire__actions {
  display: flex;
  /* Deux pixels entre quatre gestes : a quatre pixels, la colonne Actions
     poussait le tableau hors de l'ecran (958 pour 886 de boite). Les cibles
     restent a 28 pixels, au-dessus du seuil de confort a la souris. */
  gap: 2px;
  align-items: center;
}
.table--serree .annuaire__actions .icon-btn--sm {
  width: 28px;
  height: 28px;
}
.table--serree .pill {
  max-width: 9rem;
}
/* L'avatar de la liste descend a 32 pixels : a 36, il etait le seul a crever
   le budget de hauteur de la ligne (densite-liste, 48 max). La fiche garde
   le portrait en grand. */
.table--serree .avatar-dot {
  width: 30px;
  height: 30px;
  line-height: 30px;
}
/* Une fiche peut porter PLUSIEURS types (note Patrick du 22.08, la tranche
   des types multiples arrive) : la cellule est un rang de pastilles qui
   s'enroule, jamais une pastille unique presumee. */
.table--serree .annuaire__types {
  display: flex;
  /* UNE SEULE LIGNE, meme a plusieurs types (le cas de Lea) : un rang qui
     s'enroule ferait grossir TOUTES les lignes de la liste, et le budget de
     hauteur (densite-liste) est ce qui fait tenir quinze fiches dans un
     ecran. Les pastilles retrecissent, et l'infobulle rend le texte entier
     (shared/tooltips.ts). */
  flex-wrap: nowrap;
  overflow: hidden;
  gap: 2px;
}
.table--serree .annuaire__types .pill {
  flex: 0 1 auto;
  min-width: 2.5rem;
}
/* La cellule des types est BORNEE, comme celle des etiquettes : sans borne,
   deux pastilles cote a cote poussent le tableau hors de l'ecran (996 pixels
   pour 886 de boite, mesure du banc). Ce qui depasse se lit en infobulle. */
.table--serree td.td--types {
  max-width: 8.5rem;
}

/* --- La barre de commande ---------------------------------------------------
   Elle remplace l'en-tete de carte : titre, description, boutons et rangee de
   filtres empiles occupaient 279 pixels avant la premiere donnee, pour un
   ecran dont le titre est deja dans le bandeau et dans la navigation. Tout ce
   qui COMMANDE la liste (chercher, filtrer, agir) tient sur une bande de 52
   pixels, et ce qui la DECRIT descend au pied du tableau, avec le compte.

   Elle est collante parce qu'une recherche qui defile hors de l'ecran oblige a
   remonter pour changer un filtre. Le z-index la place au-dessus des lignes
   sans passer devant le tiroir de navigation (80) ni les dialogues. */
.cmdbar {
  position: sticky;
  /* SOUS le bandeau, jamais a zero. Le bandeau est lui-meme collant (z-index
     20) : ancree a zero, la barre se collait au meme endroit et disparaissait
     entierement derriere lui au premier defilement, mesure au banc (52 px de
     recouvrement sur 52). C'est le defaut que la garde de l'ecran des
     factures verifie sur .cmdbar. */
  top: var(--topbar-h);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 52px;
  padding: var(--space-2) 0;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  /* Le repli vaut aussi pour la barre entiere : compteur et actions passent
     sous les filtres sur un ecran etroit, au lieu d'etre pousses hors champ
     (recette du 16.08, « 1-25 s... » coupe au bord). */
  flex-wrap: wrap;
}
/* ELLE REMONTE DANS LA MARGE DE LA CARTE, et ne s'y ajoute pas.
   Posee dans le flux, la barre laissait au-dessus d'elle les 32 pixels de marge
   interieure de la carte : une bande vide entre le bandeau et la premiere
   commande, qui compte double puisqu'elle repousse aussi tout le tableau. La
   barre EST le haut de la carte : elle en reprend donc la largeur pleine et la
   marge horizontale, et le filet qu'elle porte tient lieu de separation.

   La carte ne doit surtout pas etre passee en `overflow: hidden` (ce que fait
   `.card--flush`) : un ancetre rogne devient le conteneur de defilement de ses
   descendants collants, et la barre cesserait alors de coller. */
@media (min-width: 901px) {
  .card > .cmdbar:first-child {
    margin: calc(var(--space-6) * -1) calc(var(--space-6) * -1) 0;
    padding-inline: var(--space-6);
  }
}
.cmdbar__search {
  /* 15rem et non 22 : la barre doit tenir en UNE rangee au format bureau de
     reference (1440), le repli ne servant qu'aux ecrans plus etroits. La
     garde de densite borne l'espace avant la premiere donnee. */
  flex: 0 1 15rem;
  min-width: 8rem;
}
.cmdbar__filters {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  /* LE REPLI REMPLACE LE DEFILEMENT (recette du 16.08). La bande defilait
     lateralement quand les filtres ne tenaient pas : des filtres coupes au
     bord (« Toutes l... »), un compteur avale, et rien n'indiquait qu'il
     fallait faire glisser. Une seconde rangee se voit ; un defilement sans
     barre ne se devine pas. La regle locale de /paiements, qui corrigeait
     deja ce defaut pour son ecran, devient le comportement de la barre. */
  flex-wrap: wrap;
}
.cmdbar__filters::-webkit-scrollbar {
  display: none;
}
/* UN FILTRE GARDE LA LARGEUR DE SON LIBELLE. En boite flexible, un enfant se
   laisse comprimer par defaut : « Tous les etats » se lisait « Tous les eta »,
   et une liste deroulante dont on ne lit pas l'option courante ne dit plus quel
   filtre est actif. Ils gardent donc leur largeur naturelle, la bande defilant
   lateralement si l'ecran ne les tient pas tous. */
.cmdbar__filters > * {
  flex: 0 0 auto;
}
.cmdbar__filters .select {
  min-width: 7rem;
  max-width: 14rem;
}
.cmdbar__filters .input {
  min-width: 7rem;
  max-width: 11rem;
}
/* Les mesures de synthese, quand un ecran en porte. Trois tuiles d'indicateurs
   pesaient 117 pixels avant la premiere ligne du tableau du personnel, pour
   dire trois nombres : ils tiennent dans la barre, au meme rang que le compte
   de resultats, qui est une mesure de la meme famille. */
.cmdbar__stats {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  flex: none;
  margin-left: auto;
  white-space: nowrap;
}
/* Au format bureau, les mesures de synthese prennent leur PROPRE rangee,
   pleine largeur, sous les commandes : trois stats melees au repli des
   filtres faisaient trois rangees en desordre sur /factures (346 px avant la
   premiere donnee, garde de densite). Deux rangees nettes : commandes, puis
   mesures. */
@media (min-width: 901px) {
  .cmdbar__stats {
    order: 10;
    flex-basis: 100%;
    margin-left: 0;
  }
}
.cmdbar__stat {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  font-size: var(--text-sm);
  color: var(--muted);
}
.cmdbar__stat b {
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* Quand les deux cohabitent, c'est le premier des deux qui prend la marge
   automatique : le second se range juste apres lui. */
.cmdbar__stats + .cmdbar__count {
  margin-left: 0;
}

/* Le compte de resultats et les actions sont pousses a droite : la lecture va
   du plus frequent (chercher) au plus rare (creer). */
/* C'EST LE COMPTE QUI CEDE, PAS LES COMMANDES. En largeur insuffisante, un
   compte insecable poussait la bande de filtres a se couper : sur le pilotage
   du personnel, « Comparé au ... » amputait le quatrieme choix de periode.
   L'information cede donc la place a la commande, et se tronque. */
.cmdbar__count {
  margin-left: auto;
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--text-sm);
  color: var(--muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Precision d'alerte accolee au compte (liste tronquee, portee reduite).
   Le ton d'avertissement du socle sans son bandeau : c'est une nuance sur un
   chiffre, pas un evenement. */
.cmdbar__warn {
  margin-left: var(--space-2);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  /* La paire du socle, deja mesuree a 5,6:1 : aucune couleur de texte neuve
     n'est introduite ici, donc rien a soumettre a la garde de contraste. */
  background: var(--warning-soft);
  color: var(--warning);
  font-size: var(--text-xs);
}

.cmdbar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
  margin-left: var(--space-2);
}
/* Sous 900 pixels, la barre passe sur deux rangees plutot que de comprimer la
   recherche a rien : elle cesse alors d'etre collante, une bande de 104
   pixels prenant plus qu'elle ne rend sur un ecran court. */
@media (max-width: 900px) {
  .cmdbar {
    position: static;
    flex-wrap: wrap;
  }
  .cmdbar__search {
    flex: 1 1 100%;
  }
  /* LE COMPTE CESSE D'ETRE INSECABLE. Sur un ecran large, un compte qui
     s'enroule sur deux lignes ferait sauter la barre ; sur un telephone, c'est
     l'inverse qui coute : « 200 resultats » suivi de la mention de troncature
     poussait le document a 528 pixels pour 375 d'ecran (mesure du banc, le
     12.08, sur la liste des factures). Le meme raisonnement vaut pour les
     actions et les mesures de synthese, qui reprennent leur droit de passer a
     la ligne. */
  .cmdbar__count {
    margin-left: 0;
    white-space: normal;
    /* LE DROIT DE S'ENROULER NE SUFFIT PAS : il faut aussi le droit de
       RETRECIR. En `flex: none`, le compte gardait la largeur de son contenu,
       enroulement autorise ou non, et poussait le document a 528 pixels pour
       375 d'ecran (mesure du banc, le 12.08, sur les factures). */
    flex: 1 1 100%;
    min-width: 0;
  }
  /* Meme raison que le compte ci-dessus : trois commandes en `flex: none`
     gardent la largeur de leurs libelles, meme autorisees a passer a la ligne.
     Le pilotage du personnel en porte trois et poussait le document a 447
     pixels pour 375 d'ecran. */
  .cmdbar__actions,
  .cmdbar__stats {
    flex: 1 1 auto;
    flex-wrap: wrap;
    margin-left: 0;
    white-space: normal;
    min-width: 0;
  }
}

/* --- Sous 768 pixels, la liste devient des fiches ---------------------------
   Le socle partage fait deja passer un tableau en cartes, mais sous 600
   pixels : entre 600 et 768, le bureau garde donc onze colonnes sur une
   tablette en portrait. Le seuil du BUREAU est releve a 768, sans toucher la
   feuille partagee : les quatre autres cockpits gardent le leur, ou il est
   justifie (ils listent peu de colonnes).

   Ce que le socle ne fait pas et qui se joue ici : une fiche montre la ligne
   d'identite et le statut, puis DEUX valeurs cles, et non les onze paires
   libelle / valeur d'un tableau aplati, qui redemanderaient de defiler
   verticalement ce qu'on venait d'economiser horizontalement. Les colonnes
   secondaires restent dans le depliant. */
@media (min-width: 601px) and (max-width: 768px) {
  .table--cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
  .table--cards,
  .table--cards tbody,
  .table--cards tr,
  .table--cards td,
  /* Meme doublet qu'en dessous de 600px (design-system.css) : un `th
     scope="row"` doit suivre exactement le patron du `td` voisin. */
    .table--cards
    th {
    display: block;
    width: auto;
  }
  .table--cards tbody tr {
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    background: var(--surface);
  }
  .table--cards tbody tr + tr {
    margin-top: var(--space-3);
  }
  .table--cards tbody td,
  .table--cards tbody th {
    display: grid;
    grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
    gap: var(--space-3);
    align-items: baseline;
    height: auto;
    max-width: none;
    white-space: normal;
    overflow-wrap: anywhere;
    padding: var(--space-2) 0;
    border-top: 0;
    font-weight: 400;
    text-align: left;
  }
  .table--cards tbody td + td,
  .table--cards tbody th + td {
    border-top: 1px solid var(--line);
  }
  .table--cards tbody td[data-label]::before,
  .table--cards tbody th[data-label]::before {
    content: attr(data-label);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--muted);
  }
  .table--cards tbody td:not([data-label]),
  .table--cards tbody th:not([data-label]) {
    grid-template-columns: minmax(0, 1fr);
  }
  .table--cards tbody tr:has(td[colspan]) {
    border: 0;
    padding: 0;
  }
}
/* --- La fiche du telephone se HIERARCHISE ----------------------------------
   Le patron partage transforme chaque cellule en paire libelle / valeur : c'est
   juste pour un tableau quelconque, et pauvre pour une liste de gestion. Une
   fiche de client montrait « ETAT : Actif » puis « NOM : ... » sur deux rangs
   etiquetes, la ou l'oeil cherche d'abord QUI, et accessoirement dans quel
   etat.

   La ligne devient donc une grille : la commande du depliant, l'etat et
   l'identite partagent le premier rang, le reste suit en pleine largeur. Ces
   trois cellules perdent leur etiquette (une pastille verte « Actif » n'a pas
   besoin qu'on lui annonce « Etat », et un nom encore moins) mais gardent leur
   `data-label` : c'est lui que les lecteurs d'ecran annoncent, et la garde du
   releve le verifie.

   LE PATRON EST DECLARE PAR L'ECRAN (`.table--liste`), et non deduit de la
   classe de la premiere colonne. La deduction a tenu jusqu'au jour ou le
   depliant a pris la premiere place : un selecteur qui devine la convention
   cesse de s'appliquer des qu'une colonne s'ajoute, en silence. Les tableaux
   du bureau qui ne suivent pas cette convention gardent le patron partage. */
@media (max-width: 768px) {
  .table--liste.table--cards tbody tr {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    align-items: center;
    gap: var(--space-2) var(--space-3);
  }
  .table--liste.table--cards tbody td:nth-child(-n + 3) {
    /* La cellule reste une grille : c'est ce que la garde du releve mesure
       pour savoir que le patron mobile s'est bien applique. */
    grid-template-columns: minmax(0, 1fr);
    border-top: 0;
    padding: 0;
  }
  .table--liste.table--cards tbody td:nth-child(-n + 3)::before {
    content: none;
  }
  .table--liste.table--cards tbody td:nth-child(n + 4) {
    grid-column: 1 / -1;
  }
  /* Le premier filet separe l'identite des valeurs qui la detaillent. */
  .table--liste.table--cards tbody td:nth-child(4) {
    border-top: 1px solid var(--line);
    padding-top: var(--space-2);
  }
  /* La ligne depliee reprend toute la largeur de la fiche. */
  .table--liste.table--cards tbody tr.row-detail {
    display: block;
  }
}

/* En fiche, l'identite et le statut se lisent sur la premiere ligne, et le
   rappel gris passe sous le nom : c'est la seule forme ou une deuxieme ligne
   de texte est un gain, la largeur y etant la ressource rare. */
@media (max-width: 768px) {
  .cell-id {
    display: block;
  }
  .cell-id__meta::before {
    content: none;
  }
  /* LE TEXTE REPREND SON DROIT DE S'ENROULER. Garder `nowrap` ici faisait
     deborder le document de 3 pixels sur la liste des clients et de 153 sur
     celle des factures, au format telephone : une adresse de courriel n'a aucun
     espace ou se couper, et la carte suivait sa cellule hors de l'ecran. */
  .cell-id__name,
  .cell-id__meta {
    white-space: normal;
    overflow-wrap: anywhere;
  }
}

/* =========================================================================
   ECRAN DES FACTURES
   Ce que le gestionnaire vient chercher, avant les lignes.
   ========================================================================= */

/* La bande des deux mesures de tete. Le composant vient du socle
   (.stat-strip / .stat), son padding est resserre ICI et la raison est
   mesuree : a --space-5 sur les quatre cotes, la bande prend 112 pixels, et
   avec le titre et la barre de commande il ne restait que sept lignes
   entieres a 1440 x 900. Chaque pixel pris au-dessus du tableau est une ligne
   de moins, et c'est exactement le defaut que cet ecran corrige.

   Bornee en largeur : un montant de cinq chiffres etale sur la moitie d'un
   ecran de 1920 ne se lit pas mieux, et la comparaison entre les deux mesures
   se fait d'autant plus mal qu'elles sont loin l'une de l'autre. */
.invoice-totals {
  width: 100%;
  margin-bottom: var(--space-3);
}
.invoice-totals .stat {
  padding: var(--space-2) var(--space-4);
  gap: var(--space-1);
}
.invoice-totals__scope {
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
}

/* Le retard, et son echeance en second rang SUR LA MEME LIGNE. Deux lignes
   empilees dans cette cellule feraient une rangee d'environ 64 pixels la ou le
   tableau en vise 44 : la densite se paie en lignes visibles, et c'est
   precisement ce que cet ecran corrige. L'echeance reste ENTIERE (elle etait
   coupee a « 19.09.20... », l'annee manquait) : c'est elle qui distingue un
   retard d'un mois d'un retard d'un an.

   Insecable, les deux. « 258 j » se coupait entre le nombre et son unite, ce
   qui suffisait a faire deux lignes d'une cellule qui n'en demande qu'une. */
.invoice-overdue {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--danger);
  white-space: nowrap;
}
.invoice-overdue__due {
  margin-left: var(--space-2);
  white-space: nowrap;
}

/* UN NUMERO NE SE COUPE JAMAIS. « 2026-80201 » tombait sur deux lignes, et une
   seule cellule enroulee suffit a faire une rangee de 59 pixels au lieu de 44 :
   la hauteur d'une rangee est celle de sa cellule la plus haute. */
.invoice-number {
  white-space: nowrap;
}

/* Le nom du payeur, lui, PEUT etre long (« Marie-Helene
   Vandenbroucke-Delacombaz-Echelle ») et ne peut pas etre raccourci sans
   perte. Il est donc borne et coupe par des points de suspension, avec le nom
   entier dans l'infobulle de la cellule et sur la fiche du payeur, a un clic.
   C'est le seul endroit de cet ecran ou quelque chose est tronque, et jamais
   l'echeance, dont l'annee est justement ce qui manquait. */
/* Les points de suspension se posent sur le LIEN, pas seulement sur la
   cellule : le socle rend tout lien de cellule `inline-block` (une ancre
   enroulee se fragmente sinon en une boite par ligne, et le geste tombe a
   cote), et un enfant inline-block deborde d'un parent en ellipsis sans jamais
   etre coupe par lui. Le nom apparaissait donc tranche net, sans le signe qui
   dit qu'il continue. */
.invoice-payer {
  max-width: 18rem;
}
.invoice-payer,
.invoice-payer a {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.invoice-payer a {
  max-width: 100%;
}

@media (max-width: 600px) {
  /* En cartes, la valeur et son echeance s'empilent, et le nom du payeur
     reprend sa largeur : la contrainte de rangee n'a plus d'objet. */
  .invoice-overdue__due {
    display: block;
    margin-left: 0;
  }
  .invoice-payer,
  .invoice-payer a {
    max-width: none;
    overflow: visible;
    white-space: normal;
    overflow-wrap: anywhere;
    text-overflow: clip;
  }
}

.invoice-legend {
  margin: var(--space-3) 0 0;
}
.invoice-pager {
  margin-top: var(--space-3);
}

.invoice-list td.amount {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.invoice-detail__heading,
.invoice-detail__identity,
.invoice-detail__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.invoice-detail__heading {
  justify-content: space-between;
  flex-wrap: wrap;
  margin-top: var(--space-2);
}
.invoice-detail__identity,
.invoice-detail__actions {
  flex-wrap: wrap;
}
.invoice-detail__heading .card__title {
  margin: 0;
}
.invoice-detail__download-error {
  margin: var(--space-3) 0 0;
}
.invoice-detail__facts {
  display: grid;
  grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin: var(--space-4) 0;
  padding: var(--space-5);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.invoice-detail__fact {
  min-width: 0;
}
.invoice-detail__fact dt {
  margin-bottom: var(--space-1);
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.invoice-detail__fact dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.invoice-detail__fact--payer {
  grid-row: span 2;
}
.invoice-detail__address {
  display: block;
  margin-top: var(--space-1);
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: 400;
}
.invoice-detail__lines-title {
  margin-top: var(--space-5);
}
.invoice-detail__lines .num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@media (max-width: 800px) {
  .invoice-detail__facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .invoice-detail__fact--payer {
    grid-row: auto;
  }
}

@media (max-width: 600px) {
  .invoice-totals {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }
  .invoice-list.table--cards tbody td.invoice-payer,
  .invoice-detail__lines.table--cards tbody td:first-child {
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .invoice-list.table--cards tbody td.num,
  .invoice-detail__lines.table--cards tbody td.num {
    text-align: left;
  }
  .invoice-detail__heading,
  .invoice-detail__actions {
    align-items: stretch;
  }
  .invoice-detail__actions {
    width: 100%;
  }
  .invoice-detail__actions .btn {
    flex: 1 1 auto;
  }
  .invoice-detail__facts {
    grid-template-columns: minmax(0, 1fr);
    padding: var(--space-4);
  }
  .invoice-detail__fact + .invoice-detail__fact {
    padding-top: var(--space-2);
    border-top: 1px solid var(--line);
  }
}

/* =========================================================================
   REGISTRE DES PAIEMENTS
   Le pointage des encaissements, que le bureau faisait facture par facture.
   ========================================================================= */

/* Les deux mesures de tete, meme traitement que celles des factures : bande
   du socle, padding resserre, largeur bornee. Chaque pixel pris au-dessus du
   tableau est une ligne de moins, et deux mesures eloignees l'une de l'autre
   se comparent mal. */
.payment-totals {
  max-width: 40rem;
  margin-bottom: var(--space-2);
}
.payment-totals .stat {
  padding: var(--space-2) var(--space-4);
  gap: var(--space-1);
}
.payment-totals__scope {
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
}

/* UNE DATE NE SE COUPE JAMAIS. « 31.07.2026 » tombant sur deux lignes suffit a
   faire une rangee de 59 pixels au lieu de 44, la hauteur d'une rangee etant
   celle de sa cellule la plus haute. C'est aussi la colonne sur laquelle l'oeil
   descend en pointant un releve : elle doit rester d'aplomb. */
.payment-date {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Le nom du payeur peut etre long et ne peut pas etre raccourci sans perte :
   borne et coupe par des points de suspension, avec le nom entier en infobulle
   et sur sa fiche, a un clic. Meme traitement que sur les factures, seul
   endroit de l'ecran ou quelque chose est tronque. */
.payment-payer {
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Un numero de facture ne se coupe pas davantage ici que sur /factures. */
.payment-invoice {
  white-space: nowrap;
}

.payment-legend {
  margin: var(--space-3) 0 0;
}
.payment-pager {
  margin-top: var(--space-3);
}

/* Sous 600 px, la carte remplace le tableau (regle du socle) et le nom du
   payeur retrouve toute sa place : il n'y a plus de colonne a tenir. */
@media (max-width: 600px) {
  .payment-payer {
    max-width: none;
    overflow: visible;
    white-space: normal;
  }
}

/* Resultats de recherche de facture, dans le panneau d'enregistrement. Liste
   sans puces : ce sont des choix, pas une enumeration a lire. */
.record-invoice-results {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-1);
}
.record-invoice-result {
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--line);
}
.record-invoice-result:last-child {
  border-bottom: 0;
}
.record-invoice-chosen {
  margin: 0;
}

/* LA BARRE DE CET ECRAN PASSE A LA LIGNE PLUTOT QUE DE CACHER DES COMMANDES.
   Le socle fait defiler la bande des filtres quand elle deborde, barre de
   defilement masquee : c'est le bon compromis pour les quarante listes du
   bureau, qui portent deux ou trois filtres. Celle-ci en porte CINQ, dont deux
   champs de date, et le compte de la selection par-dessus. Mesure en capture a
   1280 px : le champ « au » sortait entierement du cadre et le compte se
   lisait « 1 a ... », sans le moindre indice qu'il fallait faire defiler pour
   les atteindre. Une commande qu'on ne voit pas et qu'on ne sait pas chercher
   n'existe pas.
   Regle LOCALE, et pas une correction du socle : le defilement reste le bon
   comportement pour les listes qui tiennent, et le changer pour toutes ferait
   sauter une rangee entiere sur des ecrans qui n'en ont pas besoin. */
[data-route="payments"] .cmdbar {
  flex-wrap: wrap;
}
[data-route="payments"] .cmdbar__filters {
  flex-wrap: wrap;
  overflow-x: visible;
}

/* =========================================================================
   LISTE D'AGENDA (apercus du tableau de bord)
   =========================================================================
   Ce que ce bloc remplace : chaque seance rendue en UNE phrase jointe par des
   points medians (date - heure - cours - professeur - lieu), ou il fallait
   LIRE pour trouver le lieu ou l'heure (retour de Patrick du 15.08). La
   structure met chaque information a la meme place sur chaque ligne : la
   colonne du QUAND a gauche, en chiffres tabulaires pour que les dates
   s'alignent au chiffre pres ; le cours en titre ; le professeur et le lieu
   en pilules discretes. L'oeil balaye au lieu de lire. */
.agenda-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}
.agenda-item {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-2) 0;
}
.agenda-item + .agenda-item {
  border-top: 1px solid var(--line);
}
.agenda-item__when {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  min-width: 5.25rem;
}
.agenda-item__date {
  font-size: var(--text-sm);
  font-weight: var(--weight-strong);
  white-space: nowrap;
}
.agenda-item__time {
  font-size: var(--text-xs);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.agenda-item__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.agenda-item__title {
  font-size: var(--text-sm);
  line-height: 1.35;
  /* Deux lignes au plus : un titre de cours long ne doit pas pousser les
     trois seances hors de la carte. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.agenda-item__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
/* La pilule d'agenda est plus discrete que la pilule de navigation : meme
   forme, corps micro, graisse normale : c'est une metadonnee, pas un statut. */
.agenda-item__pill {
  font-size: var(--text-2xs);
  font-weight: 500;
  padding: 0 var(--space-2);
  min-height: 1.25rem;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ===========================================================================
   Tâches du bureau : attribution, entité liée, priorité, pièce jointe.
   =========================================================================== */

.task-portfolio {
  max-width: 48rem;
  margin-bottom: var(--space-3);
}
.task-portfolio .kpi {
  padding: var(--space-2) var(--space-4);
}

/* Une échéance ne se coupe jamais, même règle que le registre des paiements :
   « 31.07.2026 » sur deux lignes fait une rangée de 59 pixels au lieu de 44, la
   hauteur d'une rangée étant celle de sa cellule la plus haute. Et c'est la
   colonne que l'oeil balaie en premier sur cet écran. */
.task-due {
  white-space: nowrap;
}

/* Les deux actions de la ligne restent côte à côte : passées l'une sous
   l'autre, elles doublent la hauteur de toutes les rangées de la table. */
.task-actions {
  display: flex;
  gap: var(--space-2);
  white-space: nowrap;
}

.task-pager {
  margin-top: var(--space-3);
}

/* Sous 600 px, la carte remplace le tableau (règle du socle) : les actions
   reprennent la largeur, et les libellés d'entité liée ne sont plus contraints
   par une colonne. */
@media (max-width: 600px) {
  .task-actions {
    justify-content: flex-start;
  }
}

/* =========================================================================
   FAMILLES REPLIABLES DE LA COLONNE (decision du 17.08)
   =========================================================================
   L'intertitre de famille devient un bouton. Il garde EXACTEMENT l'allure de
   l'intertitre du socle (`.sidebar__group-label`, dont il porte la classe) :
   seuls le reset de bouton, la cible de clic pleine largeur et le chevron
   s'ajoutent ici. La regle vit dans la feuille propre a l'admin parce que le
   repli est propre au bureau : les espaces mon/pro/prof gardent leur colonne
   permanente, decision du 10.08 qui reste juste a leur echelle. */
.sidebar__group-toggle {
  appearance: none;
  background: none;
  border: 0;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  text-align: left;
  font: inherit;
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast);
}

/* Au survol, l'intertitre passe au blanc plein : il annonce qu'il se clique,
   sans ajouter de fond qui alourdirait une colonne deja dense. */
.sidebar__group-toggle:hover {
  color: var(--on-dark);
}

.sidebar__group-toggle:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

/* Le chevron ne porte AUCUNE information a lui seul : l'etat est dit par
   aria-expanded, que la lecture d'ecran annonce. Il est donc purement
   decoratif pour qui n'a pas la couleur ni la forme sous les yeux. */
.sidebar__group-toggle .ic {
  flex: 0 0 auto;
  opacity: 0.7;
}

/* =========================================================================
   BLOC DE FICHE : LA LECTURE D'ABORD (decision du 17.08)
   =========================================================================
   Les fiches empilaient leurs formulaires ouverts : une cinquantaine de
   champs de saisie sur la fiche du collaborateur, dont presque aucun ne
   change apres l'embauche. Le bloc s'ouvre desormais en LECTURE et « Modifier »
   revele le formulaire.

   La grille de lecture reprend `.row-detail__list` du depliant de tableau,
   avec deux differences qui tiennent a la place disponible : des colonnes
   plus larges (une adresse ou un niveau de langue tiennent mal en 12rem) et
   de l'air entre les rangees, une fiche n'ayant pas la contrainte de densite
   d'une ligne de tableau. */
/* Pastilles de lecture d'une fiche (lieux de cours, etiquettes) : des boites
   qui se balaient d'un coup d'oeil, la ou une enumeration a virgules se lit
   mot a mot. */
.fiche-chips {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.fiche-bloc .row-detail__list {
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-4) var(--space-5);
  padding: var(--space-4) 0 var(--space-2);
  border-top: 1px solid var(--line);
}

/* La fiche ne redefinit PLUS ses paires : la hierarchie des etiquettes et
   valeurs est UNE SEULE regle (.row-detail__list plus haut, alignee sur le
   .deflist du socle). Deux passes de contraste (20.08) ont ete avalees par
   l'ancienne redefinition posee ici par la fiche v2, plus specifique et
   invisible depuis la regle de base : le classique de la cascade. Si la fiche
   veut un jour un traitement a elle, il se pose SUR la regle de base, jamais
   en la redefinissant entierement. */

/* Le bouton d'edition tient sa place a droite du titre, sur sa ligne de base.
   Contour plutot que fond plein : c'est une action SECONDAIRE de consultation,
   l'action principale d'une fiche restant ce que le bloc contient. */
.fiche-bloc__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  /* L'air entre le bouton et le filet des paires : sans lui, le bouton, plus
     haut que le sur-titre, reposait sur la ligne (retour Patrick, 23.09). */
  padding-bottom: var(--space-3);
}

/* Le sur-titre n'a plus à porter l'air sous lui : la rangée s'en charge. */
.fiche-bloc__head .eyebrow {
  margin-block-end: 0;
}

.fiche-bloc__title {
  flex: 1 1 20rem;
  max-width: 100%;
}

.fiche-bloc__edit {
  flex: 0 0 auto;
  border: 1px solid var(--line-strong);
  background: var(--surface);
}

.fiche-bloc__edit:hover {
  border-color: var(--navy);
  color: var(--navy);
}

/* Pastille et identite forment un bloc : `.page-head` aligne ses enfants sur
   leur base, et la pastille posee seule y tombait sous le titre. */
/* --- L'en-tete d'une fiche, a deux rangees ----------------------------------
   Une seule rangee faisait tenir cote a cote l'identite, les cases de type,
   l'invitation, le statut et l'impersonation : au premier libelle long la
   pastille s'enroulait sur deux lignes et les commandes se chevauchaient
   (retour Patrick du 22.08). Depuis le choix multiple des types, le defaut
   est STRUCTUREL : une personne qui cumule trois fonctions n'entre pas dans
   le reliquat d'une rangee.

   L'identite et les commandes gardent la premiere rangee, aux deux bouts.
   Les types prennent la seconde, sur toute la largeur : ils s'y alignent
   autant qu'il en faut, et rien ne pousse plus personne. */
.fiche-tete {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.fiche-tete__rangee {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* L'enroulement est la SOUPAPE : sous une certaine largeur les commandes
     passent sous l'identite plutot que de la comprimer. Sans lui, le nom se
     tronque pour laisser passer trois boutons. */
  flex-wrap: wrap;
  gap: var(--space-3);
}
.fiche-tete__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  /* Les commandes ne retrecissent pas : c'est l'identite qui cede, elle a une
     ellipse pour ca. Un bouton a moitie coupe ne se clique pas.
     MAIS la rangee, elle, doit pouvoir s'ENROULER : avec un flex-basis auto et
     shrink 0, sa largeur restait la somme de tous les boutons sur une ligne
     (607 px mesures pour 375 de viewport, fiche staff au telephone, 31.08), et
     le document entier s'elargissait. Un layout plus large que le viewport
     fausse tous les taps de la page en emulation mobile : deux parcours E2E
     mouraient plus bas, sur des ecrans que personne n'avait touches. max-width
     contraint la rangee, le wrap fait le reste : chaque bouton reste entier,
     ils s'empilent au lieu de deborder. */
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
}
/* L'adresse du compte, en tete de fiche : une adresse est un mot insecable, et
   les adresses de test font quarante caracteres. Sans cassure autorisee, le
   span poussait le document au-dela du viewport telephone (mesure au 31.08),
   et un layout plus large que l'ecran fausse tous les taps de la page. */
[data-champ="email-compte"] {
  overflow-wrap: anywhere;
}

.fiche-tete__types {
  /* Toute la largeur : c'est le point de la seconde rangee. */
  width: 100%;
  min-width: 0;
}

.subject-head__identity {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

/* =========================================================================
   LE RETOUR D'UNE PAGE (remplace la pastille « Fermer · X », 17.08)
   =========================================================================
   Une fleche et la destination : le geste que tout le marche emploie. Muet
   en gris, il s'affirme au survol ; il ne porte NI cadre ni fond, pour ne
   pas se coller a la ligne d'en-tete de la carte. */
.backlink {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--muted);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 500;
  padding: var(--space-1) var(--space-2);
  margin-left: calc(-1 * var(--space-2));
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast);
}

.backlink:hover {
  color: var(--navy);
}

.backlink:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

/* L'en-tete d'une page de creation ou de detail respire : la pastille collait
   le retour au titre, et le titre au formulaire. */
.card > .card__header {
  margin-bottom: var(--space-2);
}

/* ─────────────────────────────────────────────────────────────────────────
   COMMANDE RAPIDE : le comptoir.
   Trois blocs empilés (le client, les lignes, l'argent), une grille de saisie
   qui se remplit à la frappe, et le total sous les yeux pendant qu'on vend.
   ───────────────────────────────────────────────────────────────────────── */
.qo__bloc {
  padding: var(--space-4) 0;
}
.qo__bloc + .qo__bloc {
  border-top: 1px solid var(--line);
}

/* Le payeur : une personne, pas un identifiant. */
/* « Qui paie ? » et « Choisir le payeur » forment UNE rangee de deux champs de
   MEME hauteur (retour Patrick du 24.08 : le petit selecteur contre le grand
   champ se lisait « pas aligne »). Le premier champ garde une largeur stable
   pour que la rangee ne saute pas quand on bascule particulier/entreprise ; le
   second prend tout le reste. */
.qo__choix-payeur {
  display: flex;
  gap: var(--space-4);
  align-items: flex-end;
  flex-wrap: wrap;
}
.qo__choix-payeur > .field:first-child {
  flex: 0 0 230px;
}
.qo__champ-large {
  flex: 1;
  min-width: 260px;
}
/* Sous 600 px, les deux champs s'empilent en pleine largeur : 230 px fixes ne
   laisseraient au champ de recherche que des miettes. */
@media (max-width: 600px) {
  .qo__choix-payeur > .field:first-child {
    flex: 1 1 100%;
  }
}
.qo__payeur {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: var(--greige);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
}
.qo__initiales {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--navy);
  color: var(--on-dark);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 0.8125rem;
  flex: 0 0 auto;
}
/* Le bloc de texte DOIT pouvoir rétrécir : sans `min-width: 0`, un enfant de
   flex garde la largeur de son contenu, et une adresse email longue poussait le
   bouton « Changer » à 390 pixels dans un écran de 375 (trouvé par le volet
   mobile de la spec). L'email se coupe donc, plutôt que la page. */
.qo__payeur-txt {
  min-width: 0;
  flex: 1;
}
.qo__payeur-nom {
  display: block;
  font-weight: 700;
}
.qo__payeur-det {
  display: block;
  color: var(--muted);
  font-size: 0.8125rem;
  overflow-wrap: anywhere;
}
.qo__changer {
  margin-left: auto;
}

/* La grille de saisie. */
.qo__entete-lignes {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
}
.qo__table td {
  vertical-align: middle;
}
/* La désignation est la colonne qu'on LIT : elle prend la place, les autres se
   contentent de leur contenu. `.input--sm` passe en width:auto, d'où la largeur
   rendue explicite ici. */
/* La désignation prend TOUTE la place qui reste : c'est la seule colonne qu'on
   lit vraiment, et « Cours d'anglais semi-privé B1.1, objectif B2 » ne se lit
   pas dans huit centimètres. Les colonnes de chiffres se contentent de leur
   contenu (`width: 1%` plus `white-space: nowrap` : le navigateur leur donne
   alors le minimum, et le reste va à la première). */
.qo__table td:first-child,
.qo__table th:first-child {
  width: auto;
}
/* Le champ de DÉSIGNATION, et lui seul : `>` et non un descendant quelconque.
   La rangée « pour qui » est elle aussi une première cellule, et ses cases à
   cocher héritaient de cette largeur : elles s'étiraient en barres marine de
   200 pixels au lieu de rester des carrés de 20. Constaté à la capture, jamais
   à la relecture du CSS. */
.qo__table td:first-child > input {
  width: 100%;
}
.qo__table th:not(:first-child),
.qo__table td:not(:first-child) {
  width: 1%;
  white-space: nowrap;
}
.qo__qte {
  max-width: 5rem;
  text-align: right;
}
.qo__prix {
  max-width: 8rem;
  text-align: right;
}
.qo__montant {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}
.qo__vide {
  margin-top: var(--space-2);
}

/* La ligne toujours prête, et ses propositions. */
.qo__neuve td {
  background: var(--ivory);
}
.qo__suggestions {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  max-width: 34rem;
  background: var(--surface);
}
.qo__suggestions li + li {
  border-top: 1px solid var(--line);
}
.qo__suggestions button {
  display: flex;
  width: 100%;
  gap: var(--space-3);
  align-items: baseline;
  background: none;
  border: 0;
  padding: 0.4rem 0.7rem;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.qo__suggestions button:hover,
.qo__suggestions button:focus-visible {
  background: var(--salmon-soft);
}
.qo__sug-prix {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}
.qo__libre {
  color: var(--navy);
  font-weight: 600;
}

/* L'argent, à droite ; les options, à gauche. */
.qo__pied {
  display: grid;
  grid-template-columns: 1fr 20rem;
  gap: var(--space-5);
  align-items: start;
}
.qo__l {
  display: flex;
  justify-content: space-between;
  padding: 0.15rem 0;
  color: var(--muted);
  font-size: 0.875rem;
}
.qo__l b {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.qo__total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: var(--space-2);
  padding: 0.6rem 0.75rem;
  background: var(--navy);
  color: var(--on-dark);
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-size: 1.25rem;
}
.qo__mention {
  font-size: 0.6875rem;
  color: var(--muted);
  margin: var(--space-1) 0 0;
  line-height: 1.4;
}
.qo__manque {
  margin: var(--space-3) 0 0;
  background: var(--salmon-soft);
  color: var(--coral-700);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.7rem;
  font-size: 0.8125rem;
}
.qo__actions {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.qo__actions .btn {
  width: 100%;
  justify-content: center;
}

/* Sous 900 px, l'argent repasse sous les options plutôt que de s'écraser. */
@media (max-width: 900px) {
  .qo__pied {
    grid-template-columns: 1fr;
  }
}

/* Sous 600 pixels, le socle transforme chaque ligne en carte (`.table--cards`).
   Les largeurs posées pour la grille de bureau n'ont alors plus de sens : une
   colonne à 38 % devient une boîte de saisie minuscule au milieu d'une carte.
   On les rend, et chaque champ occupe sa carte. */
@media (max-width: 600px) {
  .qo__table td:first-child,
  .qo__table td:first-child > input,
  .qo__qte,
  .qo__prix {
    width: 100%;
    max-width: none;
    text-align: left;
  }
  /* Et surtout : les cellules de chiffres rendent leur `width: 1%`.
     C'est la valeur qui donne a la grille de bureau ses colonnes serrees, mais
     en mode carte la rangee devient un bloc, et 1 % de ce bloc fait TROIS
     PIXELS : la cellule se refermait sur elle-meme, son libelle et sa valeur se
     superposaient, et le champ de saisie tombait a 26 pixels. Mesure au
     navigateur a 375 px, sur la commande rapide en production. */
  .qo__table th:not(:first-child),
  .qo__table td:not(:first-child) {
    width: auto;
    white-space: normal;
  }
  .qo__suggestions {
    max-width: 100%;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   COMMANDE COMPLÈTE : la composition à gauche, le récapitulatif à droite.
   Le récapitulatif reste sous les yeux pendant qu'on compose : il porte le
   total, ce qui manque, et les quatre gestes de fin (relire, s'envoyer un
   essai, envoyer pour signature, enregistrer). Sous 1000 px il repasse en
   dessous, où il se lit comme la conclusion de la page.
   ───────────────────────────────────────────────────────────────────────── */
.co {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 19.5rem;
  gap: var(--space-5);
  align-items: start;
}
.co__recap {
  position: sticky;
  top: calc(var(--topbar-h) + var(--space-4));
  max-height: calc(100vh - var(--topbar-h) - var(--space-5) * 2);
  overflow-y: auto;
  background: var(--greige);
  border-radius: var(--radius-sm);
  padding: var(--space-4);
}
/* Les en-tetes de colonnes peuvent se plier ici, a la difference du comptoir :
   « PRIX UNITAIRE TTC » sur une seule ligne reclame 140 pixels, et c'est la
   DESIGNATION qui les paie, alors qu'elle est la seule colonne qu'on lit. Les
   cellules, elles, gardent leur nowrap : un montant coupe en deux se lit mal. */
.co .qo__table th:not(:first-child) {
  white-space: normal;
}
/* Et les champs de chiffres se resserrent : une quantite tient en trois
   caracteres, un prix en sept. Ce qu'ils rendent va a la designation, qui est
   la seule colonne dont la largeur change quelque chose a la lecture.

   Borne haute obligatoire : sous 600 px le socle passe en cartes, ou chaque
   champ occupe sa rangee. Sans la borne, ces deux largeurs l'emportaient sur
   la remise a plat du mode carte (specificite 0,2,0 contre 0,1,0) et les
   champs se retrouvaient a quatre caracteres SOUS leur propre libelle. */
@media (min-width: 601px) {
  .co .qo__qte {
    max-width: 4rem;
  }
  .co .qo__prix {
    max-width: 6.5rem;
  }
}
.co__recap-ligne {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}
.co__recap-tete {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: 0.875rem;
}
.co__recap-tete b {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.co__recap-personnes {
  list-style: none;
  margin: var(--space-1) 0 0;
  padding: 0;
}
.co__recap-personnes li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: 0.8125rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.co__promesse {
  margin: var(--space-3) 0 0;
  line-height: 1.5;
}

/* Les personnes retenues : une puce par personne, retirable. */
.co__chips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  padding: 0;
}
.co__chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--greige);
  border-radius: 999px;
  padding: 0.25rem 0.35rem 0.25rem 0.25rem;
  min-width: 0;
}
.co__initiales {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 50%;
  background: var(--navy);
  color: var(--on-dark);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 0.6875rem;
}
.co__chip-nom {
  font-weight: 600;
  font-size: 0.8125rem;
}
.co__chip-det {
  font-size: 0.75rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 14rem;
}

/* Pour qui : les cases sous la désignation, jamais une colonne de plus. Une
   sixième colonne écraserait le libellé, qui est ce qu'on lit en premier. */
.co__pour {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}
/* La silhouette vient du socle (`.check`) : ici, seulement ce qui est propre a
   cette liste, ou l'on coche une personne par ligne de commande. */
.co__case {
  white-space: nowrap;
}

/* L'EDITEUR DE POSITIONS A LA BEXIO (26.08 au soir).

   La barre des types d'entree : quatre boutons a plat, jamais un menu. Elle
   dit le vocabulaire du devis avant meme la premiere ligne. */
.co__barre-ajout {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

/* LA LECTURE DU TABLEAU : chaque position est un ilot. Le survol teinte la
   rangee ET ses sous-rangees, le montant se lit en chiffres tabulaires, la
   designation domine. Un tableau de saisie sans reperes faisait chercher a
   quelle position appartenait la rangee « pour qui » (remarque du 26.08 :
   pas assez intuitif). */
.co__table-positions [data-co-ligne] > td {
  padding-top: var(--space-3);
}
.co__table-positions .qo__montant {
  font-variant-numeric: tabular-nums;
  font-size: var(--text-body);
  white-space: nowrap;
}
.co__table-positions [data-co-texte] > td,
.co__table-positions [data-co-sous-total] > td {
  padding-block: var(--space-2);
}

/* LE CALCUL SOUS LA DESIGNATION, lisible comme une phrase : quantite x prix
   - rabais. Sept colonnes cote a cote partaient au defilement horizontal des
   l'ecran de bureau (capture du 26.08) ; trois colonnes et une rangee de
   calcul tiennent partout, mobile compris, sans rien perdre.

   Chaque champ porte son etiquette AU-DESSUS, en petites capitales eteintes :
   on sait toujours quel chiffre on corrige, sans en-tete de colonne a aller
   relire trois rangees plus haut. */
.co__calc {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.co__calc-champ {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.co__calc-champ > span {
  text-align: left;
  font-size: 0.625rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.co__table-positions .qo__qte {
  width: 3.5rem;
  text-align: right;
}
.co__table-positions .qo__prix {
  width: 5.75rem;
  text-align: right;
}
.co__sous-total-retirer {
  display: block;
  margin-left: auto;
  margin-top: var(--space-1);
}

/* Le numero de position : une colonne fine, chiffres eteints. Il se lit quand
   on le cherche (« la position 3 »), il ne rivalise pas avec la designation. */
/* LE DOCUMENT S'ECRIT, IL NE SE REMPLIT PAS (passe design du 27.08, sur la
   remarque de Patrick : des inputs bordes empiles font formulaire
   administratif, pas devis d'ecole de langues).

   Les champs de texte perdent leur bordure au repos : la designation se lit
   comme un titre, les precisions comme sa ligne grise, la phrase comme un
   paragraphe. Le survol pose un fond a peine teinte qui dit « ceci s'edite »,
   le focus un lisere marine net : l'etat se voit toujours, il ne crie jamais.

   Les zones de texte GRANDISSENT avec leur contenu (field-sizing, Chrome
   moderne ; une ligne minimum ailleurs) : une precision longue se lit en
   entier au lieu de defiler dans une meurtriere. */
.co__champ {
  font: inherit;
  color: inherit;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  padding: var(--space-1) var(--space-2);
  width: 100%;
  display: block;
}
.co__champ:hover {
  background: var(--surface-alt);
}
.co__champ:focus-visible {
  outline: none;
  border-color: var(--navy);
  background: var(--surface);
}
.co__champ::placeholder {
  color: var(--muted);
  opacity: 0.7;
}
/* LA CASE DICTEE PAR LE CATALOGUE. Sans marque visible, elle a exactement
   l'apparence d'une case libre : le bureau clique, rien ne se passe, et il
   croit a une panne plutot qu'a une regle. La capture d'ecran l'a montre, le
   test ne pouvait pas : `disabled` fonctionnait deja. */
.co__champ:disabled {
  color: var(--muted);
  background: var(--surface-alt);
  border-color: transparent;
  cursor: not-allowed;
}
.co__champ:disabled:hover {
  background: var(--surface-alt);
}
.co__champ-titre {
  font-weight: 600;
  font-size: var(--text-body);
}
.co__champ-precisions,
.co__champ-phrase {
  font-size: var(--text-sm);
  color: var(--ink-soft, inherit);
  resize: none;
  min-height: 1.9rem;
  field-sizing: content;
}
.co__champ-phrase {
  font-size: var(--text-body);
}

/* Les chiffres du calcul : un simple trait sous la valeur, comme une case de
   releve. La bordure complete revenait a poser quatre boites par ligne. */
.co__champ-chiffre {
  display: inline-block;
  width: auto;
  border: 0;
  border-bottom: 1px solid var(--line-strong);
  border-radius: 0;
  background: transparent;
  padding: var(--space-1) var(--space-1) 2px;
  font-variant-numeric: tabular-nums;
}
.co__champ-chiffre:hover {
  background: var(--surface-alt);
}
.co__champ-chiffre:focus-visible {
  outline: none;
  border-bottom-color: var(--navy);
  background: var(--surface);
}

/* LE DOCUMENT COMMENCE A LA PREMIERE POSITION (passe du 27.08, « avant-
   gardiste et intuitif »). L'en-tete gris POS/DESIGNATION/MONTANT repetait ce
   que les etiquettes des champs disent deja : il passe aux lecteurs d'ecran
   seuls. Le numero devient un filigrane, grand et pale, comme la marge d'un
   contrat ; le montant s'affirme en marine ; chaque position se termine par un
   filet fin. L'oeil suit : numero, titre, chiffres, montant. */
.co__pos {
  /* La colonne du numero : la pastille porte le style, la cellule ne fait que
     l'aligner sur la premiere ligne du titre. */
  vertical-align: top;
  padding-top: var(--space-2);
}
.co__table-positions .qo__montant {
  color: var(--navy);
  font-weight: 650;
}
.co__table-positions [data-co-ligne]:not([data-co-texte]):not([data-co-sous-total]) > td {
  border-top: 1px solid var(--line);
}
.co__table-positions tbody tr:first-child > td {
  border-top: 0;
}

/* LES DEUX ETATS D'UNE POSITION (le geste Bexio, 27.08).

   Au repos elle se LIT : titre en semi-gras, details gris, le calcul en une
   ligne eteinte. Le survol teinte la rangee et fait poindre le curseur : tout
   dit « cliquez pour ecrire », rien ne le crie.

   Ouverte, elle devient l'etabli : lisere marine a gauche, fond a peine
   souleve, et « Termine » pour la refermer. UNE seule a la fois : un devis de
   six positions ne montre plus trente champs. */
.co__resume {
  cursor: pointer;
}
.co__resume:hover > td {
  background: var(--surface-alt);
}
.co__resume-titre {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  font-size: var(--text-body);
  color: inherit;
  cursor: pointer;
  text-align: left;
}
.co__resume-precisions {
  margin: 2px 0 0;
  font-size: var(--text-sm);
  color: var(--muted);
  white-space: pre-line;
}
.co__resume-calc {
  margin: var(--space-1) 0 0;
  font-size: var(--text-xs);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.co__ouverte > td {
  background: var(--surface-alt);
}
.co__ouverte > td:first-child {
  box-shadow: inset 3px 0 0 var(--navy);
}
.co__terminer {
  display: block;
  margin-left: auto;
  margin-bottom: var(--space-2);
}

/* LA ZONE D'AJOUT se reconnait sans se lire : un lisere pointille, le langage
   universel du « quelque chose se pose ici ». Le champ qu'elle contient reste
   un vrai champ. */
.qo__neuve > td {
  padding-block: var(--space-3);
}
.qo__neuve [data-qo-saisie] {
  border-style: dashed;
  background: var(--surface);
}
.qo__neuve [data-qo-saisie]:focus-visible {
  border-style: solid;
}

/* LES COLONNES DE LA TABLE DES POSITIONS, contre l'heritage du comptoir.
   L'ecran d'avant mettait la designation en PREMIERE colonne : ses regles
   (`.qo__table th:first-child { width: auto }`, les autres a 1 %) vivent plus
   haut et sont PLUS SPECIFIQUES qu'une classe seule. Depuis la colonne des
   numeros, first-child est le NUMERO : il heritait du width:auto et s'etirait
   a un demi-tableau pendant que la designation, non-first, s'ecrasait a 1 %
   (mesure a la sonde le 27.08 : 472 px contre 136). Les selecteurs ci-dessous
   repetent .co__table-positions pour battre cet heritage la ou il s'applique,
   et NULLE PART ailleurs : le motif width 1 % / 100 % du depot, simplement
   rendu au bon vainqueur. */
.co__table-positions th:first-child,
.co__table-positions td:first-child:not([colspan]) {
  /* Un nombre a deux chiffres, rien de plus (remarque de Patrick, 27.08).
     Les cellules a COLSPAN sont exclues : la rangee d'ajout, « pour qui » et
     le detail par personne commencent par une cellule pleine largeur, et le
     1 % + nowrap les ecrasait a 22 pixels, bouton de suggestion compris (sonde
     du 27.08 : le clic mobile n'aboutissait jamais). */
  width: 1%;
  white-space: nowrap;
  padding-inline: var(--space-1);
  text-align: center;
}
.co__table-positions td.co__designation {
  width: 100%;
  white-space: normal;
}
.co__table-positions th.co__col-montant {
  text-align: right;
}
/* LA PASTILLE DU NUMERO DE POSITION. Un chiffre nu au milieu d'une colonne
   vide ne se rattachait a rien : ni a sa ligne, ni au document. La pastille
   lui donne un corps, un fond creme et le marine de la marque, et la cellule
   l'aligne sur la premiere ligne du titre plutot que sur le milieu de la
   hauteur (retour de Patrick, 27.08). */
.co__pastille {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 0.35rem;
  border-radius: var(--radius-sm);
  background: var(--ivory);
  color: var(--navy);
  font-size: var(--text-xs);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
/* Ligne ouverte : la pastille prend le marine plein, comme l'onglet actif
   d'un classeur. On voit d'un coup d'oeil QUELLE position on edite. */
.co__ouverte .co__pastille {
  background: var(--navy);
  color: var(--ivory);
}

/* La designation et ses precisions s'EMPILENT, pleine largeur. Sans le bloc,
   deux inputs inline se posaient cote a cote : le second, ecrase, ne montrait
   plus que les points de suspension de son placeholder (« ... », remarque de
   Patrick du 27.08), et un long titre de cours restait coupe. */
.co__designation .co__champ {
  display: block;
  width: 100%;
}
.co__designation .co__champ-chiffre {
  display: inline-block;
  width: auto;
}

/* Quantite, unite et rabais : des champs courts. Sans largeur bornee, sept
   colonnes se partagent l'espace a parts egales et la designation, seule
   colonne qu'on LIT, etouffe. */
.co__unite {
  width: 4rem;
}
.co__rabais {
  width: 4rem;
  text-align: right;
}

/* Le rabais applique s'affiche SOUS le montant net, en petit et eteint : le
   montant reste la reponse, le rabais son explication. */
.co__rabais-applique {
  display: block;
  font-size: var(--text-xs);
  color: var(--muted);
  font-weight: 400;
}

/* Les gestes de rangee, pousses a droite : monter, descendre, dupliquer,
   retirer. Un groupe compact, apres les personnes cochees. */
.co__gestes {
  display: inline-flex;
  gap: var(--space-1);
  margin-left: auto;
}

/* LES DEUX NATURES QUI NE VENDENT RIEN, et qui doivent se distinguer d'une
   prestation AU PREMIER COUP D'OEIL : sans cela, une phrase libre ressemble a
   une position dont on aurait oublie le prix, et le bureau la corrige au lieu
   de la lire.

   La phrase prend toute la largeur, sans colonnes. Le sous-total porte le fond
   creme et le trait marine du document imprime : ce qu'on voit a l'ecran est
   ce qui sortira. */
.co__ligne-libre {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.co__ligne-libre > .input {
  flex: 1 1 auto;
  min-width: 0;
}
[data-co-texte] > td {
  background: var(--surface-alt);
}
[data-co-sous-total] > td {
  background: var(--ivory);
  border-top: 1px solid var(--navy);
  font-weight: 600;
}
.co__sous-total-libelle {
  font-weight: 600;
}
/* Le montant d'un sous-total ne se saisit pas : il se LIT. Rien ne doit y
   ressembler a un champ, sans quoi on essaie de le corriger. */
.co__sous-total-montant {
  font-weight: 700;
  color: var(--navy);
}

/* L'ECHEANCIER DU PAIEMENT FRACTIONNE : un apercu, pas un formulaire. Il se
   lit sous les champs, en retrait, pour qu'on voie d'un coup d'oeil ce que le
   client lira sur son offre. */
.co__echeancier {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.co__echeancier p {
  margin: 0 0 var(--space-2);
}

/* LA PRESTATION OFFERTE : « Offert » a la place du montant, dans la meme
   graisse que les autres montants. Un chiffre grise se lirait comme un prix
   qu'on aurait oublie de remplir ; le mot dit un geste, pas un oubli. */
[data-co-offert] > td {
  background: var(--ivory);
}
.co__montant-offert {
  font-weight: 700;
  color: var(--navy);
}

/* Les gestes d'ajout secondaires, sous le champ de saisie : discrets par leur
   taille, jamais caches derriere un menu. */
.co__ajouts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* LE DETAIL D'UN PARTICIPANT : son libelle, son cours, son enseignant.
   Une rangee par personne retenue, sous la rangee des cases a cocher.

   Retrait a gauche, et le nom en tete : la rangee se lit comme une sous-ligne
   de la prestation, pas comme une prestation de plus. Sans ce retrait, trois
   participants donnaient trois rangees de meme poids visuel que le produit
   qu'ils suivent, et l'oeil ne savait plus ou commencait la position suivante.

   Les trois champs partagent la largeur restante et tombent l'un sous l'autre
   sous 720px : trois selecteurs cote a cote y devenaient illisibles, et la
   grille partait au defilement horizontal. */
.co__personne {
  /* DEUX RANGÉES ÉTIQUETÉES : le nom et le libellé personnalisé d'abord, le
     cours et l'enseignant dessous à parts égales. Quatre colonnes serrées
     TRONQUAIENT leurs propres libellés (« Cours de la prestat », remarque de
     Patrick du 27.08) : à deux par rangée, chaque champ a la place de son
     contenu, et les colonnes restent alignées d'une personne à l'autre. */
  display: grid;
  grid-template-columns: minmax(9rem, max-content) 1fr 1fr;
  grid-template-areas:
    "nom libelle libelle"
    ".   cours   enseignant";
  align-items: end;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  padding-left: var(--space-5);
  margin-block: var(--space-2);
}
.co__personne-nom {
  grid-area: nom;
  align-self: center;
}
.co__personne-libelle {
  grid-area: libelle;
}
.co__personne-cours {
  grid-area: cours;
}
.co__personne-enseignant {
  grid-area: enseignant;
}
.co__personne .input,
.co__personne .co__champ {
  min-width: 0;
  width: 100%;
}
.co__personne-nom {
  flex: 0 0 auto;
  font-size: var(--text-xs);
  color: var(--muted);
  white-space: nowrap;
}
.co__personne-ligne > td {
  border-top: 0;
  padding-top: 0;
}
@media (max-width: 720px) {
  .co__personne {
    grid-template-columns: 1fr;
    grid-template-areas:
      "nom"
      "libelle"
      "cours"
      "enseignant";
    padding-left: var(--space-3);
  }
}

/* Les precisions de la position, sous son titre : plus petites que lui, parce
   qu'elles l'expliquent sans lui disputer la lecture. */
.co__precisions {
  margin-top: var(--space-1);
  font-size: var(--text-xs);
}

/* La rangée « pour qui » appartient à la ligne qui la précède : elle n'en est
   séparée par aucun filet, et c'est elle qui porte le trait du bas. */
.co__pour-ligne > td {
  border-top: 0;
  padding-top: 0;
}
/* « Retirer » ferme la rangée, poussé à droite : le geste destructeur ne se
   trouve pas sous le pouce qui coche les personnes. */
.co__retirer {
  margin-left: auto;
}

/* Créer la fiche du client sans quitter la vente : le formulaire prend la
   largeur du bloc et se détache par un fond, pour qu'on voie d'un coup d'oeil
   qu'on est sorti de la recherche. */
.qo__nouveau {
  flex: 1 1 100%;
  margin-top: var(--space-3);
  padding: var(--space-4);
  background: var(--greige);
  border-radius: var(--radius-sm);
}
.qo__nouveau-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* LE REPLI DU COMPTOIR. Le bouton se place seul sur sa ligne, en retrait des
   quatre champs essentiels : il ouvre une SECTION, il n'agit pas sur le champ
   qui le precede. */
.qo__nc-replier {
  align-self: flex-start;
  margin-top: var(--space-3);
}
/* Le detail se distingue du bloc essentiel par un filet et un retrait, sans
   carte imbriquee : une carte dans une carte dans un formulaire de vente
   empile trois bordures pour un seul niveau de lecture. */
.qo__nc-detail {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
/* Le groupe de cases est un <fieldset> : le navigateur lui pose une bordure et
   une marge natives qu'aucun autre champ du formulaire n'a. On les retire, et
   la legende reprend la graisse des libelles voisins. */
.qo__nc-lieux {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}
.qo__nc-lieux > legend {
  padding: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  color: var(--ink);
}
.qo__nc-cases {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}
/* Le bouton d'ouverture ferme la rangée de recherche, à la ligne : collé au
   champ, il se lisait comme une action DU champ. Mis en avant le 24.08
   (retour Patrick) : un lien discret cachait LE geste du comptoir au
   téléphone, créer la fiche de la personne qu'on a en ligne. */
/* Le bouton va A LA LIGNE sans s'etirer : le champ de recherche absorbe toute
   la place libre (flex:1), il ne reste donc jamais de quoi loger le bouton sur
   la rangee, et un element auto se pose au debut de la ligne suivante. Une
   base de 100 % l'aurait ETIRE en barre pleine largeur. */
.qo__nouveau-ouvrir {
  flex: 0 0 auto;
  align-self: flex-start;
}
.co__actions {
  margin-top: var(--space-4);
}

/* Le bloc de l'offre : l'accroche et la validite cote a cote, le mot dessous,
   parce qu'un paragraphe ne se relit pas dans une demi-colonne. */
.co__offre {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 11rem;
  gap: var(--space-3) var(--space-4);
}
/* Les cellules de cette grille sont des .field : la regle generique
   `.field + .field { margin-top }` du socle, prevue pour des champs empiles,
   poussait « Valable jusqu'au » 16 px sous « Accroche » (retour Patrick du
   24.08, « rien n'est aligne »). Dans une grille, l'ecart est le gap, jamais
   une marge. */
.co__offre > .field {
  margin-top: 0;
}
.co__offre-mot {
  grid-column: 1 / -1;
}
.co__offre textarea {
  min-height: 6rem;
  resize: vertical;
  line-height: 1.5;
}
@media (max-width: 700px) {
  .co__offre {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 1000px) {
  .co {
    grid-template-columns: 1fr;
  }
  .co__recap {
    position: static;
  }
}
@media (max-width: 600px) {
  .co__chip-det {
    display: none;
  }
  /* Le socle transforme chaque rangée en carte bordée. La rangée « pour qui »
     en formerait une seconde, détachée de la ligne dont elle parle : on la
     recolle sous sa carte, sans bord haut ni marge. */
  .table--cards tbody tr.co__pour-ligne {
    margin-top: 0;
    border-top: 0;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
  }
  .table--cards tbody tr:has(+ .co__pour-ligne) {
    border-bottom: 0;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
  }
  .co__pour-ligne > td {
    display: block;
  }
}

/* ── LA LIGNE OUVERTE EST UN FORMULAIRE, PAS UN DOCUMENT ──────────────────
   Correction d'urgence du 27.08 (Patrick : « on ne comprend rien, on ne sait
   pas ou ecrire »). Le style sans bordure a ete applique aux DEUX etats de la
   position ; il ne vaut que pour l'etat de REPOS, ou la ligne se lit comme
   sur le document imprime. Des qu'on l'ouvre pour la remplir, il faut voir
   les cases : une case invisible n'est pas sobre, elle est introuvable.

   Donc, ligne ouverte : bordure franche, fond blanc, etiquette collee a SON
   champ. Ligne au repos : rien ne change, le resume reste un document. */
.co__ouverte .co__champ {
  border-color: var(--line-strong);
  background: var(--surface);
}
.co__ouverte .co__champ:hover {
  border-color: var(--navy);
  background: var(--surface);
}
.co__ouverte .co__champ:focus-visible {
  border-color: var(--navy);
  box-shadow: 0 0 0 3px var(--ivory);
}
/* Les chiffres reprennent leur boite : le simple trait sous la valeur ne
   disait pas ou finissait la case, et l'etiquette flottait au-dessus du
   vide. */
.co__ouverte .co__champ-chiffre {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  text-align: right;
}
/* ET LA CASE DICTEE PAR LE CATALOGUE REPREND LE DESSUS. La regle generale plus
   haut ne suffisait pas : celle qui precede, de meme specificite mais plus
   tardive dans le fichier, la battait, et la case verrouillee restait blanche
   comme une case libre. Ni la relecture du CSS ni le test de comportement ne
   pouvaient le dire : `disabled` fonctionnait deja. C'est la capture d'ecran,
   puis la comparaison des fonds calcules, qui l'ont montre. */
.co__ouverte .co__champ-chiffre:disabled,
.co__ouverte .co__champ:disabled {
  background: var(--surface-alt);
  border-color: transparent;
  color: var(--muted);
  cursor: not-allowed;
}
/* L'ETIQUETTE AU-DESSUS DE SON CHAMP, jamais a cote. Chaque case porte sa
   largeur, l'etiquette prend celle de la case : « PRIX UNITAIRE TTC » ne peut
   plus se retrouver au-dessus du champ voisin. */
.co__ouverte .co__calc {
  align-items: start;
  /* Les quatre cases tiennent sur UNE ligne dans la colonne de designation
     (~380 px de champs), et retombent proprement en dessous quand l'ecran
     n'en laisse pas la place. */
  gap: var(--space-2);
}
.co__ouverte .co__calc-champ > span {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-body);
  white-space: nowrap;
}
.co__ouverte .co__calc-champ {
  gap: 4px;
}
.co__ouverte .qo__qte {
  width: 4.5rem;
}
.co__ouverte .qo__prix {
  width: 6.5rem;
}
.co__ouverte .co__rabais {
  width: 4.5rem;
}
.co__ouverte .co__unite {
  width: 6rem;
}
/* Le libelle des deux champs de texte : « Designation » etait un placeholder,
   donc il disparaissait des la premiere lettre et ne disait plus quel champ
   on remplissait. */
.co__etiquette {
  display: block;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-body);
  margin-bottom: 4px;
}
.co__etiquette + .co__champ {
  margin-bottom: var(--space-2);
}

/* ── Le mot riche (mot d'accompagnement de l'offre) ──────────────────────
   Même vocabulaire que les champs document de la commande : repos discret,
   survol en surface alternative, focus au liseré marine. La barre est un
   outil, pas un meuble : elle ne pèse rien tant qu'on n'écrit pas. */
.co__mot {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.co__mot:focus-within {
  border-color: var(--navy);
}
.co__mot-barre {
  display: flex;
  gap: 2px;
  padding: var(--space-1);
  border-bottom: 1px solid var(--line);
}
.co__mot-bouton {
  font: inherit;
  font-size: var(--text-sm);
  line-height: 1;
  min-width: 28px;
  padding: var(--space-1);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.co__mot-bouton:hover {
  background: var(--surface-alt);
  color: var(--text-body);
}
.co__mot-bouton.is-active {
  background: var(--ivory);
  border-color: var(--navy);
  color: var(--navy);
}
.co__mot-bouton[data-mot-bouton="bold"] {
  font-weight: 700;
}
.co__mot-bouton[data-mot-bouton="italic"] {
  font-style: italic;
}
/* TipTap pose .tiptap sur l'élément éditable. La hauteur suit le texte,
   comme les textarea du mode document (field-sizing), avec un plancher de
   lettre courte. */
.co__mot-zone .tiptap {
  min-height: 5.5rem;
  padding: var(--space-2);
  outline: none;
  color: var(--text-body);
}
.co__mot-zone .tiptap p {
  margin: 0 0 var(--space-2);
}
.co__mot-zone .tiptap p:last-child {
  margin-bottom: 0;
}
.co__mot-zone .tiptap ul,
.co__mot-zone .tiptap ol {
  margin: 0 0 var(--space-2);
  padding-left: var(--space-4);
}

/* =========================================================================
   FICHE PRODUIT : CE QUE CONTIENT LE PRODUIT
   -------------------------------------------------------------------------
   Le bloc de composition est de la DONNEE EN LECTURE, posee au-dessus d'un
   formulaire. Sans surface propre, il se confondait avec les champs qui le
   suivent et le bureau ne voyait plus ou finit ce qui se lit et ou commence
   ce qui se modifie.
   ========================================================================= */
.fp__composition {
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
}
.fp__composition-titre {
  margin: 0 0 var(--space-3);
  font-weight: 600;
  color: var(--ink, inherit);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.fp__composition dl {
  margin: 0;
  row-gap: var(--space-3);
}
.fp__composition dt {
  margin: 0 0 2px;
}
.fp__composition dd {
  margin: 0;
  font-weight: 600;
}
/* Le prix fige occupe la case ou se trouvait le champ de saisie : meme
   alignement que les libelles voisins, pour que la grille ne se torde pas. */
.fp__prix-fige .fp__prix-montant {
  margin: 0;
  font-weight: 600;
}
/* La phrase qui explique l'absence de composition se pose sous le badge, pas
   collee a lui : sans respiration, elle se lisait comme une suite du statut. */
.fp__sans-structure {
  margin-top: var(--space-2);
}
/* LE LIBELLE GENERE occupe la case ou se trouvait le champ « Designation » :
   meme alignement que les champs voisins, pour que la grille ne se torde pas
   quand un produit remplace un article simple. La liste des six langues se
   lit dessous, en secondaire : c'est une preuve, pas une saisie. */
.fp__libelle-genere {
  margin: 0 0 var(--space-2);
  font-weight: 600;
}
.fp__libelle-langues {
  margin: 0 0 var(--space-2);
  display: grid;
  row-gap: 2px;
}
.fp__libelle-langues dd {
  margin: 0;
}

/* =========================================================================
   LA MATRICE DES DROITS AU TELEPHONE : UN ROLE, SES CAPACITES.
   Releve du 03.09 : sous 600 pixels, le socle « une ligne = une carte »
   transformait la matrice (38 capacites x 10 roles) en 380 cartes, pour une
   page de 24 930 pixels de haut. La mise en cartes empile ce que le tableau
   juxtapose : elle sert un tableau de DONNEES, pas une croix. L'ecran choisit
   donc une branche de la croix (un role) et deroule ses capacites groupees par
   domaine, comme le bureau les groupe.

   LA LIGNE ENTIERE EST LA CIBLE. Le <label> couvre la case et son libelle :
   viser une case de 24 pixels dans une liste de trente-huit est le geste qu'on
   rate au doigt, et le rater ici coche un droit pour tous les porteurs du role.
   La case garde 24 pixels (WCAG 2.2 AA, cible minimale), la ligne 44.
   ========================================================================= */
.capacite-groupe {
  margin: var(--space-4) 0 var(--space-2);
  color: var(--muted);
}
.capacite-ligne {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
  min-height: 44px;
  padding: var(--space-2) 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
}
.capacite-ligne--verrouillee {
  cursor: default;
  color: var(--muted);
}
.capacite-ligne__case {
  width: 24px;
  height: 24px;
  margin: 0;
  flex: none;
}
.capacite-ligne__texte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.capacite-ligne__nom {
  overflow-wrap: anywhere;
}
.capacite-ligne__verrou {
  display: flex;
  align-items: start;
  gap: var(--space-2);
  overflow-wrap: anywhere;
}
.capacite-ligne__cadenas {
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: 2px;
}
/* Le bouton d'enregistrement clot la liste : au bout de trente-huit lignes, il
   doit rester atteignable au pouce sans revenir en haut. */
.capacite-enregistrer {
  margin-top: var(--space-4);
  width: 100%;
  min-height: 44px;
}

/* Journal d'audit : le motif se lit en entier, sur plusieurs lignes, sans
   défilement horizontal ; c'est la colonne qu'un humain lit en premier. */
.table .audit-motif,
.table td.audit-motif {
  white-space: normal;
  min-width: 16rem;
  max-width: 28rem;
}

/* PARCOURS DE MISE EN OEUVRE (chantier 6) : une frise ordonnée, un marqueur par
   étape, et l'état lu par la couleur du marqueur autant que par son badge.

   Sous 600 px, la frise devient une pile de cartes : le rail vertical et son
   retrait coûteraient la moitié de la largeur utile sur un téléphone, et les
   actions d'une étape doivent rester atteignables au pouce. */
.journey {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.journey__step {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.journey__marker {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--line);
}
.journey__body {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.journey__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  overflow-wrap: anywhere;
}
.journey__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.journey__actions .btn,
.journey__actions .pill {
  min-height: 24px;
}
@media (max-width: 600px) {
  .journey__step {
    flex-direction: column;
  }
  .journey__actions .btn,
  .journey__actions .pill {
    width: 100%;
    min-height: 44px;
    justify-content: center;
  }
}

/* EN-TETE DE COLONNE TRIABLE (chantier 2, tranche 3).
 *
 * Un BOUTON dans le <th>, et non un onClick sur le <th> lui-meme : seul le
 * bouton est atteignable au clavier, et c'est lui qui annonce qu'il y a quelque
 * chose a faire ici. Il herite de la typographie de l'en-tete pour que rien ne
 * change a l'oeil, sauf la fleche.
 *
 * La fleche vient de aria-sort, donc d'un seul etat : une classe supplementaire
 * poserait une seconde verite, et les deux finiraient par se contredire. */
.th-sort {
  all: unset;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  /* Cible tactile : 24 px minimum sous le doigt (WCAG 2.2, 2.5.8). */
  min-height: 24px;
}

.th-sort:focus-visible {
  outline: 2px solid var(--focus, currentColor);
  outline-offset: 2px;
}

th[aria-sort="ascending"] .th-sort::after {
  content: "\2191";
}

th[aria-sort="descending"] .th-sort::after {
  content: "\2193";
}

/* La colonne inactive garde sa place : sans ce caractere transparent, les
 * en-tetes sautent de quelques pixels a chaque tri. */
th[aria-sort="none"] .th-sort::after {
  content: "\2195";
  opacity: 0.3;
}

/* La liste des offres sépare l'introduction, les actions et les filtres. */
.offer-list__header {
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-4);
}
.offer-list__header > :first-child {
  flex: 1 1 18rem;
}
.offer-list__header .editor-note {
  margin: var(--space-2) 0 0;
}
.offer-list__header .editor-actions {
  padding-top: 0;
  justify-content: flex-start;
}
.offer-list__filters {
  max-width: 40rem;
  margin-block: var(--space-5);
  gap: var(--space-4);
}
.offer-list__filters .field {
  margin-block: 0;
}
.offer-list__filters .select {
  min-height: 2.75rem;
}

/* Les formules se comparent comme des choix, avec leur montant toujours lisible. */
:root[data-densite="outil"] .qo__entete-lignes {
  display: grid;
  justify-content: stretch;
  gap: var(--space-2);
  margin-block-end: var(--space-4);
}
:root[data-densite="outil"] .qo__entete-lignes > .eyebrow,
:root[data-densite="outil"] .qo__bloc > .eyebrow {
  margin: 0;
  color: var(--navy);
  font-size: var(--text-h3);
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: normal;
  text-transform: none;
}
:root[data-densite="outil"] .qo__entete-lignes > .muted {
  max-width: 65ch;
  font-size: var(--text-sm);
  line-height: 1.6;
}
.offer-variants {
  margin-block: var(--space-5);
}
.offer-variants__choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: var(--space-3);
}
.offer-variant {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  min-width: 0;
  min-height: 6.5rem;
  padding: var(--space-4);
  border: 1px solid var(--field-border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--navy);
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.offer-variant__title {
  padding-inline-end: var(--space-5);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.offer-variant__total {
  margin-block-start: auto;
  color: inherit;
  font-size: var(--text-body);
  font-weight: 700;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
}
.offer-variant__check {
  position: absolute;
  inset-block-start: var(--space-4);
  inset-inline-end: var(--space-4);
  width: 1.25rem;
  height: 1.25rem;
}
.offer-variant[aria-pressed="true"] {
  border-color: var(--navy);
  background: color-mix(in srgb, var(--navy) 5%, var(--surface));
  box-shadow: inset 0 0 0 1px var(--navy);
}
.offer-variant:not(:disabled):hover {
  border-color: var(--navy);
  background: color-mix(in srgb, var(--navy) 8%, var(--surface));
}
.offer-variant:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 3px;
}
.offer-variant:disabled {
  opacity: 0.55;
  cursor: default;
}
.offer-variants__actions:empty {
  display: none;
}
.offer-variants__actions .btn {
  min-height: 2.75rem;
}
@media (max-width: 600px) {
  .offer-list__filters {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Comptes et accès. Chaque responsabilité occupe une section stable : état de
   synchronisation, configuration avancée, raccourci RBAC, filtres et annuaire. */
.accounts-page {
  display: grid;
  gap: var(--space-4);
  min-width: 0;
}
.accounts-page__header {
  display: grid;
  justify-items: start;
  gap: var(--space-3);
}
.accounts-page__header .pill {
  margin: 0;
}
.accounts-alerts {
  display: grid;
  gap: var(--space-2);
}
.accounts-alerts:empty {
  display: none;
}
.accounts-alerts .alert {
  margin: 0 !important;
}
.accounts-advanced {
  margin: 0;
}
.accounts-advanced > .fold__body > .card {
  margin-block-start: var(--space-2);
  box-shadow: none;
}
.accounts-page__heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  width: 100%;
  min-width: 0;
}
.accounts-directory > .card__header {
  margin-block-end: var(--space-4);
}
.accounts-filters {
  display: grid;
  grid-template-columns: minmax(14rem, 2fr) minmax(12rem, 1fr) auto;
  align-items: end;
  gap: var(--space-3);
  margin-block-end: var(--space-5);
}
.accounts-filters > .field {
  min-width: 0;
  margin: 0;
}
.accounts-filter-toggle {
  flex-direction: row;
  justify-content: flex-start;
  min-height: 44px;
  align-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.accounts-capabilities [data-testid="matrice-capacites"] th:first-child {
  width: clamp(260px, 28vw, 320px);
  min-width: 260px;
  max-width: 320px;
  text-align: left;
}
.accounts-capabilities [data-testid="matrice-capacites"] tr[data-category] > th {
  text-align: left;
}
.accounts-table th:nth-child(3),
.accounts-table td:nth-child(3) {
  min-width: 11rem;
}
.accounts-table th:last-child,
.accounts-table td:last-child {
  min-width: 10rem;
}
.accounts-table td:nth-child(-n + 3) {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: anywhere;
}
.accounts-row-actions,
.accounts-row-actions__links {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}
.accounts-row-actions {
  flex-direction: column;
}
.accounts-row-actions__links {
  flex-wrap: wrap;
}
.account-actions {
  position: relative;
}
.account-actions > summary {
  list-style: none;
}
.account-actions > summary::-webkit-details-marker {
  display: none;
}
.account-actions > summary::after {
  width: 0.45rem;
  height: 0.45rem;
  margin-inline-start: var(--space-2);
  border-inline-end: 1.5px solid currentColor;
  border-block-end: 1.5px solid currentColor;
  content: "";
  transform: rotate(45deg) translateY(-0.1rem);
}
.account-actions[open] > summary::after {
  transform: rotate(225deg) translate(-0.1rem, -0.1rem);
}
.account-actions__menu {
  display: grid;
  gap: var(--space-1);
  margin-block-start: var(--space-2);
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}
.account-actions__menu .btn {
  justify-content: flex-start;
  width: 100%;
}
.account-reason__validation {
  visibility: hidden;
}
.account-reason__validation--visible {
  visibility: visible;
}
@media (max-width: 900px) {
  .accounts-filters {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  .accounts-filter-toggle {
    grid-column: 1 / -1;
  }
}
@media (max-width: 600px) {
  .accounts-page {
    gap: var(--space-3);
  }
  .accounts-page__header,
  .accounts-directory {
    padding: var(--space-4);
  }
  .accounts-filters {
    grid-template-columns: minmax(0, 1fr);
  }
  .accounts-filter-toggle {
    grid-column: auto;
  }
  .accounts-table th:nth-child(3),
  .accounts-table td:nth-child(3),
  .accounts-table th:last-child,
  .accounts-table td:last-child {
    min-width: 0;
  }
  .accounts-row-actions,
  .account-actions,
  .account-actions > summary,
  .accounts-row-actions__links,
  .accounts-row-actions__links a {
    width: 100%;
  }
  .accounts-row-actions__links a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* Les souhaits vendus restent lisibles à côté des corrections du bureau. */
.planning-offer-wishes {
  display: grid;
  gap: var(--space-3);
  margin-block: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}

[data-point-de-controle][data-provisioning-active="true"] > :not([data-provisioning-operation]) {
  display: none;
}

[data-point-de-controle][data-programme-computed="true"] [data-legacy-planning] {
  display: none;
}
.planning-offer-wishes__heading {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.planning-offer-wishes__values {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  margin: 0;
}
.planning-offer-wishes__values > div {
  display: grid;
  align-content: start;
  gap: var(--space-1);
  min-width: 0;
}
.planning-offer-wishes__values dt {
  color: var(--muted);
  font-size: var(--text-sm);
}
.planning-offer-wishes__values dd {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}
@media (max-width: 600px) {
  .planning-offer-wishes__values {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
  }
}
