/* ════════════════════════════════════════════════════════════════════════════
   SOCLE — l'apparence commune des 16 pages du site (2 publiques + espace Maxime).
   Direction « Établi » : le site comme un outil qu'on sort dehors. Sans-serif,
   contrastes francs, cadres sobres, aucune ombre, chiffres alignés. Grandes cibles.
   Clair ou sombre au choix : on lit la carte au soleil et la liste de courses le soir.

   Ce fichier est le SEUL habillage de ces pages : ni framework, ni compilation, ni
   dépendance. `custom.css` (qui sert encore à l'espace Mathilde) n'est plus chargé ici.
   Vocabulaire des classes : CLAUDE.md § « Vocabulaire de l'espace Maxime ». Sans préfixe.

   Règles non négociables, vérifiées par deploy/verifier-mise-en-page.py :
     1. champ de saisie ≥ 16 px (en dessous, iOS zoome au focus) ;
     2. cible tactile ≥ 44 px ;  3. aucun défilement horizontal ;
     4. rien sous 15 px — la taille se pilote par les `:root { font-size }`, tout le
        reste est en rem.
   Les règles 1 et 4 sont tenues par des `max(var(--mini), …)` / `max(var(--mini-champ), …)` sur les tailles
   sur les tailles en fraction de rem, et non par le calcul : avant le 2026-08-19, la base
   du paysage était VERROUILLÉE à 17,8 px parce que `.bloc-titre` (0,85 rem) y tombait
   pile sur 15,13 px. Un plancher qui dépend de l'arithmétique interdit de baisser quoi
   que ce soit.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── La taille du texte : une base × un facteur ──
   La base dépend de l'écran (les trois `font-size` de ce fichier) ; le facteur est le
   réglage de Maxime, posé par socle.js avant le rendu et retenu sur l'appareil. Tout le
   reste étant en `rem`, ces deux nombres pilotent le site entier. */
:root {
  --taille: 1;
  font-size: calc(18px * var(--taille));   /* repli ; voir plus bas */
  /* Les deux planchers SUIVENT le facteur, au lieu d'être deux constantes. Écrits en
     dur, ils empêchaient tout cran vers le bas : à 0,85, le corps du texte serait passé
     SOUS des métas bloquées à 15 px, hiérarchie inversée. Proportionnels, l'échelle
     entière se contracte d'un bloc. Au palier « normale » ils valent exactement 15 et
     16 px — les deux règles non négociables, tenues telles quelles. */
  --mini:       calc(15px * var(--taille));   /* rien sous ça (règle 4) */
  --mini-champ: calc(16px * var(--taille));   /* un champ sous ça fait zoomer iOS (règle 1) */
}

/* ── Réglages, palette claire ──
   Le thème est TOUJOURS explicite : socle.js pose data-theme avant le rendu, il n'y a
   donc pas de troisième état à gérer ici. */
:root {
  --fond:     #F4F4F2;
  --surface:  #FFFFFF;
  --texte:    #16181A;
  --pale:     #55595E;
  --filet:    #D7D9D5;
  --accent:   #8A5200;   /* ocre, assombri pour rester lisible sur fond clair */
  --accent-t: #FBF0DC;   /* le même, en aplat de fond */
  --rayon:    10px;
  --large:    52rem;

  /* Cinq FAMILLES, définies une seule fois. Elles ne se distinguent pas par la nuance
     mais par la famille : on doit savoir où l'on est d'un coup d'œil, sans lire.
     Chaque teinte a son aplat (-t), le même ton posé en fond. */
  --terre:  #8A5200;  --terre-t:  #FBF0DC;
  --foret:  #2F6B4F;  --foret-t:  #E3F0E8;
  --marine: #2A4C86;  --marine-t: #E4EBF7;
  --prune:  #7A3A62;  --prune-t:  #F6E7F0;
  --brique: #A3492A;  --brique-t: #FAE6DF;
  --canard: #1C6068;  --canard-t: #DEEEF0;
  /* 7ᵉ famille, ajoutée le 2026-08-20 avec l'appli Secrétariat : un bleu-gris
     d'administration — la teinte du papier, et la seule qui restait franchement
     distincte des six autres à l'œil, marine compris. */
  --ardoise: #44546A; --ardoise-t: #E7EAEF;

  /* Une couleur par appli (espace privé)… */
  --app-cuisine:  var(--terre);  --app-cuisine-t:  var(--terre-t);
  --app-vtt:      var(--foret);  --app-vtt-t:      var(--foret-t);
  --app-petanque: var(--marine); --app-petanque-t: var(--marine-t);
  --app-jcvm:     var(--prune);  --app-jcvm-t:     var(--prune-t);
  --app-compta:   var(--brique); --app-compta-t:   var(--brique-t);
  --app-partenaires: var(--canard); --app-partenaires-t: var(--canard-t);
  --app-secretariat: var(--ardoise); --app-secretariat-t: var(--ardoise-t);
  /* Les documents de cours, ajoutés le 2026-09-05. Sur la garde de Maxime cohabitent
     Cuisine (terre), VTT (forêt) et JCVM (prune) : marine et ardoise sont les deux
     familles qui s'en détachent le mieux, et elles se distinguent l'une de l'autre
     (bleu franc contre bleu-gris) — canard aurait été trop proche d'ardoise. */
  --app-pcsi:     var(--marine); --app-pcsi-t:     var(--marine-t);
  --app-inspe:    var(--ardoise); --app-inspe-t:   var(--ardoise-t);

  /* …et une couleur par contenu de la face élève. Deux tables distinctes qui puisent au
     même jeu de familles : « Documents » n'a pas à s'appeler la couleur de la Cuisine. */
  --sujet-pcsi:       var(--terre);  --sujet-pcsi-t:       var(--terre-t);
  --sujet-nsi:        var(--marine); --sujet-nsi-t:        var(--marine-t);
  --sujet-maxime:     var(--foret);  --sujet-maxime-t:     var(--foret-t);
  --sujet-mathilde:   var(--brique); --sujet-mathilde-t:   var(--brique-t);
  --sujet-jcvm:       var(--prune);  --sujet-jcvm-t:       var(--prune-t);
  --sujet-documents:  var(--terre);  --sujet-documents-t:  var(--terre-t);
  --sujet-colles:     var(--foret);  --sujet-colles-t:     var(--foret-t);
  --sujet-calendrier: var(--marine); --sujet-calendrier-t: var(--marine-t);
  --sujet-flash:      var(--prune);  --sujet-flash-t:      var(--prune-t);
  --sujet-python:     var(--brique); --sujet-python-t:     var(--brique-t);
}

