/* ==================================================================
   ALLENASSI — feuille de style de l'application
   Toutes les couleurs viennent de charte-tokens.css : aucun hexa ici.
   ================================================================== */

/* ==================================================================
   1. GABARIT DE PAGE
   ================================================================== */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.contenu {
  flex: 1;
  width: 100%;
  max-width: var(--largeur-max);
  margin: 0 auto;
  padding: 32px var(--gouttiere) 64px;
}

/* DE L'AIR ENTRE DEUX SECTIONS DE PAGE PUBLIQUE.
   Les <section> de la vitrine n'en avaient aucune : la derniere carte de
   « Ce que vous recevez » touchait presque le surtitre de « Ce
   qu'Allenassi ne fait pas », si bien que les deux blocs se lisaient
   comme un seul. Un surtitre est un DEBUT — il lui faut le silence qui
   le precede, sinon il passe pour la legende de ce qui est au-dessus.
   La marge se pose sur la section suivante, jamais sur la premiere :
   celle-ci suit deja le hero, qui porte son propre espacement. */
.contenu > section + section { margin-top: 72px; }

@media (max-width: 720px) {
  .contenu > section + section { margin-top: 48px; }
}

/* Colonne de lecture centree : formulaire de connexion, page d'erreur. */
.contenu-etroit {
  max-width: 560px;
  margin-inline: auto;
}

/* --- En-tete public ---------------------------------------------- */
.entete {
  border-bottom: 1px solid var(--bordure);
  background: var(--surface);
}

.entete-interieur {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: var(--largeur-max);
  margin: 0 auto;
  padding: 16px var(--gouttiere);
}

.marque {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--texte);
  font-family: var(--police-titre);
  font-size: 1.25rem;
  font-weight: 600;
  text-decoration: none;
}

.marque img { width: 32px; height: 32px; border-radius: var(--rayon-petit); }

.nav-publique {
  display: flex;
  align-items: center;
  gap: 20px;
  font-size: 0.95rem;
}

/* :not(.bouton) EST INDISPENSABLE ICI.
   « .nav-publique a » pese 0-1-1, « .bouton » 0-1-0 : sans l'exclusion,
   la regle de navigation gagne et impose son encre grise au bouton
   « Commencer ». Sur le vert fonce du bouton, ce gris devenait
   illisible — c'est exactement ce qu'on voyait sur la vitrine. */
.nav-publique a:not(.bouton) { color: var(--texte-doux); text-decoration: none; }
.nav-publique a:not(.bouton):hover { color: var(--texte); }
.nav-publique .bouton { text-decoration: none; }

/* La marque : le carre prend la couleur d'accent, le pictogramme est
   evide en blanc dedans. */
.marque-logo {
  flex: none;
  color: var(--accent);
  border-radius: var(--rayon-petit);
}

/* --- Pied de page ------------------------------------------------ */
.pied {
  border-top: 1px solid var(--bordure);
  background: var(--surface-2);
  color: var(--texte-doux);
  font-size: 0.875rem;
}

.pied-interieur {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  justify-content: space-between;
  max-width: var(--largeur-max);
  margin: 0 auto;
  padding: 28px var(--gouttiere);
}

.pied a { color: var(--texte-doux); }
.pied a:hover { color: var(--texte); }
.pied-liens { display: flex; flex-wrap: wrap; gap: 20px; }

/* ==================================================================
   2. SHELL APPLICATIF (pages connectees)
   Sidebar a gauche, contenu a droite. Sur telephone la sidebar passe
   au-dessus, en barre horizontale defilante.
   ================================================================== */
.appli { display: flex; min-height: 100vh; }

.barre-laterale {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  gap: 24px;
  padding: 20px 16px;
  border-right: 1px solid var(--bordure);
  background: var(--surface-2);
}

.barre-laterale .marque { font-size: 1.1rem; }

/* --- Bouton burger, pilote par une case a cocher masquee -----------
   Sans JavaScript : la case porte l'etat, le CSS lit :checked. Le menu
   reste donc utilisable si un script ne se charge pas, et rien n'est
   ecrit en ligne — la CSP l'interdirait. */
.bascule-menu { position: absolute; opacity: 0; pointer-events: none; }

.barre-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.burger { display: none; }

.nav-appli { display: flex; flex-direction: column; gap: 2px; }

/* LE TITRE DE RUBRIQUE N'ETAIT PAS STYLE DU TOUT.
   « .nav-titre » etait un <p> nu : corps de paragraphe, couleur du
   texte courant, marges par defaut. Il pesait donc PLUS que les entrees
   qu'il coiffe, sans rien qui dise qu'il n'est pas cliquable — on lisait
   une liste plate ou « Mon programme » etait a la fois le nom du groupe
   et sa premiere entree.
   Il devient une etiquette de section : petites capitales espacees,
   attenuees, avec de l'air au-dessus. Ce n'est pas la TAILLE qui le rend
   visible, c'est la NATURE — c'est la meme convention que « .surtitre »
   partout ailleurs dans le produit, et l'oeil la reconnait deja. */
.nav-titre {
  margin: 18px 0 4px;
  padding: 0 12px;
  color: var(--texte-tres-doux);
  font-family: var(--police-corps);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Le premier groupe n'a pas de titre : pas d'air en trop avant lui. */
.nav-groupe:first-child .nav-titre { margin-top: 0; }

.nav-appli a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: var(--rayon-petit);
  color: var(--texte-doux);
  font-size: 0.95rem;
  text-decoration: none;
  transition: background var(--transition), color var(--transition);
}

.nav-appli a:hover { background: var(--surface-3); color: var(--texte); }

.nav-appli a[aria-current="page"] {
  background: var(--accent-doux);
  color: var(--accent-texte);
  font-weight: 500;
}

/* L'icone prend la couleur de son entree — grise au repos, verte sur
   l'entree active — parce qu'elle est tracee en « currentColor ».
   « flex: none » l'empeche de se comprimer quand un libelle est long :
   c'est le texte qui doit ceder, jamais le reperage visuel. */
.nav-icone {
  flex: none;
  opacity: 0.75;
}

.nav-appli a:hover .nav-icone,
.nav-appli a[aria-current="page"] .nav-icone { opacity: 1; }

.barre-laterale-pied {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--bordure);
  color: var(--texte-tres-doux);
  font-size: 0.8rem;
}

.appli-corps { flex: 1; min-width: 0; }

/* Pleine largeur : dans le shell applicatif, la barre laterale donne
   deja la marge de gauche. Brider le contenu a 1240px et le centrer
   laissait deux bandes vides sur un ecran large, alors que ce sont
   justement les tableaux de depenses et de comptes qui ont besoin de
   place. */
.appli-contenu {
  width: 100%;
  padding: 28px var(--gouttiere) 64px;
}

/* ==================================================================
   3. COMPOSANTS
   ================================================================== */

/* --- Titre de section -------------------------------------------- */
.titre-section {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 24px;
}

.surtitre {
  margin: 0 0 6px;
  color: var(--accent-texte);
  font-family: var(--police-corps);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* --- Cartes ------------------------------------------------------ */
.grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}

.carte {
  padding: 20px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  box-shadow: var(--ombre);
}

.carte h3 { margin-bottom: 6px; }
.carte p:last-child { margin-bottom: 0; }

.carte-lien {
  color: inherit;
  text-decoration: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.carte-lien:hover {
  border-color: var(--bordure-forte);
  box-shadow: var(--ombre-flottante);
}

/* Chiffre-cle d'un tableau de bord. */
.chiffre {
  display: block;
  font-family: var(--police-titre);
  font-size: 2rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.chiffre-positif { color: var(--vert); }
.chiffre-negatif { color: var(--rouge); }

.legende { color: var(--texte-doux); font-size: 0.875rem; }

/* --- Pastilles de statut ----------------------------------------- */
.pastille {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--rayon-pilule);
  background: var(--surface-3);
  color: var(--texte-doux);
  font-size: 0.78rem;
  font-weight: 500;
  white-space: nowrap;
}

.pastille-ok { background: var(--vert-doux); color: var(--vert); }
.pastille-attente { background: var(--ambre-doux); color: var(--ambre); }
.pastille-retard { background: var(--rouge-doux); color: var(--rouge); }
.pastille-info { background: var(--bleu-doux); color: var(--bleu); }

/* --- Boutons ------------------------------------------------------ */
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 20px;
  border: 1px solid transparent;
  border-radius: var(--rayon-petit);
  background: var(--accent);

  /* L'encre du bouton primaire est CALCULEE a partir de la couleur
     choisie (voir _php/config.php), pas figee en blanc. Sur un accent
     clair — terracotta, jaune, vert d'eau — un texte blanc tombe sous
     le contraste AA et devient penible a lire ; l'encre bascule alors
     au noir. Sur un accent sombre elle reste blanche. C'est la seule
     facon de laisser choisir n'importe quelle couleur sans produire un
     bouton illisible (regle 10e). */
  color: var(--accent-encre, var(--texte-inverse));
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.bouton:hover { background: var(--accent-fonce); color: var(--accent-encre, var(--texte-inverse)); }

.bouton-secondaire {
  border-color: var(--bordure-forte);
  background: var(--surface);
  color: var(--texte);
}

.bouton-secondaire:hover { border-color: var(--texte-doux); background: var(--surface); color: var(--texte); }

.bouton-discret {
  padding: 6px 12px;
  border-color: transparent;
  background: none;
  color: var(--texte-doux);
  font-size: 0.9rem;
}

.bouton-discret:hover { background: var(--surface-3); color: var(--texte); }

.bouton-bloc { width: 100%; }

/* --- Formulaires -------------------------------------------------- */
.champ { margin-bottom: 18px; }

.champ label {
  display: block;
  margin-bottom: 6px;
  font-size: 0.9rem;
  font-weight: 500;
}

.champ .aide {
  margin: 6px 0 0;
  color: var(--texte-doux);
  font-size: 0.83rem;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="date"],
input[type="number"],
input[type="search"],
select,
textarea {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte);
  font-family: inherit;
  font-size: 1rem;
  transition: border-color var(--transition);
}

input:hover,
select:hover,
textarea:hover { border-color: var(--texte-tres-doux); }

textarea { min-height: 120px; resize: vertical; }

/* Champ leurre anti-robot : invisible a l'oeil, present dans le DOM. */
.pot-de-miel {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --- Messages ----------------------------------------------------- */
.message {
  margin-bottom: 24px;
  padding: 14px 16px;
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--texte-doux);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  font-size: 0.95rem;
}

.message-ok { border-left-color: var(--vert); background: var(--vert-doux); }
.message-erreur { border-left-color: var(--rouge); background: var(--rouge-doux); }
.message-info { border-left-color: var(--bleu); background: var(--bleu-doux); }

/* --- Tableaux ----------------------------------------------------- */
.table-enveloppe { overflow-x: auto; }

table.donnees {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.93rem;
}

table.donnees th,
table.donnees td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--bordure);
  text-align: left;
  vertical-align: top;
}

