/* ALPAGA — feuille de style reprise de BUDGEO, avec SA charte graphique
   (GRETA CFA Loire : orange #e2600c, en-tête anthracite à liseré orange,
   fond beige clair), pour que les deux outils se ressemblent. Les règles
   propres à ALPAGA sont regroupées en fin de fichier (préfixe .alp-). */
/* -- Palette orange/gris (ajoutée 26/08, remplace le bleu d'origine) : charte
   graphique du GRETA-CFA de la Loire, sur retour utilisateur ("il faut que
   ça claque et que cela mette en évidence les étapes de saisie"). Le bleu
   d'origine (--primary: #2f5d9f) était une couleur neutre de démarrage,
   choisie avant que la charte de la structure ne soit précisée — tout
   `var(--primary...)` dans ce fichier en hérite automatiquement, donc boutons,
   liens actifs, onglets et surlignages de sélection basculent en orange sans
   modification ailleurs. --ink/--ink-2 (gris anthracite chaud) habillent
   l'en-tête pour un contraste fort avec l'orange, plutôt que de mettre de
   l'orange partout (garder l'accent concentré sur les boutons, l'en-tête,
   les onglets actifs et les étapes numérotées le rend plus lisible sur une
   longue saisie qu'un habillage 100% orange). --primary-light sert aux
   surlignages doux (ligne sélectionnée, message d'info) qui utilisaient
   auparavant un bleu clair codé en dur, désormais remplacé par une teinte
   orange pâle cohérente avec le nouveau --primary. --*/
:root {
  --bg: #f4f1ee;
  --surface: #ffffff;
  --border: #e3ddd6;
  --text: #262320;
  --text-muted: #6f6a63;
  --primary: #e2600c;
  --primary-hover: #b94d08;
  --primary-light: #fdecdf;
  --ink: #2b2a28;
  --ink-2: #3d3b38;
  --danger: #b3261e;
  --danger-bg: #fdecea;
  --success: #1e7d3c;
  --success-bg: #eaf7ee;
  /* 17/09 : nouveau type d'heures "PRD" (couleurTypeHeures, scenario.js) —
     bleu, hue inédite dans cette palette (PPR=orange, PPA=vert, PAD=gris),
     contraste 6,45:1 sur blanc, largement au-dessus du seuil WCAG AA
     (4,5:1) pour du texte de tableau. */
  --info: #1d5fa8;
  --info-bg: #e8f0fa;
  /* 06/10 : nouveau type d'heures "PAP - VAE" (couleurTypeHeures, scenario.js)
     — violet, 5e hue distincte dans cette palette (PPR=orange, PAP=violet,
     PPA=vert, PAD=gris, PRD=bleu), contraste 7,10:1 sur blanc, au-dessus du
     seuil WCAG AA (4,5:1) pour du texte de tableau. */
  --vae: #6d28d9;
  /* 11/09 v2 : repère "ajouté automatiquement au catalogue AMMON, encore à
     créer/régulariser dans le vrai AMMON" (migration 022, retour
     utilisateur : « le mettre en couleur différente afin que je puisse
     identifier facilement ») — teinte ambre volontairement distincte de
     --primary (déjà très utilisé ailleurs, notamment sur les sous-totaux de
     "Tous les produits") et de --danger/--success (déjà réservés aux
     erreurs/succès). Voir `.produit-manuel-ammon`/`.ref-row-manuel`. */
  --flag-manuel: #92660a;
  --flag-manuel-bg: #fdf1d9;
  --radius: 8px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* 11/09 : hauteurs mesurées en JS (scenario.js, mesureProduitsSticky) pour
     caler .tabs-sticky / .produits-overview-head sous le bandeau qui les
     précède, sans jamais se chevaucher — valeurs de repli ci-dessous au cas
     où le JS n'aurait pas encore tourné (1er rendu). */
  --app-header-h: 62px;
  --tabs-h: 49px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
}

/* En-tête en gris anthracite + liseré orange (26/08) : bannière sombre sur
   toutes les pages pour un contraste fort avec l'orange des boutons/onglets
   actifs juste en dessous — c'est cette bannière + les étapes numérotées
   (voir .step-num plus bas) qui portent l'essentiel du "ça claque" demandé,
   le reste de l'interface (fonds de carte, tableaux) restant neutre pour ne
   pas fatiguer l'œil sur une longue saisie. */