/* En sombre, les mêmes teintes seraient sourdes : elles s'éclaircissent, et les aplats
   deviennent des fonds profonds. */
:root[data-theme="sombre"] {
  --fond: #121415; --surface: #1B1E20; --texte: #ECEDEA; --pale: #A3A9AD;
  --filet: #2E3336; --accent: #F0AC46; --accent-t: #2E2417;

  /* Seules les FAMILLES commutent : les tables applis et sujets pointent dessus et
     n'ont rien à redire ici. */
  --terre:  #F0AC46;  --terre-t:  #2E2417;
  --foret:  #74C296;  --foret-t:  #16281F;
  --marine: #8FB4EC;  --marine-t: #17202F;
  --prune:  #D492BC;  --prune-t:  #2B1B25;
  --brique: #E8956F;  --brique-t: #2E1A13;
  --canard: #6FC3CC;  --canard-t: #13272A;
  --ardoise: #9FB2CC; --ardoise-t: #1A2029;
}

/* La page entière prend la couleur de son appli : une seule déclaration par appli,
   valable dans les deux thèmes puisqu'elle pointe vers des variables déjà commutées. */
body[data-app="cuisine"]  { --accent: var(--app-cuisine);  --accent-t: var(--app-cuisine-t); }
body[data-app="vtt"]      { --accent: var(--app-vtt);      --accent-t: var(--app-vtt-t); }
body[data-app="petanque"] { --accent: var(--app-petanque); --accent-t: var(--app-petanque-t); }
body[data-app="jcvm"]     { --accent: var(--app-jcvm);     --accent-t: var(--app-jcvm-t); }
body[data-app="compta"]   { --accent: var(--app-compta);   --accent-t: var(--app-compta-t); }
body[data-app="partenaires"] { --accent: var(--app-partenaires); --accent-t: var(--app-partenaires-t); }
body[data-app="secretariat"] { --accent: var(--app-secretariat); --accent-t: var(--app-secretariat-t); }
body[data-app="pcsi"]     { --accent: var(--app-pcsi);     --accent-t: var(--app-pcsi-t); }
body[data-app="inspe"]    { --accent: var(--app-inspe);    --accent-t: var(--app-inspe-t); }

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Les contrôles de formulaire n'héritent PAS de la police de la page : sans cette
   règle, tout `input` laissé sans classe retombe aux 13,3 px du navigateur — et sous
   16 px, iOS zoome au focus. Les classes du socle (.champ, .bouton) restent maîtresses. */
input, select, textarea, button { font-family: inherit; font-size: 1rem; }

body {
  background: var(--fond);
  color: var(--texte);
  font: 400 1rem/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
  min-height: 100vh;
}
/* Quand une barre d'actions est collée en bas, le contenu doit pouvoir défiler
   au-dessus d'elle : sinon la dernière ligne reste inatteignable. Une barre qui
   n'apparaît qu'au besoin (le panier du cahier) ne réserve rien tant qu'elle est
   `hidden` — sans quoi la page garderait un vide permanent en bas. */
body:has(.actions.bas:not([hidden])) { padding-bottom: 4.9rem; }

img { max-width: 100%; }

/* ══ En-tête : compacte, collante, elle ne mange pas l'écran ══
   Construite par socle.js à partir d'une seule table d'applis (assets/js/socle.js) :
   ajouter une appli est une ligne, pas seize fichiers. */