table.donnees th {
  color: var(--texte-doux);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

table.donnees td.nombre,
table.donnees th.nombre {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

table.donnees tbody tr:hover { background: var(--surface-2); }

/* Un montant ou une date ne se coupe jamais en deux : « 19 230,40 € »
   reparti sur trois lignes se lit comme trois nombres. */
table.donnees td.nombre,
table.donnees td.cellule-date { white-space: nowrap; }

/* --- Accueil public ----------------------------------------------- */
.accroche {
  padding: 56px 0 40px;
  border-bottom: 1px solid var(--bordure);
}

.accroche h1 { max-width: 18ch; }

.accroche .chapeau {
  max-width: 60ch;
  color: var(--texte-doux);
  font-size: 1.1rem;
}

.actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }

/* --- Page hors ligne ---------------------------------------------- */
.hors-ligne {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--gouttiere);
  text-align: center;
}

/* --- Contenu editorial (mentions legales, confidentialite) --------- */
.texte-long { max-width: 70ch; }
.texte-long h2 { margin-top: 2em; }
.texte-long ul { padding-left: 1.2em; }
.texte-long li { margin-bottom: 0.4em; }

/* ==================================================================
   4. RESPONSIVE — la sidebar ne devient verticale qu'a partir de 900px
   ================================================================== */
@media (width < 900px) {
  .appli { flex-direction: column; }

  .barre-laterale {
    gap: 0;
    padding: 12px var(--gouttiere);
    border-right: none;
    border-bottom: 1px solid var(--bordure);
  }

  /* Le bouton apparait, le menu se replie. Treize entrees en barre
     defilante, c'etait illisible au pouce : on ne voyait que les trois
     premieres et rien n'indiquait qu'il y en avait dix autres. */
  .burger {
    display: flex;
    flex-direction: column;
    gap: 5px;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 10px;
    border: 1px solid var(--bordure-forte);
    border-radius: var(--rayon-petit);
    background: var(--surface);
    cursor: pointer;
  }

  .burger-trait {
    display: block;
    height: 2px;
    border-radius: 2px;
    background: var(--texte);
    transition: transform var(--transition), opacity var(--transition);
  }

  .bascule-menu:focus-visible ~ .barre-tete .burger {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* Ouvert, le bouton devient une croix : l'etat se lit sans avoir a
     dérouler la page. */
  .bascule-menu:checked ~ .barre-tete .burger-trait:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .bascule-menu:checked ~ .barre-tete .burger-trait:nth-child(2) { opacity: 0; }
  .bascule-menu:checked ~ .barre-tete .burger-trait:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  .nav-appli,
  .barre-laterale-pied { display: none; }

  .bascule-menu:checked ~ .nav-appli,
  .bascule-menu:checked ~ .barre-laterale-pied { display: block; }

  .bascule-menu:checked ~ .nav-appli {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--bordure);
  }

  .nav-appli a {
    padding: 12px;          /* cible confortable au pouce */
    font-size: 1rem;
  }

  .barre-laterale-pied { margin-top: 12px; }
}

@media (width >= 900px) {
  /* Largeur pilotee par un jeton : le menu doit contenir son plus long
     libelle sur une seule ligne (regle 11i). */
  .barre-laterale { width: var(--barre-laterale); }
}

@media print {
  .barre-laterale,
  .entete,
  .pied,
  .actions { display: none; }

  body { background: none; }
}


/* ==================================================================
   5. FORMULAIRES DE SAISIE
   ================================================================== */

/* Respiration entre les blocs d'une page d'admin : sans elle, le titre
   d'une section colle aux cartes de la precedente et l'on ne voit plus
   ou l'une finit et l'autre commence. */
.appli-contenu > section { margin-top: 44px; }
.appli-contenu > .grille + section { margin-top: 40px; }
.appli-contenu > section:first-of-type { margin-top: 32px; }

/* Grille de champs : une colonne sur telephone, deux des qu'il y a la
   place. Les champs larges (adresse, notes) prennent toute la ligne. */
.grille-champs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0 20px;
}

.champ-large { grid-column: 1 / -1; }

.barre-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  margin-top: 8px;
}

.barre-actions-fin { margin-left: auto; }

/* Bloc de formulaire dans une carte. */
.carte-formulaire {
  padding: 24px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  margin-bottom: 32px;
}

.carte-formulaire h2 { margin-bottom: 20px; }

/* Ligne inactive d'un tableau : presente, mais visiblement retiree. */
tr.inactive td { color: var(--texte-tres-doux); }

/* Total d'une colonne de parts : vert s'il tombe juste, rouge sinon. */
.total-parts {
  font-family: var(--police-titre);
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
}

.total-juste { color: var(--vert); }
.total-faux  { color: var(--rouge); }

/* Barre de recherche et de filtres : les champs s'alignent et le bouton
   se cale sur leur ligne de base. */
.barre-recherche {
  display: flex;
  flex-wrap: wrap;
  gap: 0 16px;
  align-items: flex-end;
  margin-bottom: 24px;
}

.barre-recherche .champ { flex: 1 1 180px; }
.barre-recherche .champ:last-child { flex: 0 0 auto; }

/* Rangee de filtres cliquables. */
.filtres {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 24px;
}

.filtres a { text-decoration: none; }
.filtres a:hover { background: var(--surface-3); color: var(--texte); }

/* Un formulaire reduit a un bouton, glisse dans une ligne de tableau :
   il doit se comporter comme du texte, pas comme un bloc. */
.form-inline { display: inline; }

/* Actions en fin de ligne de tableau. */
td.actions {
  white-space: nowrap;
  text-align: right;
}

td.actions a { margin-left: 12px; font-size: 0.88rem; }

/* Galerie de photos : des vignettes de taille reguliere, sans etirer
   les images — un tirage en portrait et une facade en paysage doivent
   cohabiter sans deformation. */