header.app-header {
  background: var(--ink);
  border-bottom: 3px solid var(--primary);
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

/* 11/09, retour utilisateur (page scénario, onglet Produits) : « laisser le
   menu supérieur avec la liste latérale en fixe [...] il faudrait qu'il
   puisse le voir au niveau de l'interface [...] c'est une fonction top ».
   Classe posée UNIQUEMENT sur scenario.html (pas sur les autres pages qui
   partagent header.app-header/.tabs) — voir .tabs-sticky ci-dessous, qui se
   cale juste en dessous via --app-header-h (mesuré en JS, scenario.js). */
.app-header-sticky {
  position: sticky;
  top: 0;
  z-index: 50;
}

header.app-header h1 {
  font-size: 18px;
  margin: 0;
  font-weight: 600;
}
/* Logo (26/08, retour utilisateur : "implanter ce logo... sur le bandeau
   supérieur") : icône seule (web/img/logo-budgeo-icon.png — fond blanc
   d'origine rendu transparent, voir doc) accolée au texte "BUDGEO" existant,
   plutôt que le logo complet (avec accroche/tagline) qui ne tiendrait pas
   dans une bande aussi fine. */
header.app-header h1 a {
  color: #fff;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
header.app-header .logo-icon {
  height: 32px;
  width: auto;
  display: block;
}

nav.app-nav {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
nav.app-nav a {
  color: #d8d5d1;
  text-decoration: none;
  padding: 6px 12px;
  border-radius: var(--radius);
  font-size: 13px;
}
nav.app-nav a:hover { background: var(--ink-2); color: #fff; }
nav.app-nav a.active { background: var(--primary); color: #fff; font-weight: 600; }

main {
  max-width: 1400px;
  margin: 0 auto;
  padding: 24px;
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 20px;
}

.card h2 { margin-top: 0; font-size: 16px; }
.card h2 .subtitle { color: var(--text-muted); font-weight: 400; font-size: 13px; }

.toolbar {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.toolbar .spacer { flex: 1; }
/* 11/09 v2 : légende de `.produit-manuel-ammon`/`.ref-row-manuel` — voir leur
   commentaire plus bas. Le "●" reprend la même couleur que le liseré de
   ligne, pour une association visuelle immédiate sans dépendre du texte. */
.legend-manuel { font-size: 12px; color: var(--flag-manuel); cursor: help; }

button, .btn {
  font: inherit;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  padding: 7px 14px;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background-color .12s, border-color .12s, color .12s, box-shadow .12s, transform .12s;
}
button:hover, .btn:hover { border-color: var(--primary); color: var(--primary); }
button.primary, .btn.primary {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 1px 2px rgba(43, 42, 40, .15);
}
button.primary:hover, .btn.primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  color: #fff;
  box-shadow: 0 3px 8px rgba(43, 42, 40, .22);
  transform: translateY(-1px);
}
button.danger { color: var(--danger); }
button.danger:hover { border-color: var(--danger); background: var(--danger-bg); }
button:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; transform: none; }

input[type=text], input[type=password], input[type=date], input[type=number], input[type=file], select, textarea {
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  background: #fff;
  color: var(--text);
}
/* 16/09, retour utilisateur : « les bordures verticales qui délimitent les
   colonnes restent épaisses » — d'abord attribué aux flèches natives
   incrémenter/décrémenter des `<input type=number>`, masquées ici un temps.
   16/09 (suite), cause réelle trouvée (voir `.row-selected` plus bas) : un
   `box-shadow` posé par erreur sur CHAQUE cellule de la ligne au lieu d'une
   seule fois en début de ligne — sans rapport avec ces flèches. Retour
   utilisateur une fois la vraie cause corrigée : « il manque les flèches
   [...] je trouvais ça bien » — remises, ce masquage n'était pas nécessaire. */
/* Anneau de focus orange (26/08) : renforce visuellement le champ en cours
   de saisie, cohérent avec la demande de mettre en évidence les étapes de
   saisie — pas seulement les titres d'étape, mais aussi le champ actif. */
input[type=text]:focus, input[type=password]:focus, input[type=date]:focus, input[type=number]:focus, input[type=file]:focus,
select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-light);
}
label { font-size: 13px; color: var(--text-muted); display: block; margin-bottom: 4px; }
.field { margin-bottom: 12px; }

table.simple {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
table.simple th, table.simple td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
table.simple th { color: var(--text-muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .02em; }
table.simple tr:hover td { background: #fafaf9; }

/* 30/09 : tableau à en-tête groupé sur 2 niveaux (ex. "Total par CFP et par
   catégorie" du Reporting Coordo & Assistante — web/js/reporting-coordo-
   assistante.js) — un séparateur vertical marque le début de chaque groupe
   de colonnes (ex. "Coordo" / "Assistante"), et les montants s'alignent
   proprement grâce aux chiffres tabulaires. */
table.simple.rep-table-groupe td, table.simple.rep-table-groupe th { font-variant-numeric: tabular-nums; }
table.simple.rep-table-groupe th.rep-th-groupe, table.simple.rep-table-groupe td.rep-td-groupe {
  border-left: 2px solid var(--border);
}
table.simple.rep-table-groupe tbody tr:nth-child(even) td { background: #fafaf9; }

/* 30/09, retour utilisateur (page Reporting → Rapports) : « l'alignement des
   entêtes de tableau et les colonnes ne sont pas alignées, il faudrait
   ajouter des bordures verticales pour bien délimiter » — bordure verticale
   entre CHAQUE colonne (contrairement à .rep-table-groupe ci-dessus, qui ne
   marque que le début de 2 groupes de colonnes). Scopée à cette page via
   .rep-table-cols (web/js/reporting-rapports.js) plutôt qu'ajoutée à
   table.simple en général, qui reste sans bordure verticale partout ailleurs
   dans l'appli (jamais demandé sur les autres grilles) — à étendre si
   d'autres pages du Reporting en ont besoin. */
table.simple.rep-table-cols th, table.simple.rep-table-cols td {
  border-left: 1px solid var(--border);
}
table.simple.rep-table-cols th:first-child, table.simple.rep-table-cols td:first-child {
  border-left: none;
}

/* 30/09 (2e retour le même jour, page Reporting → Rapports) : « je pense que
   tu peux améliorer la présentation des tableaux, ils font plat, je pense
   que de la couleur ne ferait pas de mal » — 3 touches de couleur,
   volontairement PORTEUSES DE SENS plutôt que décoratives (jamais sur le
   texte des chiffres eux-mêmes, qui reste en --text pour la lisibilité —
   seule la ligne Total, déjà distincte via .rep-row-groupe-produit, a un
   texte coloré, inchangé ici) :
   1) fond légèrement teinté sur l'en-tête + soulignement plus marqué, pour
      le détacher franchement du corps du tableau (jusque-là seule la
      typo — majuscules, gris — le distinguait) ;
   2) zébrage léger des lignes (#fafaf9, déjà la teinte de survol de
      table.simple — réutilisée pour rester cohérente) pour guider l'œil
      sur les tableaux à beaucoup de lignes (Établissement, Service) ;
   3) un liseré de couleur en haut de CHAQUE colonne numérique, reprenant la
      couleur du graphique juste au-dessus dans la même page (orange = CA,
      comme le graphique "CA par …" ; gris = EHST/Nb stagiaires/Effectif
      réel, comme leurs graphiques) — relie visuellement tableau et
      graphique sans avoir à répéter une légende. */
table.simple.rep-table-cols thead th {
  background: var(--bg);
  border-bottom: 2px solid var(--ink-2);
}
table.simple.rep-table-cols tbody tr:nth-child(even):not(.rep-row-groupe-produit) td { background: #fafaf9; }
table.simple.rep-table-cols th.num-primary { border-top: 3px solid var(--primary); }
table.simple.rep-table-cols th.num-secondary { border-top: 3px solid var(--ink-2); }

/* Ligne sélectionnable au clic (ajoutée 26/08 : grille "Répartition" de la
   fiche produit — cliquer une section la rend active pour l'étape 3, sans
   dupliquer son affichage ailleurs). Surlignage recoloré en orange le même
   jour (repalette) : la teinte bleue d'origine jurait avec la nouvelle
   charte — liseré à gauche en plus du fond, pour rester repérable même pour
   un utilisateur peu sensible à la teinte.
   15/09, retour utilisateur : « peux-tu changer la couleur de la section
   sélectionnée ? [...] comme les sections sont maintenant colorées [couleur
   par groupe, voir `.groupe-couleur-N` plus bas, zip -168] on ne voit plus
   très bien la sélection dans certaines situations » — `--primary-light`
   (orange très pâle) gagne bien en priorité CSS sur une couleur de groupe
   (voir la note sur `.groupe-couleur-N`), mais restait, en LUMINOSITÉ, trop
   proche des 8 teintes pastel de groupe pour qu'une ligne sélectionnée saute
   encore aux yeux à côté d'elles. Remplacé un temps par un gris neutre avec
   liseré --ink sur tout le pourtour — jugé, au retour suivant, trop marqué
   (liseré) et trop terne (couleur).
   16/09, retour utilisateur : capture d'écran d'un autre ERP dont la
   sélection est « nette et précise » — fond orange plein (nettement plus
   saturé que `--primary-light`, donc jamais confondu avec les teintes
   pastel de groupe) + liseré fin sur tout le pourtour, dans un orange plus
   soutenu que le fond. Couleurs reprises telles quelles depuis cette
   capture (mesurées au pixel) plutôt que réinventées, à la demande
   explicite de l'utilisateur (« notamment la couleur de fond [...] je veux
   la même chose »).
   16/09 (suite), retour utilisateur sur ce même jour : « la bordure je la
   trouve trop marquée [...] laisser que la ligne verticale de la 1ère
   colonne » — liseré sur tout le pourtour supprimé, ne reste qu'un liseré
   à gauche (revient au principe du tout premier surlignage, avant le
   passage "pourtour complet" du 15/09, mais dans les couleurs de l'ERP de
   référence ci-dessus). Fond inchangé. */
table.simple.edit-table tbody tr.row-selectable { cursor: pointer; }
/* 16/09 (suite, correctif de fond) : le liseré ci-dessous était posé sur
   CHAQUE `td` de la ligne au lieu du bord gauche de la ligne entière —
   `box-shadow` s'applique par élément, donc il se répétait à chaque
   colonne, créant exactement les "lignes verticales séparatrices" signalées
   (confirmé par capture zoomée x4 de l'utilisateur, visible à chaque
   frontière de colonne, y compris entre 2 colonnes sans aucun champ). Posé
   maintenant uniquement sur la 1ère cellule de la ligne (`:first-child`),
   pour un unique liseré au bord gauche de la ligne, comme voulu à l'origine. */
table.simple.edit-table tbody tr.row-selected td { background: #ffc076; }
table.simple.edit-table tbody tr.row-selected td:first-child { box-shadow: inset 3px 0 0 #dea767; }
table.simple.edit-table tbody tr.row-selected:hover td { background: #f7b567; }
/* 16/09 (suite) : malgré les correctifs JS successifs (fond "puce" du
   select, fond figé après sauvegarde — voir editableTable.js), certains
   champs d'une ligne sélectionnée continuaient à s'afficher dans leur
   propre case blanche/beige plutôt que de se fondre dans le fond plein
   ci-dessus — retour utilisateur : « c'est l'affichage de la sélection qui
   me pose pb [...] ceux sont les séparations des colonnes » (les bords de
   ces cases, mis côte à côte sur la ligne, donnent l'impression de
   colonnes séparées au lieu d'un bloc uni). Cause exacte non confirmée
   (fond posé en JS, en `style="background: ..."` sur le champ lui-même,
   donc plus prioritaire que n'importe quelle règle CSS normale) — plutôt
   que de continuer à corriger au cas par cas chaque endroit du JS qui peut
   poser ce fond, ce `!important` reprend la main de façon générale et
   garantie sur TOUTE ligne sélectionnée, quel que soit le fond que le JS a
   pu y poser. Seule perte : le bref clignotement de confirmation
   (vert/rouge) après une sauvegarde ne se voit plus si la ligne est
   sélectionnée à ce moment précis — un compromis mineur face à l'objectif
   d'un bloc visuellement uni. Le anneau de focus (frappe en cours, voir
   plus haut) reste visible : exclu via :not(:focus). */
table.simple.edit-table tbody tr.row-selected td input:not(:focus) {
  background: transparent !important;
}
/* 16/09 (suite), retour utilisateur : « sur le champ même groupe que le
   liseré a disparu » — le border-color forcé transparent ci-dessous (posé en
   même temps que le fond, par prudence) supprimait aussi la bordure normale
   du select au repos, qui n'était pourtant pas en cause dans les lignes
   verticales séparatrices (cause réelle : voir `.row-selected td:first-child`
   plus haut). Seul le fond reste neutralisé, la bordure du select est
   restaurée. */
table.simple.edit-table tbody tr.row-selected td select {
  background: transparent !important;
}
/* Tri/filtre client des grilles (02/09, retour utilisateur sur "Ressources
   matérielles" : « c'est dommage que sur les entêtes on ne puisse pas les
   trier, voir filtrer par mot clés » — voir editableTable.js,
   `sortable`/`filterable`). En-tête triable : léger fond au survol pour
   signaler l'interactivité (le curseur "pointer" et le `title` sont déjà
   posés en JS). Zone de filtre : simple champ texte au-dessus du tableau,
   largeur volontairement contrainte (le style global `input[type=text]`
   n'impose pas de largeur, donc pleine ligne par défaut). */
table.simple.edit-table thead th.sortable-th:hover { background: var(--bg); }
.table-filter { margin-bottom: 8px; }
.table-filter input[type=text] { max-width: 320px; width: 100%; }

/* -- 08/09, retour utilisateur : « dans le cadre de la mutualisation,
   serait-il possible de mettre en évidence [...] toutes les affectations RH
   qui sont concernées par le produit en sélection. Dès que c'est à 0 %, il
   faudrait les mettre en grisé » (grille "Ressources déjà affectées",
   étape 3 de la fiche produit — voir mountFicheRhGrid). Cette grille
   n'affiche déjà QUE les lignes concernées par le produit actif (via sa
   section, y compris mutualisée) ; parmi elles, certaines n'ont en réalité
   AUCUNE part du coût attribuée à CE produit précis (tout est porté par
   le(s) autre(s) produit(s) qui partagent la même section) — `.rh-part-
   nulle` les estompe pour les distinguer d'un coup d'œil des lignes qui
   comptent réellement dans son total, sans les masquer ni les rendre
   inaccessibles (toujours lisibles/éditables — on peut par exemple leur
   forcer un % via "% mut/ressource"). Opacité plutôt que fond grisé : reste
   cohérent avec le zébrage/surlignage déjà utilisés par cette même table
   (`tr:hover`, `.row-selected` ci-dessus) sans leur rentrer en conflit. --*/
table.simple tbody tr.rh-part-nulle td { color: var(--text-muted); opacity: .55; }
table.simple tbody tr.rh-part-nulle:hover td { opacity: .85; }

/* -- 16/09, retour utilisateur : « mettre en évidence les affectations
   rattachées à la section en sélection » (même grille "Ressources déjà
   affectées", étape 3 de la fiche produit — voir mountFicheRhGrid).
   Jusqu'ici, sélectionner une section à l'étape 2 filtrait cette grille pour
   ne montrer QUE ses lignes ; elle affiche maintenant toujours TOUTES les
   lignes du produit actif, et surligne simplement celles rattachées à la
   section sélectionnée (`sectionForActivite(code_activite) ===
   ficheActiveRhSection`, voir scenario.js). Fond orange + liseré à gauche
   dans la même teinte que `.row-selected` ci-dessus, mais moins saturé :
   reprend le même langage visuel ("orange = en lien avec la section active")
   sans se confondre avec la ligne réellement sélectionnée (fond plein) dans
   la grille de la section, à l'étape 2.
   16/09 (suite), retour utilisateur : « il n'y a quasi pas de différence
   entre les lignes en surbrillance et celles qui ne le sont pas » — un 1er
   essai (#fff3e6, à mi-chemin entre le blanc et `.row-selected`) restait
   trop proche du fond blanc des cellules pour se voir d'un coup d'œil,
   surtout à côté du fond de page (--bg: #f4f1ee), lui aussi très clair.
   Resaturé nettement (#ffdbae, plus red proche de `.row-selected` que du
   blanc) pour qu'une ligne liée se distingue vraiment au premier regard,
   tout en restant clairement en retrait du fond plein `.row-selected`
   (#ffc076) pour ne jamais laisser croire qu'elle est, elle, sélectionnée.
   Peut se combiner avec `.rh-part-nulle` (une ligne peut être à la fois à
   part nulle pour le produit actif ET rattachée à la section sélectionnée) :
   les deux classes touchent des propriétés différentes (fond ici, opacité/
   couleur de texte là), donc pas de conflit — la ligne reste identifiable
   comme "liée" même estompée. Sans sélection, cette classe n'est jamais
   posée (voir `section_liee_selection` dans scenario.js) : comportement
   inchangé. --*/
table.simple tbody tr.affectation-section-liee td { background: #ffdbae; }
table.simple tbody tr.affectation-section-liee td:first-child { box-shadow: inset 3px 0 0 #dea767; }
table.simple tbody tr.affectation-section-liee:hover td { background: #ffcf96; }

/* 11/09 v2 (migration 022, retour utilisateur : « le mettre en couleur
   différente afin que je puisse identifier facilement et les créer dans
   AMMON ») : ligne d'un produit "manuel" dont l'entrée `produits_ammon` a
   été ajoutée automatiquement par BUDGEO — doublon provisoire, pas encore
   la vraie fiche officielle du catalogue AMMON/Pilote. `.produit-manuel-
   ammon` : tableau "Tous les produits" (web/js/scenario.js,
   mountProduitsGrid). `.ref-row-manuel` : même sémantique, réutilisée telle
   quelle (18/09) pour toute ligne "créée par BUDGEO, pas encore régularisée
   dans le vrai référentiel Pilote" d'une grille des Référentiels
   (web/js/referentiels.js) — le tableau "Catalogue AMMON"
   (`ajoute_manuellement`, 11/09 v2) ET, depuis le 18/09, le tableau
   "Sections" (`cree_par_cfp`, migration 006 — retour utilisateur : « peux
   mettre en évidence dans le référentiel Section les sections qui ont été
   générées par BudgeO »). Liseré à gauche (comme un repère d'alerte
   discret) plutôt qu'un fond plein sur toute la ligne : reste lisible
   par-dessus le zébrage/survol déjà en place, sans jamais se confondre avec
   `.subtotal-row`/`.row-selected` (fonds pleins, eux). */
table.simple tbody tr.produit-manuel-ammon td,
table.simple tbody tr.ref-row-manuel td {
  background: var(--flag-manuel-bg);
}
table.simple tbody tr.produit-manuel-ammon td:first-child,
table.simple tbody tr.ref-row-manuel td:first-child {
  box-shadow: inset 3px 0 0 var(--flag-manuel);
}

/* -- Couleur de ligne par groupe (13/09, "Action de formation" — retour
   utilisateur : « pourrais-tu appliquer une couleur aux lignes sections
   correspondant à un groupe. Une couleur par groupe pour les sections »).
   Grilles "Sections" (fiche produit ET admin, voir groupeColorMapForRows/
   groupeColorMapFiche/groupeColorMapAdmin dans web/js/scenario.js) : une
   section liée à un groupe (racine ou membre, via "Même groupe que") reçoit
   un fond de ligne — une teinte parmi 8, assignée par hachage du code de la
   racine, stable dans le temps pour un même groupe. Une section indépendante
   ne reçoit aucune de ces classes, fond inchangé.
   8 teintes volontairement à l'écart de --primary (déjà partout : boutons,
   puce "liée" de la colonne "Même groupe que" elle-même — la confondre avec
   une couleur de groupe aurait effacé la distinction entre "cette section EST
   liée" et "à CE groupe précis") et de --danger/--success/--flag-manuel
   (déjà réservés erreur/succès/repère AMMON). Fond plein (pas un simple
   liseré comme `.produit-manuel-ammon`) : c'est justement la demande — un
   repère visible d'un coup d'œil sur toute la ligne, pas seulement en
   bordure. Sélecteur à 2 classes (`table.simple` + `.groupe-couleur-N`) :
   plus spécifique que `table.simple tr:hover td` (reste visible au survol) ;
   moins spécifique que `table.simple.edit-table tbody tr.row-selected td`
   (une ligne sélectionnée garde la priorité visuelle, comme pour toute autre
   couleur de fond de ligne). --*/
table.simple tbody tr.groupe-couleur-1 td { background: #e8f0fb; }
table.simple tbody tr.groupe-couleur-2 td { background: #f1eafb; }
table.simple tbody tr.groupe-couleur-3 td { background: #e6f7f5; }
table.simple tbody tr.groupe-couleur-4 td { background: #fbe9f3; }
table.simple tbody tr.groupe-couleur-5 td { background: #eaeafd; }
table.simple tbody tr.groupe-couleur-6 td { background: #f3f6e3; }
table.simple tbody tr.groupe-couleur-7 td { background: #f5ece3; }
table.simple tbody tr.groupe-couleur-8 td { background: #e6f2f7; }

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
}
.badge.brouillon { background: var(--primary-light); color: var(--primary-hover); }
.badge.actif { background: var(--success-bg); color: var(--success); }
.badge.archive { background: #eee; color: #666; }

/* -- Marge brute/nette (02/09, retour utilisateur sur le 1er essai : « le
   choix du style n'est pas bon, visuellement le cfp sera perdu [...] Il faut
   changer ça, propose moi quelque chose de plus cohérent visuellement, moins
   compliqué » — le 1er essai empilait deux signaux à la fois : fond de tuile
   coloré + texte coloré sur la fiche produit, fond de ligne + liseré sur le
   récap. Reproposé sur maquette (2 options), l'utilisateur a choisi « option
   B partout » : un seul vocabulaire visuel, le badge déjà utilisé pour le
   statut du scénario (.badge.actif/.brouillon/.archive ci-dessus), réutilisé
   à l'identique pour la marge — plus de fond de tuile ni de fond de ligne. --*/
.stat .value.marge-positive { color: var(--success); }
.stat .value.marge-negative { color: var(--danger); }
.stat .badge { margin-top: 4px; }
.badge.marge-ok { background: var(--success-bg); color: var(--success); }
.badge.marge-deficit { background: var(--danger-bg); color: var(--danger); }

.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
  flex-wrap: wrap;
}
/* Voir .app-header-sticky ci-dessus (même retour utilisateur, 11/09) : se
   cale juste sous le bandeau BUDGEO (sticky lui aussi) et reste visible
   pendant tout le défilement d'un onglet — fond opaque nécessaire (--surface,
   comme .card qui la contient) puisque le contenu de l'onglet défile
   dessous. */
.tabs-sticky {
  position: sticky;
  top: var(--app-header-h);
  z-index: 45;
  background: var(--surface);
}
.tabs button {
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  padding: 10px 14px;
  color: var(--text-muted);
}
.tabs button.active {
  color: var(--primary);
  border-bottom-color: var(--primary);
  font-weight: 600;
}

.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* -- Onglet Produits (11/09) : bandeau "Vue d'ensemble" --
   Voir .app-header-sticky/.tabs-sticky plus haut (même retour utilisateur).
   Le bandeau reste collé juste sous .tabs-sticky, replié par défaut (corps
   #produits-overview-body cf. attribut [hidden]) — son contenu (le tableau
   "Tous les produits" déplacé tel quel) n'est pas modifié, seule sa
   présentation change.
   11/09 (retour en arrière, même jour) : une liste latérale des produits (à
   gauche de la fiche) avait été ajoutée puis retirée le jour même — retour
   utilisateur après essai en conditions réelles : « je souhaiterai revenir
   en arrière, c'est à dire conserver le bandeau fixe avec le récap des
   produits. il faut supprimer la liste des produits à gauche qui a altéré
   la visibilité de la ligne section ». Les règles .produits-split/
   .produits-side-list/.produits-side-main ont donc été retirées. */
.produits-overview-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 12px 14px;
  margin-bottom: 14px;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  position: sticky;
  top: calc(var(--app-header-h) + var(--tabs-h));
  z-index: 40;
}
.produits-overview-head .ov-icon { font-size: 14px; }
.produits-overview-head .ov-title { font-weight: 700; color: var(--ink); font-size: 13.5px; }
.produits-overview-head .ov-summary { font-size: 12px; color: var(--text-muted); margin-left: auto; }
.produits-overview-head .chev { color: var(--text-muted); font-size: 11px; transition: transform .15s; flex: 0 0 auto; }
.produits-overview-head .chev.open { transform: rotate(180deg); }
#produits-overview-body { margin-bottom: 14px; }

/* `overflow: hidden` (corrigé le 26/08, retour utilisateur : "je n'ai pas la
   possibilité de supprimer les produits via l'interface même en tant
   qu'admin") coupait purement et simplement les colonnes qui dépassaient de
   `.grid-wrap` sans aucun moyen de les atteindre — pas de barre de
   défilement, rien : la grille Produits (12 colonnes dont "CFP" 150px et
   "Nom" 240px) déborde de ~80px sur un écran 1440px de large, ce qui
   masquait ENTIÈREMENT la colonne de suppression (🗑, dernière colonne,
   40px) ; la grille Personnels, un peu moins large, n'en laissait dépasser
   qu'un éclat à peine cliquable. `overflow-x: auto` rend le débordement
   défilable horizontalement (comme les autres contenus larges de l'appli,
   ex. tableaux d'export) au lieu de le rendre invisible — corrige les 2
   grilles (et toute grille future assez large pour déborder), sans rien
   changer pour les grilles qui tiennent déjà dans leur conteneur. */
.grid-wrap { border: 1px solid var(--border); border-radius: var(--radius); overflow-x: auto; }

/* -- `.section-card`/`.section-card-head` (11/09 v6, retour utilisateur :
   « l'entête des sections n'est pas suffisamment visible [...] ta
   proposition avec la maquette A était très bien » — voir le bandeau
   "Sections de ce produit" de mockup_action_formation.html) : jusqu'ici un
   `.grid-wrap` n'était qu'un cadre autour du tableau, sans aucun titre qui
   lui soit propre — la seule indication de ce qu'il contenait restait le
   pavé d'instructions au-dessus, parfois loin (étape 2 de la fiche produit).
   `.section-card` habille `.section-card-head` + `.grid-wrap` d'un seul
   cadre commun (le `.grid-wrap` imbriqué perd son propre cadre juste
   dessous, pour ne pas doubler la bordure) : le titre reste collé au
   tableau qu'il annonce, quel que soit le reste de la page au-dessus. Repris
   pour l'instant sur les 2 grilles de sections/groupes (fiche produit +
   onglet admin "Répartition UO / Sections") — réutilisable pour toute autre
   grille qui gagnerait à afficher clairement ce qu'elle liste. --*/
.section-card { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.section-card-head {
  background: var(--surface);
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
.section-card .grid-wrap { border: none; border-radius: 0; }

/* Grille Produits (02/09, retour utilisateur : « la barre de défilement va
   poser pb, il faudrait arriver à faire sans ») : même après resserrement
   des largeurs de colonnes (voir mountProduitsGrid dans scenario.js), la
   grille débordait encore de `.grid-wrap` — en cause, `table-layout: auto`
   (par défaut, `table.simple` ci-dessus) qui laisse le navigateur élargir
   une colonne au-delà de la largeur déclarée dès que son contenu (un mot
   d'en-tête qui ne peut pas se couper, un <input type=number>...) réclame
   plus de place, et qui répartit l'espace EXCÉDENTAIRE de façon inégale
   entre colonnes plutôt que de respecter les largeurs demandées. Résultat :
   ~70 à 100px de débordement selon la largeur d'écran, malgré des largeurs
   qui, additionnées, tenaient largement dans `.grid-wrap`.
   `table-layout: fixed` force au contraire le navigateur à respecter
   strictement les largeurs posées par editableTable.js (`width`, ajouté au
   `minWidth` déjà présent) : un contenu trop long passe à la ligne ou
   défile DANS sa cellule au lieu d'élargir la colonne. Scopé à CETTE seule
   grille (pas un changement global de `table.simple.edit-table`, qui
   pourrait mal se comporter sur une grille aux colonnes moins généreuses
   ailleurs dans l'appli, ex. Personnels/Ressources matérielles, non
   vérifiées avec ce mode). */
#grid-produits table { table-layout: fixed; }
/* `overflow-wrap: anywhere` sur les en-têtes de CETTE grille : un mot
   d'en-tête trop long pour tenir sur sa colonne resserrée ("(CONSOLIDÉ)"...)
   ne peut normalement pas se couper à un espace puisqu'il n'en contient pas
   — sans ce réglage, il dépasse visuellement de sa cellule (et regonfle le
   scrollWidth du conteneur, réintroduisant un peu de défilement horizontal
   malgré `table-layout: fixed` ci-dessus). Coupe en dernier recours
   n'importe où dans le mot plutôt que de déborder (les largeurs de colonnes
   ci-dessus visent déjà à limiter les cas où ça arrive réellement). */
#grid-produits table thead th { overflow-wrap: anywhere; }

.msg {
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 14px;
  font-size: 13px;
  box-shadow: 0 4px 14px rgba(43, 42, 40, 0.18);
}
.msg.error { background: var(--danger-bg); color: var(--danger); }
.msg.success { background: var(--success-bg); color: var(--success); }
.msg.info { background: var(--primary-light); color: var(--primary-hover); }

/* -- 07/09, retour utilisateur : « ça reste en rouge et ça ne veut pas le
   prendre » — l'échec de sauvegarde d'une cellule (ex. "Chapitre (CR)" de
   la grille "Charges fixes (PMUT)", tout en bas d'une très longue page)
   AFFICHAIT bien un message d'erreur explicite via showMessage() (voir
   web/js/api.js), mais dans #messages, en HAUT de la page — invisible
   pour un utilisateur qui a scrollé jusqu'à une grille en bas de l'onglet
   admin, d'où l'impression que "ça ne prend pas" sans aucune explication
   visible (le message disparaît de lui-même après quelques secondes,
   voir showMessage). `position: sticky` le maintient visible en haut du
   viewport quel que soit le défilement dans la page, sans changer sa place
   dans le flux normal quand la page n'est pas scrollée. z-index élevé pour
   passer au-dessus des en-têtes de grille/listes déroulantes (combobox,
   z-index 20 max ailleurs dans ce fichier). Aucun encombrement visuel tant
   qu'il est vide (`#messages` ne prend de la place qu'une fois un `.msg`
   ajouté dedans). */
#messages {
  position: sticky;
  top: 0;
  z-index: 100;
}

.muted { color: var(--text-muted); }
.empty-state { text-align: center; padding: 40px 20px; color: var(--text-muted); }

.stat-row { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 6px; }
.stat { background: var(--bg); border-radius: var(--radius); padding: 12px 16px; min-width: 140px; }
.stat .value { font-size: 22px; font-weight: 700; }
.stat .label { font-size: 12px; color: var(--text-muted); }

/* -- 27/09, encart "Parcours moyen / ETP / GROUPES" en tête de l'étape 2 de
   la fiche produit (retour utilisateur : « il faudrait afficher ça de
   manière assez gros ») — variante agrandie de .stat ci-dessus, réservée à
   cet encart (les tuiles existantes — marge, totaux reporting... — gardent
   .stat seule, inchangées). Voir web/scenario.html #fiche-parcours-encart et
   refreshFicheParcoursEncart (web/js/scenario.js). */
.stat-lg { padding: 18px 22px; min-width: 200px; }
.stat-lg .label { font-size: 13px; font-weight: 600; color: var(--text); }
.stat-lg .value { font-size: 32px; }

/* -- Page dédiée "Carnet de commandes AMMON" (web/carnet-commandes.html,
   25/09) : retour utilisateur — « le carnet de commandes est très important
   pour la structure [...] il faudrait prévoir une interface à part avec une
   charte graphique plus adaptée » — sorti de la page Reporting générique
   (où il n'était qu'une carte parmi d'autres) vers sa propre page, avec un
   bandeau d'en-tête affirmé (fond anthracite, comme l'en-tête de l'appli) et
   des tuiles/graphiques légèrement surélevés (ombre douce), plutôt que le
   simple .form-card utilisé partout ailleurs — sans s'écarter de la palette
   orange/anthracite existante (voir le commentaire en tête de ce fichier). */
.cc-hero {
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius);
  padding: 24px 28px;
  margin-bottom: 22px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  box-shadow: 0 6px 18px rgba(43, 42, 40, 0.22);
}
.cc-hero-text h2 { margin: 0 0 6px; font-size: 21px; color: #fff; }
.cc-hero-text p { margin: 0; color: rgba(255, 255, 255, 0.72); font-size: 13.5px; max-width: 620px; }
.cc-hero-controls { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }
.cc-hero-controls label { color: rgba(255, 255, 255, 0.82); font-size: 12px; text-transform: uppercase; letter-spacing: .03em; display: block; margin-bottom: 6px; }
.cc-hero-controls select { min-width: 300px; }
/* 01/10 : les 2 filtres service/cfp (retour utilisateur : « filtrage par
   service ou par CFP [...] pour apporter du confort aux admins ») affichent
   des libellés courts (code service, code cfp) — 300px serait disproportionné,
   contrairement au sélecteur de scénario (nom + code + exercice, plus long). */
.cc-hero-controls select.cc-filtre-select { min-width: 170px; }
.cc-hero .btn.primary { background: var(--primary); border-color: var(--primary); }

.cc-kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin-bottom: 22px; }
.cc-kpi { background: var(--surface); border: 1px solid var(--border); border-top: 3px solid var(--primary); border-radius: var(--radius); padding: 14px 16px; box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10); }
.cc-kpi.cc-kpi-muted { border-top-color: var(--ink-2); }
.cc-kpi-value { font-size: 25px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.15; }
.cc-kpi-label { font-size: 11.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; margin-top: 5px; }

.cc-charts { display: grid; grid-template-columns: 1fr; gap: 18px; margin-bottom: 22px; }
@media (min-width: 980px) { .cc-charts { grid-template-columns: 1fr 1fr; } }
.cc-chart-panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10); }
.cc-chart-panel h3 { margin: 0 0 12px; font-size: 12.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
/* 28/09, retour utilisateur : « les graphiques puissent être exportés en
   image » — bouton ajouté par web/js/chart-export.js (initChartExports),
   commun à .cc-chart-panel ET .rep-chart-panel (contrairement au reste de la
   charte de ces 2 familles de panneaux, volontairement dupliqué — voir plus
   bas —, ce bouton est un mécanisme générique sans raison de diverger). */
.chart-panel-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 12px; }
.chart-panel-header h3 { margin: 0; }
.chart-export-btn { padding: 3px 10px; font-size: 11.5px; flex-shrink: 0; white-space: nowrap; }
/* 26/09, 3e graphique ajouté ("Facturé vs à facturer") : une simple barre
   empilée n'a pas besoin d'être contrainte à une colonne étroite comme les
   graphiques par produit/service — prend toute la largeur disponible. */
@media (min-width: 980px) { .cc-chart-panel--large { grid-column: 1 / -1; } }
.cc-section-title { margin: 0 0 12px; font-size: 12.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }

/* 26/09, retour utilisateur : « les produits non référencés au budget [...]
   il faut [...] les implanter dans le 1er tableau » — un produit conventionné
   dans AMMON mais absent du budget de ce scénario (prévisionnel à 0€, voir
   server/lib/reporting.js buildCarnetCommandes/horsScenario) est signalé par
   ce badge plutôt que relégué dans un 2nd tableau séparé. */
.cc-badge-hors-scenario {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
  white-space: nowrap;
}
.cc-row-hors-scenario td { color: var(--text-muted); }

/* 08/10, retour utilisateur : « il faudrait exclure du conventionné les
   produits en PZ [...] il faudrait les afficher pour info » — un produit
   "PZ..." (frais annexe déjà reversé par ailleurs, voir
   server/lib/reporting.js estFraisAnnexeReverse) reste une ligne normale du
   tableau (jamais masquée, contrairement à ce que son nom pourrait suggérer),
   simplement signalée par ce badge — bleu `--info`, teinte DISTINCTE de
   `--danger` (déjà utilisé pour "hors scénario" juste au-dessus, un statut
   différent : absence du budget, pas une exclusion volontaire des totaux). */
.cc-badge-exclu-totaux {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--info-bg);
  color: var(--info);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
  white-space: nowrap;
}
.cc-row-exclu-totaux td { font-style: italic; }

/* 08/10, retour utilisateur : « il y a toujours des différences de
   codification entre le produit conventionné et le produit du budget
   prévisionnel [...] un tableau de correspondance » — action "Associer à…"
   affichée sous le libellé de CHAQUE ligne "hors scénario" ci-dessus (voir
   web/js/carnet-commandes.js). Compacte et sur sa propre ligne (jamais
   assez de place à côté du badge pour un select + bouton), d'où le
   `display: flex` + `margin-top`. */
.cc-correspondance-action { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.cc-correspondance-select { font-size: 12px; padding: 2px 4px; max-width: 220px; }
.cc-correspondance-btn, .cc-correspondance-retirer { font-size: 12px; padding: 2px 8px; }

/* 26/09 (même jour), retour utilisateur sur le 1er correctif ci-dessus :
   « les sous totaux par LP ne sont pas mis en évidence, le choix de la
   couleur de fond n'est pas efficiente [...] et il manque les totaux
   globaux ». Hiérarchie à 3 niveaux, classes DÉDIÉES à cette page (jamais
   `.subtotal-row*`, utilisées par "Tous les produits" — voir
   web/js/carnet-commandes.js pour le pourquoi de cette séparation) :
     - `--lp`   : sous-total de granularité fine (Ligne de produit) — fond
                  neutre gris-beige, filet anthracite, TEXTE NORMAL (pas
                  atténué comme l'ancien `.subtotal-row--lp` : c'est
                  précisément ce qui le rendait peu visible).
     - `--service` : sous-total de regroupement (Service) — fond orange pâle,
                  filet orange, plus affirmé que le niveau LP.
     - `--total`   : total général (tout confondu) — bandeau anthracite plein,
                  texte blanc : la ligne la plus visible du tableau. */
.cc-subtotal-row td { font-weight: 700; border-top: 1px solid var(--border); }
.cc-subtotal-row--lp td { background: rgba(61, 59, 56, 0.07); border-top: 1px solid var(--ink-2); color: var(--text); }
.cc-subtotal-row--service td { background: var(--primary-light); border-top: 2px solid var(--primary); color: var(--ink); }
.cc-subtotal-row--total td {
  background: var(--ink);
  color: #fff;
  border-top: 2px solid var(--primary);
  font-size: 13.5px;
  padding-top: 10px;
  padding-bottom: 10px;
}

/* -- Page dédiée "Suivi financier" (web/suivi-financier.html, 01/10) : retour
   utilisateur — « il faut historiser à chaque import [...] un onglet suivi
   financier (même charte graphique que le carnet de commandes) ».
   `.sf-hero*`/`.sf-kpi*`/`.sf-charts`/`.sf-chart-panel`/`.sf-section-title` :
   DUPLIQUÉES à l'identique de `.cc-hero*`/`.cc-kpi*`/`.cc-charts`/
   `.cc-chart-panel`/`.cc-section-title` ci-dessus plutôt que réutiliser ces
   classes — même raisonnement de découplage volontaire que `.rep-*` face à
   `.cc-*` (voir plus bas) : la charte visuelle est partagée VOLONTAIREMENT à
   l'instant T, mais les 2 pages n'ont pas vocation à rester couplées si l'une
   reçoit un ajustement propre à son contenu plus tard. `.chart-export-btn`
   (bouton d'export PNG, mécanisme générique sans raison de diverger, voir
   plus haut) reste commun : `.sf-chart-panel` a simplement été ajoutée au
   sélecteur de web/js/chart-export.js (initChartExports), aux côtés de
   `.cc-chart-panel`/`.rep-chart-panel`. -- */
.sf-hero {
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius);
  padding: 24px 28px;
  margin-bottom: 22px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  box-shadow: 0 6px 18px rgba(43, 42, 40, 0.22);
}
.sf-hero-text h2 { margin: 0 0 6px; font-size: 21px; color: #fff; }
.sf-hero-text p { margin: 0; color: rgba(255, 255, 255, 0.72); font-size: 13.5px; max-width: 620px; }
.sf-hero-controls { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }
.sf-hero-controls label { color: rgba(255, 255, 255, 0.82); font-size: 12px; text-transform: uppercase; letter-spacing: .03em; display: block; margin-bottom: 6px; }
.sf-hero-controls select { min-width: 300px; }
.sf-hero-controls select.sf-service-select { min-width: 220px; }

.sf-kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin-bottom: 22px; }
.sf-kpi { background: var(--surface); border: 1px solid var(--border); border-top: 3px solid var(--primary); border-radius: var(--radius); padding: 14px 16px; box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10); }
.sf-kpi-value { font-size: 25px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.15; }
.sf-kpi-label { font-size: 11.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; margin-top: 5px; }
.sf-kpi-ecart { font-size: 12px; margin-top: 4px; font-variant-numeric: tabular-nums; color: var(--text-muted); }

.sf-charts { display: grid; grid-template-columns: 1fr; gap: 18px; margin-bottom: 22px; }
@media (min-width: 980px) { .sf-charts { grid-template-columns: 1fr 1fr; } }
.sf-chart-panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10); }
.sf-chart-panel h3 { margin: 0 0 12px; font-size: 12.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
@media (min-width: 980px) { .sf-chart-panel--large { grid-column: 1 / -1; } }
.sf-section-title { margin: 0 0 12px; font-size: 12.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
/* Écart (€) vs l'import précédent — même sémantique que .marge-positive/
   .marge-negative ailleurs dans l'appli (hausse = plutôt bonne nouvelle côté
   activité, baisse = à surveiller), jamais appliquée au prévisionnel (son
   évolution ne dépend pas de l'import AMMON, pas de jugement de valeur ici). */
.sf-ecart-positif { color: var(--success); }
.sf-ecart-negatif { color: var(--danger); }

/* -- Solde recettes - dépenses (02/10, retour utilisateur : « ajouter la
   partie dépense en corrélation avec les recettes ») : bandeau distinct des
   `.sf-kpi` ci-dessus — recettes et dépenses viennent de 2 imports
   indépendants (cadences différentes), chacun avec SA PROPRE date, d'où les
   2 dates affichées séparément plutôt qu'une date unique qui laisserait
   croire à un calcul synchronisé. Volontairement plus large/mis en avant
   qu'un `.sf-kpi` : c'est l'indicateur de synthèse de toute la page. -- */
.sf-solde { background: var(--surface); border: 1px solid var(--border); border-top: 3px solid var(--ink); border-radius: var(--radius); padding: 18px 22px; margin-bottom: 22px; display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10); }
.sf-solde-label { font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; margin-bottom: 6px; }
.sf-solde-value { font-size: 30px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.sf-solde-detail { font-size: 12.5px; color: var(--text-muted); display: flex; flex-direction: column; gap: 4px; text-align: right; }

/* -- Page "Reporting" (web/reporting.html) : Annexe DDF et rapport Coordo &
   Assistante, retour utilisateur du 27/09 — « tu as une seule page pour tout
   et les collègues ne descendent pas en bas de la page [...] j'aimerai que
   tu adoptes la logique du regroupement par produit [...] j'ai bien aimé le
   carnet de commande et j'aimerai que tu implantes quelques graphiques avec
   la même charte graphique que le carnet de commande ».
     - `.rep-chart-panel`/`.rep-charts`/`.rep-section-title` : DUPLIQUÉES à
       l'identique de `.cc-chart-panel`/`.cc-charts`/`.cc-section-title`
       ci-dessus plutôt que réutiliser directement ces classes — même
       raisonnement que `.cc-subtotal-row*` face à `.subtotal-row*` (voir plus
       haut) : la charte visuelle est partagée VOLONTAIREMENT à l'instant T,
       mais les 2 pages n'ont pas vocation à rester couplées si l'une reçoit
       un ajustement propre à son contenu plus tard (ex. les 3 rounds de
       retours reçus sur le Carnet de commandes avant que sa présentation ne
       se stabilise).
     - `.rep-row-groupe-produit` : ligne d'EN-TÊTE de produit (regroupement
       "produit puis, en dessous, ses lignes DDF/Coordo/Assistante" — dans le
       sens INVERSE des sous-totaux `.cc-subtotal-row*` ci-dessus, qui closent
       un groupe au lieu de l'ouvrir). Un seul niveau de regroupement ici
       (pas de hiérarchie à 3 niveaux comme le Carnet de commandes) : reprend
       le même poids visuel que `.cc-subtotal-row--service` (le niveau de
       regroupement, pas le total général). -- */
.rep-charts { display: grid; grid-template-columns: 1fr; gap: 18px; margin: 16px 0 22px; }
@media (min-width: 980px) { .rep-charts { grid-template-columns: 1fr 1fr; } }
.rep-chart-panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10); }
.rep-chart-panel h3 { margin: 0 0 12px; font-size: 12.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
@media (min-width: 980px) { .rep-chart-panel--large { grid-column: 1 / -1; } }
.rep-section-title { margin: 18px 0 12px; font-size: 12.5px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
.rep-row-groupe-produit td { font-weight: 700; background: var(--primary-light); border-top: 2px solid var(--primary); color: var(--ink); }

/* -- 27/09 (même jour), retour utilisateur suite au 1er ajustement ci-dessus :
   « pour le choix du scénario il faut appliquer le même bandeau que le
   carnet de commandes, je le trouve chouette [...] les graphiques sont trop
   gros, on ne voit pas le libellé des produits, les couts sont exprimés en
   K€ [...] il faut qu'ils soient plus claquant [...] peux-tu faire un menu
   pour avoir des pages séparées ». Conséquences :
     - `.rep-hero*` : DUPLIQUÉE à l'identique de `.cc-hero*` ci-dessus, même
       raisonnement de découplage volontaire que `.rep-chart-panel` face à
       `.cc-chart-panel` (voir plus haut) — appliquée au sélecteur de
       scénario de web/reporting.html (page menu) ET des 2 nouvelles pages
       dédiées ci-dessous.
     - Page "Reporting" (web/reporting.html) : devient une page MENU (mêmes
       raisons que la sortie du Carnet de commandes le 25/09) — le détail
       Annexe DDF et Coordo & Assistante est déplacé chacun vers sa propre
       page (`web/reporting-ddf.html`, `web/reporting-coordo-assistante.html`,
       voir leurs web/js/*.js dédiés). `.rep-menu`/`.rep-menu-card` : tuiles
       de navigation vers ces 2 pages, dans l'esprit `.cc-kpi` mais cliquables.
     - Graphiques (web/js/reporting-charts.js, nouveau fichier partagé par les
       2 pages dédiées ci-dessus — regroupe ce qui était réparti dans
       web/js/reporting.js) : montants ré-exprimés en € pleins (plus de
       "k€"/"M€" sur les barres), piste de fond claire derrière chaque barre
       (repère visuel même pour une petite valeur), largeur de la colonne des
       libellés augmentée + libellés moins tronqués, et le graphique "Coordo
       vs Assistante par produit" repasse en barres HORIZONTALES (comme les
       autres graphiques de la page) au lieu de barres verticales à libellés
       pivotés à -40°, illisibles. -- */
.rep-hero {
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius);
  padding: 24px 28px;
  margin-bottom: 22px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  box-shadow: 0 6px 18px rgba(43, 42, 40, 0.22);
}
.rep-hero-text h2 { margin: 0 0 6px; font-size: 21px; color: #fff; }
.rep-hero-text p { margin: 0; color: rgba(255, 255, 255, 0.72); font-size: 13.5px; max-width: 620px; }
.rep-hero-controls { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }
.rep-hero-controls label { color: rgba(255, 255, 255, 0.82); font-size: 12px; text-transform: uppercase; letter-spacing: .03em; display: block; margin-bottom: 6px; }
.rep-hero-controls select { min-width: 300px; }
.rep-hero .btn.primary { background: var(--primary); border-color: var(--primary); }

.rep-menu { display: grid; grid-template-columns: 1fr; gap: 16px; margin-bottom: 22px; }
@media (min-width: 780px) { .rep-menu { grid-template-columns: 1fr 1fr; } }
.rep-menu-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--primary);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: 0 4px 14px rgba(43, 42, 40, 0.10);
  text-decoration: none;
  color: var(--text);
  transition: box-shadow .15s ease, transform .15s ease;
}
.rep-menu-card:hover { box-shadow: 0 8px 22px rgba(43, 42, 40, 0.16); transform: translateY(-1px); }
.rep-menu-card h3 { margin: 0 0 6px; font-size: 16px; color: var(--ink); }
.rep-menu-card p { margin: 0; font-size: 13px; color: var(--text-muted); }
.rep-menu-card .rep-menu-arrow { float: right; color: var(--primary); font-size: 18px; }
.rep-menu-card.is-disabled { opacity: .5; pointer-events: none; }
.rep-page-back { display: inline-block; margin-bottom: 14px; font-size: 13px; }

/* 07/09, tuile "Charge de structure imputée" (étape 5 fiche produit) :
   précision courte sous la valeur pour rappeler qu'elle ne compte pas dans
   la marge brute, sans avoir à recolorer toute la tuile (garder .stat neutre
   et réserver la couleur sémantique à .marge-positive/.marge-negative
   ci-dessus, qui portent un vrai signal bon/mauvais — la charge de structure
   n'est ni l'un ni l'autre, juste une nature de charge différente). */
.stat .hint { font-size: 11px; color: var(--text-muted); margin-top: 2px; }

/* -- 07/09, retour utilisateur : « cette zone n'est pas terrible en terme de
   présentation [...] les colonnes sont pas bien alignées » (étape 5 "Charges
   fixes générales" de la fiche produit, captures à l'appui). `.num` : classe
   dédiée pour toute cellule numérique de table.simple — text-align:right
   posé une fois pour toutes (au lieu d'un style inline par cellule comme
   avant) + chiffres tabulaires (`font-variant-numeric: tabular-nums`) pour
   que les chiffres d'une colonne s'empilent exactement à la même largeur
   d'une ligne à l'autre au lieu de "danser" selon les chiffres qu'ils
   contiennent — c'est ce qui donnait l'impression de colonnes mal alignées
   même quand elles étaient déjà right-alignées. Générique, réutilisable par
   toute grille de l'appli qui affiche des montants en table.simple. --*/
table.simple td.num, table.simple th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* Table de ventilation par chapitre (étape 5) : `table-layout: fixed` +
   largeurs de colonnes explicites pour que les 4 colonnes gardent une
   largeur stable quel que soit le nombre de chapitres/montants affichés
   (sans ça, `table-layout: auto` par défaut réajuste les largeurs à chaque
   rendu selon le contenu le plus long, ce qui déplace les colonnes d'un
   chapitre à l'autre). */
.fiche-pmut-chapitres { table-layout: fixed; }
.fiche-pmut-chapitres th:first-child, .fiche-pmut-chapitres td:first-child { width: 34%; }
.fiche-pmut-chapitres th:not(:first-child), .fiche-pmut-chapitres td:not(:first-child) { width: 22%; }

/* Bloc "Détail du calcul" (étape 5) : totaux du SCÉNARIO (dénominateurs
   EHST/CA, totaux par catégorie) repoussés en retrait visuel derrière un
   <details> replié par défaut — ces chiffres servent à comprendre le calcul
   mais ne concernent pas directement CE produit, contrairement aux tuiles
   .stat et à la ventilation par chapitre ci-dessus qui restent visibles
   d'emblée. Natif HTML (<details>/<summary>), pas de JS nécessaire pour le
   plier/déplier. */
.fiche-pmut-detail { margin-top: 12px; }
.fiche-pmut-detail summary {
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
  color: var(--text-muted);
  padding: 4px 0;
}
.fiche-pmut-detail summary:hover { color: var(--text); }
.fiche-pmut-detail[open] summary { margin-bottom: 8px; }
.fiche-pmut-detail table.simple td, .fiche-pmut-detail table.simple th { font-size: 12px; }

.upload-grid, .form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 4px 16px;
}
/* 11/09 v2 (retour utilisateur : « tout afficher sur une ligne ») :
   Code produit/Nom/CFP(admin)/% CA fonds publics/Type/Ligne produit de la
   fiche produit (étape 1) — `auto-fit` plutôt que le `auto-fill` par défaut
   de `.form-grid` ci-dessus : un champ masqué (`display:none`, voir
   showFpState dans scenario.js) libère alors vraiment sa place au lieu de
   laisser une colonne vide, et les champs visibles se partagent toute la
   largeur disponible. `minmax(150px,1fr)` (plus étroit que les 200px par
   défaut) pour que les 6 champs tiennent sur une seule ligne à largeur
   d'écran normale ; passent à la ligne suivante seulement en dessous de
   ~150px par champ (écran étroit). --*/
#fp-identite-fields { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.form-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}
.form-card h3 { margin: 0 0 12px; font-size: 15px; }

/* -- Étapes de saisie mises en évidence (26/08, retour utilisateur : "il faut
   que ça claque et que cela mette en évidence les étapes de saisie") : les 3
   titres "1. Choisir ou démarrer un produit" / "2. Ventiler par section..." /
   "3. Affecter des ressources humaines" de la fiche produit (web/scenario.html)
   utilisent désormais un badge rond orange numéroté (.step-num) au lieu d'un
   simple "1." en texte — repère visuel fort et immédiat pour se situer dans
   le parcours en 3 étapes, cohérent avec le reste de la charte orange/gris. --*/
.step-title { display: flex; align-items: center; gap: 10px; }
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--primary);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
}
.field input, .field select { width: 100%; }
/* Exception : les cases à cocher/boutons radio (ex. "Section" de l'étape 3
   fiche produit, ajouté le 26/08) ne doivent pas s'étirer à 100% de large
   comme un champ texte — ça éloigne visuellement le rond de son libellé. */
.field input[type="checkbox"], .field input[type="radio"] { width: auto; }
.field .req { color: var(--danger); }
.upload-item {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 12px;
}
.upload-item.loaded { border-color: var(--success); background: var(--success-bg); }
.upload-item .file-name { font-size: 12px; color: var(--text-muted); margin-top: 4px; word-break: break-all; }

/* Tabulator overrides légers pour matcher le style de l'appli */
.tabulator { border: none; font-size: 13px; }
.tabulator-row.tabulator-row-even { background: #fafbfc; }

/* -- Combobox "fait maison" (28/08) : remplace <input list>+<datalist> pour
   les champs "Produit (catalogue AMMON)" et "Ressource", dont le rendu natif
   du navigateur s'est révélé peu fiable (aucune suggestion sous Firefox,
   fonctionnait sous Edge — voir web/js/scenario.js, attachCombobox()). Un
   simple <div> positionné sous le champ, identique quel que soit le
   navigateur. Le `.field` parent reçoit `position: relative` en JS au premier
   attachement (attachCombobox), pas ici, pour ne pas affecter les autres
   champs `.field` de l'appli qui n'en ont pas besoin. --*/
.combobox-list {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 20;
  margin-top: 2px;
  max-height: 240px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 4px 14px rgba(43, 42, 40, 0.14);
}
.combobox-item {
  padding: 7px 10px;
  font-size: 13px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.combobox-item:last-child { border-bottom: none; }
.combobox-item:hover, .combobox-item.active { background: var(--primary-light); }
.combobox-empty { padding: 7px 10px; font-size: 13px; color: var(--text-muted); font-style: italic; }
.tabulator-header { background: var(--bg); }

/* -- Lignes de sous-total (28/08) : tableau "Tous les produits", groupé par
   service puis ligne produit (voir mountProduitsGrid dans web/js/scenario.js
   et le support `__subtotal__` de createEditableTable dans editableTable.js).
   `--service` (sous-total de service, regroupe toutes ses lignes produit) est
   plus marqué que `--lp` (sous-total intermédiaire par ligne produit). --*/
.subtotal-row td { font-weight: 700; background: var(--bg); border-top: 1px solid var(--border); }
.subtotal-row--lp td { color: var(--text-muted); }
.subtotal-row--service td { background: var(--primary-light); border-top: 2px solid var(--primary); }

/* -- Ligne dépliable (`expandable` de createEditableTable, 08/09) : 2 zones
   de complétude par section (Janv.->Août / Sept.->Déc.) sur la grille
   "Répartition par section" de la fiche produit (étape 2) — voir
   ficheRepartitionGrid dans web/js/scenario.js. Retour utilisateur :
   « je ne pense pas que tu puisses faire tenir ça sur 1 ligne » — maquette
   comparant 3 options validée : « on part sur ta recommandation, c'est
   top » (option "ligne dépliable"). Styles portés depuis la maquette
   (/tmp/mockup-periodes-section.html), en reprenant les tokens réels de
   l'app plutôt que les couleurs figées de la maquette (qui devait, elle,
   rester lisible quel que soit le thème du visualiseur d'artefact). --*/
.expand-toggle-btn {
  width: 22px;
  height: 22px;
  border-radius: 5px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: 11px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
  line-height: 1;
}
.expand-toggle-btn:hover { border-color: var(--primary); color: var(--primary); }
tr.detail-row td { padding: 0; border-bottom: 1px solid var(--border); background: var(--bg); }
.detail-inner { padding: 14px 16px 16px 40px; display: flex; gap: 16px; flex-wrap: wrap; align-items: stretch; }
/* Conteneur des cartes "Session/Promo" (16/09, migration 024 — remplace les
   2 cartes fixes P1/P2 par une liste à nombre variable, voir
   renderPeriodesDetail dans scenario.js) : même comportement flex-wrap que
   .detail-inner (les cartes s'enchaînent et repassent à la ligne selon la
   largeur disponible) mais sans son padding (déjà porté par .detail-inner
   sur le conteneur externe) et toujours en pleine largeur de ce dernier
   (flex-basis 100%), pour que "+ Ajouter une promotion" et le reste du
   détail restent sous les cartes plutôt qu'à côté d'une carte isolée. */
/* Grille (pas flex) : avec flex-wrap, une dernière ligne incomplète (ex. 1
   carte seule) s'étirait pour remplir toute la largeur (flex-grow hérité de
   "flex: 1 1 240px") et prenait une "forme" différente des cartes pleines
   (retour utilisateur 16/09). En grid, les colonnes sont calculées une fois
   pour tout le conteneur : une carte isolée sur la dernière ligne garde
   exactement la même largeur que les autres, le reste de la ligne restant
   simplement vide plutôt que d'être comblé par cette carte. */
.promotions-cards-wrap { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; flex: 1 1 100%; }
.period-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px 12px; min-width: 0; }
/* La carte "Informations complémentaires" est un .period-card autonome,
   enfant DIRECT de .detail-inner (pas de .promotions-cards-wrap) : elle doit
   garder sa pleine largeur comme .duree-parcours-readout juste au-dessus,
   contrairement aux cartes "Session/Promo" qui sont désormais dimensionnées
   par la grille de .promotions-cards-wrap et n'ont plus besoin de flex-basis. */
.detail-inner > .period-card { flex: 1 1 100%; }
.period-card .period-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--primary-hover); margin-bottom: 8px; }
/* En-tête d'une carte "Session/Promo" (16/09) : titre + bouton 🗑 de
   suppression de CETTE promotion (voir onDeletePromotion dans scenario.js) —
   remplace le simple <div class="period-title"> autonome d'avant la
   migration 024 pour les cartes de promotion ; la carte "Informations
   complémentaires" (sans bouton supprimer, rien à voir avec les promotions)
   garde son <div class="period-title"> seul, inchangé. */
.period-card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.period-card-head .period-title { margin-bottom: 0; }
/* Appellation d'une carte Session/Promo (17/09 : <select> de suggestions ;
   21/09, migration 026 : remplacé par un simple champ TEXTE LIBRE — retour
   utilisateur, « je pense qu'il est préférable de laisser le CFP les
   appeler comme il le souhaite », voir labelInput() dans scenario.js).
   Garde la couleur/le poids de .period-title mais sans la MAJUSCULE/
   l'espacement de lettres (peu lisible sur un libellé long), et un peu plus
   compact que l'<input> générique de l'appli. */
.period-card-head input.period-title-input {
  text-transform: none;
  letter-spacing: normal;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--primary-hover);
  padding: 3px 6px;
  border-color: var(--border);
  flex: 1 1 auto;
  min-width: 0;
}
/* Lien "Poursuit..." + "Même effectif" entre 2 cartes (21/09, migration 026)
   — le <select> de lien réutilise .period-field (déjà stylé pour <input>
   ci-dessous, étendu ici aux <select>) ; la case à cocher reprend le même
   esprit que .info-complementaire-row (texte 12.5px), pleine largeur sous
   les champs de la carte. */
.period-field select {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 5px 7px;
  font-size: 12.5px;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
}
.period-field select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-light); }
.period-chk { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--primary-hover); margin-top: 8px; }
.period-chk input { width: auto; }
.period-card-del {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 4px;
  opacity: .55;
}
.period-card-del:hover { opacity: 1; background: var(--bg); }
.period-fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
/* 22/09, retour utilisateur (capture d'écran) : sur une carte étroite (2
   cartes/ligne ou plus), "Durée du parcours (h)" passe sur 2 lignes alors
   que ses voisines de la même rangée de grille ("Nb stagiaires", "PVU (€)")
   restent sur 1 — chaque <label> n'occupant alors QUE la hauteur de son
   propre texte, le champ <input> qui le suit démarre plus bas pour la carte
   au libellé le plus long, désalignant visuellement toute la rangée. Fixer
   une hauteur de réserve à 2 lignes (line-height 1.3 × 2 = 2.6em, relatif à
   la taille de police du label lui-même) sur TOUS les labels — y compris
   ceux tenant sur 1 seule ligne — pousse alors l'ensemble des champs d'une
   même rangée à démarrer à la même position, qu'ils wrappent ou non. Testé
   jusqu'à la largeur de carte minimale (240px, `.promotions-cards-wrap`) :
   aucun libellé ne dépasse 2 lignes à cette largeur. */
.period-field label { display: block; font-size: 10.5px; line-height: 1.3; min-height: 2.6em; color: var(--text-muted); margin-bottom: 3px; }
.period-field input {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 5px 7px;
  font-size: 12.5px;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
}
.period-field input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-light); }
/* Carte "Informations complémentaires" du détail dépliable ▸ (12/09,
   TEP/Partagée avec/% mut/section — voir renderPeriodesDetail dans
   scenario.js) : réutilise .period-card telle quelle pour rester cohérente
   avec les cartes "Session/Promo" juste à côté (nombre variable depuis le
   16/09, migration 024) ; seul l'espacement entre ses 2 premières lignes
   (TEP, Partagée avec) est propre à cette carte, les autres cartes n'empilant
   que des .period-field. */
.info-complementaire-row { font-size: 12.5px; margin-bottom: 8px; }
/* EHST calculée par période (08/09, migration 018 : durée du parcours ×
   nb stagiaires, remplace l'ancienne saisie directe de l'EHST par période) —
   lecture seule, affichée sous les 3 champs de la carte. */
.period-ehst-readout { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--border); font-size: 11px; font-weight: 600; color: var(--ink-2); }
/* CA de la carte (30/09, retour utilisateur : « afficher en gras ou d'une
   couleur visible le CA à l'intérieur de la carte ») — même couleur que le
   CA consolidé du produit (.fiche-ca-highlight), mais taille réduite car
   affiché à l'intérieur d'un .period-ehst-readout compact plutôt qu'en pied
   de fiche. */
.period-ca-highlight { color: var(--primary); }
/* Case "Prise de risque (CFP)" (30/09) : réutilise .period-chk telle quelle
   (même gabarit que "Même effectif") — seule la couleur du texte est propre
   à cette case, pour rester lisible même une fois la carte passée en fond
   rouge pastel (.period-card--risque ci-dessous). */
.period-chk--risque { color: var(--danger); }
/* Carte "Session/Promo" cochée "Prise de risque (CFP)" (30/09, retour
   utilisateur : « il faudrait que la carte change instantanément de couleur
   (rouge pastel par exemple) ») — réutilise --danger/--danger-bg, déjà la
   convention de l'appli pour signaler un état à surveiller (voir
   .badge.marge-deficit, .msg.error), plutôt qu'introduire une nouvelle
   teinte non éprouvée. */
.period-card--risque { background: var(--danger-bg); border-color: var(--danger); }
.consol-arrow { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--text-muted); font-size: 20px; padding: 0 2px; }
.consol-hint { font-size: 10.5px; color: var(--text-muted); text-align: center; max-width: 100px; margin-top: 2px; }
/* Valeur consolidée calculée (verrouillée dès qu'au moins une période est
   saisie) — même esprit que la colonne TEP (`color: var(--primary)` posé en
   ligne par editableTable.js) mais un peu de contexte visuel en plus, vu que
   la cellule était éditable l'instant d'avant. */
.consolidated-computed { display: inline-flex; align-items: center; gap: 4px; color: var(--ink-2); font-weight: 600; }
.consolidated-computed .lock-icon { font-size: 10px; opacity: .6; }

/* "Durée du parcours" consolidée (08/09, retour utilisateur : « cette notion
   de parcours est intéressante et manque à la ligne section [...] sans
   casser l'affichage actuel ») — repère en lecture seule affiché UNIQUEMENT
   dans la ligne dépliée (voir dureeParcoursConsolidee/renderPeriodesDetail
   dans scenario.js), délibérément PAS une nouvelle colonne de la grille
   elle-même. Pleine largeur, sous les cartes "Session/Promo" (nombre
   variable depuis le 16/09, migration 024) + la flèche de consolidation. */
.duree-parcours-readout {
  flex: 1 1 100%;
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--primary-hover);
}