.tete {
  position: sticky; top: 0; z-index: 10;
  display: grid; grid-template-columns: 1fr auto auto auto; align-items: center; gap: .5rem .6rem;
  padding: .6rem 1rem; background: var(--surface); border-bottom: 1px solid var(--filet);
}
.marque {
  font-weight: 650; font-size: 1.05rem; color: var(--texte); text-decoration: none;
  letter-spacing: -.01em; display: inline-flex; align-items: center; gap: .4rem;
  min-height: 44px;
}
.menu {
  grid-column: 1 / -1; min-width: 0; display: flex; gap: .3rem;
  overflow-x: auto; scrollbar-width: none;
  /* Une rangée de pastilles défilantes, et non une barre d'onglets : le nombre
     d'applis et de pages n'est pas borné, il en viendra d'autres. */
}
.menu::-webkit-scrollbar { display: none; }
.menu a {
  /* 44 px de haut même quand le texte n'en fait que 20 : c'est un doigt qui vise. */
  display: inline-flex; align-items: center; min-height: 44px;
  padding: .4rem .8rem; border-radius: 999px; font-size: max(var(--mini), .95rem); font-weight: 500;
  color: var(--pale); text-decoration: none; white-space: nowrap;
}
.menu a.actif { background: var(--accent-t); color: var(--accent); font-weight: 600; }

/* Commutateur clair/sombre, taille du texte, déconnexion : trois icônes, un gabarit. */
.bascule, .taille, .quitter {
  width: 46px; height: 46px; border-radius: 999px; cursor: pointer; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--fond); border: 1px solid var(--filet); color: var(--texte);
  font-size: 1.15rem; line-height: 1; text-decoration: none;
}
.bascule::before { content: "☾"; }
:root[data-theme="sombre"] .bascule::before { content: "☀"; }
.quitter::before { content: "⏻"; }
/* Le « A » du bouton grossit avec le réglage : le bouton MONTRE ce qu'il règle. */
.taille { font-weight: 700; }
.taille::before { content: "A"; font-size: calc(.8rem + .35rem * var(--taille)); }

.page { max-width: var(--large); margin: 0 auto; padding: 1.25rem 1rem 2rem; }

.titre { padding: .5rem 0 1.5rem; }
.titre h1 { font-size: clamp(2rem, 8vw, 2.5rem); font-weight: 700; letter-spacing: -.02em; line-height: 1.15; }
.chapeau { color: var(--pale); margin-top: .35rem; font-size: 1.05rem; }

.bloc { margin-bottom: 2rem; }
.bloc-titre {
  font-size: max(var(--mini), .85rem); letter-spacing: .08em; text-transform: uppercase; color: var(--pale);
  font-weight: 600; margin-bottom: .7rem;
}
.discret { text-transform: none; letter-spacing: 0; font-weight: 400; }

/* ══ Vignettes : une carte par appli, sur la page de garde ══ */
.vignettes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
.vignette {
  display: block; padding: .95rem; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--filet); border-radius: var(--rayon);
}
.vignette:active { border-color: var(--accent); }
.vignette-icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.4rem; height: 2.4rem; border-radius: 8px; margin-bottom: .55rem;
  background: var(--accent-t); color: var(--accent); font-size: 1.2rem;
  overflow: hidden;
}
.vignette-icone img { width: 100%; height: 100%; object-fit: cover; }

/* ── Le logo du club, et ses DEUX versions ──
   L'écusson du JCVM existe en traits noirs (fond clair) et en traits blancs (fond
   sombre) : posé à l'envers, il disparaît dans son propre fond. Le thème étant
   toujours explicite ici, la bascule se fait sur `data-theme` — `content` sur une
   balise `img` remplace la source, et c'est la seule façon de le faire sans toucher
   aux sept endroits qui affichent ce logo, dont trois vivent dans du JavaScript.
   ⚠ Les documents du club font exception : leur feuille reste blanche même en thème
   sombre, et `document.css` y rétablit donc la version à traits noirs. */
:root[data-theme="sombre"] img[src*="logo-club.png"] {
  content: url("../img/logo-club-sombre.png");
}
/* Une vignette porte la couleur de SON appli, quelle que soit celle de la page. */
.vignette[data-app="cuisine"]  { --accent: var(--app-cuisine);  --accent-t: var(--app-cuisine-t); }
.vignette[data-app="vtt"]      { --accent: var(--app-vtt);      --accent-t: var(--app-vtt-t); }
.vignette[data-app="petanque"] { --accent: var(--app-petanque); --accent-t: var(--app-petanque-t); }
.vignette[data-app="jcvm"]     { --accent: var(--app-jcvm);     --accent-t: var(--app-jcvm-t); }
.vignette[data-app="compta"]   { --accent: var(--app-compta);   --accent-t: var(--app-compta-t); }
.vignette[data-app="partenaires"] { --accent: var(--app-partenaires); --accent-t: var(--app-partenaires-t); }
.vignette[data-app="secretariat"] { --accent: var(--app-secretariat); --accent-t: var(--app-secretariat-t); }
.vignette[data-app="pcsi"]     { --accent: var(--app-pcsi);     --accent-t: var(--app-pcsi-t); }
.vignette[data-app="inspe"]    { --accent: var(--app-inspe);    --accent-t: var(--app-inspe-t); }
/* `anywhere` et non `break-word` : les deux cassent un mot trop long À L'AFFICHAGE,
   mais seul `anywhere` réduit aussi la LARGEUR MINIMALE de la vignette. Avec
   `break-word`, « Secrétariat » gonflait la colonne de la grille et faisait déborder
   la page de garde de 19 px en portrait — le mot se coupait bien, la grille refusait
   quand même de rétrécir (constaté le 2026-08-20 par la sonde de mise en page). */