.galerie {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

.vignette {
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

.vignette img {
  display: block;
  width: 100%;
  height: 180px;
  object-fit: cover;
  background: var(--surface-2);
}

.vignette figcaption {
  padding: 12px 14px;
  font-size: 0.85rem;
  line-height: 1.5;
}

.vignette figcaption .pastille { margin: 4px 4px 0 0; }

/* Etat vide : ni tableau fantome, ni fausses donnees. */
.vide {
  padding: 32px;
  border: 1px dashed var(--bordure-forte);
  border-radius: var(--rayon);
  color: var(--texte-doux);
  text-align: center;
}

.vide p:last-child { margin-bottom: 0; }

@media (width >= 720px) {
  .grille-champs { grid-template-columns: 1fr 1fr; }
}


/* ==================================================================
   6. CALENDRIER MENSUEL

   Une occupation est une PLAGE, pas un point : elle se dessine en barre
   continue, decoupee a la semaine. Les barres se placent avec
   grid-column, d'ou les classes .cal-cN (colonne de depart) et .cal-lN
   (nombre de jours) — la CSP interdit l'attribut style, et sept colonnes
   ne font que quatorze classes.
   ================================================================== */
.calendrier {
  overflow: hidden;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

.cal-entetes {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border-bottom: 1px solid var(--bordure);
  background: var(--surface-2);
}

.cal-entete {
  padding: 10px 6px;
  color: var(--texte-doux);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: uppercase;
}

/* Chaque semaine est sa propre grille : les barres s'empilent sous la
   ligne des numeros, et le placement automatique les serre. */
.cal-semaine {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 3px 2px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--bordure);
}

.cal-semaine:last-child { border-bottom: none; }

.cal-jour {
  grid-row: 1;
  min-height: 46px;
  padding: 6px 8px;
  border-right: 1px solid var(--bordure);
}

.cal-jour:nth-child(7n) { border-right: none; }
.cal-jour.hors-mois { background: var(--fond); }
.cal-jour.hors-mois .cal-numero { color: var(--texte-tres-doux); }

.cal-jour.aujourdhui { background: var(--accent-doux); }
.cal-jour.aujourdhui .cal-numero { color: var(--accent-texte); font-weight: 600; }

.cal-numero {
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--texte-doux);
}

.cal-bande {
  overflow: hidden;
  min-width: 0;
  margin: 0 2px;
  padding: 3px 8px;
  border-left: 3px solid var(--texte-doux);
  border-radius: var(--rayon-petit);
  background: var(--surface-3);
  color: var(--texte);
  font-size: 0.78rem;
  line-height: 1.4;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cal-bande.annulee { opacity: 0.45; text-decoration: line-through; }
.cal-suite { color: var(--texte-doux); }

/* Colonne de depart et longueur, en jours. */
.cal-c1 { grid-column-start: 1; }
.cal-c2 { grid-column-start: 2; }
.cal-c3 { grid-column-start: 3; }
.cal-c4 { grid-column-start: 4; }
.cal-c5 { grid-column-start: 5; }
.cal-c6 { grid-column-start: 6; }
.cal-c7 { grid-column-start: 7; }

.cal-l1 { grid-column-end: span 1; }
.cal-l2 { grid-column-end: span 2; }
.cal-l3 { grid-column-end: span 3; }
.cal-l4 { grid-column-end: span 4; }
.cal-l5 { grid-column-end: span 5; }
.cal-l6 { grid-column-end: span 6; }
.cal-l7 { grid-column-end: span 7; }

/* Une teinte par membre, stable d'un mois sur l'autre ; le bleu est
   reserve aux locations, pour qu'on distingue au premier coup d'oeil
   « la famille » de « des inconnus qui paient ». */
.cal-loc { border-left-color: var(--bleu);   background: var(--bleu-doux); }
.cal-m0  { border-left-color: var(--accent); background: var(--accent-doux); }
.cal-m1  { border-left-color: var(--vert);   background: var(--vert-doux); }
.cal-m2  { border-left-color: var(--ambre);  background: var(--ambre-doux); }
.cal-m3  { border-left-color: var(--rouge);  background: var(--rouge-doux); }
.cal-m4  { border-left-color: var(--texte-doux); background: var(--surface-3); }
.cal-m5  { border-left-color: var(--accent-fonce); background: var(--accent-doux); }

.cal-legende {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

.cal-legende .cal-bande { margin: 0; }

/* Sur telephone, sept colonnes de barres deviennent illisibles : on
   garde la grille mais on masque le texte des barres, la couleur et
   l'infobulle suffisent, et le tableau sous la grille reste la source
   detaillee. */
@media (width < 640px) {
  .cal-jour { min-height: 38px; padding: 4px; }
  .cal-bande { padding: 3px 4px; font-size: 0.68rem; }
}


/* ==================================================================
   7. GABARIT D'ADMINISTRATION (regle 11 du CLAUDE.md global)

   Barre de compte en haut a droite, gamme de tons pour les boutons,
   grilles de cartes, et la page-liste type : texte d'introduction,
   barre d'outils, tableau triable d'une ligne de haut, pagination.

   Tout est branche sur les jetons : rien ici ne connait la couleur
   choisie dans la page Configuration.
   ================================================================== */

/* --- Barre laterale : un libelle par ligne, jamais coupe ---------- */
.nav-appli a { white-space: nowrap; }
.nav-appli a:has(+ *) { overflow: hidden; text-overflow: ellipsis; }

/* --- Barre du haut : le compte, et rien d'autre -------------------
   Le compte quitte le pied du menu pour le coin haut droit, ou tout le
   monde va le chercher. La barre reste collee en haut : sur une longue
   liste, on ne remonte pas 200 lignes pour se deconnecter. */
.appli-entete {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: flex-end;
  padding: 10px var(--gouttiere);
  border-bottom: 1px solid var(--bordure);
  background: var(--fond);
}

.appli-entete-fil {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--texte-tres-doux);
  font-size: 0.85rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Menu de compte : un <details> natif. Il s'ouvre au clic et au
   clavier sans une ligne de script — le script ne fait qu'ajouter le
   confort (fermeture au clic exterieur et a Echap). */
.menu-compte { position: relative; }

.menu-compte > summary {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 7px 12px;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-pilule);
  background: var(--surface);
  color: var(--texte);
  font-size: 0.9rem;
  cursor: pointer;
  list-style: none;
}

.menu-compte > summary::-webkit-details-marker { display: none; }
.menu-compte > summary:hover { background: var(--surface-2); }
.menu-compte[open] > summary { border-color: var(--texte-doux); }

.menu-compte-pastille {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent-doux);
  color: var(--accent-texte);
  font-size: 0.75rem;
  font-weight: 600;
}

.menu-compte-panneau {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 30;
  min-width: 232px;
  padding: 8px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  box-shadow: var(--ombre-flottante);
}

.menu-compte-qui {
  padding: 8px 10px 12px;
  border-bottom: 1px solid var(--bordure);
  margin-bottom: 6px;
  color: var(--texte-doux);
  font-size: 0.85rem;
}

.menu-compte-qui strong { display: block; color: var(--texte); font-size: 0.95rem; }

.menu-compte-panneau a,
.menu-compte-panneau button {
  display: block;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--rayon-petit);
  background: none;
  color: var(--texte);
  font-family: inherit;
  font-size: 0.92rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu-compte-panneau a:hover,
.menu-compte-panneau button:hover { background: var(--surface-3); color: var(--texte); }

/* --- Gamme de tons (regle 11c) ------------------------------------
   Une seule regle de bouton, six jeux de variables. Ajouter un ton, ce
   n'est pas ecrire une regle de plus : c'est declarer trois variables.
   L'encre est calculee cote serveur pour rester lisible quelle que soit
   la couleur choisie — c'est ce qui evite le bouton jaune a texte blanc. */
.bouton-rouge  { --ton: var(--ton-rouge);  --ton-survol: var(--ton-rouge-fonce);  --ton-encre: var(--ton-rouge-encre); }
.bouton-orange { --ton: var(--ton-orange); --ton-survol: var(--ton-orange-fonce); --ton-encre: var(--ton-orange-encre); }
.bouton-jaune  { --ton: var(--ton-jaune);  --ton-survol: var(--ton-jaune-fonce);  --ton-encre: var(--ton-jaune-encre); }
.bouton-vert   { --ton: var(--ton-vert);   --ton-survol: var(--ton-vert-fonce);   --ton-encre: var(--ton-vert-encre); }
.bouton-bleu   { --ton: var(--ton-bleu);   --ton-survol: var(--ton-bleu-fonce);   --ton-encre: var(--ton-bleu-encre); }
.bouton-gris   { --ton: var(--ton-gris);   --ton-survol: var(--ton-gris-fonce);   --ton-encre: var(--ton-gris-encre); }

.bouton-rouge,
.bouton-orange,
.bouton-jaune,
.bouton-vert,
.bouton-bleu,
.bouton-gris {
  background: var(--ton);
  color: var(--ton-encre);
}

.bouton-rouge:hover,
.bouton-orange:hover,
.bouton-jaune:hover,
.bouton-vert:hover,
.bouton-bleu:hover,
.bouton-gris:hover {
  background: var(--ton-survol);
  color: var(--ton-encre);
}

/* Meme ton, en contour : pour une action coloree qui ne doit pas peser
   autant qu'un aplat (un « Supprimer » de fin de ligne, par exemple). */
.bouton-contour {
  border-color: var(--ton, var(--bordure-forte));
  background: none;
  color: var(--ton-texte, var(--texte));
}

.bouton-contour:hover { background: var(--ton-doux, var(--surface-3)); color: var(--ton-texte, var(--texte)); }

.bouton-rouge.bouton-contour  { --ton-texte: var(--ton-rouge-texte);  --ton-doux: var(--ton-rouge-doux); }
.bouton-orange.bouton-contour { --ton-texte: var(--ton-orange-texte); --ton-doux: var(--ton-orange-doux); }
.bouton-jaune.bouton-contour  { --ton-texte: var(--ton-jaune-texte);  --ton-doux: var(--ton-jaune-doux); }
.bouton-vert.bouton-contour   { --ton-texte: var(--ton-vert-texte);   --ton-doux: var(--ton-vert-doux); }
.bouton-bleu.bouton-contour   { --ton-texte: var(--ton-bleu-texte);   --ton-doux: var(--ton-bleu-doux); }
.bouton-gris.bouton-contour   { --ton-texte: var(--ton-gris-texte);   --ton-doux: var(--ton-gris-doux); }

.bouton-petit { padding: 6px 12px; font-size: 0.85rem; }

/* --- Grilles de cartes (regle 11d) --------------------------------
   Les proportions demandees, nommees par ce qu'elles font. Tout
   s'effondre en une colonne sur telephone : deux tiers d'un ecran de
   375px ne sont pas deux tiers, ce sont deux colonnes illisibles. */
.grille-1-1,
.grille-1-2,
.grille-2-1,
.grille-3 {
  display: grid;
  gap: 20px;
  align-items: start;
  margin-bottom: 24px;
}

@media (width >= 900px) {
  .grille-1-1 { grid-template-columns: 1fr 1fr; }
  .grille-1-2 { grid-template-columns: 1fr 2fr; }
  .grille-2-1 { grid-template-columns: 2fr 1fr; }
  .grille-3   { grid-template-columns: repeat(3, 1fr); }
}

/* --- Introduction de rubrique (regle 11e) -------------------------
   Deux ou trois phrases avant le tableau. Ce n'est pas de l'ornement :
   une liste sans phrase d'accroche oblige chacun a deduire du contenu
   des colonnes ce que la rubrique contient et a quoi elle sert. */
/* Le texte d'introduction occupe TOUTE la largeur du contenu, comme
   le reste de la page (regle 11d). Une mesure de lecture etroite
   convient a une page editoriale ; dans une interface de travail, elle
   laisse un vide a droite du texte que rien ne vient occuper, et donne
   l'impression que la page n'est pas finie. */
.liste-intro {
  margin-bottom: 24px;
  color: var(--texte-doux);
}

.liste-intro p:last-child { margin-bottom: 0; }

/* --- Barre d'outils ----------------------------------------------- */
.barre-outils {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--bordure);
  margin-bottom: 4px;
}

.barre-outils-gauche,
.barre-outils-droite {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.barre-outils-droite { justify-content: flex-end; }

.outils-recherche {
  display: flex;
  gap: 8px;
  align-items: center;
}

.outils-recherche input[type="search"] {
  width: min(320px, 60vw);
  padding: 9px 12px;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte);
  font-family: inherit;
  font-size: 0.92rem;
}

.outils-taille {
  display: flex;
  gap: 8px;
  align-items: center;
  color: var(--texte-doux);
  font-size: 0.85rem;
}

.outils-taille select {
  padding: 7px 10px;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte);
  font-family: inherit;
  font-size: 0.9rem;
}

/* Le bouton de repli du selecteur : inutile des que le script tourne. */
.js .sans-js { display: none; }

.outils-aide > summary {
  list-style: none;
  cursor: pointer;
}

.outils-aide > summary::-webkit-details-marker { display: none; }

.outils-aide-contenu {
  position: absolute;
  right: var(--gouttiere);
  z-index: 15;
  max-width: 460px;
  padding: 18px 20px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  margin-top: 10px;
  background: var(--surface);
  box-shadow: var(--ombre-flottante);
  color: var(--texte-doux);
  font-size: 0.9rem;
}

.outils-aide-contenu p:last-child { margin-bottom: 0; }

/* --- Tableau de liste (regle 11 e/f) ------------------------------ */
.table-enveloppe-liste {
  overflow-x: auto;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  margin-top: 20px;
  background: var(--surface);
}

table.donnees th a.tri {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  color: inherit;
  text-decoration: none;
}

table.donnees th a.tri:hover { color: var(--texte); }
table.donnees th a.tri.est-actif { color: var(--accent-texte); }
.tri-fleche { font-size: 0.9em; opacity: 0.55; }
table.donnees th a.tri.est-actif .tri-fleche { opacity: 1; }

/* Une ligne tient sur UNE ligne (regle 11f). Le texte trop long est
   coupe par des points de suspension, pas replie : c'est ce qui permet
   de balayer verticalement une liste de 20 lignes sans que la hauteur
   de chacune depende de son contenu. Le titre complet reste accessible
   au survol et au lecteur d'ecran par l'attribut title. */
table.donnees.donnees-lignes td,
table.donnees.donnees-lignes th {
  /* Un peu plus serre que le tableau editorial : une liste de 20 lignes
     doit tenir sous les yeux sans defiler, et chaque ligne fait la meme
     hauteur puisque rien ne se replie. */
  padding: 10px 12px;
  vertical-align: middle;
  white-space: nowrap;
}