/* "Produit consolidé" (résumé sous la grille de répartition) : le CA est
   placé en dernier et mis en évidence (08/09, retour utilisateur : « et en
   bout de ligne le CA (plus en évidence) »), pour ressortir visuellement
   des autres chiffres consolidés (durée, EHST, PVU moyen, stagiaires). */
.fiche-ca-highlight {
  font-size: 15px;
  color: var(--primary);
}

/* -- Grille "Répartition par section" (fiche produit, étape 2), 08/09 :
   retour utilisateur : « peux tu faire en sorte que la ligne section tienne
   sur la page sans passer par la barre de défilement ? ». `table-layout:
   fixed` (même correctif que #grid-produits, 28/08) force le navigateur à
   respecter STRICTEMENT les largeurs déclarées par editableTable.js
   (`width`/`minWidth`) au lieu de les ignorer et de répartir l'espace
   excédentaire de façon inégale dès qu'un contenu réclame plus de place —
   débordement aggravé par l'ajout de la colonne de bascule ▸/▾
   (`expandable`, zip -124/-125).
   `width: auto` (ajouté juste après, même jour — 2ème retour : « c'est pas
   encore ça [...] tu peux réduire la colonne section ») CORRIGE UN EFFET DE
   BORD du réglage ci-dessus : `table.simple` impose par ailleurs `width:
   100%` (règle générale, plus haut dans ce fichier) — combiné à
   `table-layout: fixed`, un écran plus large que la somme des largeurs
   déclarées ne laissait plus l'espace en trop inutilisé (souhaité) mais
   RÉÉTIRAIT chaque colonne proportionnellement pour occuper les 100% quand
   même, la colonne "Section" (dont le contenu — un code court, ex.
   "IIC-LGJMI0" — ne réclame naturellement aucune largeur particulière)
   héritant visuellement de tout l'écart. `width: auto` restaure le
   comportement HTML par défaut d'une table (largeur = somme des colonnes
   déclarées, jamais étirée pour occuper le conteneur) tout en conservant le
   respect strict des largeurs de `table-layout: fixed` ci-dessus — les 2
   réglages sont complémentaires, pas redondants. --*/