.vignette-nom { display: block; font-weight: 600; font-size: 1.1rem; overflow-wrap: anywhere; }
/* Le résumé dit un ÉTAT (« 12 articles à acheter »), jamais une table des matières. */
.vignette-resume { display: block; font-size: max(var(--mini), .95rem); color: var(--pale); margin-top: .15rem; overflow-wrap: break-word; }
/* Une porte annoncée, pas encore ouverte : elle dit ce que l'année apportera sans faire
   croire que c'est disponible. À écrire en <span>, jamais en <a> — on ne clique pas dans
   le vide, et rien ne promet un lien au lecteur d'écran. */
.vignette.bientot { opacity: .62; }
.vignette.bientot .vignette-icone { background: var(--filet); color: var(--pale); }

/* ══ Cartes : une grille illustrée ══
   La VIGNETTE présente une appli (page de garde) ; la CARTE présente un contenu — une
   recette, demain un parcours. Une photo vaut mieux qu'un titre pour choisir ce qu'on
   va cuisiner : c'est l'image qu'on reconnaît, pas le nom qu'on lit. */
.grille { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; }
.carte {
  position: relative; display: block; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--filet); border-radius: var(--rayon);
  overflow: hidden;
}
.carte:active { border-color: var(--accent); }
/* Une carte qui porte des commandes (case à cocher, réglage des couverts) ne peut pas
   ÊTRE un lien : un bouton ne s'imbrique pas dans un <a>. Le lien vit alors DEDANS, et
   il faut le dénuder ici — sinon le titre ressort souligné et bicolore, bleu puis violet
   une fois la recette ouverte, là où la carte-lien des autres pages est sobre. */
.carte a { display: block; color: inherit; text-decoration: none; }
.carte a:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; border-radius: var(--rayon); }
/* Sur une machine à souris seulement : au doigt, un survol resterait collé après le tap. */
@media (hover: hover) { .carte:hover { border-color: var(--accent); } }
/* Rapport fixe : les photos n'ont pas toutes le même cadrage, la grille si — sans quoi
   les rangées se décalent en escalier. Un contenu sans image garde la même place. */
.carte-photo {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  background: var(--accent-t);
}
/* L'EMBLÈME tient la place de la photo là où il n'y a rien de vrai à photographier :
   un aplat de la couleur du contenu, son icône au centre. Inventer une image pour
   « Calendrier » brouillerait le signal de la page des documents, où une photo désigne
   un chapitre du cours. Hauteur en rem, jamais un rapport fixe : une carte pleine
   largeur deviendrait un panneau. */
.carte-embleme {
  display: flex; align-items: center; justify-content: center;
  min-height: 4.6rem; font-size: 2rem; line-height: 1;
  background: var(--accent-t); color: var(--accent);
  border-bottom: 1px solid var(--filet);
}
/* Un logo tient la place de l'icône : `contain` et non `cover`, une marque ne se
   recadre pas. Il garde l'aplat de sa couleur autour de lui. */
.carte-embleme img { height: 2.6rem; width: auto; max-width: 70%; object-fit: contain; }

/* Le contenu ouvert tient toute la rangée : la garde doit dire d'un coup d'œil ce qui
   est disponible aujourd'hui, et ce qui ne l'est pas encore. */
.carte.majeure { grid-column: 1 / -1; }
/* Annoncé, pas encore ouvert. Écrit en <span>, jamais en <a> — on ne clique pas dans le
   vide. La teinte reste : chaque contenu a déjà son identité. */
.carte.bientot { opacity: .62; }
/* Chaque carte porte la couleur de SON contenu, quelle que soit celle de la page. */
.carte[data-sujet="pcsi"]       { --accent: var(--sujet-pcsi);       --accent-t: var(--sujet-pcsi-t); }
.carte[data-sujet="nsi"]        { --accent: var(--sujet-nsi);        --accent-t: var(--sujet-nsi-t); }
.carte[data-sujet="maxime"]     { --accent: var(--sujet-maxime);     --accent-t: var(--sujet-maxime-t); }
.carte[data-sujet="mathilde"]   { --accent: var(--sujet-mathilde);   --accent-t: var(--sujet-mathilde-t); }
.carte[data-sujet="jcvm"]       { --accent: var(--sujet-jcvm);       --accent-t: var(--sujet-jcvm-t); }
.carte[data-sujet="documents"]  { --accent: var(--sujet-documents);  --accent-t: var(--sujet-documents-t); }
.carte[data-sujet="colles"]     { --accent: var(--sujet-colles);     --accent-t: var(--sujet-colles-t); }
.carte[data-sujet="calendrier"] { --accent: var(--sujet-calendrier); --accent-t: var(--sujet-calendrier-t); }
.carte[data-sujet="flash"]      { --accent: var(--sujet-flash);      --accent-t: var(--sujet-flash-t); }
.carte[data-sujet="python"]     { --accent: var(--sujet-python);     --accent-t: var(--sujet-python-t); }

.carte-corps { padding: .5rem .65rem .65rem; }