table.donnees.donnees-lignes td {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.donnees.donnees-lignes td.cellule-large { max-width: 460px; }
table.donnees.donnees-lignes td.cellule-etroite { max-width: 150px; }

/* La colonne Actions reste collee a droite pendant que le tableau
   defile. Sur une liste large — un chantier a huit colonnes — les
   boutons sortaient de l'ecran, et il fallait faire defiler
   horizontalement pour atteindre « Modifier ». Ce sont justement les
   trois choses qu'on vient faire sur une ligne : elles ne doivent
   jamais disparaitre. */
table.donnees th.col-actions,
table.donnees td.col-actions {
  position: sticky;
  right: 0;
  z-index: 2;
  width: 1%;
  background: var(--surface);
  text-align: right;
  white-space: nowrap;
}

/* Un filet marque la separation quand le tableau defile dessous, et le
   survol de ligne doit repeindre la cellule collee comme les autres. */
table.donnees td.col-actions { box-shadow: -1px 0 0 var(--bordure); }
table.donnees th.col-actions { box-shadow: -1px 0 0 var(--bordure); }
table.donnees tbody tr:hover td.col-actions { background: var(--surface-2); }

.actions-ligne {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: flex-end;
}

.actions-ligne .bouton { padding: 4px 10px; font-size: 0.8rem; }

/* La colonne de vignette n'a pas besoin d'un intitule visible : l'image
   se presente toute seule. Le mot reste pour le lecteur d'ecran. */
table.donnees th.col-photo {
  overflow: hidden;
  width: 1%;
  text-indent: -9999px;
}

/* --- Pagination (regle 11e) --------------------------------------- */
.pagination {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin: 18px 0 8px;
}

.pagination-compte {
  margin: 0;
  color: var(--texte-doux);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

.pagination-pages {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
  padding: 0;
  margin: 0;
  list-style: none;
}

.pagination-lien {
  display: inline-block;
  min-width: 36px;
  padding: 7px 10px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte-doux);
  font-size: 0.88rem;
  text-align: center;
  text-decoration: none;
}

.pagination-lien:hover { background: var(--surface-3); color: var(--texte); }

.pagination-lien.est-actif {
  border-color: var(--accent);
  background: var(--accent-doux);
  color: var(--accent-texte);
  font-weight: 600;
}

.pagination-lien.est-inerte {
  border-color: var(--bordure);
  background: none;
  color: var(--texte-tres-doux);
  cursor: default;
}

.pagination-saut { padding: 0 4px; color: var(--texte-tres-doux); }

/* --- Modal de confirmation de suppression (regle 11h) -------------
   Un <dialog> natif : le navigateur se charge du piege a focus, de la
   touche Echap et du fond inerte. Le script ne fait que l'ouvrir et
   recopier le libelle de la ligne visee. */
dialog.modal {
  width: min(460px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
}

dialog.modal::backdrop { background: rgb(20 20 19 / 45%); }

.modal-corps { padding: 26px 26px 20px; }
.modal-corps h2 { margin-bottom: 10px; font-size: 1.25rem; }
.modal-corps p { color: var(--texte-doux); }
.modal-cible { color: var(--texte); font-weight: 600; }

.modal-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: flex-end;
  padding: 16px 26px 22px;
  border-top: 1px solid var(--bordure);
  background: var(--surface-2);
}

/* --- Fiche de detail (bouton « Voir ») ---------------------------- */
.fiche-champs {
  display: grid;
  gap: 2px 24px;
  margin: 0;
}

.fiche-champs dt {
  color: var(--texte-tres-doux);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.fiche-champs dd { margin: 0 0 16px; }

@media (width >= 640px) {
  .fiche-champs { grid-template-columns: 200px 1fr; }
  .fiche-champs dt { padding-top: 2px; }
}

@media (width < 900px) {
  .barre-outils-gauche,
  .barre-outils-droite { width: 100%; }

  .barre-outils-droite { justify-content: flex-start; }
  .outils-recherche { flex: 1 1 100%; }
  .outils-recherche input[type="search"] { width: 100%; }
  .outils-aide-contenu { right: 0; left: 0; max-width: none; }
}

/* --- Page Configuration : echantillons de la gamme (regle 11k) -----
   Les pastilles de couleur ne peuvent pas porter un style en ligne :
   la CSP bloque l'attribut style comme elle bloque une balise <style>
   (regle 7). Elles lisent donc les jetons, exactement comme le reste de
   l'interface — ce qui a un avantage : l'apercu ne peut pas mentir, il
   affiche ce que l'application affichera. */
.gamme {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 12px;
}

.echantillon {
  padding: 14px 10px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  font-size: 0.78rem;
  text-align: center;
}

.echantillon-accent { background: var(--accent);     color: var(--accent-encre); }
.echantillon-rouge  { background: var(--ton-rouge);  color: var(--ton-rouge-encre); }
.echantillon-orange { background: var(--ton-orange); color: var(--ton-orange-encre); }
.echantillon-jaune  { background: var(--ton-jaune);  color: var(--ton-jaune-encre); }
.echantillon-vert   { background: var(--ton-vert);   color: var(--ton-vert-encre); }
.echantillon-bleu   { background: var(--ton-bleu);   color: var(--ton-bleu-encre); }
.echantillon-gris   { background: var(--ton-gris);   color: var(--ton-gris-encre); }

.champ-couleur input[type="color"] {
  width: 100%;
  height: 52px;
  padding: 4px;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  cursor: pointer;
}

.mesure {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding: 14px 16px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  margin-top: 16px;
  background: var(--surface-2);
  color: var(--texte-doux);
  font-size: 0.85rem;
}

.mesure strong { color: var(--texte); font-variant-numeric: tabular-nums; }

/* --- Vignette dans une ligne de liste ------------------------------
   36px : assez pour reconnaitre un chantier d'un coup d'oeil, assez
   petit pour que la ligne garde la meme hauteur que les autres. */
.col-photo { width: 1%; }

.miniature-liste {
  display: block;
  width: 36px;
  height: 36px;
  border-radius: var(--rayon-petit);
  object-fit: cover;
  background: var(--surface-2);
}

.miniature-vide { border: 1px dashed var(--bordure-forte); background: none; }

table.donnees.donnees-lignes td .legende { font-size: 0.78rem; }
table.donnees.donnees-lignes td .pastille { margin-left: 6px; }

/* --- Largeurs de colonnes des listes (regle 11e) -------------------
   Le tableau d'une liste passe en table-layout: fixed : la largeur des
   colonnes est DECLAREE, elle ne depend plus du contenu du jour. Sans
   cela, un intitule un peu long poussait les dernieres colonnes sous
   les boutons d'action colles a droite, et la meme page changeait
   d'allure d'une semaine a l'autre.

   Les largeurs sont des classes et non des styles en ligne : la CSP
   bloque l'attribut style au meme titre qu'une balise <style>. */
table.donnees.donnees-lignes { table-layout: fixed; }

.l-min  { width: 56px; }
.l-4    { width: 4%; }
.l-6    { width: 6%; }
.l-8    { width: 8%; }
.l-10   { width: 10%; }
.l-12   { width: 12%; }
.l-14   { width: 14%; }
.l-16   { width: 16%; }
.l-20   { width: 20%; }
.l-24   { width: 24%; }
.l-30   { width: 30%; }
/* 260 px et non 232 : « Voir · Modifier · Supprimer » mesure 254 px
   dans le navigateur. A 232, le dernier bouton etait rogne sur TOUTES
   les listes, y compris sur grand ecran — on lisait « Supprimer » avec
   la bordure droite coupee en deux. Mesure, pas estimation. */
.l-actions { width: 260px; }

/* Sous 720 px, les largeurs declarees ne s'appliquent plus du tout :
   le tableau passe en dimensionnement automatique et defile. Voir le
   bloc « LE TABLEAU DEFILE » en fin de fichier. */

/* --- Marges du contenu applicatif ---------------------------------
   La gouttiere de 64px vaut pour les pages publiques et editoriales,
   ou une ligne trop longue se lit mal. Une page d'admin, elle, porte
   des tableaux : ces 128px de marge etaient pris sur les colonnes de
   donnees, et c'est la derniere colonne qui payait. On les rend au
   contenu (regle 11d : les pages d'admin occupent toute la largeur). */
@media (width >= 1024px) {
  .appli-contenu { padding-right: 32px; padding-left: 32px; }
}


/* ==================================================================
   8. COMPOSANTS PROPRES A ALLENASSI

   Formulaires longs (le profil), questionnaire de sante, reperes
   chiffres du tableau de bord, et repli du calendrier en agenda sur
   petit ecran.

   Toutes les couleurs viennent des jetons : rien ici ne connait la
   couleur choisie dans la page Configuration.
   ================================================================== */

/* --- Pages publiques etroites (connexion, inscription) ------------
   Un formulaire de six champs sur 1100 pixels de large oblige l'oeil a
   traverser l'ecran entre le libelle et la valeur. */
.formulaire-etroit {
  max-width: 560px;
  margin: 0 auto;
}

/* --- Pages de texte legal -----------------------------------------
   Mesure de lecture courte : au-dela d'environ 70 caracteres par
   ligne, l'oeil perd le debut de la ligne suivante. */
.texte-legal {
  max-width: 68ch;
  margin: 0 auto;
}

.texte-legal h2 {
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--bordure);
}

.texte-legal h3 { margin-top: 28px; }
.texte-legal ul { padding-left: 20px; }
.texte-legal li { margin-bottom: 8px; }

/* --- Groupes de cases a cocher ------------------------------------
   Un <fieldset> nomme par sa <legend> : c'est ce qui permet au lecteur
   d'ecran d'annoncer « Allergies, case Oeufs, non cochee » plutot que
   « case, non cochee ». */
.groupe-cases {
  margin: 0 0 24px;
  padding: 20px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

.groupe-cases legend {
  padding: 0 8px;
  color: var(--texte);
  font-family: var(--police-titre);
  font-size: 1.05rem;
  font-weight: 600;
}

.groupe-cases .aide { margin-top: 8px; }

.cases {
  display: grid;
  gap: 8px;
  margin-top: 12px;
  grid-template-columns: 1fr;
}

/* Zone cliquable large : sur telephone, une case a cocher de 16 pixels
   se rate une fois sur trois. Le libelle entier declenche la case. */
.case {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  background: var(--fond);
  cursor: pointer;
  line-height: 1.45;
}

.case:hover { border-color: var(--bordure-forte); background: var(--surface-3); }

.case input {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--accent);
}

.case:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-doux);
}

/* Le focus se voit sur la ligne entiere, pas seulement sur la petite
   case : au clavier, on suit la ligne, pas le carre. */
.case:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.case-large { grid-column: 1 / -1; }