/* 11/09 (3e correctif, remplace `table-layout: fixed` + `width: auto`
   ci-dessus) — HISTORIQUE, RETIRÉ LE MÊME JOUR (4e correctif, voir plus bas) :
   avait basculé cette grille en mise en page flexbox (chaque <tr> devenant
   un conteneur flex qui repasse ses cellules à la ligne plutôt que de
   déborder), pour compenser les -276px pris par la liste latérale des
   produits alors ajoutée à côté de la fiche. Cette liste latérale a été
   retirée le jour même (retour en arrière, voir .produits-overview-head
   plus haut) — mais le flexbox est resté en place, présenté à tort comme un
   "filet de sécurité sans coût" : avec de VRAIES valeurs (libellé de
   section long, nom d'établissement long...), chaque <tr> peut se replier à
   un point différent de la ligne d'en-tête (qui replie ses propres textes
   indépendamment) — les 2 lignes de repli ne s'alignent alors PLUS sous
   leurs bonnes colonnes, un tableau devient illisible (retour utilisateur,
   capture à l'appui : « j'ai l'affichage section qui est maintenant
   endommagé »). Un <table> classique n'a pas ce défaut : une cellule dont
   le contenu est trop long fait simplement PASSER SON TEXTE à la ligne À
   L'INTÉRIEUR d'elle-même (plus grande hauteur de ligne), sans jamais
   déplacer une cellule hors de sa colonne — c'est ce comportement qui est
   restauré ci-dessous (4e correctif).
   4e correctif (11/09, même jour) : retour à `table-layout: fixed` +
   `width: auto` (même principe que #grid-produits/.fiche-pmut-chapitres/
   #grid-personnels ailleurs dans ce fichier) — chaque colonne respecte
   strictement sa largeur déclarée par editableTable.js (`width`), la table
   ne s'étire jamais au-delà de la somme de ses colonnes (~1180px avec les
   11 colonnes + bascule ▸/▾ + suppression, voir web/js/scenario.js) : ce
   total tient désormais sans barre de défilement sur un écran d'ordinateur
   courant (1366px et plus), la liste latérale ayant été retirée. Un texte
   trop long dans une colonne NON éditable (Libellé section, Ets, Partagée
   avec) passe simplement à la ligne dans sa cellule ; un <input> numérique
   ne le peut pas nativement (voir les largeurs déjà élargies par colonne,
   ex. EHST/PVU à 110px) — seul cas où une valeur réelle pourrait encore
   être coupée, sans casser l'alignement pour autant. Sur un écran plus
   étroit que ~1180px (rare, en dessous des résolutions d'ordinateur
   courantes), `.grid-wrap` reprend son rôle habituel : une barre de
   défilement horizontale apparaît plutôt que de déborder — moins
   confortable qu'une seule ligne toujours visible, mais un tableau qui
   scroll reste lisible, contrairement à un tableau dont les colonnes ne
   s'alignent plus. */