/* Une feuille de TD existe en DEUX versions : l'énoncé seul et l'énoncé avec ses
   corrigés. Deux tuiles jumelles (essayé le 20/08) obligeaient à chercher deux fois le
   même TD dans deux listes — d'où deux liens dans UNE tuile, demandé le 21/08.
   Les deux liens partagent la largeur : côte à côte, ils se comparent d'un regard, là
   où l'un au-dessus de l'autre aurait suggéré une hiérarchie qui n'existe pas.
   `min-height` à 44 px : c'est la cible tactile minimale, non négociable. */
.carte-liens { display: flex; border-top: 1px solid var(--filet); }
.carte-lien {
  flex: 1 1 0; min-width: 0; min-height: 44px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .1rem; padding: .4rem .3rem; text-align: center;
  color: var(--accent); text-decoration: none; font-weight: 600;
}
/* Un filet sépare les deux, sans encadrer le premier ni le dernier. */
.carte-lien + .carte-lien { border-left: 1px solid var(--filet); }
.carte-lien:active { background: var(--accent-t); }
@media (hover: hover) { .carte-lien:hover { background: var(--accent-t); } }
/* Plancher de lisibilité : en portrait, aucun second plan sous 1 rem. */
.carte-lien-nom { font-size: max(1rem, .92em); line-height: 1.15; }
.carte-lien-meta { font-size: max(var(--mini), .8em); color: var(--pale); font-weight: 400; }
/* Le corrigé annoncé mais pas encore publié tient sa place, grisé et NON cliquable —
   même règle que `.carte.bientot` : on ne clique pas dans le vide. La place réservée
   vaut mieux qu'un lien qui apparaît un jour sans prévenir : l'élève sait qu'il existe
   et qu'il arrivera. `cursor: default` empêche la main de promettre un clic. */
.carte-lien.bientot { color: var(--pale); font-weight: 500; cursor: default; }
.carte-lien.bientot:hover, .carte-lien.bientot:active { background: none; }
.carte-lien.bientot .carte-lien-meta { font-style: italic; }
.carte-nom {
  display: block; font-weight: 600; font-size: 1.05rem; line-height: 1.25;
  overflow-wrap: break-word; hyphens: auto;
}
/* `break-word` comme sur le nom : dans une grille à deux colonnes, un mot un peu long
   (« Mathématiques ») dépasse la carte et va mordre sur sa voisine. */
.carte-meta { display: block; font-size: max(var(--mini), .95rem); color: var(--pale); margin-top: .1rem;
              overflow-wrap: break-word; hyphens: auto; }
/* Ce qui se pose SUR la photo : le numéro du cahier, une case à cocher. Le fond porté
   garde le repère lisible aussi bien sur un plat sombre que sur une meringue. */