@media (width >= 640px) {
  .cases { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (width >= 1024px) {
  .cases { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- Ajouter a une liste fermee -----------------------------------
   Un seul bouton sous les cases, et le formulaire d'ajout dans un
   modal.

   Ce qu'il a fallu defaire pour en arriver la : les champs etaient
   d'abord poses A PLAT sous la liste. Pour les limitations, cela
   donnait sous la grille des limitations une SECONDE grille, aux memes
   libelles et aux memes dimensions — deux tableaux identiques empiles,
   et plus rien pour dire lequel etait lequel. Un ajout merite sa
   propre surface (regle 11g), pas la suite de la liste. */
.ajout-action {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--bordure);
}

/* Le modal d'ajout porte un formulaire, pas une question : il lui faut
   plus de largeur que la confirmation de suppression, et de quoi
   defiler quand la liste de rattachement est longue. */
dialog.modal-ajout {
  width: min(620px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
}

.modal-ajout .modal-corps {
  overflow-y: auto;
  max-height: calc(100vh - 170px);
}

.modal-ajout .champ { margin-top: 14px; }

/* Le libelle qui introduit les cases de rattachement. */
.modal-ajout .aide-cases {
  margin: 18px 0 8px;
  color: var(--texte);
  font-size: 0.95rem;
  font-weight: 500;
}

/* --- Cases de rattachement, en pastilles --------------------------
   Les memes libelles que la liste du profil, mais ils ne disent pas la
   meme chose : la-bas « je suis concerne », ici « ma limitation
   ressemble a ca ». Ils ne doivent donc surtout pas avoir la meme
   forme — d'ou des pastilles qui s'enchainent au fil du texte, la ou
   la liste principale aligne de grandes cartes en trois colonnes. */
.cases-rattachement {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
}

.cases-rattachement .case {
  align-items: center;
  padding: 5px 12px 5px 9px;
  border-radius: var(--rayon-pilule);
  background: var(--fond);
  font-size: 0.86rem;
  line-height: 1.3;
}

.cases-rattachement .case input {
  width: 15px;
  height: 15px;
  margin-top: 0;
}

/* --- Questionnaire de sante ---------------------------------------
   Une question par bloc, oui et non cote a cote. Pas de valeur par
   defaut cochee : « non » preselectionne serait rempli sans etre lu,
   et c'est precisement la reponse qui laisse passer. */
.question-sante {
  margin: 0 0 16px;
  padding: 18px 20px;
  border: 1px solid var(--bordure);
  border-left: 3px solid var(--accent);
  border-radius: var(--rayon);
  background: var(--surface);
}

.question-sante legend {
  padding: 0;
  font-family: var(--police-corps);
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.5;
}

.cases-oui-non {
  grid-template-columns: repeat(2, minmax(0, 160px));
  margin-top: 14px;
}

.cases-oui-non .case { justify-content: center; align-items: center; }

/* --- Cartes a intention particuliere ------------------------------- */

/* Etape a franchir : ce qui manque avant d'obtenir un programme. */
.carte-etape {
  border-left: 3px solid var(--accent);
  background: var(--accent-doux);
}

/* Rubrique pas encore livree. Un ton neutre, volontairement : une
   promesse ne doit pas avoir l'air d'une alerte. */
.carte-preparation {
  border-style: dashed;
  background: var(--surface-2);
}

.carte-preparation h2 { margin-bottom: 8px; }

/* Bloc qui contient des donnees de sante. Le liseré n'est pas
   decoratif : il signale a l'utilisateur ou se trouve ce qui est
   sensible, et a nous ou regarder en priorite. */
.carte-sensible { border-left: 3px solid var(--ambre); }

/* Action irreversible. */
.carte-danger {
  border-left: 3px solid var(--rouge);
  background: var(--rouge-doux);
}

/* --- Reperes chiffres du tableau de bord --------------------------- */
.reperes {
  display: grid;
  gap: 12px;
  margin: 20px 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.repere {
  padding: 14px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--fond);
  text-align: center;
}

.repere-valeur {
  margin: 0;
  font-family: var(--police-titre);
  font-size: 1.6rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.repere-libelle {
  margin: 4px 0 0;
  color: var(--texte-doux);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

@media (width >= 640px) {
  .reperes { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* --- Rappel medical, au pied de chaque page de l'application ------- */
.avis-medical {
  max-width: 68ch;
  margin: 48px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--bordure);
  color: var(--texte-tres-doux);
  font-size: 0.82rem;
  line-height: 1.5;
}

/* ==================================================================
   9. CALENDRIER — navigation, jetons, et repli en agenda

   Complete la section 6, reprise de Fucone. Ici, un jour porte des
   RENDEZ-VOUS (une seance, des courses), pas des plages : d'ou les
   jetons empiles dans la cellule plutot que des barres etalees sur
   plusieurs colonnes.
   ================================================================== */
.cal-navigation {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 12px;
  flex-wrap: wrap;
}

.cal-mois {
  margin: 0;
  font-family: var(--police-titre);
  font-size: 1.25rem;
  font-weight: 600;
  text-transform: capitalize;
}

.cal-bascule {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  flex-wrap: wrap;
}

/* Un jeton = un rendez-vous. Le libelle est toujours ecrit : la
   couleur ne porte jamais l'information seule (regle 10e). */
.cal-jeton {
  overflow: hidden;
  margin: 4px 0 0;
  padding: 4px 7px;
  border-left: 3px solid var(--texte-doux);
  border-radius: var(--rayon-petit);
  background: var(--surface-3);
  font-size: 0.74rem;
  line-height: 1.3;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cal-jeton-detail {
  display: block;
  color: var(--texte-doux);
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
}

.cal-jeton-seance { border-left-color: var(--accent); background: var(--accent-doux); }
.cal-jeton-courses { border-left-color: var(--bleu);  background: var(--bleu-doux); }

.calendrier .cal-jour { min-height: 84px; }

/* --- Repli en agenda -----------------------------------------------
   Sous 720 px, sept colonnes de cases ne se lisent plus. La MEME
   grille se replie en liste verticale : aucun contenu n'est duplique
   dans le HTML, seul le CSS change. Les jours sans rendez-vous
   disparaissent — une liste de trente lignes dont vingt disent
   « rien » ne se lit pas davantage.

   Le nom du jour vient de l'attribut data-libelle : en colonne unique,
   l'en-tete « Lun / Mar / Mer » ne dit plus rien. */
@media (width < 720px) {
  .calendrier:not(.cal-force-mois) .cal-entetes { display: none; }

  .calendrier:not(.cal-force-mois) .cal-semaine {
    gap: 0;
    padding-bottom: 0;
    border-bottom: none;
    grid-template-columns: 1fr;
  }

  .calendrier:not(.cal-force-mois) .cal-jour.hors-mois,
  .calendrier:not(.cal-force-mois) .cal-jour.cal-vide { display: none; }

  .calendrier:not(.cal-force-mois) .cal-jour {
    /* grid-row: auto est INDISPENSABLE ici. La regle de base pose
       « grid-row: 1 » pour aligner les sept jours d'une semaine sur
       une rangee ; en agenda, la grille n'a plus qu'une colonne, et
       sept cases qui reclament toutes la rangee 1 se rangent en
       COLONNES implicites. Le jour ne faisait plus que 81 px de large
       et son contenu debordait hors du cadre, invisible. */
    grid-row: auto;
    min-height: 0;
    padding: 12px 14px;
    border-right: none;
    border-bottom: 1px solid var(--bordure);
  }

  .calendrier:not(.cal-force-mois) .cal-numero { display: none; }

  .calendrier:not(.cal-force-mois) .cal-jour::before {
    content: attr(data-libelle);
    display: block;
    margin-bottom: 6px;
    color: var(--texte-doux);
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.03em;
    text-transform: capitalize;
  }

  .calendrier:not(.cal-force-mois) .cal-jour.aujourdhui::before {
    color: var(--accent-texte);
    font-weight: 600;
  }

  .calendrier:not(.cal-force-mois) .cal-jeton { white-space: normal; }

  /* Grille forcee sur petit ecran : elle deborde, elle defile. Mieux
     vaut un tableau qu'on fait glisser qu'un tableau illisible. */
  .cal-force-mois { overflow-x: auto; }
  .cal-force-mois .cal-entetes,
  .cal-force-mois .cal-semaine { min-width: 620px; }
}

/* Agenda force, quelle que soit la largeur. */
.cal-force-agenda .cal-entetes { display: none; }

.cal-force-agenda .cal-semaine {
  gap: 0;
  padding-bottom: 0;
  border-bottom: none;
  grid-template-columns: 1fr;
}

.cal-force-agenda .cal-jour.hors-mois,
.cal-force-agenda .cal-jour.cal-vide { display: none; }

.cal-force-agenda .cal-jour {
  /* Meme raison que dans le repli automatique : sans grid-row: auto,
     les sept cases se disputent la rangee 1 d'une grille qui n'a plus
     qu'une colonne, et le contenu deborde du cadre. */
  grid-row: auto;
  min-height: 0;
  padding: 12px 14px;
  border-right: none;
  border-bottom: 1px solid var(--bordure);
}

.cal-force-agenda .cal-numero { display: none; }

.cal-force-agenda .cal-jour::before {
  content: attr(data-libelle);
  display: block;
  margin-bottom: 6px;
  color: var(--texte-doux);
  font-size: 0.82rem;
  font-weight: 500;
  text-transform: capitalize;
}

.cal-force-agenda .cal-jeton { white-space: normal; }

/* Bouton verrouille : present pour que la colonne Actions garde la
   meme forme d'une ligne a l'autre, mais inactif et annonce comme tel.
   Le retirer purement et simplement ferait sauter l'alignement des
   trois boutons sur les lignes voisines. */
.bouton.est-inerte {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Fil d'Ariane des pages de formulaire : le chemin de retour vers la
   liste, dans l'etat ou on l'avait laissee. */
.fil-ariane {
  margin: 0 0 8px;
  color: var(--texte-doux);
  font-size: 0.85rem;
}

.fil-ariane a { color: var(--accent-texte); }

/* --- Case d'allergène : la case et son niveau sur la même ligne ------
   « Contient » et « trace » ne sont pas deux allergènes différents,
   c'est un même allergène à deux degrés de certitude. Les séparer en
   deux listes obligerait à chercher un aliment à deux endroits. */
.case-allergene {
  display: flex;
  align-items: center;
  gap: 6px;
}

.case-allergene .case {
  flex: 1;
  min-width: 0;
}

.select-niveau {
  flex: none;
  width: 96px;
  padding: 6px 8px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte-doux);
  font-family: inherit;
  font-size: 0.8rem;
}

/* Liste simple : une énumération dans une carte, sans puces lourdes. */
.liste-simple {
  margin: 0 0 12px;
  padding-left: 18px;
  color: var(--texte-doux);
  font-size: 0.9rem;
}

.liste-simple li { margin-bottom: 6px; }

/* --- Composition d'une recette -------------------------------------
   Le formulaire d'ajout se détache de la liste au-dessus : sans ce
   filet, on ne sait plus si l'on regarde ce qui est enregistré ou ce
   qu'on est en train de saisir. */
.ligne-ajout {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px dashed var(--bordure-forte);
}

/* Étapes numérotées : le numéro est un repère qu'on relit du coin de
   l'œil, une main dans la casserole. */
.etapes {
  margin: 0 0 12px;
  padding-left: 0;
  list-style: none;
  counter-reset: etape;
}

.etapes li {
  position: relative;
  margin-bottom: 12px;
  padding: 12px 12px 12px 48px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--fond);
  counter-increment: etape;
}

.etapes li::before {
  content: counter(etape);
  position: absolute;
  top: 12px;
  left: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--rayon-pilule);
  background: var(--accent);
  color: var(--accent-encre);
  font-family: var(--police-titre);
  font-size: 0.85rem;
  font-weight: 600;
}

.etapes li p { margin: 0 0 8px; }
.etapes .actions-ligne form { display: inline; }

/* --- Trace d'une conversation SMTP ---------------------------------
   Chaque échange sur sa ligne, en chasse fixe, avec un liseré qui dit
   d'un coup d'œil où ça s'est arrêté. Le but est qu'on puisse copier
   la ligne fautive et la coller telle quelle dans une recherche. */
.trace-smtp {
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: ui-monospace, "sf mono", menlo, consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.5;
}

.trace-smtp li {
  margin-bottom: 2px;
  padding: 6px 10px;
  border-left: 3px solid var(--bordure-forte);
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
  overflow-wrap: anywhere;
}

.trace-smtp .trace-ok   { border-left-color: var(--vert);  background: var(--vert-doux); }
.trace-smtp .trace-ko   { border-left-color: var(--rouge); background: var(--rouge-doux); }
.trace-smtp .trace-info { border-left-color: var(--bleu);  background: var(--bleu-doux); font-family: var(--police-corps); }

/* ==================================================================
   10. GRAPHIQUES

   Le SVG est écrit par PHP (_php/graphique.php) ; toute son apparence
   est ici, en jetons de charte. Aucune couleur n'est écrite dans le
   SVG lui-même : il suit donc le thème choisi dans la page
   Configuration, comme le reste.

   Marques fines, grille effacée, une seule valeur écrite en clair —
   celle qu'on cherche. Une étiquette sur chaque point transformerait
   la courbe en tableau mal rangé.
   ================================================================== */
.graphique {
  display: block;
  width: 100%;
  height: auto;
  /* Assez haut pour que le SVG remplisse la carte : sous 300 px, le
     rapport du viewBox (900 x 340) bridait la largeur et laissait deux
     bandes blanches de chaque côté. */
  max-height: 360px;
}

/* La grille se devine, elle ne se lit pas. */
.gr-grille {
  stroke: var(--bordure);
  stroke-width: 1;
}

.gr-axe {
  fill: var(--texte-tres-doux);
  font-family: var(--police-corps);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.gr-axe-y { text-anchor: end; }

/* L'aire n'ajoute pas d'information : elle donne du corps à une ligne
   qui, seule, flotte au milieu d'un cadre vide. D'où sa discrétion. */
.gr-aire {
  fill: var(--accent);
  opacity: 0.08;
}

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

/* Anneau de la couleur du fond autour de chaque point : c'est ce qui
   les détache quand deux pesées sont proches. */
.gr-point {
  fill: var(--accent);
  stroke: var(--surface);
  stroke-width: 2;
}

/* Le poids visé : un trait tireté, jamais confondu avec la mesure. */
.gr-cible {
  stroke: var(--texte-doux);
  stroke-width: 1;
  stroke-dasharray: 5 4;
}

.gr-cible-texte {
  fill: var(--texte-doux);
  font-family: var(--police-corps);
  font-size: 11px;
}

/* La seule valeur écrite en clair : la dernière.

   Le liseré de la couleur du fond, dessiné SOUS le texte grâce à
   paint-order, lui creuse un halo dans la courbe. Sans lui, le trait
   passait au travers des chiffres — c'est le genre de collision qui ne
   se voit qu'une fois le graphique affiché, jamais dans le code. */
.gr-valeur {
  fill: var(--texte);
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 4;
  stroke-linejoin: round;
  font-family: var(--police-titre);
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-anchor: end;
}

/* SUR TÉLÉPHONE, LE TEXTE DU SVG DOIT GROSSIR — beaucoup.

   Un SVG en largeur 100 % se réduit tout entier : sur 390 px d'écran,
   le viewBox de 900 unités est affiché à 0,38× — un texte de 12
   unités sort à 4,5 pixels, illisible. Les valeurs ci-dessous sont
   exprimées en unités du viewBox, pas en pixels d'écran : 26 unités
   donnent environ 10 pixels réels. C'est le genre de défaut qui ne se
   voit qu'en regardant la page sur un téléphone. */
@media (width < 640px) {
  .graphique { max-height: none; }
  .gr-axe { font-size: 26px; }
  .gr-valeur { font-size: 34px; stroke-width: 8; }
  .gr-cible-texte { font-size: 24px; }
  .gr-ligne { stroke-width: 4; }
  .gr-point { r: 7; stroke-width: 4; }
  .gr-grille { stroke-width: 2; }
  .gr-cible { stroke-width: 2; stroke-dasharray: 10 8; }
}

/* LE TABLEAU DÉFILE, IL NE S'ÉCRASE PAS.

   Sur grand écran, les largeurs en pourcentage du colgroup donnent à
   la liste une allure stable, indépendante des données du jour : c'est
   tout l'intérêt de `table-layout: fixed` (règle 11f).

   Sur téléphone, ce même mécanisme se retourne contre nous. Huit
   colonnes à répartir sur 500 px donnent 45 px chacune : ce n'est plus
   la donnée qui est tronquée, c'est L'EN-TÊTE. « Prot.↕ » devient
   « Pro… », et l'on ne sait plus ce que la colonne contient.

   Sous 720 px, on rend donc la main au navigateur : chaque colonne
   prend la largeur de son contenu, le tableau défile dans son
   conteneur, et un plafond de 260 px empêche un titre de recette à
   rallonge de pousser le reste hors de portée — il s'y tronque, avec
   son texte complet dans l'attribut title.

   Toutes ces valeurs ont été mesurées dans un navigateur à 390 px de
   large, pas estimées : c'est le seul moyen de savoir ce qui dépasse. */
@media (width < 720px) {
  .table-enveloppe-liste table.donnees.donnees-lignes {
    table-layout: auto;
    /* `width: auto` et non `min-width: max-content` : sur un tableau,
       max-content donne des largeurs aberrantes — mesuré à 24 000 px
       pour douze lignes. Le dimensionnement automatique suffit : les
       cellules sont en nowrap, la table prend donc la largeur de son
       contenu, et min-width la maintient au moins à celle du cadre
       quand il y a peu de colonnes. */
    width: auto;
    min-width: 100%;
  }

  /* Les largeurs déclarées ne valent que pour le grand écran. */
  .table-enveloppe-liste col { width: auto; }

  /* Plafond : au-delà, on tronque plutôt que d'allonger le défilement. */
  .table-enveloppe-liste td,
  .table-enveloppe-liste th { max-width: 260px; }

  /* La colonne d'actions garde ses trois boutons entiers. */
  .table-enveloppe-liste td.col-actions { max-width: none; }
}
}

/* --- Une limitation et son substitut, sur la même ligne -------------
   Les séparer en deux listes ferait perdre le lien : on ne coche pas
   « épaule » d'un côté et « remplacer par » de l'autre. C'est une
   seule décision, elle tient sur une ligne. */
.limitation {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.limitation .case {
  flex: 1 1 240px;
  min-width: 0;
}

.select-substitut {
  flex: 1 1 260px;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte);
  font-family: inherit;
  font-size: 0.85rem;
}

/* Tant que la limitation n'est pas cochée, son substitut n'a pas de
   raison d'attirer l'oeil. */
.limitation:not(:has(input:checked)) .select-substitut {
  opacity: 0.5;
}

/* --- Flèches de réordonnancement ------------------------------------
   Deux formulaires côte à côte dans une cellule : chacun est un POST
   avec son jeton, jamais un lien — un préchargeur de navigateur ne
   doit pas pouvoir réorganiser le parcours du magasin. */
.fleches-ordre {
  display: inline-flex;
  gap: 2px;
}

.fleches-ordre form { display: inline; }

.fleches-ordre button {
  min-width: 30px;
  padding: 4px 6px;
}

/* Le rayon qu'on regarde, dans la liste de ses voisins de parcours. */
.parcours-actuel {
  padding: 4px 8px;
  margin-left: -8px;
  border-radius: var(--rayon-petit);
  background: var(--accent-doux);
  list-style: none;
}

/* Une donnée encore valable mais qui mérite d'être revue. */
.texte-attention { color: var(--ambre); }

/* ==================================================================
   11. MODÈLES DE SÉANCE
   ================================================================== */

/* Un avertissement qui n'est pas une erreur : la page s'affiche, mais
   ce qu'elle montre ne pourra pas servir en l'état. */
.message-attention {
  border-left-color: var(--ambre);
  background: var(--ambre-doux);
}

/* Une ligne de tableau qui pose problème. Le liseré ne suffit pas à
   porter l'information (règle 10e) : la cellule « Choix » affiche le
   chiffre zéro, et l'attribut title donne le motif. */
.ligne-alerte > td { background: var(--ambre-doux); }

/* La ligne dont il est question MAINTENANT, dans un tableau qui montre
   une suite d'etapes — les cycles jusqu'a l'objectif, par exemple. Le
   fond ne porte jamais l'information seul : la cellule dit « celui-ci »
   en toutes lettres (regle 10e). */
.ligne-active > th,
.ligne-active > td { background: var(--accent-doux); }

/* Les actions rangées à droite du titre de page. */
.titre-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Le déroulé de la séance, tel qu'il sera lu. */
.deroule-seance {
  margin: 0;
  padding: 0;
  list-style: none;
}

.deroule-phase h3 {
  margin: 24px 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--bordure);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--texte-doux);
}

.deroule-phase:first-child h3 { margin-top: 0; }

.deroule-bloc {
  padding: 12px 0 12px 16px;
  border-left: 2px solid var(--accent-doux);
}

.deroule-titre {
  margin: 0;
  font-weight: 500;
}

.deroule-titre .legende { margin-left: 8px; }

.deroule-volume {
  margin: 2px 0 0;
  font-variant-numeric: tabular-nums;
}

.deroule-consigne {
  margin: 6px 0 0;
  color: var(--texte-doux);
  font-size: 0.93rem;
}

/* Les exercices candidats d'un bloc. */
.bloc-candidats {
  padding: 16px 0;
  border-top: 1px solid var(--bordure);
}

.bloc-candidats:first-of-type { border-top: 0; }

.bloc-candidats h3 {
  margin: 0 0 8px;
  font-size: 1rem;
}

.bloc-candidats h3 .legende { margin-left: 8px; }

/* ==================================================================
   12. LE PROGRAMME ALIMENTAIRE
   ================================================================== */

/* Le jour en cours se repère d'un coup d'œil dans une semaine :
   c'est la seule carte que l'on cherche en ouvrant la page. */
.carte-aujourdhui {
  border-left: 3px solid var(--accent);
  background: var(--accent-doux);
}

.jour-entete {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}

.jour-entete h2 {
  margin: 0;
  font-size: 1.1rem;
}

.jour-entete h2 .legende { margin-left: 8px; }

.jour-total {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* Un repas : le moment à gauche, le plat au milieu, la case à droite.
   Sous 600px la colonne du moment passe au-dessus — une étiquette de
   80px et un titre de recette ne cohabitent pas sur un téléphone. */
.liste-repas {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* .repas EST UN VOCABULAIRE PARTAGE — NE PAS LE REDEFINIR.
   Il sert aussi au deroule des seances (app/seances.php), qui pose la
   meme structure « etiquette | corps » sans troisieme colonne. Une
   version de ce fichier avait fait passer .repas de trois colonnes a
   deux pour les repas composes : la phase de seance s'est mise a
   occuper toute la largeur et le mouvement a ete repousse a droite.
   Aucune erreur, aucun journal — juste une page de seances illisible.
   Ce qui est propre aux repas composes porte donc ses propres classes,
   plus bas (.repas-creneau, .composante). */
.repas {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  align-items: baseline;
  gap: 4px 16px;
  padding: 12px 0;
  border-top: 1px solid var(--bordure);
}

.repas:first-child { border-top: 0; }

.repas-moment {
  color: var(--texte-doux);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.repas-titre { margin: 0; font-weight: 500; }

/* --- Le repas compose (L16), et lui seul ---------------------------
   Un dejeuner porte jusqu'a quatre plats. Le nom du creneau appartient
   alors au BLOC et non a la ligne : il tient la colonne de gauche, les
   composantes s'empilent en face. */
.repas-creneau {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 4px 16px;
  padding: 12px 0;
  border-top: 1px solid var(--bordure);
}

.repas-creneau:first-child { border-top: 0; }

.liste-composantes {
  margin: 0;
  padding: 0;
  list-style: none;
}

.composante {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 4px 16px;
  padding: 6px 0;
}

/* Un filet TRES leger entre deux composantes du meme repas : elles se
   lisent ensemble, contrairement a deux creneaux qui se separent. */
.composante + .composante { border-top: 1px dashed var(--bordure); }

/* Le total du creneau, sous son nom : c'est le chiffre qui remplace
   celui que portait l'ancienne ligne unique. Porte par .repas-creneau
   pour ne pas atteindre les .repas-moment des seances. */
.repas-creneau .repas-moment .legende {
  display: block;
  margin-top: 2px;
  letter-spacing: 0;
  text-transform: none;
}

/* « Entrée », « Plat », « Pain », « Dessert » : une etiquette, pas un
   titre. Elle ne porte jamais seule l'information — l'ordre de service
   la redit, et le nom de la recette suit immediatement (regle 10e). */
.repas-composante {
  display: inline-block;
  min-width: 62px;
  margin-right: 6px;
  color: var(--texte-doux);
  font-size: 0.78rem;
  font-weight: 400;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.repas-detail {
  margin: 2px 0 0;
  color: var(--texte-doux);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}

/* Un repas fait reste lisible : on l'atténue, on ne le barre pas.
   Une liste barrée devient illisible dès qu'on en coche la moitié. */
.repas-fait .repas-titre a,
.repas-fait .repas-detail { color: var(--texte-doux); }

.repas-fait .repas-moment { opacity: 0.6; }

@media (width < 600px) {
  .repas { grid-template-columns: 1fr auto; }
  .repas .repas-moment { grid-column: 1 / -1; }

  .repas-creneau { grid-template-columns: 1fr; }
  .repas-creneau .repas-moment { margin-bottom: 4px; }
  .repas-creneau .repas-moment .legende { display: inline; margin-left: 6px; }
  /* En telephone l'etiquette passe au-dessus du titre : « Entrée » et
     « Salade de thon et haricots rouges » sur la meme ligne de 320px,
     c'est le titre qui se coupe. */
  .repas-composante { display: block; margin-bottom: 2px; }
}

/* Ingrédients et étapes. */
.liste-ingredients {
  margin: 0;
  padding: 0;
  list-style: none;
}

.liste-ingredients li {
  padding: 6px 0;
  border-top: 1px solid var(--bordure);
}

.liste-ingredients li:first-child { border-top: 0; }

.ingredient-quantite {
  display: inline-block;
  min-width: 6.5em;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

.ingredient-facultatif { color: var(--texte-doux); }

.liste-etapes {
  margin: 0;
  padding-left: 1.4em;
}

.liste-etapes li { padding: 6px 0; }

/* La navigation entre semaines réutilise la pagination des listes. */
.pagination-etat {
  color: var(--texte-doux);
  font-variant-numeric: tabular-nums;
}

/* Un repère qui porte du texte plutôt qu'un nombre : une date, un
   libellé de sport. Le grand corps de la serif est calibré pour trois
   ou quatre caractères ; « 24/08/2026 » y est tronqué sur un écran de
   375 px. */
.repere-valeur-texte {
  font-size: 1.15rem;
  line-height: 1.3;
  letter-spacing: 0;
}

/* Les jetons du calendrier deviennent cliquables : chacun mène à sa
   page. Un calendrier qui affiche une séance sans permettre de
   l'ouvrir oblige à retrouver le même jour dans une autre rubrique. */
a.cal-jeton {
  display: block;
  text-decoration: none;
  color: inherit;
}

a.cal-jeton:hover,
a.cal-jeton:focus-visible {
  filter: brightness(0.97);
}

/* LES REPAS EN AMBRE, PAS EN VERT.
   Ils portaient « --vert » (#1f834e) tandis que les séances portent
   « --accent » (#1f7a4d) : deux verts à six unités l'un de l'autre, donc
   deux jetons impossibles à distinguer dans une case de calendrier — et
   une légende qui annonçait trois couleurs alors qu'on n'en voyait que
   deux. Le vert de statut a beau exister, il ne peut pas servir de teinte
   distinctive dans un produit dont l'accent EST vert.
   L'ambre s'écarte franchement du vert des séances et du bleu de la
   cuisine d'avance. Le libellé reste écrit dans chaque jeton : la couleur
   ne porte jamais l'information seule (règle 10e). */
.cal-jeton-repas { border-left-color: var(--ambre); background: var(--ambre-doux); }

/* Une séance faite reste lisible, simplement atténuée : la barrer
   rendrait la grille illisible dès la deuxième semaine. */
.cal-jeton-fait { opacity: 0.62; }

/* ==================================================================
   13. LA LISTE DE COURSES
   ================================================================== */

/* Elle se lit une main sur le chariot : les lignes sont hautes, le
   bouton est large, et le prix reste aligné à droite pour qu'on
   puisse suivre la colonne du regard. */
.liste-courses {
  margin: 0;
  padding: 0;
  list-style: none;
}

.course {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: 4px 14px;
  padding: 10px 0;
  border-top: 1px solid var(--bordure);
}

.course:first-child { border-top: 0; }

/* Le nom est une <label> : il se comporte comme du texte, mais il
   coche l'article. Le curseur le dit, et la zone cliquable couvre
   toute la largeur restante de la ligne. */
.course-nom {
  display: block;
  margin: 0;
  font-weight: 500;
  cursor: pointer;
}

.course-detail {
  margin: 2px 0 0;
  color: var(--texte-doux);
  font-size: 0.9rem;
}

.course-prix {
  margin: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Un article pris s'atténue sans se barrer : sur une liste de
   cinquante lignes à moitié cochée, le texte barré devient illisible
   au moment précis où l'on cherche ce qu'il reste à prendre. */
.course-prise .course-nom,
.course-prise .course-detail,
.course-prise .course-prix { color: var(--texte-doux); }

.course-prise .course-nom { font-weight: 400; }

@media (width < 600px) {
  .course {
    grid-template-columns: auto 1fr;
  }
  /* Le prix passe sous le nom plutôt que de comprimer le libellé :
     « Steak haché de bœuf 5 % » ne tient pas à côté d'un bouton et
     d'un montant sur 375 px. */
  .course-prix {
    grid-column: 2;
    color: var(--texte-doux);
    font-size: 0.9rem;
  }
}

/* La cible tactile de la liste de courses.
   Trente pixels de haut se ratent une fois sur trois quand on tient
   un téléphone d'une main et un cabas de l'autre. Quarante-quatre est
   le minimum recommandé, et c'est ici qu'il compte le plus. */
.course-case button {
  min-height: 44px;
  min-width: 74px;
}


/* ==================================================================
   14. LA VISITE GUIDEE (regle 12) — portee de Fucone
   ==================================================================
   Le halo sur la cible, la bulle et sa fleche, le compteur, le pied.
   Aucune position n'est ecrite ici : elles sont posees en JS
   (element.style.left), parce que la CSP interdit l'attribut style
   au meme titre qu'une balise <style>. */

.visite-halo {
  position: fixed;
  z-index: 900;
  border-radius: var(--rayon-petit);
  box-shadow: 0 0 0 9999px rgb(20 20 19 / 62%);

  /* AUCUN ÉVÉNEMENT : le halo couvre l'écran entier par son ombre. Sans
     cette ligne, il avalerait tous les clics — y compris ceux des
     boutons de la bulle, pourtant au-dessus. */
  pointer-events: none;
  transition: top 180ms ease, left 180ms ease, width 180ms ease, height 180ms ease;
}

.visite-halo-vide {
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
}

.visite-bulle {
  position: fixed;
  z-index: 901;
  width: min(340px, calc(100vw - 32px));
  padding: 18px 20px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  box-shadow: var(--ombre-flottante);
}

.visite-bulle h2 {
  margin-bottom: 8px;
  font-size: 1.05rem;
}

.visite-texte {
  margin-bottom: 0;
  color: var(--texte-doux);
  font-size: 0.9rem;
  line-height: 1.6;
}

.visite-texte strong { color: var(--texte); }

.visite-fleche {
  position: absolute;
  width: 12px;
  height: 12px;
  border-top: 1px solid var(--bordure);
  border-left: 1px solid var(--bordure);
  background: var(--surface);
  transform: rotate(45deg);
}

.visite-fleche-gauche { top: 22px; left: -7px; }

.visite-fleche-droite { top: 22px; right: -7px; transform: rotate(225deg); }

.visite-fleche-haut   { top: -7px; left: 26px; }

.visite-fleche-bas    { bottom: -7px; left: 26px; transform: rotate(225deg); }

.visite-pied {
  display: flex;
  gap: 8px;
  align-items: center;
  padding-top: 14px;
  border-top: 1px solid var(--bordure);
  margin-top: 16px;
}

.visite-compteur {
  /* Sans cette réserve, la largeur du compteur change entre « 1 / 9 » et
     « 10 / 9 » et les deux boutons sautent d'un pixel à chaque étape. */
  min-width: 44px;
  margin-right: auto;
  color: var(--texte-tres-doux);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

.visite-passer {
  position: absolute;
  top: 10px;
  right: 12px;
  padding: 4px 6px;
  border: 0;
  background: none;
  color: var(--texte-tres-doux);
  font-family: inherit;
  font-size: 0.78rem;
  text-decoration: underline;
  cursor: pointer;
}

.visite-passer:hover { color: var(--texte); }

.visite-cible {
  position: relative;
  z-index: 899;
}

[data-visite-auto][hidden] { display: none; }

  .visite-bulle {
    top: auto !important;
    right: 12px;
    bottom: 12px;
    left: 12px !important;
    width: auto;
  }

  .visite-fleche { display: none; }

@media (width < 640px) {
  .visite-bulle {
    top: auto !important;
    right: 12px;
    bottom: 12px;
    left: 12px !important;
    width: auto;
  }
  .visite-fleche { display: none; }
}

/* La case à cocher de la liste de courses : large, parce qu'on la
   vise d'un pouce en tenant un cabas. */
.course-coche {
  width: 26px;
  height: 26px;
  margin: 9px 24px 9px 0;
  accent-color: var(--accent);
  cursor: pointer;
}

/* Pendant que le serveur répond. Discret : l'aller-retour dure
   quelques dizaines de millisecondes, un indicateur voyant
   clignoterait à chaque article. */
.course-en-cours { opacity: 0.6; }

/* L'échec, lui, doit se voir : une case qui reste cochée alors que
   rien n'a été enregistré ferait sortir du magasin sans le pain. */
.course-echec {
  background: var(--rouge-doux);
  outline: 2px solid var(--rouge);
  outline-offset: 2px;
}

/* ==================================================================
   14. LA CHARGE (L10)

   « À mettre : 45 kg » se lit avant tout le reste du bloc : c'est la
   seule ligne qu'on cherche des yeux entre deux séries, souvent debout,
   le telephone a bout de bras. D'ou l'accent, le corps un peu plus
   gros, et le liseré à gauche qui la détache des consignes.
   ================================================================== */
.deroule-charge {
  margin: 6px 0 0;
  padding: 4px 0 4px 10px;
  border-left: 3px solid var(--accent);
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
}

.deroule-charge .legende {
  display: block;
  margin-top: 2px;
  font-size: 0.85rem;
}

/* Le champ de saisie d'une charge : un nombre court, pas une phrase.
   Un champ pleine largeur inviterait a y ecrire « environ 40 ». */
.champ-etroit {
  width: 7em;
  padding: 6px 8px;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-petit);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-variant-numeric: tabular-nums;
}

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

/* Une etiquette lue par les lecteurs d'ecran, absente a l'oeil.

   PAS « display: none », qui la retirerait AUSSI de la restitution
   vocale : dans un tableau de saisie, « Charge » repete douze fois ne
   dit pas de quel mouvement il s'agit, et c'est precisement ce que
   cette etiquette porte. La technique du cadre de 1px reste la seule
   qui masque a l'oeil sans masquer a la voix. */
.etiquette-invisible {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Une ligne de courses au prix impossible. Le motif est ecrit a cote en
   toutes lettres : la couleur ne fait que le rendre reperable dans une
   liste de cinquante articles, elle ne porte pas l'information. */
.course-prix-suspect {
  color: var(--rouge);
  font-weight: 600;
}

.course-prix-suspect .legende {
  display: block;
  color: var(--texte-doux);
  font-weight: 400;
}

/* Un repere qui annonce un depassement. Le libelle dit deja « par
   rapport a vos 100 € » et le nombre porte son signe : la couleur ne
   fait que le rendre reperable (regle 10e). */
.repere-valeur-alerte { color: var(--rouge); }

/* ==================================================================
   15. LA TRAJECTOIRE PREVUE (L12)

   Deux lignes sur le meme graphique, et il faut qu'on sache en un coup
   d'oeil laquelle est une MESURE et laquelle est un CALCUL.

   La courbe des pesees garde le trait plein et l'accent : c'est un
   fait. La trajectoire prevue est tiretee et grise : c'est une
   promesse du programme, pas une observation. Les confondre ferait
   croire qu'on a pese des jours ou l'on n'est pas monte sur la
   balance.
   ================================================================== */
.gr-prevu {
  stroke: var(--texte-tres-doux);
  stroke-width: 2;
  stroke-dasharray: 7 5;
  stroke-linecap: round;
}

.gr-prevu-texte {
  fill: var(--texte-tres-doux);
  font-size: 13px;
  font-family: var(--police-corps);
}

/* Le point prevu au jour de la derniere pesee : creux, pour rester
   second par rapport au point plein de la mesure. */
.gr-point-prevu {
  fill: var(--surface);
  stroke: var(--texte-tres-doux);
  stroke-width: 2;
}

/* L'ECART, TRAIT VERTICAL ENTRE PREVU ET REEL.
   C'est la reponse a « ou j'en suis » — et elle se lit sans chiffre :
   un trait court, on est dans les clous ; un trait long, on ne l'est
   pas. Le sens (au-dessus ou en dessous) se lit a la position, pas a
   la couleur : quelqu'un qui distingue mal les couleurs voit quand
   meme de quel cote penche la difference (regle 10e). */
.gr-ecart {
  stroke: var(--texte-doux);
  stroke-width: 1.5;
  stroke-dasharray: 2 3;
}

/* ==================================================================
   MENTION DE TRAITEMENT (RGPD) EN LIGNE
   ==================================================================
   Dire ou part le texte est obligatoire, mais un pave bleu de trois
   paragraphes au-dessus d'un champ de quatre lignes inverse le rapport :
   la mention pese plus lourd que ce qu'elle commente, et se saute pour
   cette raison meme.
   Une seule coulee, corps reduit, filet d'accent a gauche : elle reste
   lisible et rattachee au champ, sans lui voler la vedette. Le contraste
   reste AA — c'est du --texte-doux sur --surface, pas du gris clair. */
.mention-traitement {
  margin: 0 0 18px;
  padding: 8px 0 8px 12px;
  border-left: 2px solid var(--bleu);
  color: var(--texte-doux);
  /* 0,85 rem = 13,6 px : nettement plus petit que le 0,95 rem du bloc
     message, sans descendre sous le plancher de lisibilite. */
  font-size: 0.85rem;
  line-height: 1.55;
}

.mention-traitement strong { color: var(--texte); }

/* ==================================================================
   ÉTIQUETTE RÉSERVÉE AU LECTEUR D'ÉCRAN — L'ALIAS MANQUANT
   ==================================================================
   liste_recherche() et plusieurs <caption> écrivent class="lecteur-seul"
   depuis le début, mais la classe n'existait que sous le nom
   « etiquette-invisible ». Le libellé destiné au lecteur d'écran
   s'affichait donc en clair au-dessus du champ de recherche, sur toutes
   les pages-liste du projet. Les deux noms cohabitent ici plutôt que de
   renommer trente occurrences : il n'y a rien à gagner à choisir. */
.lecteur-seul,
.etiquette-invisible {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==================================================================
   TICKETS DE CAISSE (L17)
   ================================================================== */

/* L'aperçu de la photo avant envoi. Hauteur bornée : un ticket
   photographié en portrait fait quatre mille pixels de haut, et
   pousserait le bouton d'envoi hors de l'écran — précisément sur le
   téléphone où l'on s'en sert. */
.apercu-ticket {
  margin: 0 0 18px;
  padding: 10px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
  text-align: center;
}

.apercu-ticket img {
  max-width: 100%;
  max-height: 320px;
  border-radius: var(--rayon-petit);
}

/* Le tableau de rapprochement. Il déroge à la règle « une ligne de
   tableau tient sur une seule ligne de hauteur » (règle 11f) : chaque
   ligne porte deux listes déroulantes, un champ de saisie et deux
   cases. Ce n'est pas une liste de consultation, c'est un formulaire
   présenté en tableau — la contrainte de hauteur y produirait des
   contrôles écrasés et intouchables au doigt. */
table.donnees.donnees-ticket td,
table.donnees.donnees-ticket th {
  white-space: normal;
  vertical-align: top;
}

table.donnees.donnees-ticket select,
table.donnees.donnees-ticket input[type="number"] {
  width: 100%;
  max-width: 220px;
  margin: 0 0 6px;
}

/* Le libellé brut du ticket en chasse fixe : c'est une transcription,
   et on la compare du regard avec la photo. En proportionnelle,
   « 4X100G » et « 4X1OOG » se ressemblent. */
.ticket-brut {
  display: block;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.ticket-libelle .legende { display: block; margin-top: 2px; }

.ticket-retenir {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ticket-retenir .case {
  padding: 6px 8px;
  font-size: 0.85rem;
}

/* Une ligne mise de côté s'efface sans disparaître : on doit pouvoir
   revenir dessus. L'opacité seule ne suffirait pas à le dire (règle
   10e) — l'état est aussi écrit dans la liste déroulante, en toutes
   lettres. */
.ligne-ignoree { opacity: 0.55; }

/* Un prix hors bornes (moins de 2 centimes ou plus de 150 € le kilo).
   La couleur ne porte pas l'information seule : le serveur refuse le
   relevé et le dit en clair au retour du formulaire. */
.prix-suspect {
  color: var(--ton-rouge-texte);
  font-weight: 600;
}

@media (max-width: 900px) {
  table.donnees.donnees-ticket select,
  table.donnees.donnees-ticket input[type="number"] {
    max-width: none;
  }
}

/* ==================================================================
   VISUELS GÉNÉRÉS PAR IA (recettes, agrès, aliments, exercices)
   ================================================================== */

/* L'image en tête de fiche. `.visuel-recette` sert côté membre, où le
   cadrage est toujours du 4/3 ; `.visuel-fiche` sert en administration,
   où la même carte affiche du 4/3 (recette) et du carré (agrès,
   aliment) — d'où l'absence d'`aspect-ratio` imposé ici, qui écraserait
   un carré dans un rectangle. */
.visuel-recette,
.visuel-fiche {
  margin: 0 0 24px;
}

.visuel-recette img,
.visuel-fiche img {
  display: block;
  width: 100%;
  max-width: 560px;
  height: auto;
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
}

/* Côté membre uniquement, le rapport est connu d'avance : le déclarer
   évite que la page saute au moment où l'image arrive — un décalage de
   200 px sous le doigt, c'est un « marquer fait » cliqué par erreur. */
.visuel-recette img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* La légende n'est pas décorative : c'est elle qui dit que l'image est
   dessinée et non photographiée. Elle ne se masque à aucun format. */
.visuel-recette figcaption,
.visuel-fiche figcaption {
  margin-top: 6px;
  max-width: 560px;
}

/* La vignette dans la liste des repas. Elle est en `aria-hidden` et
   hors tabulation dans le HTML : le nom du plat juste à côté est déjà
   un lien vers la même recette, et deux liens successifs vers la même
   page font répéter l'intitulé au lecteur d'écran. */
.composante-vignette {
  grid-template-columns: auto 1fr auto;
}

.vignette-repas {
  display: block;
  line-height: 0;
}

.vignette-repas img {
  width: 64px;
  height: 48px;
  object-fit: cover;
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
}

/* Sous 600 px, la vignette repasse au-dessus du texte plutôt que de
   lui voler la moitié de la largeur : « Poêlée de poulet au citron »
   sur trois lignes se lit plus mal qu'une image un peu plus grande. */
@media (max-width: 600px) {
  .vignette-repas img {
    width: 52px;
    height: 39px;
  }
}

/* La colonne « Visuel » d'une liste. La cellule reste à la hauteur
   d'une ligne (règle 11f) : c'est l'image qui se plie, jamais la
   ligne qui s'étire. */
.cellule-vignette {
  padding-top: 4px;
  padding-bottom: 4px;
  text-align: center;
}

.cellule-vignette img {
  display: block;
  width: 44px;
  height: 33px;
  object-fit: cover;
  border-radius: 4px;
  background: var(--surface-2);
}

/* Un aplat à la place de l'image manquante : sans lui, la colonne se
   troue et l'on ne distingue plus « pas encore illustrée » de « image
   qui n'a pas fini de charger ». Il ne porte aucune information à lui
   seul — le filtre « Sans visuel » de la barre d'outils en donne la
   liste, et le compteur du bouton en donne le nombre (règle 10e). */
.vignette-absente {
  display: block;
  width: 44px;
  height: 33px;
  border: 1px dashed var(--bordure);
  border-radius: 4px;
  background: var(--surface-2);
}

/* --- LE DESSIN D'UN MOUVEMENT ------------------------------------
   Les trois autres familles illustrent des objets ; un exercice
   illustre un geste. D'où un dessin à plat plutôt qu'une photo, et
   deux tailles : grande dans la séance qu'on suit, petite dans la
   semaine qu'on survole. */

/* Dans le déroulé d'une séance : le dessin à gauche, tout le texte à
   droite. Seuls les blocs illustrés deviennent une grille — sans quoi
   un mouvement sans dessin laisserait une colonne vide de 132 px et le
   déroulé perdrait son alignement au premier exercice non illustré. */
.deroule-bloc-illustre {
  display: grid;
  grid-template-columns: 132px 1fr;
  align-items: start;
  gap: 16px;
}

.vignette-mouvement {
  width: 132px;
  height: 99px;
  object-fit: cover;
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
}

/* En téléphone le dessin rétrécit mais reste à gauche : passé
   au-dessus du texte, il repoussait le nom du mouvement et les séries
   sous la ligne de flottaison — or c'est ce qu'on vient lire entre
   deux séries, le téléphone à la main. */
@media (width < 600px) {
  .deroule-bloc-illustre { grid-template-columns: 84px 1fr; gap: 12px; }

  .vignette-mouvement { width: 84px; height: 63px; }
}

/* Dans la semaine : la troisième colonne de `.repas`, celle que la
   liste des séances laissait vide. `align-self` parce que la grille
   aligne ses cellules sur la ligne de base du texte — une image y
   pendrait sous la ligne. */
.vignette-mouvement-liste {
  align-self: center;
  width: 64px;
  height: 48px;
  object-fit: cover;
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
}

@media (width < 600px) {
  .vignette-mouvement-liste { width: 48px; height: 36px; }
}

/* La description envoyée au générateur, dans l'atelier des visuels.
   Elle se lit MOT A MOT — c'est tout l'intérêt de la page — donc elle
   garde ses retours à la ligne, ne se tronque pas, et défile dans son
   propre conteneur plutôt que de pousser la page (règle 11f). */
.bloc-prompt {
  margin: 0;
  padding: 16px;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
  line-height: 1.6;
}

/* Le bouton de génération en lot est un formulaire (POST + CSRF) posé
   au milieu de boutons qui sont des liens : sans cela, il descendrait
   à la ligne tout seul. */
.outils-lot {
  display: inline-flex;
  align-items: center;
  margin: 0;
}

/* La case à cocher illustrée du profil (matériel de sport). L'image
   ne remplace jamais le libellé : elle se pose à côté. */
.case-illustree {
  align-items: center;
  gap: 10px;
}

.case-vignette {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: var(--rayon-petit);
  background: var(--surface-2);
  flex: 0 0 auto;
}

/* Sous 600 px, l'image rétrécit plutôt que de pousser le libellé sur
   quatre lignes. */
@media (max-width: 600px) {
  .case-vignette {
    width: 40px;
    height: 40px;
  }
}

/* L'avancement du lot d'images, à côté de son bouton. Il est en
   aria-live dans le HTML : sans texte visible ET annoncé, une
   génération de quatre minutes ressemble à une page figée. */
.lot-etat {
  align-self: center;
  margin-left: 10px;
  color: var(--texte-doux);
  font-size: 0.88rem;
}