#fiche-grid-repartition table { table-layout: fixed; width: auto; }

/* Grille "Personnels" (09/09, retour utilisateur : « le badge de
   consommation n'est pas visible il faut utiliser la barre de défilement »)
   — même correctif, et même cause, que la grille Produits ci-dessus
   (`table-layout: auto` par défaut laissant une colonne à contenu non
   coupable — un <input type=number>, un intitulé d'en-tête long — s'élargir
   au-delà de sa largeur déclarée, déjà repéré comme un début de débordement
   sur CETTE grille précise au moment du correctif Produits, voir plus haut
   « la grille Personnels [...] n'en laissait dépasser qu'un éclat à peine
   cliquable »). Combiné au masquage de la colonne "Code matière" (~100px
   libérés, voir mountPersonnelsGrid) : plus assez de dépassement pour que
   "Conso." (dernière colonne) passe sous la barre de défilement. Pas de
   `width: auto` ici (à la différence de la grille de répartition ci-dessus) :
   avec 11 colonnes plutôt que 3-4, un éventuel espace excédentaire se répartit
   proportionnellement sans qu'aucune ne ballonne de façon visible. */
#grid-personnels table { table-layout: fixed; }
#grid-personnels table thead th { overflow-wrap: anywhere; }

/* "Fléchage des consos" par personnel (09/09) — ligne dépliable de la
   grille Personnels, voir renderFlechagePersonnelDetail (web/js/scenario.js).
   Même esprit que .duree-parcours-readout/.period-card (grille de
   répartition par section) : contenu en lecture seule, dans la ligne de
   détail. `.subtotal-row` (déjà utilisée par le tableau "Tous les produits")
   est réutilisée telle quelle pour la ligne de total. */