.carte-coin {
  position: absolute; top: .35rem; display: flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px;
}
.carte-coin.gauche { left: .35rem; }
.carte-coin.droite { right: .35rem; }
.carte-coin .badge { background: rgba(0, 0, 0, .55); color: #fff; backdrop-filter: blur(2px); }

/* ══ Listes : des rangées franches, séparées par un filet ══ */
.liste { list-style: none; background: var(--surface); border: 1px solid var(--filet); border-radius: var(--rayon); overflow: hidden; }
.ligne {
  display: flex; align-items: center; gap: .8rem; padding: .8rem .9rem;
  text-decoration: none; color: inherit; min-height: 62px;
  border-bottom: 1px solid var(--filet);
}
.liste > *:last-child .ligne, .liste > .ligne:last-child { border-bottom: 0; }
.ligne:active { background: var(--accent-t); }
.miniature { width: 46px; height: 46px; border-radius: 8px; flex-shrink: 0; object-fit: cover; }
.pastille { width: 15px; height: 15px; border-radius: 50%; flex-shrink: 0; }
.ligne-corps { flex: 1; min-width: 0; }
.ligne-nom { display: block; font-weight: 550; font-size: 1.12rem; overflow-wrap: break-word; }
/* `anywhere` et non `break-word` : une méta porte des courriels et des adresses, qui
   n'offrent aucune coupure naturelle — « famille.chaverondier.parents@wanadoo.fr »
   déborde de sa ligne et se fait couper en silence (relevé le 2026-08-22 par la règle
   nº 5 de verifier-mise-en-page.py). Le nom, lui, garde `break-word` : il a des espaces. */
.ligne-meta { display: block; font-size: max(var(--mini), .95rem); color: var(--pale);
              font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.fleche { color: var(--pale); font-size: 1.1rem; }

.badge {
  font-size: max(var(--mini), .85rem); font-weight: 700; padding: .25rem .55rem; border-radius: 6px;
  background: var(--accent); color: var(--surface); white-space: nowrap;
}
:root[data-theme="sombre"] .badge { color: #14161A; }
.badge.creux { background: none; color: var(--pale); border: 1px dashed var(--filet); font-weight: 500; }

/* ══ Cocher : la cible et la lisibilité avant tout — c'est la liste de courses ══ */
.rayon {
  font-size: max(var(--mini), .85rem); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); margin: 1.25rem 0 .5rem;
}
.cocher { list-style: none; background: var(--surface); border: 1px solid var(--filet); border-radius: var(--rayon); overflow: hidden; }
.cocher label {
  display: flex; align-items: center; gap: .9rem; padding: .75rem .9rem;
  border-bottom: 1px solid var(--filet); min-height: 58px; cursor: pointer;
}
.cocher > *:last-child label, .cocher > label:last-child { border-bottom: 0; }
.cocher input[type="checkbox"] { width: 1.8rem; height: 1.8rem; accent-color: var(--accent); flex-shrink: 0; }
.article { flex: 1; min-width: 0; font-size: 1.18rem; font-weight: 500; }
/* D'OÙ VIENT L'ARTICLE. Au rayon, savoir que ces 200 g de mozzarella sont là pour la
   salade décide d'en prendre plus, ou de renoncer au plat. La flèche ocre l'accroche à
   sa ligne : en gris pâle seul, la mention se lisait comme une rature. */
.article small {
  display: block; font-size: max(var(--mini), .88rem); font-weight: 400; color: var(--pale);
  margin-top: .1rem; line-height: 1.3;
}
.article small::before { content: "↳ "; color: var(--accent); font-weight: 600; }
/* La recette d'origine est cliquable. C'est la COULEUR qui le dit, pas un soulignement :
   sur trois lignes de titre, les traits mangeraient la ligne d'à côté. */
/* Même règle pour le retour au site depuis l'écran de connexion : sans elle, le lien
   sortait bleu puis violet souligné, seule tache de couleur navigateur du site. */
.pied a { color: var(--accent); text-decoration: none; }
.article small a { color: var(--accent); text-decoration: none; }
.article small a:active { text-decoration: underline; }
.cocher input:checked ~ .article small a { color: var(--pale); }
/* Barrée avec sa ligne, mais sans que la flèche ne reste allumée sur un article pris. */
.cocher input:checked ~ .article small::before { color: var(--pale); }
.quantite { color: var(--pale); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 1.05rem; }
.cocher input:checked ~ .article { text-decoration: line-through; color: var(--pale); font-weight: 400; }

/* ══ Chiffres : le bandeau de grands nombres ══ */
.chiffres { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; margin-bottom: 1rem; }
.chiffres > div {
  background: var(--surface); border: 1px solid var(--filet); border-radius: var(--rayon);
  padding: .6rem .75rem;
}
/* Un chiffre de tableau de bord n'a pas à crier : c'est sa POSITION en haut de page et
   son cadre qui le donnent à voir, pas sa taille. Descendu de 1,5 à 1,3 rem le 19/08,
   les quatre encarts prenant un tiers d'un écran de téléphone. */
.chiffre { display: block; font-size: 1.3rem; line-height: 1.2; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.chiffre-libelle { display: block; font-size: max(var(--mini), .8rem); line-height: 1.3; letter-spacing: .06em; text-transform: uppercase; color: var(--pale); }

/* ══ Actions : champ + boutons ══
   Statique par défaut ; la variante `.bas` se colle au pouce, pour l'action qu'on
   répète debout dans un rayon. */
.actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0; }
/* `display: flex` l'emporterait sur l'attribut `hidden` : à écrire explicitement. */
.actions[hidden] { display: none; }
.actions.bas {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20; margin: 0;
  flex-wrap: nowrap; padding: .55rem .8rem;
  padding-bottom: calc(.55rem + env(safe-area-inset-bottom));
  background: var(--surface); border-top: 1px solid var(--filet);
}
.champ {
  flex: 1 1 12rem; min-width: 0; min-height: 50px; padding: .6rem 1rem;
  /* Jamais le raccourci `font:` avec `inherit` en famille : la déclaration entière est
     invalide, elle est jetée, et l'élément retombe aux 13,3 px du navigateur. */
  font-family: inherit; font-size: max(var(--mini-champ), 1.05rem); color: var(--texte);
  background: var(--fond); border: 1px solid var(--filet); border-radius: 999px;
}
select.champ { cursor: pointer; }
/* Un texte de plusieurs lignes ne se met pas en pilule : les coins ronds rognent la
   première et la dernière ligne. Rayon de boîte, et redimensionnable en hauteur seule —
   la largeur, elle, est celle du formulaire. */
textarea.champ { border-radius: var(--rayon); resize: vertical; min-height: 4.5rem;
                 line-height: 1.4; padding: .6rem .85rem; }
.champ:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
/* Le sélecteur posé dans une ligne : plus discret, jamais plus petit que 16 px. */
.champ.mini { flex: 0 1 auto; min-height: 44px; font-size: max(var(--mini-champ), .95rem); padding: .35rem .7rem; border-radius: 8px; }

.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  min-height: 50px; padding: .6rem 1.1rem; cursor: pointer;
  font-family: inherit; font-size: max(var(--mini), 1.02rem); font-weight: 600; line-height: 1.2;
  /* Surtout PAS `white-space: nowrap` : au plus grand palier, « Générer la liste de
     courses → » faisait 530 px de large et poussait toute la page hors de l'écran.
     Un libellé long se casse sur deux lignes, le bouton reste dans la page. */
  text-align: center; color: var(--texte); text-decoration: none;
  background: var(--fond); border: 1px solid var(--filet); border-radius: 999px;
}
.bouton.fort { background: var(--accent); border-color: var(--accent); color: var(--surface); }
:root[data-theme="sombre"] .bouton.fort { color: #14161A; }
.bouton.actif { background: var(--accent-t); border-color: var(--accent); color: var(--accent); }
.bouton.carre { width: 50px; padding: .6rem; flex: 0 0 auto; }
.bouton[hidden] { display: none; }

/* Petite croix de fin de ligne : discrète mais toujours 44 px de cible. */
.retirer {
  width: 44px; height: 44px; flex-shrink: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; color: var(--pale); font-size: 1.35rem; line-height: 1;
}
.retirer:active { color: var(--accent); }

.vide { color: var(--pale); text-align: center; padding: 2rem 1rem; }

/* Jauge d'avancement : ce qui est fait sur ce qui était prévu. Née au tableau de bord
   de la compta, montée ici quand le budget prévisionnel l'a demandée à son tour.
   Elle ne porte jamais l'information seule — le texte au-dessus dit les deux nombres. */
.barre { height: .5rem; background: var(--filet); border-radius: 999px; overflow: hidden; }
.barre > div { height: 100%; background: var(--accent); }
/* Au-delà du prévu, la jauge passe à la couleur des dépenses : un dépassement se voit
   avant de se lire. */
.barre.depasse > div { background: var(--brique); }

/* ══ Modale : une feuille qui monte du bas, là où est le pouce ══
   Montée du <style> de menu.html au socle le 2026-08-18, quand la compta en a eu
   besoin à son tour : la règle du chantier était « si une deuxième page en a besoin,
   elle monte dans le socle — pas avant ». Centrée dès qu'on est couché. */
.modale {
  position: fixed; inset: 0; z-index: 30; display: flex; align-items: flex-end;
  background: rgba(0, 0, 0, .45);
}
.modale[hidden] { display: none; }
.modale-boite {
  width: 100%; max-height: 85vh; overflow-y: auto; padding: 1rem;
  background: var(--surface); border-radius: var(--rayon) var(--rayon) 0 0;
}
.modale .liste { margin: .6rem 0; }
/* Une option proposée est un bouton — on ne navigue pas, on choisit — d'où le
   décapage de l'apparence que le navigateur donne à <button>. */
.modale .ligne { width: 100%; background: none; border: 0; border-bottom: 1px solid var(--filet);
                 text-align: left; font: inherit; color: inherit; cursor: pointer; }

/* ══ Formulaire : une étiquette, un champ, l'un sous l'autre ══
   Deux colonnes ne tiennent pas dans un téléphone debout, et un formulaire de saisie
   comptable a huit champs : c'est la pile ou rien. */
.form { display: grid; gap: .7rem; }
.form label { display: grid; gap: .2rem; font-weight: 600; }
.form label > span { font-weight: 400; color: var(--pale); font-size: max(var(--mini), .95rem); }
.form .champ, .form select, .form textarea { width: 100%; }
.form .paire { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; }

/* La date en tête de ligne, colonne fixe : c'est le repère qu'on suit du doigt en
   faisant défiler un registre, et il doit rester aligné d'une ligne à l'autre. */
.jour { flex: 0 0 3.2rem; color: var(--pale); font-variant-numeric: tabular-nums; }

/* Un montant se lit et se compare en colonne : chiffres alignés, largeur constante. */
.montant { font-variant-numeric: tabular-nums; white-space: nowrap; }
.montant.recette { color: var(--app-vtt); }
.montant.depense { color: var(--app-compta); }

.pied { text-align: center; padding: 1.5rem 1rem; color: var(--pale); font-size: max(var(--mini), .9rem); }

/* ══ Écran couché : téléphone en paysage, tablette, ordinateur ══
   Le déclencheur est l'ORIENTATION, pas la largeur : un téléphone en paysage fait
   souvent moins de 700 px de large (667 sur un SE) et ratait cette disposition, alors
   que c'est exactement là qu'elle sert. La largeur ne reste que pour les tablettes
   tenues debout, assez larges pour quatre colonnes. */
@media (orientation: landscape), (min-width: 700px) {
  /* 17,8 → 16 px le 2026-08-19 : « la taille d'écriture est encore trop grande », sur
     téléphone couché ET sur ordinateur. Les 17,8 px n'étaient pas un choix de lisibilité
     mais la valeur MINIMALE que permettait le plancher calculé (voir l'en-tête). Une
     fois les planchers écrits en `max()`, la base redevient libre : 16 px, celle d'un
     navigateur, sur un écran qui n'a aucun besoin d'économiser la largeur. */
  :root { font-size: calc(16px * var(--taille)); }
  .vignettes { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .grille { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .chiffres { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  body:has(.actions.bas) { padding-bottom: 0; }
  .tete { grid-template-columns: auto 1fr auto auto auto; padding: .7rem 2rem; }
  /* La page de garde n'a pas de pastilles (elle EST la navigation) : sans elles, trois
     éléments pour quatre colonnes ramèneraient les icônes contre la marque. */
  .tete:not(:has(.menu)) { grid-template-columns: 1fr auto auto auto; }
  .menu { grid-column: 2; grid-row: 1; justify-content: flex-end; }
  .actions.bas { position: static; border: 0; padding: 0; margin-top: 1rem; background: none; }
  .modale { align-items: center; justify-content: center; padding: 1rem; }
  .modale-boite { max-width: 34rem; border-radius: var(--rayon); }
  /* `clamp(2rem, 8vw, 2.5rem)` butait sur son maximum dès 560 px de large : tout écran
     couché recevait le même titre de 44,5 px, calibré pour un téléphone debout. */
  .titre h1 { font-size: 2rem; }
}

/* ══ Téléphone COUCHÉ ══
   Même orientation que l'ordinateur, mais 390 px de hauteur au lieu de 900 : la
   hauteur y est encore plus rare qu'en portrait. Or jusqu'au 2026-08-19, le paysage
   n'héritait d'AUCUNE des compactions du portrait — le titre y restait à 44,5 px, les
   pilules à 50, les blocs à 2 rem d'écart, exactement comme sur un écran de 1920. Maxime
   l'a signalé : « en mode paysage c'est toujours très gros ». Ce n'était pas le TEXTE
   (17,8 px, c'est peu), c'était le décor autour.

   `max-height: 500px` isole le téléphone couché : un portable fait 900 px de haut, une
   tablette couchée ~800. On rogne le décor — titre, marges, hauteur des cibles — jamais
   le corps du texte ni le plancher de 44 px des cibles tactiles, qui reste la règle. */
@media (orientation: landscape) and (max-height: 500px) {
  .page { padding: .4rem .8rem 1.2rem; }
  .titre { padding: .1rem 0 .7rem; }
  .titre h1 { font-size: 1.5rem; }
  .chapeau { margin-top: .15rem; }
  .bloc { margin-bottom: 1.1rem; }
  .bloc-titre { margin-bottom: .5rem; }
  .tete { padding: .35rem .9rem; }
  .pied { padding: .8rem 1rem; }
  .actions { margin: .6rem 0; }
  /* 50 → 44 px : le minimum tactile, pas moins. Six pixels par cible, sur un écran qui
     n'en a que 390, c'est une ligne de liste rendue à chaque rangée de boutons. */
  .champ, .bouton { min-height: 44px; padding: .4rem .9rem; }
  .bouton.carre { width: 44px; padding: .5rem; }
}

/* ══ Écran debout : le téléphone tenu normalement ══
   La hauteur est la ressource rare. On rogne le décor — titre, marges, cartouches —
   jamais les cibles ni la taille du texte : c'est du chrome qu'on récupère, pas de la
   lisibilité. */
@media (orientation: portrait) and (max-width: 699px) {
  /* Le texte le plus gros du site est ici. Les marges étant en rem, elles grossissent
     avec lui : on les resserre d'autant, sinon le gain de lisibilité se paie en
     défilement. 20,5 → 22 → 24 px les 12 et 13 août, sur téléphone réel : c'est la liste
     de courses lue à bout de bras qui commande. */
  :root { font-size: calc(26px * var(--taille)); }
  .page { padding-top: .6rem; padding-left: .7rem; padding-right: .7rem; }
  .titre { padding: .25rem 0 .9rem; }
  .titre h1 { font-size: 1.7rem; }
  .bloc { margin-bottom: 1.35rem; }

  /* ── Plancher du second plan ──
     Monter la racine ne rattrape pas ce qui est écrit en fraction de rem : les métas,
     libellés et légendes restaient 15 % sous le corps de texte, et ce sont eux qu'on
     plisse les yeux pour lire. En portrait, le second plan revient au niveau du texte
     courant ; il reste distinct par sa COULEUR (--pale) et sa graisse, pas par sa
     petitesse. Les capitales espacées (titres de bloc, rayons) gardent un cran de moins :
     à taille égale elles pèseraient plus lourd que les titres eux-mêmes. */
  .chapeau, .ligne-meta, .article small, .vignette-resume, .carte-meta, .pied, .menu a,
  .badge, .quantite, .fleche { font-size: 1rem; }
  .bloc-titre, .rayon { font-size: max(var(--mini), .95rem); }
  /* `.chiffre-libelle` a QUITTÉ ce plancher le 19/08 : il n'est pas un second plan
     qu'on cherche à lire, mais l'étiquette d'un nombre écrit trois fois plus gros
     juste au-dessus. À 0,95 rem il montait à 24,7 px en portrait — la taille du
     texte courant, en capitales espacées — et gonflait chaque encart à 128 px. */
  .chapeau { margin-top: .2rem; }

  /* Une vignette passe en icône à gauche, texte à droite : deux fois moins haute
     qu'empilée, et toujours au-dessus de 44 px. */
  .vignette {
    display: grid; grid-template-columns: auto 1fr; gap: 0 .75rem;
    grid-template-areas: "visuel nom" "visuel desc";
    align-content: center; padding: .7rem .8rem; min-height: 62px;
  }
  /* Un bloc qui ne contient qu'une vignette la donne pleine largeur : une demi-largeur
     resterait bancale, avec un vide à droite. En paysage, les quatre colonnes suffisent. */
  .vignettes.seule { grid-template-columns: 1fr; }
  .vignette-icone { grid-area: visuel; align-self: center; margin-bottom: 0; }
  .vignette-nom { grid-area: nom; align-self: end; }
  .vignette-resume { grid-area: desc; align-self: start; }

  .chiffres { gap: .5rem; }
  .chiffres > div { padding: .5rem .6rem; }
  .chiffre { font-size: 1.1rem; }
  .chiffre-libelle { font-size: max(var(--mini), .6rem); }
  .rayon { margin-top: 1rem; }
}