.flechage-detail { padding: 10px 4px; }
.flechage-detail-titre { font-size: 12.5px; font-weight: 700; color: var(--primary-hover); margin-bottom: 8px; }
.flechage-table { width: 100%; }
.flechage-table th, .flechage-table td { font-size: 12.5px; }
/* 09/09 : 2ème source de consommation ("Charges fixes [PMUT]", en plus des
   affectations normales) — sous-titre de chaque tableau, et total général
   des 2 sources additionnées (celui qui doit réconcilier avec le badge
   "Conso.", voir renderFlechagePersonnelDetail). */
.flechage-sous-titre { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; color: var(--text-muted); margin: 14px 0 4px; }
.flechage-sous-titre:first-of-type { margin-top: 0; }
.flechage-total-general { margin-top: 10px; padding-top: 8px; border-top: 2px solid var(--primary); font-weight: 700; text-align: right; color: var(--primary); }
/* 29/09 : liens Produit/Section du tableau "Affectations normales" (retour
   utilisateur : « il faudrait réajuster la présentation et [...] mettre des
   liens sur les produits et sections, l'activité n'est pas nécessaire ») —
   volontairement un simple lien texte (pas une puce pleine comme
   .chip-produit-partage, trop lourd visuel dans une cellule de tableau déjà
   dense) : rouvre la fiche du produit (et présélectionne la section pour le
   lien "Section", voir openFicheProduit/preselectSectionCode côté JS). */
.flechage-lien {
  background: none; border: none; padding: 0; font: inherit; font-size: 12.5px;
  color: var(--primary-hover); text-decoration: underline; text-decoration-color: transparent;
  cursor: pointer;
}
.flechage-lien:hover { text-decoration-color: currentColor; color: var(--primary); }
/* 29/09 (suite) : largeurs de colonnes figées (retour utilisateur : « tu dois
   réduire la largeur des colonnes section, heures et cout affecté pour
   laisser la place au produit/code produit ») — `table-layout: fixed` +
   `<colgroup>` (voir web/js/scenario.js) donne enfin sa part à la colonne
   Produit, dont le texte "CODE — Libellé" est souvent le plus long de la
   ligne. Scopé au seul tableau "Affectations normales" (via cette 2e classe,
   en plus de .flechage-table) : le tableau "Charges fixes (PMUT)" juste en
   dessous n'a pas ce problème (4 colonnes bien plus courtes) et reste en
   table-layout: auto. */
.flechage-table-affectations { table-layout: fixed; }
.flechage-table-affectations th, .flechage-table-affectations td { overflow-wrap: break-word; }
/* Colonne "Supprimer" (corbeille 🗑, retour utilisateur : « serait-il possible
   de mettre une corbeille en face de chaque afin de pouvoir supprimer
   l'affectation [...] avec un message d'alerte avant suppression définitive »)
   — bouton centré, mêmes réflexes que la corbeille générique d'editableTable.js
   (confirm() natif) mais posée ici directement dans le HTML généré du détail
   dépliable, pas dans une grille éditable. */
.flechage-col-supprimer { text-align: center; }
.flechage-col-supprimer button { padding: 3px 8px; }

/* -- Étape 1 "Choisir ou démarrer un produit" — nouvelle présentation (03/09,
   retour utilisateur : « je ne suis pas satisfait de la présentation [...]
   peux tu me proposer une présentation qui met plus en évidence la
   sélection du produit »). Ajoute un encart de confirmation une fois une
   formation choisie (.fp-selected-card) — voir web/js/scenario.js pour le
   câblage. Le filtre CFP a d'abord été tenté en pastilles cliquables
   (.fp-cfp-pills, fusion demandée avec la maquette "option B"), remplacées
   par un simple menu déroulant compact après retour sur le déploiement réel :
   « les vignettes CFP ne vont pas terribles, elles prennent trop de place et
   ça laisse un encart vide, il faudrait revenir à l'option B sans les
   vignettes » — .fp-cfp-field ci-dessous habille juste ce <select> pour
   qu'il reste compact à côté de la recherche, sans les classes .pill/
   .fp-cfp-pills/.fp-cfp-locked du 1er essai (retirées, plus utilisées). --*/
.fp-search-row { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 4px; }
.fp-cfp-field { flex: 0 0 auto; min-width: 190px; max-width: 240px; margin-bottom: 0; }
.fp-search-main { flex: 1; min-width: 280px; }
.fp-search-main > label { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
.fp-search-input-wrap { position: relative; }
.fp-search-input-wrap svg { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); opacity: .45; pointer-events: none; }
input.fp-big-search {
  width: 100%; font-size: 15px; padding: 12px 14px 12px 38px; border-radius: 10px;
  border: 1.5px solid var(--border); background: #fff; font: inherit; color: var(--text);
}
input.fp-big-search:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-light);
}

.fp-selected-card {
  display: none; align-items: center; gap: 14px;
  background: #fff; border: 1.5px solid var(--primary); border-radius: 10px;
  padding: 14px 16px; margin-bottom: 4px;
}
.fp-selected-card.show { display: flex; }
.fp-selected-card .fp-card-icon {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 999px;
  background: var(--primary-light); color: var(--primary-hover);
  display: flex; align-items: center; justify-content: center; font-size: 18px;
}
.fp-selected-card .fp-card-info { flex: 1; min-width: 0; }
.fp-selected-card .fp-card-info .fp-card-name { font-weight: 700; font-size: 14.5px; }
.fp-selected-card .fp-card-info .fp-card-meta { font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }
.fp-selected-card .fp-card-info .fp-card-meta .chip {
  display: inline-block; background: var(--bg); border: 1px solid var(--border);
  border-radius: 999px; padding: 1px 8px; font-size: 11.5px; margin-right: 6px;
}
.fp-selected-card button.fp-card-change {
  flex: 0 0 auto; border: 1px solid var(--border); background: #fff; border-radius: 6px;
  padding: 7px 12px; font-size: 12.5px; cursor: pointer; color: var(--text); font: inherit;
}
.fp-selected-card button.fp-card-change:hover { border-color: var(--primary); color: var(--primary-hover); }

/* Puces cliquables de la colonne "Partagée avec" (fiche produit, étape 2,
   03/09) — voir renderPartageCell dans web/js/scenario.js. Un clic ouvre la
   fiche du produit partageant la section (retour utilisateur : « mettre les
   codes produits et un lien direct pour ouvrir les produits partagés »). */
.chips-partage { display: flex; flex-wrap: wrap; gap: 4px; }
.chip-produit-partage {
  display: inline-block; background: var(--primary-light); border: 1px solid var(--primary);
  color: var(--primary-hover); border-radius: 999px; padding: 1px 8px; font-size: 11.5px;
  font: inherit; cursor: pointer; line-height: 1.6;
}
.chip-produit-partage:hover { background: var(--primary); color: #fff; }

/* ALPAGA : lien « Déconnexion » lisible sur l'en-tête bleu nuit. */
header.app-header #user-info { color: #d8d2cb; }
header.app-header #user-info .muted { color: #b3aca4; }
header.app-header #user-info a { color: #f6a46a; }
header.app-header #user-info a:hover { color: #ffffff; }

/* ---- ALPAGA : écran Référentiels ---- */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
.alp-etapes { margin: 0 0 14px; padding-left: 20px; color: var(--text); line-height: 1.7; }
.alp-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin: 12px 0; }
.alp-stats > div { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; color: var(--text-muted); font-size: 13px; }
.alp-stats span { display: block; font-size: 22px; font-weight: 700; color: var(--text); }
.alp-h3 { font-size: 14px; margin: 14px 0 6px; }
.alp-liste { margin: 0; padding-left: 20px; font-size: 13px; line-height: 1.6; }
.alp-liste.err li { color: var(--danger); }
.alp-avert { margin-top: 10px; border: 1px solid #ecd9a8; background: #fdf8ea; border-radius: var(--radius); padding: 8px 12px; }
.alp-avert summary { cursor: pointer; font-weight: 600; color: #7a5600; }
.alp-avert .alp-liste { margin-top: 8px; color: #5c4300; }
details.alp-bloc { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 10px; background: var(--surface); }
details.alp-bloc > summary { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; padding: 12px 14px; cursor: pointer; }
.alp-bloc-code { font-weight: 700; color: var(--primary); min-width: 44px; }
.alp-bloc-titre { font-weight: 600; flex: 1 1 320px; }
.alp-cat { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); background: var(--bg); padding: 2px 8px; border-radius: 10px; }
.alp-mod-meta { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.alp-bloc-corps { padding: 0 14px 12px 14px; }
details.alp-module { border-top: 1px solid var(--border); }
details.alp-module > summary { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px; padding: 9px 4px 9px 18px; cursor: pointer; }
.alp-mod-titre code, .alp-contenus code, td code { font-size: 12px; color: var(--text-muted); }
.alp-mod-corps { padding: 4px 4px 12px 18px; }
.alp-interv { display: inline-block; font-size: 12px; background: var(--primary-light); color: var(--primary-hover); border-radius: 10px; padding: 2px 8px; margin: 0 4px 4px 0; }
.alp-type { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 4px; }
.alp-type.th { background: #e4eef8; color: #1d4f7a; }
.alp-type.pr { background: #fbebdd; color: #8a4513; }
.alp-type.np { background: #eef1f4; color: #4c5966; }
.alp-manque { color: #8a5300; font-weight: 600; }
table.simple.alp-contenus td { padding-top: 6px; padding-bottom: 6px; }

/* ---- ALPAGA : écrans Saisie et Pilotage (v0.4) ---- */
.alp-barre-session { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; margin-bottom: 16px; flex-wrap: wrap; }
.alp-barre-session select { min-width: 260px; }
.alp-sauvegarde { font-size: 13px; color: var(--text-muted); }
.alp-sauvegarde.ok { color: var(--success); }
.alp-sauvegarde.attente { color: #7a5600; }
.alp-sauvegarde.erreur { color: var(--danger); font-weight: 600; }
.alp-code { font-size: 12px; font-weight: 400; color: var(--text-muted); background: var(--bg); padding: 2px 6px; border-radius: 4px; }

.alp-saisie { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.alp-modules { flex: 1 1 250px; max-width: 300px; margin: 0; }
.alp-saisie-main { flex: 999 1 600px; min-width: 0; }
.alp-groupe { margin-bottom: 12px; }
.alp-groupe-titre { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); margin: 0 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
button.alp-mod-btn { display: block; width: 100%; text-align: left; margin: 0 0 4px; padding: 8px 10px; min-height: 44px; font-weight: 400; }
button.alp-mod-btn.actif { border-color: var(--primary); background: var(--primary-light); color: var(--text); font-weight: 600; }
.alp-mod-ligne { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; }
.alp-pct { color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.alp-barre { display: block; height: 4px; border-radius: 2px; background: #e3e8ec; margin-top: 6px; overflow: hidden; }
.alp-barre span { display: block; height: 100%; background: var(--primary); }

.alp-onglets { display: flex; gap: 4px; flex-wrap: wrap; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.alp-onglets [role="tab"] { border: 0; border-bottom: 3px solid transparent; border-radius: 0; background: transparent; padding: 10px 14px; min-height: 44px; box-shadow: none; color: var(--text-muted); }
.alp-onglets [role="tab"][aria-selected="true"] { border-bottom-color: var(--primary); color: var(--text); font-weight: 600; }
.alp-onglets [role="tab"]:hover { transform: none; color: var(--text); }

.alp-grille-scroll { overflow-x: auto; }
table.alp-grille { border-collapse: collapse; width: 100%; font-size: 13px; }
table.alp-grille th, table.alp-grille td { padding: 5px 4px; border-bottom: 1px solid #eef2f5; vertical-align: middle; }
table.alp-grille thead th { font-size: 12px; font-weight: 600; color: var(--text-muted); text-align: center; background: #f7f9fa; }
.alp-col-contenu { text-align: left !important; min-width: 280px; font-weight: 500; }
.alp-col-contenu .alp-type { margin-right: 6px; }
.alp-col-app { min-width: 58px; }
.alp-col-taux { text-align: right !important; padding-right: 10px !important; font-variant-numeric: tabular-nums; white-space: nowrap; }
.alp-date { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--text-muted); text-align: center; }
table.alp-grille td { text-align: center; }
button.alp-cell { width: 46px; height: 32px; min-height: 32px; padding: 0; border-radius: 5px; font-size: 12px; font-weight: 700; box-shadow: none; }
button.alp-cell:hover { transform: none; filter: brightness(0.95); }
.alp-cell { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 22px; border-radius: 4px; font-size: 11px; font-weight: 700; box-sizing: border-box; }
.alp-cell.A { background: #1e6b45; color: #fff; border: 0; }
.alp-cell.PA { background: #f6d9a8; color: #6b4100; border: 0; }
.alp-cell.NC { background: #fff; color: #a3332a; border: 2px solid #a3332a; }
.alp-cell.vide { background: #f1f4f6; color: #7a8794; border: 1px dashed #c5ced6; font-weight: 400; }
button.alp-cell:disabled { opacity: 1; cursor: default; }
button.alp-lien { border: 0; background: none; box-shadow: none; padding: 0 4px; min-height: 0; font-size: 11px; color: var(--primary); text-decoration: underline; font-weight: 400; }
button.alp-lien:hover { transform: none; color: var(--primary-hover); }
.alp-legende { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; padding-top: 12px; font-size: 13px; }
.alp-legende .spacer { flex: 1; }

.alp-form-seance { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 16px; background: #fbfcfd; }
.alp-champs { display: flex; flex-wrap: wrap; gap: 0 14px; }
.alp-champs .field { flex: 1 1 140px; }
.alp-champs input, .alp-champs select { width: 100%; }
.alp-contenus-choix { border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 12px; margin: 4px 0 0; max-height: 240px; overflow-y: auto; }
.alp-contenus-choix legend { font-size: 13px; font-weight: 600; padding: 0 4px; }
.alp-check { display: flex; align-items: flex-start; gap: 8px; padding: 4px 0; font-size: 13px; cursor: pointer; }
.alp-check input { width: auto; margin-top: 2px; }
.alp-commentaire { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; margin-bottom: 8px; }
.alp-com-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; font-size: 13px; }
.alp-com-tete .spacer { flex: 1; }
.alp-commentaire p { margin: 6px 0 0; white-space: pre-line; }

.alp-kpi { margin: 0 0 16px; }
.alp-kpi small { font-size: 14px; font-weight: 400; color: var(--text-muted); }
.alp-txt-retard { color: #8a5300 !important; }
.alp-txt-nc { color: var(--danger) !important; }
.alp-deux-col { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.alp-deux-col > .card { flex: 1 1 520px; min-width: 0; margin: 0; }
table.alp-matrice td { padding: 3px !important; }
.alp-heat { display: flex; align-items: center; justify-content: center; height: 32px; min-width: 56px; border-radius: 4px; font-size: 13px; font-variant-numeric: tabular-nums; }
.alp-heat-0 { background: #f4f1ee; color: #8a847c; }
.alp-heat-1 { background: #fdf1e7; color: #262320; }
.alp-heat-2 { background: #f9c9a3; color: #262320; }
.alp-heat-3 { background: #ef9a5c; color: #2b1a0e; }
.alp-heat-4 { background: #c4520a; color: #fff; }
.alp-nc { display: inline-flex; min-width: 28px; height: 26px; align-items: center; justify-content: center; border-radius: 13px; font-weight: 600; }
.alp-nc.alerte { background: #a3332a; color: #fff; }
tr.alp-sorti td, tr.alp-sorti th { opacity: .55; }
.alp-statut { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.alp-st-ok { background: #ddefe3; color: #14532d; }
.alp-st-retard { background: #fcebd2; color: #6b4100; }
.alp-st-termine { background: #e4eef8; color: #1d4f7a; }
.alp-st-neutre { background: #eef1f4; color: #4c5966; }
table.alp-affect input[type="checkbox"] { width: 18px; height: 18px; }
a.btn { text-decoration: none; display: inline-flex; align-items: center; }
table.simple tbody th { text-transform: none; letter-spacing: 0; font-size: 14px; color: var(--text); }
.alp-code { white-space: nowrap; }
#suivi-modules td:first-child { min-width: 200px; }
table.alp-matrice tbody th { white-space: nowrap; padding-top: 4px; padding-bottom: 4px; }

/* ---- ALPAGA : écran Ma progression (v0.5) ---- */
main.alp-progression { max-width: 1180px; }
.alp-prog-tete { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 20px; margin-bottom: 14px; }
.alp-prog-tete h1 { margin: 0; font-size: 28px; }
.alp-prog-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
a.btn.primary { color: #fff; }
a.alp-lien-discret { font-size: 13px; color: var(--primary); }
.alp-confidentiel { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: var(--radius); background: #efe9e3; color: #3d3b38; font-size: 13px; margin-bottom: 16px; }
.alp-txt-a { color: #1e6b45 !important; }
.alp-txt-pa { color: #8a5300 !important; }
.alp-pcarte { padding: 0; overflow: hidden; }
.alp-pcarte-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.alp-pcarte-tete h2 { margin: 0; flex: 1 1 auto; }
.alp-pleg { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; color: var(--text-muted); }
.alp-pleg i { display: inline-block; width: 11px; height: 11px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.s-A { background: #1e6b45; } .s-PA { background: #e9b45c; } .s-NC { background: #a3332a; } .s-aborde { background: #c5ced6; } .s-a_venir { background: #eef1f4; box-shadow: inset 0 0 0 1px #c5ced6; }
details.alp-pbloc { border-bottom: 1px solid #eef2f5; }
details.alp-pbloc > summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; padding: 14px 20px; cursor: pointer; min-height: 52px; }
.alp-pbloc-titre { flex: 1 1 320px; font-weight: 600; }
.alp-pbloc-code { display: inline-block; min-width: 34px; color: var(--primary); font-weight: 700; }
.alp-pbarre { flex: 0 1 300px; min-width: 160px; display: flex; height: 10px; border-radius: 5px; overflow: hidden; background: #eef1f4; }
.alp-pbarre span { display: block; height: 100%; }
.alp-pbloc-nb { width: 120px; text-align: right; font-size: 13px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.alp-pbloc-corps { padding: 0 20px 16px 54px; display: flex; flex-direction: column; gap: 10px; }
.alp-pmod { border: 1px solid #e3e8ec; border-radius: 8px; padding: 10px 14px; }
.alp-pmod-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; }
.alp-pmod-titre { flex: 1 1 240px; font-weight: 500; }
.alp-points { display: flex; flex-wrap: wrap; gap: 3px; max-width: 360px; }
.alp-point { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 4px; font-size: 9px; font-weight: 700; color: #fff; flex: none; }
.alp-point.s-PA { color: #6b4100; background: #f6d9a8; }
.alp-point.s-NC { background: #fff; color: #a3332a; box-shadow: inset 0 0 0 1.5px #a3332a; }
.alp-pmod-nb { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
details.alp-pmod-detail summary { font-size: 12px; color: var(--primary); cursor: pointer; margin-top: 6px; }
details.alp-pmod-detail ul { list-style: none; padding: 0; margin: 6px 0 0; font-size: 13px; }
details.alp-pmod-detail li { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.alp-pcom { margin-top: 8px; padding: 8px 12px; border-left: 3px solid var(--primary); background: #f7f4f1; border-radius: 0 6px 6px 0; font-size: 13px; }
.alp-pcom p { margin: 4px 0 0; white-space: pre-line; }
/* liens dans les tableaux et cartes : couleur de la charte (au lieu du bleu par défaut) */
main a:not(.btn) { color: var(--primary-hover); }
main a:not(.btn):hover { color: var(--primary); }
