/* =========================================================================
   MIJOTE — LA FEUILLE.

   Une seule. Elle remplace app.css, vues.css et patrons-app.css, qui se
   recouvraient et se contredisaient : dix allures de bouton, quatre familles
   de caractères, trois allures de titre, et trois écrans jamais repris.

   ORDRE DU FICHIER, et il ne change pas :
     1. les jetons          ce que DESIGN.md déclare
     2. le socle            reset, coque d'application, ce qui ne bouge pas
     3. les composants      trois boutons, un champ, une gélule, un interrupteur
     4. les vues            l'accueil dans ses trois organisations
     5. la fiche            l'écran qu'on regarde en cuisinant
     6. les écrans          nav, feuilles, formulaire, livres, recherche
     7. les états           vide, chargement, réduit

   LE CONTRAT « CUISINE CHIC » — fourni par Félix le 08/08/2026, appliqué ici
   à la lettre après vingt directions refusées. Ses termes, qui priment sur
   toutes les doctrines précédentes de cette feuille :

     fond #FAFAFA ultra-propre     cartes blanches qui ressortent
     noir profond #111412          vert sauge #2D6A4F + clair #E8F5E9
     angles très arrondis (24/16)  ombres extrêmement légères
     titres gros, noirs, SERIF     sous-titres gris #71717A
     badges discrets vert clair    icônes filaires fines (Lucide — déjà là)
     immenses espaces blancs       recherche sans bordure, fond gris subtil

   DEUX FAMILLES :

     Playfair Display  TOUT ce qui est éditorial : titres, noms de recette,
                       sections, ET la parole citée (son italique est fait
                       pour ça). Nommée par le contrat. Variable 400-600.
     system-ui         l'interface : boutons, champs, badges, étapes. Le
                       contrat demande « ultra-lisible » ; la native l'est,
                       et elle coûte zéro octet.

   Ce que ce contrat ENTERRE : le grain de papier, les trames de gravure, la
   lettrine, les filets doubles, la couverture sur fond de nuit. La gravure
   des ingrédients RESTE — c'est du filaire fin, exactement son registre.
   ========================================================================= */

@font-face { font-family: "Playfair"; src: url("../polices/playfair-display-latin-wght-normal.woff2") format("woff2"); font-weight: 400 600; font-display: swap; }
@font-face { font-family: "Playfair"; src: url("../polices/playfair-display-latin-wght-italic.woff2") format("woff2"); font-weight: 400 600; font-style: italic; font-display: swap; }

/* L'ÉCHELLE TYPOGRAPHIQUE, huit degrés et pas un de plus.
     34  le titre d'un écran            19  un titre de section
     22  un titre de feuille, la fiche  17  le texte courant et les champs
     15  une mention secondaire         13  une gélule, une aide
     12  une étiquette en capitales     11  le colophon
   Onze corps traînaient, dont 14, 16 et 18 qui ne servaient qu'à un écran
   chacun : c'est le signe d'une feuille écrite en plusieurs fois. */

/* ============================================================ 1. LES JETONS */
:root {
  /* Le fond appartient à la FAMILLE DES ACCENTS : un blanc cassé teinté du
     vert de l'app. Le crème neutre d'avant se lisait « gris sale » sous une
     palette chaude et verte — diagnostic du 08/08, et il était juste. Les
     surfaces de service suivent la même teinte, en s'assombrissant. */
  /* Le fond VERT a été essayé et refusé (« pas fan ») : retour au crème
     chaud, l'option appétissante du même diagnostic. Le vert de famille
     survit là où il a plu — les ombres, et les zones d'image DANS les
     cartes blanches. Les filets et bords redeviennent chauds : un liseré
     verdâtre sur du crème se lit comme une erreur d'écran. */
  /* ⚠️ DIRECTION REPRISE LE 14/08, carte blanche de Félix : « reprends
     tout, même le design global si besoin ». Après vingt directions
     refusées, celle-ci n'est pas un goût, c'est la thèse du projet : LE
     BUT FINAL EST UN LIVRE IMPRIMÉ. L'app doit donc ressembler au livre
     qu'elle fabrique — du papier, de l'encre, des filets, une typographie
     qui porte. Ce qui disparaît : les boîtes flottantes à coins ronds et
     à ombres, qui donnaient l'application de bien-être générique que
     Félix a refusée quatre fois de suite.

     Trois gestes, et ils tiennent presque entièrement dans ces jetons :
       1. le papier remplace le blanc (une seule surface, pas deux) ;
       2. les ombres deviennent des FILETS ;
       3. les coins s'ouvrent (24 px → 4). */
  /* ⚠️ DIRECTION VALIDÉE LE 14/08 : « tout ce qu'on vient de faire là, faut
     l'appliquer à l'app ». Ces valeurs viennent de site/essai-4.css, la
     maquette approuvée après quatre essais — « la table », en bento.
     Le papier s'éclaircit (on sortait d'un beige d'archive jugé triste), et
     l'encre se réchauffe. */
  /* ⚠️ PALETTE « PALMIER », CHOISIE LE 19/08. « J'arrive pas à trouver une
     couleur pour l'app, je te laisse choisir » (Félix). Les six couleurs
     viennent de sa planche du 19/08 ; ce sont les seules du lot qui couvrent
     tous les rôles sans qu'on dérive quoi que ce soit.
       crème #FFFEF2 · blush #FBC7B5 · ambre #F39A41
       vermillon #F15B22 · forêt #3B5943 · encre #211E1D
     Le changement de fond : le FORÊT prend la tête et l'AMBRE devient
     l'accent. L'ancienne tangerine #E8720C ne pouvait porter aucun texte —
     blanc 3,07, encre 4,17, les deux sous le seuil — et c'était la seule
     exception de contraste qui restait dans l'app. Elle disparaît. */
  /* ⚠️ CE JETON SERT AUSSI D'ENCRE CLAIRE. Le blanc pur n'est aucune des six
     couleurs de la planche : partout où l'app écrivait en #FFFFFF sur un
     aplat, elle sortait de la palette pour un gain invisible (7,79 contre
     7,68 sur le forêt). Les 28 occurrences sont passées à ce jeton, et la
     palette est désormais fermée sur elle-même. */
  --blanc: #FFFEF2;          /* le papier de la page ET l'encre claire */
  --carte: #FFFFFF;          /* une feuille posée dessus                    */
  --bande: #F4F0E6;
  --creme: #F4F0E6;
  --trait: #E4DED1;          /* le filet, qui fait tout le travail          */
  --bord:  #D6CFBE;

  /* LA PALETTE DE FÉLIX, retenue le 08/08 après essais sur l'app : BASALTE.
     basalt black #363636 · tangerine #F58F1F · leaf green #457534 ·
     sea grey #7D8285.
     Le basalte est l'encre, la feuille est l'accent, la tangerine est le
     feu, le gris de mer une provenance. Les gris de l'interface restent
     neutres — c'est la palette la plus sobre du jour, et c'est sa force :
     la couleur y est un événement. */
  /* Un presque-noir CHAUD plutôt qu'un basalte neutre : sur un papier
     crème, un gris neutre grisaille tout. Mesuré sur --blanc. */
  --encre:   #211E1D;    /* 16,33:1 sur le papier — l'encre de la planche */
  --encre-2: #56514A;    /*  7,54:1 */
  /* ⚠️ MESURÉ SUR --blanc SEULEMENT, ET POSÉ SUR LA CARTE CRÈME. #746E60
     donnait 5,07 sur le blanc — d'où le « 4,86 » écrit ici — mais 4,46 sur
     #F4F0E6, la surface où il sert vraiment. Assombri du minimum : il passe
     désormais sur les trois fonds clairs (5,54 / 5,18 / 4,87). */
  --encre-3: #6E685A;    /*  5,54:1 sur --blanc · 4,87:1 sur la carte crème */
  --sur-encre: #C9CBCC;

  /* Le leaf green : l'accent unique. Assez profond pour écrire ET remplir —
     le premier accent du jour qui n'a pas besoin de deux crans. Le jeton
     garde son nom historique --sauge : quarante règles le lisent. */
  /* ⚠️ LE FORÊT REMPLACE LE LEAF GREEN, ET IL MÈNE. Même rôle, même jeton
     (106 règles le lisent), mais il porte le blanc à 7,79 au lieu de 5,46 :
     tout ce qui écrivait en blanc dessus y gagne. */
  --sauge:       #3B5943;   /* le forêt de la planche                      */
  --sauge-plein: #3B5943;
  --sauge-clair: #E4EDE6;   /* sa pâle : l'encre y tient à 13,84        */

  /* La tangerine : COULEUR PRINCIPALE et CTA, par décision de Félix
     (tableau des rôles du 08/08). Elle remplit les grands gestes — Ajouter,
     Enregistrer, Aux fourneaux, le chrono — et comme elle est trop claire
     pour porter du blanc, elle écrit toujours à l'encre (5,08:1). */
  --feu: #F39A41;   /* l'ambre : accent, et l'encre y tient à 7,50   */
  /* ⚠️ UN SEUL ORANGE, ET IL EST VIF. J'avais introduit un second orange,
     plus sourd, pour pouvoir écrire en blanc dessus. « Bah non du coup
     c'est pas voulu » (Félix) : deux oranges dans une palette, c'est une
     palette ratée. La sienne tient en quatre mots — ORANGE VIF, VERT,
     CHARBON, BLANC — et il n'y a rien à y ajouter.

     Ce qui se déduit alors, par la mesure et non par le goût : sur
     l'orange vif, le blanc tombe à 3,07:1 et le charbon à 3,94:1, tous
     deux sous le seuil. C'est donc L'ENCRE de la palette qui écrit sur
     l'orange, à 4,94:1 (et non 6,09 : chiffre corrigé le 19/08, il ne
     correspondait à aucune mesure). L'orange vif est une couleur de FOND et
     d'ACCENT ; il ne porte pas de texte clair, et aucun réglage n'y
     changera rien. */
  /* Ce qui ÉCRIT sur le feu : un vert de forêt presque noir — « vert sur
     orange, plus joli » (Félix). Le duo agrumes, et il reste.

     ⚠️ LE CHIFFRE ÉCRIT ICI ÉTAIT FAUX. Deux commentaires annonçaient 5,38
     puis 6,09 pour #143A0D sur #E8720C ; la mesure donne 4,17, donc sous le
     seuil, et c'est ce que portait « Ajouter une recette » en 12 px. Aucune
     décision n'avait été prise là-dessus : c'était une erreur, maquillée par
     un chiffre inventé. Même vert, deux crans plus sombre, 4,94 mesuré. */
  --sur-feu: #211E1D;   /* l'encre de la planche, 7,50 sur l'ambre     */
  /* Le feu quand c'est LUI qui écrit : la tangerine tombait à ~2,5:1 en
     petit corps (revue du 12/08). Même famille, assombrie jusqu'à AA :
     4,5:1 sur la crème, 5,4:1 sur la carte. Les fonds et bordures gardent
     --feu ; seul le texte prend --feu-texte. */
  --feu-texte: #925C27;   /* l'ambre descendu de 40 % : 5,48 / 4,88     */
  /* Le fond pâle du feu, pour les marques posées DANS un texte (« à
     prendre » sur une ligne d'ingrédient) : le feu plein y crierait. */
  --feu-clair: #FBC7B5;   /* le blush de la planche                    */
  /* ⚠️ LA PLANCHE APPORTE SA PROPRE COULEUR D'ERREUR — la seule des 31 reçues.
     Réservée à ce qui a ÉCHOUÉ (saisie invalide, suppression), jamais à un
     ingrédient qui manque : un manque est le sujet de l'app, pas une panne. */
  --erreur: #F15B22;
  --erreur-texte: #B5441A;   /* le vermillon qui écrit : 5,44 / 4,85        */

  /* ⚠️ L'ASSOCIATION QUE LA PLANCHE DÉCLARE, DANS LES DEUX SENS. « Tu n'as pas
     gardé les couleurs et les associations orange/vert, inversement »
     (Félix, 19/08). La planche apparie le forêt et l'ambre l'un sur l'autre,
     et j'avais mis de l'encre neutre à la place.

     ⚠️ Mais l'accord mesure 3,53 : c'est au-dessus du seuil des ÉLÉMENTS
     D'INTERFACE (3,0) et en dessous de celui du TEXTE courant (4,5). Il sert
     donc aux SIGNES — pictogramme, anneau, contour, filet — et le texte
     garde l'encre. Lire la planche là où elle parle, mesurer là où elle se
     tait : même règle que sur la page d'essai. */
  --signe-sur-feu:   #3B5943;   /* le forêt sur l'ambre — 3,53 */
  --signe-sur-sauge: #F39A41;   /* l'ambre sur le forêt — 3,53 */

  /* LA PROVENANCE, À DEUX CRANS ET EN PASTEL.

     Trois erreurs derrière nous, et chacune a sa ligne ici :
       - des couleurs CALCULÉES pour un seuil de contraste, donc sans parenté
       - sept aplats saturés dans une liste, qui lisaient comme un tableur
       - une seule valeur par teinte, qui obligeait à choisir entre porter du
         noir et se lire sur du blanc

     Le remède est celui de tous les vrais systèmes de couleur : DEUX CRANS par
     teinte, pris dans la même famille. Le pastel remplit, l'encre écrit.
     Pastels en OKLCH à L=0,905 C=0,055 ; encres à L=0,42 C=0,13. Teintes prises
     dans le garde-manger et vérifiées sur leur teinte d'ARRIVÉE en sRGB, jamais
     entre 12 et 38 degrés, la plage de 750g et de Marmiton.

     Pire cas : 13,40:1 pour l'encre noire sur un pastel, 7,31:1 pour une encre
     de provenance sur du blanc. */
  /* Les teintes de provenance sortent TOUTES de la palette de Félix — plus
     un seul pastel inventé. Ton sur ton : le fond porte la couleur franche,
     l'encre est une ombre ou une lumière de la même famille. L'alternance
     fonds profonds / fonds clairs donne le rythme que les pastels n'avaient
     pas. Tous les couples tiennent 4,5:1 (pire cas : 4,58 sur le gris de mer).

     TROIS CRANS désormais, parce que la palette a des fonds PROFONDS :
       --pX        le FOND (la couleur franche, celle qu'on voit)
       --pX-encre  ce qui écrit SUR ce fond (ombre ou lumière de la famille)
       --pX-mot    ce qui écrit SUR BLANC au nom de cette personne — le cran
                   qui manquait : sur les fonds sombres, l'encre est claire
                   et devenait invisible dès qu'elle quittait son fond. */
  --p0: #ECF1E8;  --p0-encre: #4C5547;  --p0-mot: #4C5547;  /* vert grisé — le web      */
  --p1: #F58F1F;  --p1-encre: #432605;  --p1-mot: #8F5307;  /* tangerine, pleine        */
  --p2: #457534;  --p2-encre: #E9F2E2;  --p2-mot: #457534;  /* leaf green, plein        */
  --p3: #363636;  --p3-encre: #E5E5E5;  --p3-mot: #363636;  /* basalt black, plein      */
  --p4: #7D8285;  --p4-encre: #16181A;  --p4-mot: #54585A;  /* sea grey, plein          */
  --p5: #E7F0DF;  --p5-encre: #3A6428;  --p5-mot: #3A6428;  /* feuille pâle             */
  --p6: #4A4E50;  --p6-encre: #E3E4E5;  --p6-mot: #4A4E50;  /* basalte adouci, plein    */
  --teinte: var(--p0);
  --teinte-encre: var(--p0-encre);
  --teinte-mot: var(--p0-mot);

  /* LA NUIT. L'accueil est une COUVERTURE, pas un sommaire, et une couverture
     ne se pose pas sur du crème. Aubergine très sombre plutôt que noir : le
     noir pur sur un écran mobile fait matériel audio, l'aubergine garde la
     chaleur du reste du système. Réservée à la une et à rien d'autre — on lit
     une recette longtemps, casserole en main, et ça se fait sur du papier. */
  --nuit: #211627;
  --sur-nuit: #F6F1EA;
  --sur-nuit-2: #B9AEBB;

  /* Playfair Display en éditorial — c'est la police que le contrat nomme.
     La native en interface : « ultra-lisible », et zéro octet embarqué. */
  --f-titre: "Playfair", Georgia, "Times New Roman", serif;
  --f-ui:    system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --f-voix:  "Playfair", Georgia, serif;

  /* LES OMBRES SONT VERTES, PAS NOIRES : teintées du vert profond de la
     palette (rgba de #2D5016), elles décollent les cartes du fond en restant
     dans la famille — une ombre noire sur fond teinté grisaille tout. */
  /* ⚠️ --ombre-1 N'EST PLUS UNE OMBRE : c'est un filet. Vingt-deux règles
     le lisent, du coup l'app entière passe des boîtes flottantes aux
     blocs posés sur la page, sans toucher à vingt-deux règles. Une carte
     de livre ne lévite pas.
     Les deux autres restent des ombres, mais seulement pour ce qui flotte
     VRAIMENT : les feuilles et les dialogues, qui doivent se détacher de
     la page parce qu'ils la recouvrent. Encre plutôt que vert : sur du
     papier, une ombre verte se lit comme une erreur d'écran. */
  --ombre-1: 0 0 0 1px var(--trait);
  --ombre-2: 0 1px 2px rgba(40, 32, 18, .06), 0 14px 30px -14px rgba(40, 32, 18, .18);
  --ombre-3: 0 2px 6px rgba(40, 32, 18, .08), 0 26px 50px -20px rgba(40, 32, 18, .26);

  --marge: 24px;             /* « padding généreux » : le contrat le demande */
  /* ⚠️ LE CONTENU EST CARRÉ, LES COMMANDES SONT RONDES. C'est la règle de
     forme du nouveau système, et elle se lit sans qu'on l'explique : ce
     qui se lit a des angles de page (4 px), ce qui se touche a des angles
     de bouton (la pilule). Avant, tout était rond à 24 px et rien ne se
     distinguait de rien. */
  /* ⚠️ « Peut-être ajouter un peu d'arrondis » (Félix) : 12 sur ce qui se
     lit, 8 sur ce qui se touche, la pilule pour ce qui se choisit. Assez
     pour adoucir un écran de téléphone, assez peu pour rester une page —
     au-delà de 16 on retombe dans l'app générique refusée quatre fois. */
  --r-pilule: 999px;
  --r-carte: 12px;
  --r-champ: 8px;
  --r-bouton: 8px;

  --barre: 80px;
  --touche: 44px;
  --rang: 48px;
  --sous: env(safe-area-inset-bottom, 0px);
  --sur:  env(safe-area-inset-top, 0px);
}

/* ============================================================== 2. LE SOCLE */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--blanc); color: var(--encre);
  font: 400 16px/1.5 var(--f-ui);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}
button, input, textarea, select { font: inherit; color: inherit; }
button { border: 0; background: none; cursor: pointer; }
h1, h2, h3, h4, p, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
:focus-visible { outline: 2px solid var(--encre); outline-offset: 2px; border-radius: 4px; }

/* LES SURFACES QUE LE NAVIGATEUR DESSINE À NOTRE PLACE.
   La sélection, le curseur de saisie, la case cochée, la barre de défilement et
   les chiffres d'un tableau arrivent avec des valeurs par défaut qui
   n'appartiennent à aucun système. Les habiller coûte huit lignes et c'est ce
   qui distingue une interface construite d'une interface assemblée. */
::selection { background: var(--sauge-clair); color: var(--encre); }
input, textarea { caret-color: var(--sauge); }
input[type="checkbox"], input[type="radio"], progress { accent-color: var(--sauge); }
* { scrollbar-width: thin; scrollbar-color: var(--bord) transparent; }
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-thumb { background: var(--bord); border-radius: 999px; }
*::-webkit-scrollbar-track { background: transparent; }
/* Un chiffre qui change ne doit pas décaler la colonne où il vit. */
.plat__t, .bloc__compte, .facette__n, .parole__calcule, .gelule, .colophon,
.filtres__resume, .source__nb, .livre-carte__nb { font-variant-numeric: tabular-nums; }
a:not([class]) { text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* Une application fait défiler un CONTENU entre deux barres qui ne bougent
   pas. C'est ce seul fait qui décide, avant toute question de couleur, si ce
   qu'on regarde est un site ou une app. */
.ecrans { height: 100%; }
.ecran {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  visibility: hidden; opacity: 0;
  transition: opacity .18s ease;
}
.ecran.actif { visibility: visible; opacity: 1; }
.defile {
  flex: 1; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  /* ⚠️ Le disque d'ajout flotte maintenant AU-DESSUS de la barre : le contenu
     doit s'arrêter plus haut, sinon la dernière carte du bento arrive à six
     pixels des onglets et deux zones sensibles se touchent (14.1). */
  padding-bottom: calc(var(--barre) + var(--sous) + 42px);
  scroll-padding-bottom: calc(var(--barre) + var(--sous));
}

/* La barre d'onglets. Pictogramme de 24 px ET libellé de 12 px : en texte seul
   elle se lit comme un menu de site, quelle que soit sa hauteur. Mesures
   relevées dans framework7.css 9.1.2. */
/* Les écrans de porte (accueil, compte) n'ont pas de navigation : voir
   aller() dans app.js. */
body.sans-barre .onglets { display: none; }
body.sans-barre .defile { padding-bottom: 0; }
.onglets {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  box-shadow: 0 -1px 0 var(--trait), 0 -12px 28px -20px rgba(23, 21, 15, .34);
  /* ⚠️ AUTANT DE COLONNES QUE D'ONGLETS, calculé et non écrit en dur : la
     valeur était figée à trois, et le quatrième onglet passait à la ligne —
     la barre débordait sous l'écran sans que rien ne le signale. */
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  height: calc(var(--barre) + var(--sous));
  padding-bottom: var(--sous);
  background: var(--blanc);
  border-top: 1px solid var(--trait);
}
/* ⚠️ CINQ ENTRÉES, ET LE DISQUE REVIENT AU CENTRE. À quatre, il était en
   troisième position sur quatre — donc décentré, ce qui se voit avant même
   qu'on lise les libellés (« faut revoir le menu en bas », Félix). Cinq
   places le remettent au milieu, et permettent aux livres de sortir du menu
   des réglages : c'est une des deux SORTIES du projet, pas une option.

   Le libellé se serre sans se couper : à 78 px par cellule, « Collection »
   passe de justesse, et un mot tronqué dans une barre de navigation est le
   signe le plus sûr qu'on y a mis une entrée de trop. */
.onglet__mot { max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.onglet {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; min-height: var(--touche);
  color: var(--encre-3);
}
.onglet__icone { width: 22px; height: 22px; flex: 0 0 auto; }
.onglet__mot { font: 500 12px/1 var(--f-ui); letter-spacing: .01em; }
/* L'état courant ne se dit jamais par la seule couleur : le trait au-dessus et
   l'épaisseur du tracé le disent aussi. */
/* L'onglet courant est VERT : l'orange agit, le vert dit où l'on est. */
.onglet.on { color: var(--sauge); }
.onglet.on .onglet__icone { stroke-width: 2.1; }
/* ⚠️ LE LIBELLÉ DE L'ONGLET COURANT PASSE EN GRAISSE. Le filet de 2 px ne
   pesait pas 4 % des pixels de la cellule, et l'audit avait raison de le
   dire : à quatre onglets, la couleur redevenait seule à informer. La
   graisse, elle, se voit en noir et blanc comme en plein soleil. */
.onglet.on .onglet__mot { font-weight: 600; }
.onglet.on::before {
  content: ""; position: absolute; top: 0;
  width: 22px; height: 2px; background: currentColor;
}
/* L'action centrale FLOTTE au-dessus de la barre. Recipely la met sous une
   toque, Recipeo sous un scanner, et les deux la font dépasser du bandeau :
   c'est ce débord qui la sort du rang des onglets et dit qu'elle ajoute au lieu
   de naviguer. Le disque restait sagement aligné avec les autres. */
/* LE DISQUE D'AJOUT, POSÉ AU-DESSUS DE LA BARRE. Même allure qu'avant — le
   seul endroit du projet où le feu se pose en aplat plein — mais il n'occupe
   plus une place de navigation : la barre ne porte que des lieux.
   ⚠️ Il se cache sur les écrans de porte, comme la barre : proposer d'ajouter
   une recette à quelqu'un qui n'est pas encore entré n'a pas de sens. */
.poser {
  position: fixed; right: var(--marge); z-index: 60;
  bottom: calc(var(--barre) + var(--sous) + 24px);
  width: 56px; height: 56px; padding: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--feu); color: var(--sur-feu);
  box-shadow: 0 10px 22px -10px rgba(245, 143, 31, .7);
}
.poser .onglet__icone { width: 26px; height: 26px; stroke-width: 2.6; }
/* Le mot « Ajouter » ne s'imprime pas sur le disque : il reste pour les
   lecteurs d'écran, qui n'ont que lui pour nommer le geste. */
.poser .onglet__mot {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
body.sans-barre .poser { display: none; }
/* Et sur tous les écrans où ajouter une recette n'a pas de sens : le disque
   y recouvrait le contenu (panne-06 de la traversée nocturne). */
body.sans-poser .poser { display: none; }
/* La barre du haut. Collante, pas fixe : elle appartient à l'écran qui défile,
   donc elle se remplace correctement quand on change de vue. */
.ours {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: baseline; gap: 10px;
  min-height: 56px; padding: max(12px, var(--sur)) var(--marge) 12px;
  background: var(--blanc);
}
/* Le signet, calé sur la ligne de base du nom plutôt que centré : une marque
   qui flotte au-dessus du mot qu'elle accompagne se lit comme une puce. */
.ours__signet { width: 17px; height: 17px; flex: 0 0 auto;
  align-self: baseline; transform: translateY(2px); color: var(--encre); }
.ours b { font: 600 22px/1 var(--f-titre); letter-spacing: -.004em; flex: 0 0 auto; }
/* Le compte s'ELLIPSE au lieu de pousser le titre sur deux lignes : depuis
   que l'ours porte deux boutons, « 80 recettes, 3 à confirmer » ne tenait
   plus sur une ligne. */
.ours > span { min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font: 400 15px/1.3 var(--f-ui); color: var(--encre-3); }
/* LES DEUX PORTES DU HAUT : le panier (avec son compte) et le rond de
   profil. Elles remplacent le lien « N livres », qui donnait au seul écran
   des livres une place que les courses et la semaine méritaient autant. */
.ours__livres { margin-left: auto; display: inline-flex; align-items: center; }
.ours__porte, .ours__profil { position: relative; display: grid; place-items: center;
  width: var(--touche); height: var(--touche); flex: 0 0 auto;
  border-radius: 50%; color: var(--encre-2); }
.ours__porte { margin-left: auto; }
.ours__porte + .ours__profil { margin-left: 0; }
.ours__profil { margin-left: auto; background: var(--bande); color: var(--encre); }
.ours__porte + .ours__profil { margin-left: 2px; }
/* Le compte des courses, en pastille verte sur le panier.
   ⚠️ .ours .ours__n et non .ours__n : « .ours span » du socle a une
   spécificité supérieure et repeignait le chiffre en gris. */
.ours .ours__n { position: absolute; top: 1px; right: -1px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center; border-radius: var(--r-pilule);
  background: var(--sauge); color: var(--blanc);
  font: 600 12px/1 var(--f-ui); font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--blanc); }

/* La feuille « Donner » : un geste principal, deux voies de secours. */
#donner .b--large { margin-top: 16px; }
.donner__autres { display: flex; flex-direction: column; align-items: stretch;
  gap: 2px; margin-top: 10px; }
.donner__autres .b--discret { text-align: left; padding: 0 4px; }

/* ======================================= 18. LA PAGE D'UNE PERSONNE
   Une page de garde de carnet : un portrait, un nom, une phrase, et ce qui
   vient d'elle. Pas de profil de réseau — ni abonnés, ni badges, ni
   palmarès. Ce qu'on compte ici est la TRANSMISSION. */
.qui { padding: 26px var(--marge) 6px; }
.qui__portrait { display: grid; place-items: center; width: 88px; height: 88px;
  border-radius: 50%; overflow: hidden;
  background: var(--teinte); color: var(--teinte-encre);
  font: 400 34px/1 var(--f-titre); }
.qui__portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qui__nom { margin-top: 16px; font: 600 34px/1.06 var(--f-titre); letter-spacing: -.004em; }
.qui__lien { margin-top: 2px; font: 600 12px/1.2 var(--f-ui); letter-spacing: .09em;
  text-transform: uppercase; color: var(--teinte-mot); }
.qui__notice { margin-top: 12px; max-width: 46ch;
  font: italic 400 17px/1.55 var(--f-voix); color: var(--encre-2); }
.qui__compte { display: flex; gap: 6px; margin-top: 16px; }
/* La provenance de la fiche est une PORTE, mais elle garde l'allure d'un
   texte : un bouton qui ressemble à un bouton, ici, ferait une deuxième
   action à côté du titre. */
.fiche__qui-porte { padding: 0; text-align: left; font: inherit; color: inherit;
  text-decoration: underline; text-decoration-thickness: 1px;
  text-decoration-color: var(--bord); text-underline-offset: 4px; }

/* ============================================== 19. LE COMPTE
   L'écran de bienvenue (celui qu'on voit avant d'entrer) et les trois
   formulaires. Tout est construit maintenant pour que le jour du serveur il
   n'y ait qu'un adaptateur à brancher. */
/* L'ACCUEIL : LA PHOTO EN HAUT, LE TEXTE SUR LE PAPIER.

   Premier essai : tout le texte SUR la photo, avec un voile. Refusé, et à
   raison — « on ne voit pas le texte, le design est claqué ». Le texte sur
   photo est toujours un compromis : ou l'image est assez sombre pour qu'on
   lise, et elle est fichue, ou elle reste belle et le texte se bat. On
   sépare donc : l'image occupe le haut et se fond dans le papier, le nom et
   les boutons vivent sur le crème, où ils sont parfaitement lisibles. */
.bienvenue { display: flex; flex-direction: column; min-height: 100%;
  background: var(--blanc); }
.bienvenue__vue { position: relative; flex: 0 0 auto; height: 46vh;
  background: var(--fond-accueil, none) center / cover no-repeat, var(--creme); }
/* Le fondu vers le papier : l'image ne s'arrête pas, elle se dissout. C'est
   ce qui évite la ligne franche — et la barre claire qu'on voyait avant. */
.bienvenue__vue::after { content: ""; position: absolute; left: 0; right: 0;
  bottom: -1px; height: 45%;
  background: linear-gradient(180deg, rgba(250,247,242,0) 0%, var(--blanc) 92%); }
.bienvenue__texte { flex: 1; display: flex; flex-direction: column;
  padding: 0 var(--marge) calc(28px + var(--sous)); margin-top: -28px; }
.bienvenue__signet { position: relative; width: 30px; height: 30px; color: var(--sauge); }
.bienvenue__nom { margin-top: 10px; font: 600 34px/1.05 var(--f-titre);
  letter-spacing: -.004em; }
.bienvenue__mot { margin-top: 10px; max-width: 26ch;
  font: 400 17px/1.55 var(--f-ui); color: var(--encre-2); }
.bienvenue__pied { margin-top: auto; padding-top: 28px;
  display: flex; flex-direction: column; gap: 10px; align-items: stretch; }
.bienvenue__note { margin-top: 2px; text-align: center;
  font: 400 13px/1.45 var(--f-ui); color: var(--encre-3); }

/* LE GESTE D'ENTRÉE EST ORANGE. Ces écrans n'avaient aucune couleur de
   marque : un bouton noir sur du crème, ça pourrait être n'importe quelle
   application. S'inscrire est une INVITATION — le même registre que « Ce
   soir, sans y penser » et que le disque Ajouter — donc l'orange, l'encre
   dessus (5,08:1). Un seul par écran, comme partout. */
.bienvenue .b--principal,
#ecran-compte .b--principal {
  background: var(--feu); color: var(--encre); border-color: var(--feu); }

/* Le mot qui dit que le serveur n'est pas là : une carte cerclée, comme
   toute mise en avant de cette app. */
.compte__attente { margin-bottom: 20px; padding: 12px 14px;
  border: 2px solid var(--feu); border-radius: var(--r-carte);
  font: 400 15px/1.5 var(--f-ui); color: var(--encre); }
/* La faute vit SOUS son champ, en rouge d'encre — jamais en rouge vif, qui
   se lit comme une alarme là où il s'agit d'aider. */
/* ⚠️ 15px et non 14 : l'échelle du projet n'a pas de 14, et le détecteur
   d'Impeccable a attrapé le corps clandestin (« sizes 14, 15, 19 »). */
.champ__faute { margin-top: 6px; font: 500 15px/1.45 var(--f-ui); color: #9E3220; }
.champ__faute:empty { display: none; }
.champ[aria-invalid="true"] { border-color: #9E3220; background: var(--carte); }
/* La case à cocher du consentement : la cible fait toute la ligne. */
.consent { display: grid; grid-template-columns: 22px 1fr; gap: 12px;
  align-items: start; min-height: var(--touche);
  font: 400 15px/1.45 var(--f-ui); color: var(--encre-2); }
.consent input { width: 22px; height: 22px; margin: 1px 0 0; }
.compte__etat { margin-top: 12px; font: 400 15px/1.45 var(--f-ui); color: var(--encre-2); }
.compte__etat:empty { display: none; }
.compte__pied { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 18px;
  padding-top: 12px; border-top: 1px solid var(--trait); }

/* L'écran des informations : du texte long, donc une mesure de lecture et
   des paragraphes qui respirent. Rien d'autre. */
.legal { padding: 6px var(--marge) 40px; }
.legal__bloc + .legal__bloc { margin-top: 8px; }
.legal p { margin-top: 12px; font: 400 17px/1.6 var(--f-ui); color: var(--encre-2);
  max-width: 62ch; }
.legal .sauvegarde { margin-top: 18px; }

/* La feuille du profil : une tête, deux groupes, la sauvegarde. */
.profil__tete { display: flex; align-items: center; gap: 12px;
  padding: 4px 0 16px; }
.profil__rond { display: grid; place-items: center; width: 48px; height: 48px;
  flex: 0 0 auto; border-radius: 50%; background: var(--sauge-clair); color: var(--sauge); }
.profil__nom { display: block; font: 500 19px/1.2 var(--f-titre); }
.profil__quoi { display: block; margin-top: 2px;
  font: 400 13px/1.3 var(--f-ui); color: var(--encre-3); }
#profil .choix { grid-template-columns: 44px 1fr auto; }
#profil .champ__etiquette { margin-top: 18px; }

.nav {
  position: sticky; top: 0; z-index: 50;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 8px; min-height: 56px; padding: max(8px, var(--sur)) 12px 8px;
  background: var(--blanc); border-bottom: 1px solid var(--trait);
}
.nav__retour, .nav__action {
  display: inline-flex; align-items: center; gap: 4px;
  min-height: var(--touche); padding: 0 8px;
  font: 500 15px/1 var(--f-ui); color: var(--encre-2);
}
.nav__action { justify-self: end; }
/* ⚠️ `display: inline-flex` ci-dessus l'emporte sur l'attribut `hidden` : sans
   cette règle, cacher l'action de la barre en JS ne cache rien du tout. */
.nav__action[hidden] { display: none; }
.nav__titre {
  font: 600 16px/1.2 var(--f-ui); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ========================================================= 3. LES COMPOSANTS
   TROIS BOUTONS, ET TROIS SEULEMENT. Chaque allure supplémentaire est une
   allure que l'utilisateur doit apprendre. Elles s'appliquent partout, y
   compris aux boutons des feuilles et du formulaire, qui avaient chacun la
   leur. */
.b, .feuille__pied button, .form__pied button, .nouveau-livre, .collerlien button,
.retour-collection, .filtres__ouvrir, #btn-export, #filtres-champs, #filtres-fermer {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; padding: 0 20px;
  font: 500 16px/1.2 var(--f-ui); letter-spacing: -.005em;
  border: 1px solid transparent; border-radius: var(--r-bouton);
  background: var(--carte); color: var(--encre); border-color: var(--bord);
  transition: transform .12s ease;
}
.b:active, .feuille__pied button:active, .form__pied button:active { transform: scale(.97); }
.b:disabled { opacity: .38; pointer-events: none; }
.b--principal, .form__pied button:last-child, .feuille__pied button:last-child,
#edition-enregistrer, #filtrables-fermer, .collerlien button {
  /* Un bouton qui porte du TEXTE écrit blanc sur basalte : l'orange, même
     avec le vert de forêt dessus, restait « pas super lisible » (Félix).
     L'orange vit là où il n'y a rien à lire : le disque Ajouter, le feu. */
  background: var(--encre); color: var(--blanc); border-color: var(--encre);
  font-weight: 600;
}
/* Le troisième rôle : annuler, revenir, ne rien faire. Jamais de fond.
   #filtres-vider tombait à 400 et .ours__livres à 14 px : deux allures de plus
   pour le même rôle, chacune née d'un écran écrit un autre jour. */
.b--discret, .nav__retour, .nav__action, #filtres-vider, #edition-annuler, .ours__livres {
  background: transparent; border-color: transparent; color: var(--encre-2);
  font: 500 16px/1.2 var(--f-ui); padding: 0 12px; border-radius: var(--r-bouton);
  min-height: 48px;
}
.ours__livres { padding: 0 10px; text-decoration: underline; text-underline-offset: 3px; }
.b--large, .nouveau-livre, .retour-collection, #btn-export { width: 100%; }

/* UN SEUL CHAMP, ET SON ÉTIQUETTE TOUJOURS VISIBLE. Le placeholder ne remplace
   jamais l'étiquette : il disparaît à la première frappe, et le référentiel le
   note « non conforme, correction : non conforme ». */
 /* ⚠️ .champ-nom EST UNE ÉTIQUETTE, PAS UN CHAMP. Elle figurait dans la liste
    de styles des champs de saisie et recevait donc leur cadre et leur fond :
    dans le formulaire, chaque libellé se posait comme une pastille par-dessus
    le champ précédent. Un <label> qui ressemble à un <input> est aussi une
    faute d'usage — on croit pouvoir écrire dedans. */
.champ__etiquette, .champ-nom, .meta__label, .parole__label, .fiche-bloc__legende, .facette__nom {
  display: block; margin: 0 0 6px;
  font: 600 12px/1.2 var(--f-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--encre-3);
}
/* Les champs suivent la barre de recherche du contrat : fond gris très
   subtil, AUCUNE bordure au repos. Le cadre n'apparaît qu'au focus, en
   sauge — c'est lui qui dit « tu es ici ». */
.champ, #recherche, #f-url, .boite__champ, input[type="text"], input[type="url"],
input[type="search"], textarea, select {
  width: 100%; min-height: 48px; padding: 10px 15px;
  font: 400 17px/1.4 var(--f-ui); color: var(--encre);
  background: var(--bande); border: 1px solid transparent; border-radius: var(--r-champ);
}
.champ:focus, #recherche:focus, #f-url:focus, .boite__champ:focus,
input[type="text"]:focus, input[type="url"]:focus, input[type="search"]:focus,
textarea:focus, select:focus { border-color: var(--sauge); background: var(--carte); outline: none; }
textarea { min-height: 96px; resize: vertical; }
select { appearance: none; padding-right: 34px;
  background-image: linear-gradient(45deg, transparent 50%, var(--encre-3) 50%),
                    linear-gradient(135deg, var(--encre-3) 50%, transparent 50%);
  background-position: right 16px center, right 11px center;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
::placeholder { color: var(--encre-3); opacity: 1; }

/* La gélule : un chiffre encapsulé. Durée, compte, état. */
.bloc__compte:empty { display: none; }
/* LE BADGE DU CONTRAT : petit, horizontal, arrondi, fond vert très clair,
   texte dans le vert foncé de l'identité. C'est lui qui porte le temps, les
   comptes et les mentions — jamais une icône lourde. */
.gelule, .plat__t, .bloc__compte, .facette__n, .filtrable__n, .source__nb, .livre-carte__nb,
.badge {
  display: inline-flex; align-items: center; min-height: 22px; padding: 0 10px;
  font: 500 13px/1 var(--f-ui); color: var(--sauge);
  background: var(--sauge-clair); border-radius: var(--r-pilule);
  font-variant-numeric: tabular-nums;
}

/* L'interrupteur, pour tout réglage à deux états. */
.inter { position: relative; display: inline-block; width: 52px; height: 32px; }
.inter input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.inter__piste { position: absolute; inset: 0; border-radius: var(--r-pilule);
  background: var(--bord); transition: background-color .2s ease; }
.inter__bouton { position: absolute; top: 3px; left: 3px; width: 22px; height: 22px;
  border-radius: 50%; background: var(--blanc);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28); transition: transform .2s ease; }
.inter input:checked ~ .inter__piste { background: var(--sauge); }
.inter input:checked ~ .inter__bouton { transform: translateX(20px); }

/* La liste encartée : elle flotte sur le fond au lieu de toucher les bords.
   ⚠️ Elle ne porte pas sa marge extérieure : l'écart au bord appartient au
   contexte qui la pose. */
.encart { width: 100%; background: var(--blanc);
  border: 1px solid var(--trait); border-radius: var(--r-carte); overflow: hidden; }

/* La boîte, qui remplace confirm(), prompt() et alert(). Les boîtes du
   navigateur s'annoncent « localhost says: » sous iOS. */
.boite-voile { position: fixed; inset: 0; z-index: 90; background: rgba(23, 21, 15, .42); }
.boite {
  position: fixed; z-index: 91; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(270px, calc(100vw - 48px));
  padding: 20px 17px 0; text-align: center;
  background: var(--carte); border-radius: var(--r-carte);
  box-shadow: 0 18px 50px -18px rgba(17, 20, 18, .30);
}
.boite__titre { font: 500 19px/1.25 var(--f-titre); }
.boite__mot { margin-top: 8px; font: 400 15px/1.45 var(--f-ui); color: var(--encre-2); }
.boite__champ { margin-top: 15px; }
.boite__pied { display: flex; margin: 20px -17px 0; border-top: 1px solid var(--trait); }
.boite__action {
  flex: 1; min-height: 48px; font: 500 16px/1 var(--f-ui); color: var(--encre);
}
.boite__action + .boite__action { border-left: 1px solid var(--trait); font-weight: 600; }
.boite-hote[aria-hidden="true"] { display: none; }

/* La feuille qui monte. Les filtres qui remplacent l'écran, c'est un site. */
.voile {
  position: fixed; inset: 0; z-index: 70; background: rgba(23, 21, 15, .42);
  opacity: 0; pointer-events: none; transition: opacity .3s ease;
}
.voile.on { opacity: 1; pointer-events: auto; }
.feuille {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  max-height: 88vh; overflow-y: auto;
  padding: 0 var(--marge) calc(20px + var(--sous));
  background: var(--blanc); border-radius: 16px 16px 0 0;
  box-shadow: 0 -8px 40px -12px rgba(23, 21, 15, .3);
  transform: translateY(100%);
  transition: transform .3s cubic-bezier(0, .8, .34, 1);
}
.feuille.on { transform: none; }
.feuille__poignee { width: 36px; height: 4px; border-radius: 2px; margin: 10px auto 15px;
  background: var(--bord); }
.feuille h2, .feuille__tete h2 { font: 500 22px/1.15 var(--f-titre); letter-spacing: 0; }
.feuille__tete { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 15px; }
.feuille__actions { display: inline-flex; align-items: center; gap: 2px; }
/* Le réglage des champs : une icône discrète, pas un bouton concurrent. */
#filtres-champs { width: 48px; padding: 0; justify-content: center;
  background: transparent; border-color: transparent; color: var(--encre-2); }
.feuille__pied { display: flex; gap: 9px; margin-top: 17px; }
.feuille__pied button { flex: 1; }
/* La croix de fermeture des feuilles (posée par app.js sur chacune). Sur
   téléphone elle n'existe pas : poignée, voile et geste suffisent. À la
   souris rien de tout ça ne se voit — elle apparaît avec le grand écran
   (« pas de bouton pour les fermer », Félix). */
.feuille__croix { display: none; }

/* La feuille du choix : le moteur à cartes. Les gélules gardent leur
   rangée défilante, les cartes s'alignent en deux colonnes — la grammaire
   de la collection, dans une feuille. */
#feuille-choisir .rapides { padding-inline: 0; }
.choisir-cartes { display: grid; grid-template-columns: 1fr 1fr;
  gap: 10px; padding: 4px 0 12px; }
.choisir-cartes .carte { width: 100%; flex: none; }

/* Les rayons de la liste de courses : une étiquette de nature (la même
   grammaire que le bento), le compte à côté, les lignes dessous. */
/* Le retrait horizontal vivait sur l'ancien .course-groupe : sans lui,
   les rayons collaient aux bords de l'écran sous 1200. Au large, le
   .defile porte déjà ses marges — on rend le retrait. */
.courses-rayons { margin-inline: var(--marge); }
.course-rayon__nom { display: flex; align-items: baseline; gap: 8px;
  margin: 16px 0 4px;
  font: 600 12px/1.2 var(--f-ui); letter-spacing: .09em;
  text-transform: uppercase; color: var(--sauge); }
.course-rayon__n { font: 400 12px/1 var(--f-ui); letter-spacing: 0;
  color: var(--encre-3); font-variant-numeric: tabular-nums; }
.course-rayon[data-rayon="panier"] .course-rayon__nom { color: var(--encre-3); }

/* Le descriptif en seuil de feuille (celui des filtres à proposer) :
   la marge vivait en inline dans le HTML, interdit par la charte. */
.choix__desc--seuil { margin: 0 0 14px 2px; }

/* Les gestes du semainier font la taille d'un doigt, à toutes les
   largeurs ; la croix de retrait ne se comprime jamais. */
#semaine [data-jour-ote] { flex: none; min-width: 44px; min-height: 44px;
  display: grid; place-items: center; }
.jour__prevoir { min-height: 44px; }
.jour__plat { min-width: 0; }

/* La coche des courses s'aligne sur la PREMIÈRE ligne de l'article : centrée
   sur deux lignes, elle flottait entre les deux (revue visuelle). */
.course { align-items: flex-start; }
.course__rond { margin-top: 3px; }

/* Un placeholder coupé net se lit comme un bug : des points de suspension. */
input[type="search"] { text-overflow: ellipsis; }

/* Le label d'un champ de boîte répète le titre de la boîte juste au-dessus :
   il reste pour les lecteurs d'écran, il disparaît des yeux.
   ⚠️ SAUF DANS UN FORMULAIRE : quand il y a dix champs, chacun a besoin de
   son nom — c'est le titre qui ne se répète pas, pas les champs. */
.boite:not([data-type="champs"]) .champ__etiquette { position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Le disque d'ajout ne recouvre plus la dernière carte : la liste garde
   la place du pouce en bas (revue visuelle, mobile). */
#ecran-collection .vue { padding-bottom: 96px; }

/* Le mot de chaque famille de la cave (posé par app.js) : caché sur
   téléphone où l'icône et la place se suffisent. */
.famille__mot { display: none; }

/* Les outils de la pièce : les pilules et le champ qui cherche, en un
   rang qui respire — la règle avait été avalée par un remplacement (le
   champ collait aux bords). */
.piece-outils { display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px; margin: 2px var(--marge) 14px; }

/* LE MEUBLE DE LA PIÈCE — un seul cadre, comme la cave. Les rayons sont
   des ÉTAGES à l'intérieur, ouverts par leur seuil ; les cases sont les
   alvéoles ; la libre est le rond pointillé du cellier. */
.buffet-d { padding: 4px 0 0; }
/* LA MATIÈRE DE LA HOME : pas de conteneur — les CASES portent la carte
   (blanc ombré sur crème), comme le bento (« garder ce style et cette
   mise en page, l'adapter à nos besoins », Félix). */
.meuble-p { margin: 0 var(--marge) 16px; }
.meuble-p__seuil { display: flex; align-items: baseline; gap: 8px;
  padding: 12px 2px 8px; }
.meuble-p__seuil + .meuble-p__cases { margin-bottom: 2px; }
.meuble-p__seuil span { font: 600 12px/1.2 var(--f-ui); letter-spacing: .09em;
  text-transform: uppercase; color: var(--sauge); }
.meuble-p__seuil small { font: 400 12px/1 var(--f-ui); color: var(--encre-3);
  font-variant-numeric: tabular-nums; }
.meuble-p__seuil::after { content: ''; flex: 1; border-top: 1px solid var(--trait); }
.meuble-p__cases { display: grid;
  grid-template-columns: repeat(var(--rangee, 4), 1fr); gap: 10px; }
.case-p { display: grid; justify-items: center; align-content: center;
  gap: 6px; margin: 0; padding: 10px 6px 9px; min-height: 96px; min-width: 0;
  background: var(--carte); border: 0; box-shadow: var(--ombre-1);
  border-radius: var(--r-carte); cursor: pointer; }
.case-p__nom { font: 500 12px/1.2 var(--f-ui); color: var(--encre);
  text-align: center; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden; }
/* ⚠️ UNE BOUTEILLE OUVERTE N'EST PAS FINIE — « un produit ouvert, pourquoi
   il est barré dans la cave, c'est chelou » (Félix, 13/08). Le barré dit
   « supprimé », or une bouteille entamée est justement celle qu'on va
   boire. Elle garde son encre pleine ; un point de feu au coin de la case
   dit qu'elle est ouverte, comme un signet. */
.case-p--entame .case-p__nom { color: var(--encre); text-decoration: none; }
.case-p--entame { position: relative; }
.case-p--entame::after { content: ''; position: absolute; top: 7px; right: 7px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--feu); }
.case-p__fam { width: 30px; height: 30px; color: var(--sauge); }
.case-p--entame .case-p__fam { color: var(--encre-3); }
/* Hors filtre : la case pâlit SUR PLACE, la grille ne bouge pas. */
.case-p--hors { opacity: .28; }
/* La case qui appelle une bouteille à ranger. */
.case-p--appelle { color: var(--sauge); }
.case-p--appelle svg { max-width: 52px; max-height: 52px; }
/* Les gestes du panneau de droite. */
.casier-cote__gestes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.case-p svg { display: block; width: 30px; height: 30px; color: var(--sauge); }
.case-p--entame svg { color: var(--sauge); }
.case-p--libre { background: transparent; border-color: transparent;
  color: var(--trait); }
.case-p--libre svg { width: 100%; height: 100%; max-width: 52px; max-height: 52px; }
.case-p--libre:active { color: var(--sauge); }
.case-p:active { transform: scale(.96); }

/* La une, registre éditorial (la planche du 13/08) : le filet double
   avant le plat, les jetons du pied — durée en sauge, couverts au feu
   pâle. Les guillemets de la phrase restent l'encre de la parole. */
.une__filet { border: 0; border-top: 1px solid var(--encre-3);
  box-shadow: 0 3px 0 -2px var(--encre-3); margin: 14px 0 12px; opacity: .5; }
.une__pied { display: flex; gap: 8px; margin-top: 12px; }
.jeton { display: inline-flex; align-items: center; min-height: 28px;
  padding: 0 12px; border-radius: var(--r-pilule);
  font: 600 12px/1 var(--f-ui); letter-spacing: .1em; text-transform: uppercase;
  background: var(--sauge-pale, #E4EDDC); color: var(--sauge); }
.jeton--feu { background: #F6E3D4; color: #A14A18; }

/* Le formulaire d'une boîte : un champ par information, empilés, qui
   défilent si l'écran est court. */
/* ⚠️ UNE BOÎTE DE 270 PX POUR DIX CHAMPS — « le popup pour compléter est
   pas responsive non plus ? » (Félix). Une alerte tient dans une colonne
   étroite ; un FORMULAIRE a besoin de la largeur qu'on lui donne, et de
   deux colonnes dès qu'il y a la place. La boîte s'élargit donc selon ce
   qu'elle porte, pas selon une valeur unique. */
.boite[data-type="champs"] { width: min(560px, calc(100vw - 32px)); }
@media (min-width: 900px) {
  .boite[data-type="champs"] { width: min(760px, calc(100vw - 64px)); }
  .boite[data-type="champs"] .boite__champs {
    grid-template-columns: 1fr 1fr; gap: 16px 24px; max-height: 62vh; }
  /* Les champs qui proposent une longue rangée gardent la pleine largeur :
     une bande qui défile coupée en deux ne défile plus. */
  .boite[data-type="champs"] .boite__ligne--suggere:not(.boite__ligne--unique) {
    grid-column: 1 / -1; }
}
.boite__champs { display: grid; gap: 16px; max-height: 52vh; overflow-y: auto;
  margin-top: 6px; padding-right: 2px; }
.boite__ligne { margin: 0; text-align: left; }
/* ⚠️ min-width: 0 SUR LES LIGNES. La rangée qui défile (flex nowrap) donne
   une largeur minimale égale à son contenu déroulé : la grille s'élargit à
   768 px, et le texte — centré par .boite — part hors du cadre. Les
   libellés « Brasserie », « Couleur » étaient RENDUS mais invisibles.
   C'est le pendant en grille du piège [hidden]/display. */
.boite__champs > * { min-width: 0; }
.boite__ligne .boite__champ { margin-top: 6px; }
/* Une ligne qui propose n'a pas besoin de son champ : il attend derrière
   « Autre… ». */
.boite__ligne--suggere .boite__champ[hidden] { display: none; }
.boite__ligne .champ__etiquette { display: block; margin-bottom: 3px; }
/* Les suggestions : on touche au lieu de taper. Petites, sous le champ,
   elles se posent et se déposent comme des filtres.
   ⚠️ NOM CHOISI CONTRE UNE COLLISION : .propose appartient déjà au module
   de partage (« ce qu'on soumet au propriétaire », partage.css) — ma règle
   perdait contre la sienne, bordure orange de 2 px sur des pastilles de
   formulaire. Deux feuilles, un seul espace de noms : vérifier avant de
   nommer. */
.boite__suggere { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
/* Un choix UNIQUE tient sur une ligne qui défile : onze styles empilés sur
   quatre rangées se lisent comme un formulaire, une bande qui glisse se
   parcourt au pouce (la grammaire du menu rapide de la home). */
.boite__suggere--rang { flex-wrap: nowrap; overflow-x: auto;
  scrollbar-width: none; padding-bottom: 2px; }
.boite__suggere--rang::-webkit-scrollbar { display: none; }
.boite__suggere--rang .suggere { flex: 0 0 auto; }
.suggere { min-height: 30px; padding: 0 11px; border-radius: var(--r-pilule);
  border: 1px solid var(--trait); background: var(--carte);
  font: 400 13px/1 var(--f-ui); color: var(--encre-2); cursor: pointer; }
.suggere.on { background: var(--sauge); border-color: var(--sauge); color: var(--blanc); }
/* Le dernier recours se voit qu'il en est un : pointillé, comme la place
   libre d'un casier — « il y a de la place ici pour ton mot ». */
.suggere--autre { border-style: dashed; color: var(--encre-3); }
/* La liste qui se réduit à la frappe : des lignes, pas des pastilles —
   on cherche un nom propre, on ne compose pas. */
/* ⚠️ QUATRIÈME MORSURE DU MÊME PIÈGE : `display: grid` bat l'attribut
   [hidden], et la liste des brasseries restait ouverte en permanence.
   Toute règle qui pose un display doit céder devant l'attribut. */
.boite__trouve[hidden] { display: none; }
.boite__trouve { display: grid; gap: 2px; margin-top: 4px;
  padding: 4px; background: var(--blanc); border: 1px solid var(--trait);
  border-radius: var(--r-carte); }
.trouve { min-height: 36px; padding: 0 10px; text-align: left;
  border-radius: 8px; font: 400 14px/1 var(--f-ui); color: var(--encre); }
.trouve:active, .trouve:hover { background: var(--sauge-clair, #E8EFE3); }
.trouve[hidden] { display: none; }

/* ============================================================== 4. LES VUES */
.vue { padding-bottom: 12px; }
.vue__titre { margin: 15px var(--marge) 16px;
  font: 600 clamp(28px, 8vw, 34px)/1.06 var(--f-titre); letter-spacing: -.004em; }

/* LA LIGNE D'OUTILS. Ranger et filtrer sont la même famille de gestes :
   « depuis le début je comprends pas [les trois grosses gélules] — on peut
   pas mettre des icônes, vu que l'on a une zone de filtre ? » (Félix). Le
   sélecteur de vue et le menu rapide partagent une seule rangée collante ;
   un filet vertical sépare les deux natures. Les filtres posés (gélules
   textes + résumé) passent à la ligne, pleine largeur. */
.outils { position: sticky; top: 0; z-index: 40;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0;
  padding: 8px var(--marge) 6px; background: var(--blanc);
  border-bottom: 1px solid var(--trait); }
.outils .choix-vue--icones { padding-right: 10px; margin-right: 2px;
  border-right: 1px solid var(--trait); }
.outils .rapides { flex: 1 1 auto; min-width: 0; padding: 0 0 0 8px; }
.outils .filtres { flex: 1 0 100%; padding: 6px 0 2px; border-bottom: 0; }
.choix-vue { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
/* ⚠️ Le variant icônes vient APRÈS la base : à spécificité égale c'est la
   dernière règle qui gagne, et écrit avant, son inline-flex ne s'appliquait
   littéralement jamais (revue du 12/08). */
.choix-vue--icones { display: inline-flex; gap: 8px; }
.choix-vue--icones .choix-vue__on { width: 52px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center; }
.choix-vue--icones .choix-vue__on .gr { color: var(--sauge); }
.choix-vue--icones .choix-vue__on.on .gr { color: var(--blanc); }
.choix-vue__on {
  min-height: var(--touche); padding: 0 8px; border-radius: var(--r-pilule);
  border: 1px solid var(--bord); background: var(--blanc); color: var(--encre-2);
  font: 500 13px/1.15 var(--f-ui); text-align: center;
}
.choix-vue__on.on { background: var(--sauge); color: var(--blanc); border-color: var(--sauge); }
.choix-vue__quoi { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; }

.vue__index { display: flex; gap: 6px; padding: 10px var(--marge);
  overflow-x: auto; scrollbar-width: none; }
.vue__index::-webkit-scrollbar { display: none; }
.vue__pion {
  flex: 0 0 auto; min-height: var(--touche); padding: 0 15px;
  border: 1px solid var(--bord); border-radius: var(--r-pilule);
  font: 500 15px/1 var(--f-ui); color: var(--encre-2);
}
.vue__pion.on { background: var(--sauge); color: var(--blanc); border-color: var(--sauge); }

.bloc { margin: 0; }
.bloc__tete { display: grid; grid-template-columns: 1fr auto auto; align-items: center;
  column-gap: 12px; padding: 20px var(--marge) 8px; }
.bloc__titre { font: 600 19px/1.25 var(--f-ui); letter-spacing: 0; }
/* La porte du rail. Cible de 44px imposée par le critère 14.1, obtenue par le
   remplissage vertical et non par une hauteur déclarée, pour que la ligne de
   titre ne s'écarte pas. */
.bloc__tout { display: inline-flex; align-items: center;
  min-height: var(--touche); padding: 0 0 0 6px; font: 500 13px/1 var(--f-ui);
  color: var(--encre-2); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 3px; cursor: pointer; }
.bloc__apercu { grid-column: 1 / -1; margin-top: 6px;
  font: 400 15px/1.42 var(--f-ui); color: var(--encre-2); }

.bloc[data-role="personne"] { margin: 0 var(--marge) 10px; padding: 15px 16px;
  border-radius: var(--r-carte); background: color-mix(in srgb, var(--teinte) 10%, var(--blanc));
  border: 1px solid color-mix(in srgb, var(--teinte) 26%, var(--blanc)); }
.bloc[data-role="personne"] > .bloc__tete { padding: 0; }
.bloc[data-role="personne"] .bloc__titre { font: 500 22px/1.1 var(--f-titre);
  letter-spacing: -.004em; color: var(--teinte-mot); }
.bloc[data-role="mot"] { margin: 8px var(--marge) 15px; padding: 13px 15px;
  border-radius: var(--r-carte); background: var(--creme); }
.bloc[data-role="mot"] > .bloc__tete { padding: 0; }
.bloc[data-role="mot"] .bloc__titre { font: 600 12px/1 var(--f-ui);
  letter-spacing: .06em; text-transform: uppercase; color: var(--encre-3); }
.bloc[data-role="source"] .bloc__titre, .bloc[data-role="aire"] .bloc__titre {
  font: 600 12px/1 var(--f-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--encre-3); }

.bloc[data-forme="liste"] .bloc__corps { padding: 0 var(--marge); }
/* scroll-padding-left N'EST PAS un doublon du padding. L'accrochage aligne le
   bord de la vignette sur le bord du PORT DE DÉFILEMENT, qui ignore le
   remplissage : au premier rendu, chaque rail se décalait tout seul de ses
   20px et la première carte sortait de l'écran, coin rogné. */
.bloc[data-forme="rail"] .bloc__corps {
  display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;
  padding: 6px var(--marge) 4px; scroll-padding-left: var(--marge);
  scrollbar-width: none;
}
.bloc[data-forme="rail"] .bloc__corps::-webkit-scrollbar { display: none; }
.bloc[data-forme="grille"] .bloc__corps { display: flex; flex-wrap: wrap; gap: 8px;
  padding: 8px var(--marge) 0; }

/* Une recette en ligne. Le séparateur démarre APRÈS la vignette, jamais au
   bord de l'écran : au bord, c'est un tableau. */
.plat {
  position: relative; display: grid;
  grid-template-columns: 40px 1fr auto; column-gap: 12px;
  align-items: center; min-height: var(--rang); padding: 10px 0;
  width: 100%; text-align: left;
}
.plat + .plat::before { content: ""; position: absolute; top: 0; left: 52px; right: 0;
  height: 1px; background: var(--trait); }
/* Le monogramme. Un disque à 12 % de la teinte, l'initiale à 100 %. La
   couleur reste porteuse de la provenance et cesse d'être une tache. */
.plat__pastille {
  grid-row: 1 / span 2; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--teinte);
  color: var(--teinte-encre);
  font: 500 16px/1 var(--f-titre); letter-spacing: 0;
}
.plat__nom { font: 500 17px/1.3 var(--f-ui); }
.plat__qui { grid-column: 2; font: 500 13px/1.3 var(--f-ui); color: var(--teinte-mot); }
.plat__qui:empty { display: none; }
.plat__t { grid-column: 3; grid-row: 1 / span 2; }
.plat__t:empty { display: none; }

.bloc[data-forme="grille"] .plat {
  display: inline-flex; align-items: center; gap: 8px; width: auto;
  min-height: var(--touche); padding: 0 15px; grid-template-columns: none;
  border: 1px solid var(--bord); border-radius: var(--r-pilule);
}
.bloc[data-forme="grille"] .plat::before { display: none; }
.bloc[data-forme="grille"] .plat__pastille { display: none; }
.bloc[data-forme="grille"] .plat__nom { font-size: 15px; }

/* Une vignette. Là où une application de cuisine pose une photographie, Mijote
   pose l'aplat de la personne, À LA MÊME PLACE ET À LA MÊME TAILLE.

   Cette phrase est dans DESIGN.md depuis le début, et la feuille ne l'appliquait
   pas : elle teintait le fond DERRIÈRE le texte, ce qui n'est pas la même
   figure. Les huit maquettes de référence relevées sur Figma — Recipely,
   Recipeo, Foodify, DishDash et les autres — portent toutes le même objet, sans
   une exception : un BLOC en haut de la carte, le texte en dessous sur du
   blanc. C'est la forme du genre, et elle ne tient pas à la photo : elle tient
   à la séparation des deux zones. Mijote peut donc l'avoir sans photo. */
.carte {
  flex: 0 0 160px; scroll-snap-align: start;
  display: flex; flex-direction: column;
  border-radius: var(--r-carte); overflow: hidden;
  background: var(--carte); color: var(--encre);
  box-shadow: var(--ombre-1);
}
/* ================================================================ LA UNE
   Une couverture, pas un sommaire. Un seul sujet, sur la nuit, qui occupe le
   haut de l'écran — c'est le remède aux deux reproches tenus par Félix :
   « c'est trop clair, trop sage » et « ça reste une liste de données ».

   Le fond sombre règle le premier. Le second se règle par l'ÉCART D'ÉCHELLE :
   la phrase que quelqu'un a dite est plus grande que tout le reste de l'écran,
   et le nom du plat lui passe après. Un écran où tout fait la même taille n'a
   rien à retenir, quelle que soit sa palette. */
/* ================================================================ LE BENTO
   « Chaque chose trouve sa place » : le haut de l'accueil est une mosaïque.
   La UNE est la grande case — la parole sur la teinte de la personne — et
   les petites cases logent chacune UNE chose : un plat à voir, « ce soir »,
   un tiroir, les livres. Deux colonnes, comme la grille des rubriques :
   le bento n'est pas un autre système, c'est la même grille, densifiée. */
.bento { display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  padding: 10px var(--marge) 4px; }
/* UNE SEULE ANATOMIE, celle des cartes de recettes : la zone en haut
   (image, gravure ou picto — même boîte), puis étiquette, nom, badge.
   « Qu'importe ce qu'on met dedans, c'est toujours une recette au final. » */
/* ⚠️ HUIT PIXELS D'AIR SOUS LA MOSAÏQUE. Une case affleurait la barre
   d'onglets à six pixels : deux zones tactiles qui se touchent, et le doigt
   qui vise l'une déclenche l'autre (14.1). */
.bento { margin-bottom: 10px; }
.bento__case { display: flex; flex-direction: column; align-items: flex-start;
  justify-content: flex-end; gap: 4px;
  min-height: 128px; padding: 0 0 13px; overflow: hidden;
  border-radius: var(--r-carte); background: var(--carte); color: var(--encre);
  box-shadow: var(--ombre-1); text-align: left;
  transition: transform .16s ease, box-shadow .16s ease; }
.bento__case .bento__etiq { padding: 2px 14px 0; }
.bento__case .bento__nom { padding: 0 14px; }
.bento__case .badge { margin-left: 14px; margin-right: 14px; }
.bento__case:active { transform: scale(.985); box-shadow: none; }
.bento__nom { font: 500 16px/1.2 var(--f-titre);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.bento__quoi { font: 400 13px/1.3 var(--f-ui); color: var(--encre-3);
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
/* L'étiquette de nature : elle dit ce que la case EST, avant son nom.
   « Un tiroir », « À relier » — le grand chiffre seul ne le disait pas. */
.bento__etiq { display: inline-flex; align-items: center; gap: 6px;
  font: 600 12px/1.2 var(--f-ui); letter-spacing: .09em;
  text-transform: uppercase; color: var(--sauge); }
.bento__case .badge { margin-top: 3px; }

/* ⚠️ MÊME HAUTEUR POUR LA PHOTO ET POUR LA GRAVURE. Deux règles écrites à
   des moments différents leur donnaient 118 px et 88 px : côte à côte dans la
   mosaïque, la case dessinée paraissait tassée sous la case photographiée
   (« adapte les hauteurs des images dans les cards », Félix). Une seule
   variable les tient maintenant toutes les deux — c'est la même place dans la
   même grille, elle ne peut pas avoir deux valeurs. */
.bento { --vue-h: 118px; }
.bento__vue { display: grid; place-items: center; align-self: stretch; height: var(--vue-h);
  background: var(--bande); color: var(--sauge); }
.bento__vue img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ⚠️ ET LE SÉLECTEUR VISAIT À CÔTÉ. Les deux cases du haut — la dernière
   arrivée et celle du soir — ne sont PAS des `.bento__case` : ce sont des
   `.carte`, parce qu'elles montrent de vraies recettes (voir vues.mjs). La
   règle ne les touchait donc jamais, et j'ai corrigé deux fois autre chose en
   croyant régler la hauteur. On cible le bento lui-même, quelle que soit la
   nature de la case qu'il contient. */
.bento .carte__dit,
.bento .carte__dit[data-image="photo"] img { height: var(--vue-h); }
/* L'image remplit sa zone sans se déformer : la hauteur est imposée, le
   cadrage s'ajuste. */
.bento .carte__dit[data-image="photo"] img { object-fit: cover; width: 100%; }

/* La case du soir est ORANGE : c'est l'invitation à cuisiner du jour, et
   dans la logique de l'app l'orange porte les gestes de cuisine. Le vert
   clair la faisait ressembler à une case d'information — elle ne ressortait
   pas. L'encre écrit dessus (5,08:1), comme sur tous les feux. */
/* LA CASE DU SOIR : le LISERÉ de feu et l'étiquette orange — le reste est
   une carte de recette comme les autres. Seul liseré épais de l'app. */
/* ⚠️ LE LISERÉ DE MISE EN AVANT NE DOIT PAS DÉCALER LE CONTENU. En `border`,
   ses deux pixels rentraient dans la boîte : la zone visuelle de la case du
   soir commençait deux pixels plus bas et finissait deux pixels plus haut que
   celle de sa voisine — d'où « toujours pas à la même hauteur » alors que les
   deux mesuraient bien 118 px. En `outline` posé vers l'intérieur, le trait
   se dessine PAR-DESSUS sans prendre de place, et les images s'alignent. */
.bento__case[data-role="soir"] { outline: 2px solid var(--feu); outline-offset: -2px; }
.bento__case[data-role="soir"] .bento__etiq { color: var(--feu-texte); }

/* LA UNE : la grande case de la mosaïque, sur la teinte de la personne.
   overflow: clip — le recul de parallaxe déplaçait la phrase par-dessus la
   suite pendant le défilement. */
/* La grande case est CLAIRE elle aussi : la dernière dalle de couleur est
   tombée avec les autres. La parole est en noir d'affiche, la salutation en
   vert, la signature à la couleur de la personne — un trait, pas une dalle. */
.une {
  grid-column: 1 / -1;
  position: relative; width: 100%; text-align: left;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 20px 20px 22px;
  border-radius: var(--r-carte);
  background: var(--carte); color: var(--encre);
  box-shadow: var(--ombre-1);
  min-height: 230px; overflow: clip; cursor: pointer;
}
.une__sur { margin-bottom: auto; padding-bottom: 18px;
  font: 600 12px/1.2 var(--f-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--sauge); }
.une__phrase {
  /* Le vrai italique de Playfair — celui pour lequel elle a été choisie. */
  font: italic 500 22px/1.3 var(--f-voix);
  display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden;
}
/* La gravure de couverture : le visage de repli quand personne n'a parlé et
   qu'aucune photo n'existe — un grand blanc n'est pas un visage (panne-07). */
.une__vue { display: flex; justify-content: center; padding: 6px 0 2px; color: var(--sauge); }
.une__vue svg { width: 96px; height: 96px; }
.une__plat { margin-top: 14px; font: 500 19px/1.2 var(--f-titre); }
.une__signe { margin-top: 5px; font: 500 12px/1.2 var(--f-ui);
  letter-spacing: .09em; text-transform: uppercase; color: var(--teinte-mot); }

/* CE QUE LA PERSONNE A DIT, et c'est là que se joue toute la carte.

   Il y avait ici un « visage » : un aplat pastel de 96px portant l'initiale du
   plat, à l'endroit exact où les applications concurrentes posent la photo.
   C'était la faute de fond — compenser l'absence de photographie par un faux
   emplacement de photographie. Une lettre dans un rectangle coloré est un
   avatar de secours, et ça se voit.

   La citation prend cette place, et elle est plus grande que le titre du plat.
   Le rapport de taille EST l'argument : ce qu'on retient d'une recette de
   famille, ce n'est pas son nom, c'est la façon dont elle a été dite. */
/* L'image de la carte est ENCHÂSSÉE : une marge de 8px et son propre rayon,
   comme dans les cartes du modèle. La zone porte la teinte très claire de la
   personne — assez pour dire la provenance, jamais assez pour salir le blanc. */
/* La zone d'image de la carte : photo ou gravure, JAMAIS un paragraphe —
   les pavés de texte dans les cartes ont été refusés. Un souvenir se
   signale par les guillemets du coin. */
/* ⚠️ FINI LES APLATS DE PROVENANCE. « Gris, orange, pastel… pourquoi tant
   d'aplats de couleur ? » (Félix) : sept cartes, sept dalles, l'accueil
   ressemblait à un nuancier. La zone d'image est NEUTRE et identique
   partout ; c'est l'ICÔNE qui porte la couleur de la personne, et le badge
   dessous qui porte son nom. La couleur redevient un trait, pas une dalle. */
/* PLEIN BORD, décision de Félix après essai des deux : l'image touche les
   bords de la carte, dont l'overflow arrondit les coins hauts. La marge
   enchâssée a été essayée puis retirée le 08/08 au soir. */
.carte__dit {
  position: relative; display: grid; place-items: center;
  overflow: hidden; min-height: 96px;
  background: var(--bande); color: var(--sauge);
}
.carte__histoire { position: absolute; top: 4px; right: 10px;
  font: italic 500 19px/1 var(--f-voix); letter-spacing: -.35em; opacity: .85;
  color: var(--encre-3); }
.carte__dit[data-image="photo"] .carte__histoire {
  color: var(--blanc); text-shadow: 0 1px 4px rgba(0,0,0,.55); opacity: 1; }
/* Sans phrase, la gravure de la catégorie tient l'image — c'est la doctrine
   du projet appliquée à sa plus petite surface. À l'encre de la personne,
   posée sur la trame comme une estampe. */
/* Le contrat interdit les classes modificatrices : c'est la donnée qui parle.
   Le test l'a rappelé au moment où j'allais l'oublier. */
.carte__dit .gr { opacity: .8; }
/* L'étiquette de rôle d'une carte du bento : même voix que les étiquettes
   de cases. Sur la carte du soir, elle prend le feu. */
.carte__etiq { padding: 10px 14px 0; font: 600 12px/1.2 var(--f-ui);
  letter-spacing: .09em; text-transform: uppercase; color: var(--sauge); }
.carte[data-role="soir"] { border: 2px solid var(--feu); }
.carte[data-role="soir"] .carte__etiq { color: var(--feu-texte); }
.carte__etiq + .carte__nom { padding-top: 2px; }
.carte__nom { padding: 12px 14px 0; font: 500 16px/1.2 var(--f-titre); letter-spacing: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* UNE seule ligne d'infos sous la carte : la durée garde sa place, le nom
   de la personne s'ellipse. Deux lignes de badges faisaient tanguer la
   grille. display inline-block sur le badge : un conteneur flex ne sait
   pas ellipser son propre texte. */
.carte__bas { display: flex; flex-wrap: nowrap; gap: 5px; overflow: hidden;
  padding: 8px 14px 14px; margin-top: auto;
  font: 500 12px/1.3 var(--f-ui); color: var(--encre-3); }
.carte__bas .badge { display: inline-block; line-height: 22px;
  white-space: nowrap; }
.carte__bas .badge + .badge { min-width: 0; overflow: hidden;
  text-overflow: ellipsis; }
.carte__bas .badge:first-child { flex: 0 0 auto; }

/* ============================================================= 5. LA FICHE */
/* UN APLAT FRANC, PAS UN DÉGRADÉ ATMOSPHÉRIQUE.

   J'ai essayé la nappe radiale des maquettes de référence : deux foyers de
   lumière dans la teinte. Recoloré, ce geste ne dit plus rien de la cuisine —
   il dit la publicité de cosmétique. Il appartenait à une PHOTOGRAPHIE de pain
   safrané, et sans elle il ne reste que l'effet.

   La langue de ce projet est ailleurs, et elle est écrite depuis le début : la
   couleur est une ENCRE IMPRIMÉE. Une encre ne rayonne pas, elle couvre. Donc
   un aplat franc, généreux, d'un bord à l'autre, sans liseré — ce qui manquait
   n'était pas de la lumière, c'était de la SURFACE. */
/* Sous le contrat clair, la tête respire sur le fond cassé ; la teinte de la
   personne ne couvre plus la page, elle ÉCRIT (l'attribution, la gravure). */
.fiche__tete {
  padding: 30px var(--marge) 46px; min-height: 168px;
  display: flex; flex-direction: column; justify-content: flex-end;
  background: var(--blanc); color: var(--encre);
}
/* Le folio ne surmonte PLUS le titre : une étiquette en capitales au-dessus
   d'un titre est un « kicker », et le plancher de qualité le bannit sans
   rachat possible. Le titre porte son propre poids. La catégorie rejoint la
   ligne de provenance, en bas, où elle est une information parmi d'autres. */
/* L'attribution, au-dessus du titre. Italique de la famille éditoriale, comme
   en tête d'un chapitre de livre. */
.fiche__de { font: italic 400 22px/1.2 var(--f-voix); color: var(--teinte-mot); }
.fiche__lien { font-style: normal; font-size: 15px; opacity: .8; }
/* Le clamp montait à 36px, un corps qui n'est PAS sur l'échelle déclarée : sur
   un large écran il inventait un neuvième degré, sans qu'aucun réglage ne le
   dise. Une échelle qu'une interpolation peut quitter n'est pas une échelle. */
.fiche__titre { margin-top: 6px; font: 600 clamp(28px, 8.4vw, 34px)/1.08 var(--f-titre);
  letter-spacing: -.004em; }
/* Ce qui SITUE le plat — catégorie, famille, région — descend sous le titre en
   étiquette discrète. C'est du classement, et le classement ne se met pas au
   même rang qu'un nom propre. */
.fiche__qui { margin-top: 12px; font: 600 12px/1.2 var(--f-ui);
  letter-spacing: .06em; text-transform: uppercase; color: var(--teinte-mot); }

/* LA FEUILLE QUI MONTE. Recipely et Recipeo posent exactement le même geste :
   le contenu remonte par-dessus la tête, coin supérieur arrondi, avec une
   petite barre de préhension au milieu. C'est la signature de forme du genre,
   celle qu'on reconnaît avant d'avoir lu un mot — et la seule qui ne coûte pas
   une photographie. Le trait de séparation disparaît : c'est le recouvrement
   qui sépare, un filet EN PLUS serait la ceinture et les bretelles. */
/* La feuille descend JUSQU'EN BAS. Sans cette hauteur, une recette encore à
   l'état de fragment — un carnet scanné dont rien n'est transcrit, ce qui sera
   le cas courant pendant des mois — laissait apparaître le fond de l'écran
   sous une feuille de six lignes : ça ne se lit pas comme une recette vide,
   ça se lit comme un rendu interrompu. */
.fiche { display: flex; flex-direction: column; min-height: 100%; }
/* LA COULEUR EST LE PAPIER, PAS UN BANDEAU.

   C'était le vrai défaut d'identité : la teinte d'une personne occupait 120px
   en haut d'écran et tout le reste retombait sur le même crème que les
   soixante-trois autres recettes. Une couleur qui ne tient qu'un bandeau est une
   étiquette de classement ; ce projet ne classe pas des recettes, il garde des
   gens.

   Donc la feuille elle-même est teintée — un lavis très clair de la même
   encre. On feuillette la collection et on voit passer les personnes. Le
   cassoulet de Mamie Yoyo n'est pas « une recette avec un bandeau vert », c'est
   une page verte.

   Le lavis reste à 20 % : au-delà, l'encre noire descend sous 15:1 et les
   filets de la marge machine cessent de se voir. */
/* La feuille est une CARTE BLANCHE qui remonte sur le fond cassé — la
   signature de forme du genre, dans la matière du contrat. Le lavis teinté
   pleine page est enterré avec le registre imprimé : la provenance se dit
   par l'attribution, l'exergue et les encres, plus par le papier. */
.fiche__corps { flex: 1; position: relative; margin-top: -20px;
  padding: 34px var(--marge) 40px;
  background: var(--carte);
  border-radius: var(--r-carte) var(--r-carte) 0 0;
  box-shadow: 0 -8px 30px -22px rgba(17,20,18,.18); }
/* PAS DE BARRE DE PRÉHENSION. Recipely et Recipeo en posent une, mais chez eux
   la feuille se tire vraiment au doigt. Ici elle ne bouge pas : une poignée qui
   annonce un geste qui n'existe pas est un mensonge d'interface, et deux objets
   qui se chevauchent sur le même bord font du bruit. C'est le cartouche de
   mesures qui tient ce bord, et lui porte une information. */
/* LE CARTOUCHE DE MESURES. Il remonte de 46px pour chevaucher la limite entre
   la tête et la feuille : c'est ce chevauchement qui le sort du flux du texte
   et en fait une fiche technique. Relevé dans mobile-design-kit (MIT), où il
   porte le temps, les parts et les calories à cheval sur la photo.
   Les colonnes sont séparées par un filet et non par un fond : quatre boîtes
   grises côte à côte forment un tableau de bord, ce qui est exactement ce
   qu'on essaie de ne pas être. */
.fiche__meta { display: flex; margin: -46px 0 24px; padding: 14px 4px;
  background: var(--carte);
  border-radius: 20px;
  box-shadow: var(--ombre-2); }
/* UNE SEULE MESURE NE S'ÉTALE PAS. Sur une recette encore fragmentaire, seule
   la durée est connue : le cartouche pleine largeur portant un chiffre au
   milieu se lit comme un rendu interrompu. Il se réduit alors à son contenu. */
.fiche__meta:has(.mesure:only-child) { width: max-content; padding: 12px 20px; }
.mesure { flex: 1; min-width: 0; padding: 0 10px; text-align: center; }
.mesure + .mesure { border-left: 1px solid var(--trait); }
/* LE CHIFFRE EN TÊTE : c'est un repère de coup d'œil, casserole en main. */
.mesure__n { display: block; font: 500 19px/1.2 var(--f-titre);
  font-variant-numeric: tabular-nums; }
/* La formulation d'origine dessous, à la voix humaine. Elle peut manquer — la
   cellule reste émise pour que les trois colonnes gardent la même hauteur. */
.mesure__dit { display: block; margin-top: 3px;
  font: 400 13px/1.3 var(--f-voix); color: var(--encre-3); }
.mesure__dit:empty::before { content: "\00a0"; }
/* Une section s'annonce par un TIRET SAUGE court au-dessus du titre serif —
   le geste éditorial du registre clair, à la place des filets d'imprimeur. */
.fiche__section { margin: 26px 0 10px;
  font: 500 22px/1.15 var(--f-titre); letter-spacing: -.004em; }
.fiche__section::before { content: ""; display: block; width: 28px; height: 3px;
  border-radius: 2px; background: var(--sauge); margin-bottom: 10px; }
.fiche__corps > .fiche__section:first-child { margin-top: 0; }

/* LA MARGE MACHINE. À gauche ce qu'un humain a écrit, à droite ce que la
   machine a lu. LA CELLULE DE DROITE EST TOUJOURS ÉMISE, MÊME VIDE : sans
   elle le filet vertical se brise et toute la figure s'effondre. */
/* Une colonne de plus pour la gravure de l'ingrédient. Elle est toujours
   présente, même vide, sinon les lignes non reconnues se décalent et la
   colonne des paroles cesse d'en être une. */
.parole { display: grid; grid-template-columns: 20px 1fr 74px; column-gap: 12px;
  align-items: baseline; padding: 9px 0; border-bottom: 1px solid var(--trait); }
/* Les icônes sont VERTES, partout — « pourquoi icône marron ? » : le cran
   « mot » de certaines personnes est un brun, et une icône qui change de
   couleur selon la fiche se lisait comme un accident. La provenance se dit
   par le texte ; l'icône parle la langue du menu rapide. */
.parole__gr { display: grid; place-items: center; padding-top: 2px;
  color: var(--sauge); opacity: .85; }
.parole__dit { font: 400 17px/1.45 var(--f-voix); }
.parole__calcule { font: 500 13px/1.4 var(--f-ui); color: var(--encre-3);
  text-align: right; font-variant-numeric: tabular-nums; }
.ingredients__groupe { margin: 16px 0 4px;
  font: 600 12px/1 var(--f-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--encre-3); }

.etapes { counter-reset: etape; }
.etape { counter-increment: etape; display: grid; grid-template-columns: 34px 1fr;
  column-gap: 12px; padding: 10px 0; }
.etape::before { content: counter(etape); display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--sauge-clair); color: var(--sauge);
  font: 600 15px/1 var(--f-ui); }
.etape p { font: 400 17px/1.55 var(--f-ui); }
/* L'étape se COCHE au doigt : le bouton est la ligne entière, le numéro
   devient une coche verte, le texte s'éteint sans se barrer — on doit
   pouvoir le relire, on cuisine. */
.etape__coche { display: block; width: 100%; padding: 0; text-align: left;
  color: inherit; font: inherit; }
.etape[data-faite]::before { content: "✓"; background: var(--sauge); color: var(--blanc); }
.etape[data-faite] p { color: var(--encre-3); }

/* Aux fourneaux : l'étape d'après, en petit — on anticipe la casserole
   suivante sans quitter celle-ci. */
/* « Ensuite » tient sur UNE ligne : c'est un coup d'œil, pas une lecture. */
.cuisson__suite { margin-bottom: 12px; font: 400 15px/1.45 var(--f-ui);
  color: var(--encre-3);
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
/* LE CONSEIL : à la voix, sur le papier teinté du feu — c'est quelqu'un qui
   parle par-dessus l'épaule, pas une consigne de plus.
   ⚠️ PAS DE FILET DE COULEUR À GAUCHE : le détecteur d'Impeccable nomme ça
   « side-tab » et le décrit comme le tic le plus reconnaissable des
   interfaces générées ; DESIGN.md l'interdit depuis l'exergue. C'est la
   COMPOSITION qui marque — le fond chaud, l'italique, la petite étiquette. */
/* LE CONSEIL : une carte CERCLÉE D'ORANGE, comme « Ce soir » dans le bento
   — c'est devenu la marque de la mise en avant dans cette app, et Félix a
   demandé qu'on la reprenne ici. Jamais d'aplat pastel : la couleur est un
   trait, le fond reste blanc. */
.cuisson__astuce { margin-bottom: 12px; padding: 11px 14px;
  border-radius: var(--r-carte); background: var(--carte);
  border: 2px solid var(--feu);
  font: italic 400 16px/1.45 var(--f-voix); color: var(--encre); }
.cuisson__astuce::before { content: "Le tour de main"; display: block;
  margin-bottom: 4px; font: 600 12px/1.2 var(--f-ui); font-style: normal;
  letter-spacing: .09em; text-transform: uppercase; color: var(--feu-texte); }

/* Le souvenir est une CITATION. Elle se marque par la composition, jamais par
   un filet de couleur à gauche : le détecteur d'Impeccable nomme ça
   « side-tab » et le décrit comme le tic le plus reconnaissable des interfaces
   générées par IA. */
/* Le souvenir est sur la MÊME encre que la page, un cran plus dense : sur un
   papier teinté, un encart crème redevient une boîte rapportée. */
/* Le souvenir : dernier aplat de personne encore debout, tombé comme les
   autres (« ces couleurs-là ça va pas » sur le gris de mer). Fond neutre,
   la voix à l'encre, la signature en gris — la provenance est déjà écrite
   en toutes lettres dans la signature. */
.exergue { margin-top: 22px; padding: 17px 20px;
  background: var(--bande);
  border-radius: var(--r-carte); }
.exergue p { font: 400 italic 17px/1.5 var(--f-voix); color: var(--encre); }
/* La signature était au PASTEL, illisible sur la crème depuis que la teinte a
   deux crans. C'est l'encre qui signe. */
.exergue cite { display: block; margin-top: 10px; font: 500 13px/1.3 var(--f-ui);
  font-style: normal; color: var(--encre-3); }
.colophon { margin-top: 22px; font: 500 12px/1 var(--f-ui);
  letter-spacing: .1em; color: var(--encre-3); }

/* ========================================================== 6. LES FILTRES */
/* Le menu rapide : la rangée de gélules à icônes, un appui = un filtre.
   Posée, la gélule passe au vert plein — le vert est la couleur de la
   SÉLECTION dans tout le système. */
.rapides { display: flex; gap: 8px; padding: 10px var(--marge) 2px;
  overflow-x: auto; scrollbar-width: none; scroll-padding-left: var(--marge); }
.rapides::-webkit-scrollbar { display: none; }
.rapide { flex: 0 0 auto; display: inline-flex; align-items: center;
  justify-content: center;
  width: 52px; min-height: var(--touche); padding: 0;
  border: 1px solid var(--bord); border-radius: var(--r-pilule);
  background: var(--carte); color: var(--encre-2); }
.rapide .gr { color: var(--sauge); }
.rapide.on { background: var(--sauge); border-color: var(--sauge); color: var(--blanc); }
.rapide.on .gr { color: var(--blanc); }
/* La pilule à MOT : la même, allongée. C'est le composant des volets
   courses/réserve et du Casiers/Liste des pièces — un seul dessin. */
.rapide--mot { width: auto; padding: 0 16px; }
/* ⚠️ 15 ET NON 14 : la pilule est le composant le plus réemployé de l'app,
   et son corps propre faisait un neuvième palier dès qu'un écran audité en
   montrait une (huit maximum, COHER-4). Elle rejoint le corps des listes
   plutôt que d'imposer le sien. */
.rapide__dit { font: 500 15px/1 var(--f-ui); }
.volets { display: flex; gap: 8px; padding: 10px var(--marge) 2px; }
/* LE CHAMP QUI CHERCHE — le même partout où l'on cherche. */
.cherche { appearance: none; -webkit-appearance: none;
  flex: 1 1 auto; min-width: 0; min-height: var(--touche);
  padding: 0 14px; border: 1px solid var(--bord); border-radius: var(--r-pilule);
  background: var(--carte); font: 400 16px/1.2 var(--f-ui); color: var(--encre); }
.cherche::placeholder { color: var(--encre-3); }
/* Le compte des filtres posés, sur la gélule des curseurs. */
.rapide { position: relative; }
.rapide__n { position: absolute; top: -4px; right: -4px;
  min-width: 19px; height: 19px; padding: 0 5px;
  display: grid; place-items: center; border-radius: var(--r-pilule);
  background: var(--sauge); color: var(--blanc);
  font: 600 12px/1 var(--f-ui); font-variant-numeric: tabular-nums; }

.filtres { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 4px var(--marge) 10px; border-bottom: 1px solid var(--trait); }
.filtres__actif { min-height: var(--touche); padding: 0 15px; border-radius: var(--r-pilule);
  background: var(--sauge); color: var(--blanc); font: 500 15px/1 var(--f-ui); }
.filtres__resume { font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }
.filtres[data-poses="0"] .filtres__resume { flex: 1 1 auto; }
.filtres__muettes { flex: 1 0 100%; min-height: var(--touche); text-align: left;
  font: 400 13px/1.4 var(--f-ui); color: var(--encre-2);
  text-decoration: underline; text-underline-offset: 3px; }

.facettes { display: block; }
.facette { margin-bottom: 20px; }
.facette__valeurs { display: flex; flex-wrap: wrap; gap: 8px; }
.facette__val { display: inline-flex; align-items: center; gap: 8px;
  min-height: var(--touche); padding: 0 15px; border-radius: var(--r-pilule);
  border: 1px solid var(--bord); background: var(--blanc); color: var(--encre);
  font: 500 15px/1 var(--f-ui); }
.facette__val.on { background: var(--sauge); color: var(--blanc); border-color: var(--sauge); }
.facette__val.on .facette__n { background: rgba(255,255,255,.18); color: var(--blanc); }
.facette__val[data-type="exclusion"]::before { content: "sans "; color: var(--encre-3); }
.facette__val[data-type="seuil"]::before { content: "moins de "; color: var(--encre-3); }
.facette__val.on::before { color: rgba(255,255,255,.72); }
.facette__muettes { margin-top: 8px; font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }

.filtrables { display: flex; flex-direction: column; gap: 8px; }
.filtrable { display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  min-height: 58px; padding: 11px 15px; border-radius: var(--r-carte);
  border: 1px solid var(--bord); background: var(--blanc); color: var(--encre-2);
  text-align: left; }
.filtrable.on { background: var(--sauge); color: var(--blanc); border-color: var(--sauge); }
.filtrable__nom { font: 500 16px/1.2 var(--f-ui); }
.filtrable__n { background: transparent; padding: 0; color: inherit; opacity: .7;
  font: 400 13px/1.3 var(--f-ui); }

/* ============================================== 7. LES AUTRES ÉCRANS
   Recherche, formulaire, livres. Ce sont les trois qui n'avaient jamais été
   repris et qui rendaient l'application « éclatée » : ils vivaient sur une
   feuille écrite six mois plus tôt, avec leurs propres boutons, leurs propres
   titres et leur propre famille de caractères. */
.recherche-haut { padding: 12px var(--marge); border-bottom: 1px solid var(--trait); }
.recherche { padding: 0; }
.resultats-info { padding: 12px var(--marge) 4px;
  font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }
.resultats { padding: 0 var(--marge); }
.resultats .ligne { position: relative; display: grid;
  grid-template-columns: 1fr auto; column-gap: 12px; align-items: center;
  width: 100%; min-height: var(--rang); padding: 11px 0; text-align: left; }
.resultats .ligne + .ligne::before { content: ""; position: absolute; top: 0; left: 0; right: 0;
  height: 1px; background: var(--trait); }
.ligne__rang { display: none; }
.ligne__titre { font: 500 17px/1.3 var(--f-ui); }
.ligne__machine { font: 500 13px/1.3 var(--f-ui); color: var(--encre-3); }
.ligne__droite { grid-row: 1 / span 2; }
.pastille { display: block; width: 12px; height: 12px; border-radius: 50%;
  background: var(--p2); box-shadow: inset -3px 0 0 var(--p2-encre); }

.entete { padding: 17px var(--marge) 6px; }
/* Deux paliers nets, pas un corps fluide : le 8vw fabriquait des corps
   hors échelle (30,1 px à 376 de large — revue du 12/08). */
.entete__titre { font: 600 28px/1.06 var(--f-titre); letter-spacing: -.004em; }
.entete__titre b { font-weight: 600; }
@media (min-width: 430px) { .entete__titre { font-size: 34px; } }
.entete__sous { margin-top: 8px; font: 400 15px/1.45 var(--f-ui); color: var(--encre-2); }

.form { padding: 16px var(--marge) 40px; }
.form__titre { margin-bottom: 16px;
  font: 500 22px/1.1 var(--f-titre); letter-spacing: -.004em;
  overflow-wrap: anywhere; }
.fiche-bloc { margin-bottom: 22px; }
.duo { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
/* align-items: end alignait le BAS des deux colonnes, donc un libellé machine
   qui passait à trois lignes remontait par-dessus la paire précédente. Les
   deux colonnes s'alignent maintenant par le haut, et le libellé court ne
   revient plus à la ligne. */
.paire-m { display: grid; grid-template-columns: 1fr 92px; gap: 10px; align-items: start; }
.paire-m__dit { min-width: 0; }
.paire-m__machine .champ-nom { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.paire-m__machine .champ { font-size: 15px; }
.aide { margin-top: 6px; font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }
.form__etat { margin-top: 12px; font: 400 15px/1.4 var(--f-ui); color: var(--encre-2); }
.form__pied { display: flex; gap: 10px; margin-top: 22px; }
.form__pied button { flex: 1; }
.form__supprimer { margin-top: 16px; width: 100%; min-height: 48px;
  border-radius: var(--r-bouton); border: 1px solid var(--feu); color: #8F5307;
  font: 500 15px/1 var(--f-ui); background: var(--carte); }
.etiq-liste { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
/* ⚠️ .etiq-m N'AVAIT AUCUNE RÈGLE. Les étiquettes s'affichaient en texte nu,
   sans cadre ni cible : rien ne disait qu'on pouvait les toucher, et rien ne
   montrait celles qui étaient posées. Elles reprennent l'allure de la gélule
   de filtre, qui fait déjà exactement ce travail ailleurs. */
.etiq-m { display: inline-flex; align-items: center;
  min-height: var(--touche); padding: 0 15px; border-radius: var(--r-pilule);
  border: 1px solid var(--bord); background: var(--blanc); color: var(--encre);
  font: 500 15px/1 var(--f-ui); }
.etiq-m.on { background: var(--sauge); color: var(--blanc); border-color: var(--sauge); }

.livres { padding: 0 var(--marge); display: grid; gap: 10px; }
.livre-carte { display: grid; grid-template-columns: 52px 1fr auto; gap: 12px;
  align-items: center; padding: 12px; text-align: left;
  border: 0; border-radius: var(--r-carte); background: var(--carte);
  box-shadow: var(--ombre-1); }
/* ⚠️ LE BLOC DE COUVERTURE ÉTAIT UN ::before, donc rien ne pouvait vivre
   dedans : la gravure se posait à côté de lui, et la carte montrait deux
   objets là où il en fallait un. C'est .couverture qui EST le bloc. */
.livre-carte .couverture { width: 52px; height: 68px; border-radius: 6px;
  background: color-mix(in srgb, var(--encre-perso, var(--p2)) 22%, var(--blanc));
  box-shadow: inset -3px 0 0 color-mix(in srgb, var(--encre-perso, var(--p2-encre)) 55%, transparent);
  color: color-mix(in srgb, var(--encre-perso, var(--p2-encre)) 88%, var(--encre)); }
.livre-carte__titre { font: 500 17px/1.2 var(--f-titre); letter-spacing: 0; }
.livre-carte__source { font: 500 12px/1.3 var(--f-ui); letter-spacing: .05em;
  text-transform: uppercase; color: var(--encre-3); }
.nouveau-livre { margin: 15px var(--marge) 0; width: calc(100% - var(--marge) * 2); }
.livre-intro { padding: 22px var(--marge) 0;
  font: 400 15px/1.55 var(--f-ui); color: var(--encre-2); }
.bandeau-livre { margin: 22px var(--marge) 0; padding: 17px;
  background: var(--creme); border-radius: var(--r-carte); }
.bandeau-livre h3 { font: 500 17px/1.2 var(--f-titre); }
.bandeau-livre p { margin: 8px 0 15px; font: 400 15px/1.5 var(--f-ui); color: var(--encre-2); }
.planches { padding: 15px var(--marge) 0; display: grid; gap: 12px; }
.planches .planche { border: 1px solid var(--trait); border-radius: var(--r-carte); overflow: hidden; }
.planches iframe { display: block; border: 0; width: 100%; }
.rubans { display: flex; gap: 8px; padding: 0 var(--marge); }

/* La feuille d'ajout : trois choix, chacun une ligne de liste. */
.collerlien { display: grid; grid-template-columns: 1fr auto; gap: 6px 10px; align-items: center;
  margin-bottom: 6px; }
/* ⚠️ La grille a DEUX colonnes et TROIS enfants : sans cette rangée pleine
   pour l'étiquette, le champ montait à côté d'elle et le bouton retombait
   dessous — le quinconce relevé par les deux agents de la nuit. */
.collerlien .champ__etiquette { grid-column: 1 / -1; }
.choix { display: grid; grid-template-columns: 44px 1fr; gap: 15px; align-items: center;
  width: 100%; min-height: 64px; padding: 12px 0; text-align: left;
  border-bottom: 1px solid var(--trait); }
.choix:last-child { border-bottom: 0; }
.choix__icone { display: grid; place-items: center; width: 44px; height: 44px;
  border-radius: 12px; background: var(--creme); }
.choix__icone svg { width: 22px; height: 22px; fill: none; stroke: var(--encre);
  stroke-width: 1.8; }
.choix__titre { font: 500 17px/1.25 var(--f-ui); }
.choix__desc { margin-top: 3px; font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }
.source-liste { display: grid; gap: 8px; }
.source { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center;
  width: 100%; min-height: 56px; padding: 10px 15px; text-align: left;
  border: 1px solid var(--bord); border-radius: var(--r-carte); background: var(--blanc); }
.source__titre { font: 500 16px/1.2 var(--f-ui); }
.source__type { font: 500 12px/1.3 var(--f-ui); letter-spacing: .05em;
  text-transform: uppercase; color: var(--encre-3); }

/* ============================================================= 8. LES ÉTATS */
.vide-doux { padding: 40px var(--marge); text-align: center; }
.vide-doux h3 { font: 500 22px/1.15 var(--f-titre); letter-spacing: -.004em; }
.vide-doux p { margin-top: 8px; font: 400 15px/1.5 var(--f-ui); color: var(--encre-2); }
.selectionnable { -webkit-user-select: text; user-select: text; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
}

/* ============================================================ 8. LES GRAVURES
   Le jeu dessiné pour le projet (mobile/js/gravures.mjs). Elles héritent
   toutes de currentColor, donc chacune prend la couleur de l'endroit où elle
   se trouve — la teinte de la personne sur une fiche, l'encre sur un chapitre,
   le clair sur la nuit. Aucune règle de couleur ici : c'est la cascade qui
   décide, et c'est ce qui empêche le jeu de se figer en douze fichiers de
   couleurs différentes. */
.gr { display: block; flex: 0 0 auto; }

/* En tête de chapitre, elle est dans la ligne de titre. vertical-align au lieu
   d'un flex sur .bloc__titre : le titre est un h2 qui peut passer à la ligne,
   et un flex l'empêcherait de le faire proprement. */
.bloc__titre .gr { display: inline-block; vertical-align: -3px; margin-right: 8px;
  color: var(--encre-2); }

/* Dans le cartouche, au-dessus du chiffre. */
.mesure .gr { margin: 0 auto 7px; color: var(--sauge); }

/* SUR LA FICHE, C'EST L'IMAGE. Elle occupe la place que les applications avec
   photo réservent au plat, en haut à droite de la tête, à l'encre de la
   provenance. Les hachures ne se déclenchent qu'à partir de 64 px : ici elle
   est appelée en 96, donc elle est gravée et non pictogrammée. */
/* ⚠️ LA GRAVURE RÉSERVE SA PLACE. Posée en absolu sans réserve, elle percutait
   les titres longs — « Couscous du vendredi » lui passait dessous. Le
   remplissage à droite n'est appliqué QUE si la tête porte une gravure, sinon
   tous les titres se retrouvent bridés pour rien. */
.fiche__tete { position: relative; }
.fiche__tete:has(> .gr) { padding-right: calc(var(--marge) + 104px); }
.fiche__tete > .gr { position: absolute; top: 26px; right: var(--marge);
  color: var(--sauge); opacity: .55; }

/* La gravure sur une gélule de filtre. gap est déjà posé par .facette__val, on
   ne remet pas de marge : deux espacements qui s'additionnent décalent le
   compte à droite et cassent l'alignement des gélules entre elles. */
.facette__val .gr { opacity: .85; }

/* La légende d'un bloc de formulaire porte la gravure de ce qu'elle demande.
   C'est l'écran qui n'avait JAMAIS été repris par la direction artistique :
   des champs gris, des libellés en capitales, aucune couleur, rien du reste de
   l'application. */
.fiche-bloc__legende { display: flex; align-items: center; gap: 8px; }
.fiche-bloc__legende .gr { color: var(--encre-3); }

/* LA COUVERTURE D'UN LIVRE porte une gravure, plus une initiale. Une lettre
   seule dans un rectangle de couleur est l'avatar de secours retiré des cartes
   de recette ; il n'y avait aucune raison de le garder ici. */
.livre-carte .couverture { display: grid; place-items: center; }
/* L'initiale reste dans le balisage pour les lecteurs d'écran, elle ne se
   voit plus : c'est la gravure qui parle. */
.livre-carte .couverture > span { display: none; }

/* ============================================================ 9. LES MÉDIAS
   Une recette peut enfin porter des photos et des vidéos. Le fichier d'origine
   est gardé octet pour octet dans originaux/ ; ce qui s'affiche ici est une
   vignette régénérable. */
.medias { display: grid; grid-template-columns: repeat(auto-fill, minmax(94px, 1fr));
  gap: 8px; margin-bottom: 12px; }
.medias:empty { margin-bottom: 0; }
.medias[aria-busy="true"] { opacity: .5; }
.media { position: relative; margin: 0; aspect-ratio: 1;
  border-radius: var(--r-champ); overflow: hidden; background: var(--creme); }
.media img, .media video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* La croix de retrait fait 44px de cible même si son disque n'en fait que 26 :
   la zone sensible et le dessin n'ont pas à avoir la même taille. */
.media__ote {
  position: absolute; top: 0; right: 0; width: var(--touche); height: var(--touche);
  display: grid; place-items: center; font: 500 20px/1 var(--f-ui);
  color: var(--blanc); text-shadow: 0 1px 3px rgba(23,21,15,.6);
}
.media__ote::before { content: ""; position: absolute; width: 26px; height: 26px;
  border-radius: 50%; background: rgba(23,21,15,.55); }
.media__ote { z-index: 1; }

/* Le bloc du souvenir, remonté juste après le classement. Il était en bas, après
   les étapes, donc invisible — alors que c'est la phrase que l'application met
   en couverture et sur les vignettes. */
.fiche-bloc[data-role="souvenir"] textarea { font: 400 17px/1.5 var(--f-voix); }

/* ================================================ 10. LA SAISIE DES MESURES
   UN champ par mesure, pas deux. On y écrit ce qu'on veut — « 45 min » ou
   « trois quarts d'heure » — et la machine rend compte de ce qu'elle a lu. */
.mesure-champ + .mesure-champ { margin-top: 14px; }
.mesure-champ__lu { margin-top: 5px; font: 500 13px/1.2 var(--f-ui);
  color: var(--encre-3); font-variant-numeric: tabular-nums; }
.mesure-champ__lu:empty { display: none; }
/* Le champ de secours n'apparaît que si la lecture a échoué : le montrer en
   permanence réclamerait une saisie inutile neuf fois sur dix. */
.mesure-champ__secours { margin-top: 9px; padding-left: 12px;
  border-left: 2px solid var(--trait); }
.mesure-champ__secours-ligne { display: flex; align-items: center; gap: 9px; }
.mesure-champ__secours-ligne .champ { width: 96px; }
.mesure-champ__secours-ligne span { font: 500 13px/1 var(--f-ui); color: var(--encre-3); }

/* ======================================= 11. LES INGRÉDIENTS, EN COLONNES
   Trois cases par ligne : quantité, unité, ingrédient. Une seule ligne de
   texte obligeait à taper « 400 g de haricots » d'un bloc et à faire confiance
   à une lecture qu'on ne voyait pas.

   Les deux premières colonnes sont ÉTROITES et fixes, la troisième prend le
   reste : sur 390px utiles il reste environ 175px pour l'ingrédient, ce qui
   suffit à lire le début et à faire défiler la suite. Un champ trop court se
   remplit quand même ; trois colonnes qui se partagent l'espace à parts égales
   rendent la quantité aussi large que le nom, ce qui est absurde. */
.lignes { display: grid; gap: 7px; margin-bottom: 12px; }
.ligne-ing { display: grid; grid-template-columns: 54px 84px 1fr 34px;
  align-items: center; gap: 6px; }
.ligne-ing .champ { min-width: 0; padding-left: 10px; padding-right: 6px; }
.ligne-ing__q { text-align: center; font-variant-numeric: tabular-nums; }
.ligne-ing__u { padding-right: 20px; font-size: 15px; }

/* Une ligne finie par deux points ouvre un groupe : « Pour le bouillon : ».
   Elle n'a alors ni quantité ni unité, et ses deux cases se retirent — les
   laisser vides et actives inviterait à remplir une ligne de titre. */
/* Les deux boutons côte à côte : ajouter un ingrédient est l'action courante,
   ajouter un groupe est la voie secondaire. Deux boutons pleins se
   concurrenceraient. */
.lignes__actions { display: flex; gap: 8px; align-items: center; }
.lignes__actions .b { flex: 1; }

.ligne-ing[data-groupe] { grid-template-columns: 1fr 34px; }
/* Un groupe se voit AVANT d'être lu : un filet à gauche et un fond de crème.
   Sans ça, une ligne de titre en petites capitales dans une pile de champs
   identiques passe pour un ingrédient mal saisi. */
.ligne-ing[data-groupe] .ligne-ing__n { background: var(--creme);
  border-color: transparent; box-shadow: inset 3px 0 0 var(--bord); }
.ligne-ing[data-groupe] .ligne-ing__q,
.ligne-ing[data-groupe] .ligne-ing__u { display: none; }
.ligne-ing[data-groupe] .ligne-ing__n { font: 600 12px/1 var(--f-ui);
  letter-spacing: .06em; text-transform: uppercase; color: var(--encre-3); }

.ligne-ing__ote { width: 34px; height: var(--touche);
  display: grid; place-items: center; font: 400 20px/1 var(--f-ui);
  color: var(--encre-3); }
/* La cible garde ses 44px par un pseudo-élément : le dessin fait 34px de large
   pour ne pas voler d'espace au nom, la zone sensible reste conforme. */
.ligne-ing__ote::after { content: ""; position: absolute; width: var(--touche);
  height: var(--touche); }
.ligne-ing { position: relative; }

/* ================================================== 12. LES ÉTAPES, EN CASES
   Une case par étape. Le NUMÉRO est un compteur CSS, jamais une donnée :
   insérer une étape au milieu ne demande aucune renumérotation, et supprimer
   la troisième ne laisse pas de trou. */
.lignes--etapes { counter-reset: etape-saisie; gap: 9px; }
.ligne-etape { counter-increment: etape-saisie;
  display: grid; grid-template-columns: 28px 1fr 34px 34px; align-items: start; gap: 6px; }
.ligne-etape::before { content: counter(etape-saisie);
  display: grid; place-items: center; width: 28px; height: 34px;
  font: 500 15px/1 var(--f-titre); color: var(--encre-3);
  font-variant-numeric: tabular-nums; }
.ligne-etape textarea { min-height: 34px; resize: none; overflow: hidden;
  line-height: 1.45; }
/* LES LIENS ÉTAPE → INGRÉDIENTS, repliés en une ligne grise par défaut :
   la machine dit ce qu'elle a compris, on ne déplie que pour corriger. */
.ligne-etape__liens { grid-column: 2 / -1; display: flex; flex-wrap: wrap;
  gap: 6px; margin: 0 0 6px; }
.ligne-etape__conseil { grid-column: 2 / -1; margin-bottom: 4px; }
.ligne-etape__astuce { font: italic 400 15px/1.45 var(--f-voix); }
.ligne-etape__resume { min-height: 34px; padding: 0; text-align: left;
  font: 400 13px/1.4 var(--f-ui); color: var(--encre-3);
  text-decoration: underline; text-decoration-color: var(--bord);
  text-underline-offset: 3px; }
.ligne-etape__liens:empty { display: none; }
.ligne-etape__quoi { flex: 0 0 100%; font: 600 12px/1.2 var(--f-ui);
  letter-spacing: .09em; text-transform: uppercase; color: var(--encre-3); }
.ligne-etape__liens .etiq-m { min-height: 36px; padding: 0 12px; }

/* ========================================== 13. LE SOMMAIRE (accueil tiroirs)
   Le rail horizontal de vignettes est LE motif que toutes les applications
   partagent : c'est lui qui faisait dire « on a déjà vu cette app ». Une revue
   numérote ses rubriques. Numéro au sérif, règle noire d'ouverture, une
   vedette pleine largeur, puis des lignes. Rien ne défile de côté. */
.bloc[data-forme="rubrique"] { margin-top: 6px; }
/* auto 1fr auto : le numéro se serre, le TITRE prend la place. L'inverse
   poussait le titre au centre de l'écran, en épigraphe flottante. */
.bloc[data-forme="rubrique"] > .bloc__tete { position: relative;
  grid-template-columns: auto 1fr auto auto;
  padding-top: 30px; margin: 0 var(--marge); padding-left: 0; padding-right: 0; }
/* Le numéro de rubrique passe à la sauge : c'est lui qui rythme la page,
   et l'accent est fait pour rythmer, pas pour crier. */
.bloc__no { font: 500 13px/1 var(--f-titre); letter-spacing: .12em;
  color: var(--sauge); font-variant-numeric: tabular-nums; margin-right: 10px; }
.bloc[data-forme="rubrique"] .bloc__titre { font: 500 22px/1.15 var(--f-titre); }
.bloc[data-forme="rubrique"] .bloc__tout { color: var(--sauge); }
/* LA GRILLE. Le modèle fourni par Félix (grille de recettes à deux colonnes)
   remplace les lignes de texte sous la vedette : l'accueil se FEUILLETTE.
   La première carte s'étale sur les deux colonnes — c'est la vedette. */
.bloc[data-forme="rubrique"] .bloc__corps { padding: 14px var(--marge) 6px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.bloc[data-forme="rubrique"] .carte { width: 100%; flex: none; }
.bloc[data-forme="rubrique"] .carte:first-child { grid-column: 1 / -1; }
.bloc[data-forme="rubrique"] .carte:first-child .carte__dit { min-height: 120px; }

/* La photo sur une carte : plein cadre, rognée. */
.carte__dit[data-image="photo"] img { display: block; width: 100%; height: 118px;
  object-fit: cover; }
.bloc[data-forme="rubrique"] .carte:first-child .carte__dit[data-image="photo"] img { height: 172px; }

/* ============================================================ 14. LE MOUVEMENT
   ⚠️ PLUS DE PARALLAXE SUR LA UNE. Elle venait de la couverture pleine
   page ; dans une carte du bento, son contenu glissait sous les yeux
   pendant le défilement — « ça doit pas scroller dans la card » (Félix).
   Une carte est un objet posé : elle bouge d'un bloc ou pas du tout. */
/* Une carte s'ENFONCE sous le doigt — elle est posée sur la page, l'appui la
   presse contre elle. L'inverse (grossir) est le tic des sites de vitrine. */
.carte { transition: transform .16s ease, box-shadow .16s ease; }
.carte:active { transform: scale(.985); box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  .carte { transition: none; }
}

/* ========================================================= 15. AUX FOURNEAUX
   L'écran pour lequel l'application existe : les mains dans la farine, le
   téléphone à quarante centimètres. Tout découle de cette distance — une
   étape à la fois en corps géant, des cibles à taper du dos d'une phalange,
   le chrono lu dans la phrase. Le fond prend le papier teinté de la personne :
   jusqu'aux fourneaux, on sait chez qui on est. */
/* L'écran DÉFILE si le contenu déborde : avec une vue plus grande, un long
   conseil et six ingrédients, un iPhone SE n'a plus la place. Rien ne doit
   être inatteignable quand on a les mains dans la farine. */
.cuisson { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column;
  /* La barre d'onglets est fixe au-dessus de tout : sans cette réserve, elle
     recouvrait la moitié des deux mains. */
  padding: 22px var(--marge) calc(var(--barre) + var(--sous) + 14px);
  background: var(--blanc); }
.cuisson__ou { font: 500 12px/1.2 var(--f-ui); letter-spacing: .09em;
  text-transform: uppercase; color: var(--teinte-mot); }
.cuisson__jauge { display: flex; gap: 5px; margin-top: 9px; }
.cuisson__jauge i { flex: 1; height: 4px; border-radius: 2px;
  background: color-mix(in srgb, var(--teinte-mot) 22%, transparent); }
.cuisson__jauge i.faite { background: var(--teinte-mot); }
.cuisson__jauge i.ici { background: var(--encre); }
/* Le corps géant. Archivo, pas le sérif : à cette distance on lit une
   consigne, pas une page. */
/* La vue de l'étape : photo ou vidéo au-dessus du texte — la gravure du
   plat tient la place tant qu'il n'y a pas d'image. Même boîte que les
   zones de cartes, en plus grand. */
/* LA VUE PREND UN FORMAT, PAS UNE HAUTEUR FIXE : 16/10 sur toute la
   largeur, plafonnée à 38 % de l'écran pour que la consigne et le tour de
   main tiennent en dessous. À 150px elle était timide — « très petite »,
   et c'est vrai dès qu'il y a une vraie image dedans. */
/* ⚠️ flex-shrink: 0 — dans une colonne flex qui déborde, une boîte à
   aspect-ratio se laisse écraser jusqu'à zéro : sur un petit téléphone la
   vue disparaissait purement et simplement. */
.cuisson__vue { flex: 0 0 auto; margin-top: 12px; aspect-ratio: 16 / 10; max-height: 38vh;
  border-radius: var(--r-carte);
  overflow: hidden; display: grid; place-items: center;
  background: var(--bande); color: var(--sauge); }
.cuisson__vue img, .cuisson__vue video { width: 100%; height: 100%;
  object-fit: cover; display: block; }
/* La vidéo est une IMAGE ANIMÉE : pas de contrôles natifs, un seul bouton
   pour le son, posé dans le coin comme sur les médias du formulaire. */
.cuisson__vue[data-video] { position: relative; }
.cuisson__son { position: absolute; right: 8px; bottom: 8px;
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: 50%; background: rgba(17, 20, 18, .55); color: var(--blanc);
  backdrop-filter: blur(4px); }
/* Pas de règle pour masquer les contrôles : la balise n'a simplement pas
   l'attribut `controls`. Un !important pour cacher ce qu'on n'a pas
   demandé serait deux fautes en une. */
.cuisson__texte { margin: auto 0; padding: 16px 0;
  font: 400 clamp(26px, 7vw, 34px)/1.32 var(--f-ui); color: var(--encre); }
.cuisson__chrono { align-self: flex-start;
  min-height: 56px; padding: 0 24px; font-size: 19px;
  font-variant-numeric: tabular-nums;
  border-color: var(--teinte-mot); color: var(--teinte-mot); }
.cuisson__chrono.en-route { background: var(--encre); color: var(--blanc);
  border-color: var(--encre); }
/* Le chrono qui sonne, c'est le FEU — la tangerine en plein, l'encre
   dessus (5,1:1) : elle est trop claire pour porter du blanc. */
.cuisson__chrono.fini { background: var(--feu); color: var(--sur-feu);
  border-color: var(--feu); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Un chrono lancé à une AUTRE étape le dit : sans ça, on voit un décompte
   sans savoir ce qu'il compte. */
.cuisson__chrono[data-ailleurs]::after { content: " · étape " attr(data-ailleurs);
  font: 500 13px/1 var(--f-ui); opacity: .8; }
/* Les deux mains. 60px : encore largement tapables à l'aveugle (la cible
   RGAA en demande 44), sans manger le tiers de l'écran — « très gros ces
   boutons » avait raison. */
.cuisson__mains { display: grid; grid-template-columns: 1fr 2fr; gap: 10px; }
.cuisson__page { min-height: 60px; border-radius: var(--r-bouton);
  border: 1px solid var(--bord); background: var(--carte);
  font: 500 22px/1 var(--f-titre); color: var(--encre);
  box-shadow: var(--ombre-1); }
/* Le grand bouton d'avance suit la porte : vert, blanc dessus. C'est le
   geste qu'on répète toute la recette — il doit être clair, pas criard. */
.cuisson__page:last-child { background: var(--sauge); color: var(--blanc);
  border-color: var(--sauge); font-weight: 600; }
.cuisson__page:disabled { opacity: .3; }
.cuisson__page:active { transform: scale(.98); }


/* ========================================================= 16. LA FERMETURE
   Les ornements d'imprimeur (lettrine, filets doubles, cadre de couverture)
   sont enterrés par le contrat « cuisine chic » — ils appartenaient au
   registre gravé. Ne survit que la fermeture de la fiche : le fleuron,
   discret et gris, et le colophon centré. Une page qui se FERME reste une
   idée juste dans n'importe quel registre. */
.cul-de-lampe { display: flex; justify-content: center; margin-top: 26px;
  color: var(--bord); }
.colophon { text-align: center; margin-top: 10px; }

/* CE QU'IL FAUT ICI, EN TROIS COLONNES DE CARTES : gravure au-dessus, nom
   court dessous — l'anatomie de carte de toute l'app, à sa plus petite
   taille. Les gélules en ligne étaient trop discrètes pour un écran qu'on
   lit à quarante centimètres, les mains prises. */
.cuisson__pastilles { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px; margin-bottom: 14px; }
.cuisson__pastille { display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 6px; min-height: 84px; padding: 10px 8px;
  border-radius: var(--r-carte); background: var(--carte); color: var(--encre);
  text-align: center; box-shadow: var(--ombre-1); }
.cuisson__pastille span { font: 500 15px/1.25 var(--f-ui);
  font-variant-numeric: tabular-nums;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
.cuisson__pastille .gr { color: var(--sauge); }
.cuisson__barre { display: flex; align-items: center; gap: 9px; margin-bottom: 14px; }
.cuisson__barre:empty { display: none; }
.cuisson__four { min-height: 40px; padding: 0 14px; gap: 7px; font-size: 15px; }

/* « AUX FOURNEAUX » N'EST PAS UN CTA. Le boudin noir pleine largeur est le
   bouton générique de toutes les applications — exactement ce qu'on chasse.
   Ici c'est une INVITATION IMPRIMÉE : un cadre à l'encre sur le papier de la
   fiche, la cocotte gravée, le nom à l'affiche. Ça se lit comme un tampon de
   chapitre, pas comme un tunnel de conversion. L'appui l'encre en plein. */
/* LA PORTE DES FOURNEAUX EN VERT. L'orange a été essayé et hésité : « je
   suis toujours pas certain de ces boutons orange, peut-être vert ». Le
   vert est déjà la couleur des icônes, des badges et de la sélection : un
   grand geste vert reste dans la famille, là où l'orange criait. L'orange
   ne garde que le disque Ajouter — un seul point de feu par écran. */
.fourneaux { display: flex; justify-content: center;
  padding: 4px var(--marge) 30px;
  background: var(--carte); }
.fourneaux__porte { display: inline-flex; align-items: center; gap: 12px;
  min-height: 58px; padding: 0 28px;
  border: 0; border-radius: var(--r-bouton);
  /* DEUX couleurs, pas trois : l'orange qui donne faim, l'encre qui écrit
     dessus (5,08:1). Le gris reste aux boutons d'ÉCRITURE — ici on part
     cuisiner. */
  background: var(--sauge); color: var(--blanc);
  font: 600 22px/1 var(--f-titre);
  box-shadow: var(--ombre-2); }
.fourneaux__porte .gr { color: var(--blanc); }
.fourneaux__porte:active { transform: scale(.98); }

/* ================================================ 17. SEMAINE ET COURSES
   L'intendance : le semainier de la semaine en cours et la liste du marché.
   Des lignes, des coches, rien qui crie — on est entre la cuisine et le
   magasin, pas dans le livre. */
/* LES GESTES D'UNE RECETTE — quatre icônes, pas quatre lignes.

   Trois états successifs, et chacun a été jugé : trois boutons côte à côte
   (« nul » — neuf lignes de texte dans des boîtes vides), puis quatre rangées
   pleine largeur (« pas fan » — une liste fade qui allonge la page d'un
   écran). Le défaut commun : ces gestes sont SECONDAIRES, et ils occupaient
   plus de place que la recette.

   Une rangée d'icônes règle les deux : symétrique, courte, et chaque geste
   porte un mot d'un seul tenant qui ne peut pas se casser. C'est ce que font
   les articles pour partager et mettre de côté — et ici le geste principal,
   « Aux fourneaux », garde son bouton plein juste au-dessus. */
.prevoir {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  /* Posés sous le cartouche, ils n'ont plus à porter le pied de la fiche :
     l'écart du dessous revient au texte de la recette. */
  padding: 2px 0 22px; background: var(--carte);
}
.prevoir .choix {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; min-height: 74px; padding: 10px 4px; border: 0;
  border-radius: var(--r-carte); background: var(--creme);
}
.prevoir .choix .choix__titre {
  font: 500 12px/1.2 var(--f-ui); color: var(--encre-2); text-align: center;
}
.prevoir .choix svg { width: 22px; height: 22px; color: var(--sauge); }

.jour { margin: 0 var(--marge); padding: 13px 0; border-bottom: 1px solid var(--trait); }
.jour__nom { font: 500 19px/1.2 var(--f-titre);
  display: flex; align-items: baseline; justify-content: space-between; }
/* La date du jour de CETTE semaine, et le jour où l'on est marqué à la
   sauge : un planning qui ne sait pas quel jour on est n'est qu'une liste. */
.jour__date { font: 500 13px/1 var(--f-ui); color: var(--encre-3); }
.jour--la .jour__nom, .jour--la .jour__date { color: var(--sauge); }
.jour__pour { display: block; font: 400 12px/1.3 var(--f-ui); color: var(--encre-3); }
.jour__gr { color: var(--sauge); display: inline-flex; flex: none; margin-top: 2px; }
/* ⚠️ Le pointillé veut dire « il y a de la place ici » — le même vocabulaire
   que les cases libres de la cave. Discret : c'est une place, pas un appel. */
.jour__prevoir {
  width: 100%; min-height: 40px; margin-top: 8px;
  border: 1.5px dashed var(--trait); border-radius: var(--r-carte);
  background: none; color: var(--encre-3); font: 400 18px/1 var(--f-ui);
}
.jour__prevoir:active { color: var(--sauge); border-color: var(--sauge); }
.semaine__tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin: 4px var(--marge) 14px;
}
.semaine__compte { font: 400 13px/1.4 var(--f-ui); color: var(--encre-2); }
.jour__repas { display: grid; grid-template-columns: 1fr 34px; align-items: center; }
.jour__plat { display: flex; gap: 8px; align-items: flex-start;
  min-height: var(--touche); text-align: left; padding: 0;
  font: 400 17px/1.3 var(--f-ui); color: var(--encre); }
.jour__actions { padding: 17px var(--marge) 0; }
.vue > .vide-doux { padding-top: 26px; }

.course { display: flex; align-items: center; gap: 11px; width: 100%;
  min-height: var(--touche); padding: 2px 0; text-align: left;
  font: 400 17px/1.35 var(--f-ui); color: var(--encre); }
.course__rond { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--bord); background: var(--carte); }
/* Pris : la coche verte, le texte s'éteint SANS se barrer — on doit pouvoir
   se relire au rayon d'à côté. */
.course.fait { color: var(--encre-3); }
.course.fait .course__rond { border-color: var(--sauge); background: var(--sauge); }
.course.fait .course__rond::after { content: "✓"; display: grid; place-items: center;
  height: 100%; color: var(--blanc); font: 600 13px/1 var(--f-ui); }

/* La sauvegarde, en pied de la feuille Ajouter : présente, pas criarde. */
.sauvegarde { display: flex; gap: 8px; margin-top: 18px; padding-top: 14px;
  border-top: 1px solid var(--trait); }
.sauvegarde .b { flex: 1; }

/* ⚠️ TOUTES LES CASES À LA MÊME HAUTEUR, MÊME QUAND UN NOM DÉBORDE. Les noms
   courts sont préférables — ils viennent d'être raccourcis — mais certains
   titres de tiroirs sont écrits par les données et personne ne peut jurer
   qu'ils tiendront sur une ligne dans toutes les langues et à toutes les
   tailles de texte. La place de deux lignes est donc réservée d'avance : la
   mosaïque garde son alignement, qu'on ait un mot ou six. */
.bento__nom { min-height: 2.4em; }

/* FEUILLETER UN CARNET. La barre de position vit en pied de recette : elle
   dit où l'on en est et donne les deux appuis qui remplacent le balayage
   pour ceux qui ne balayent pas. */
.feuilleter {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 10px var(--marge) 34px; background: var(--carte);
}
.feuilleter__cote {
  min-width: var(--touche); min-height: var(--touche);
  font: 400 22px/1 var(--f-ui); color: var(--sauge);
}
.feuilleter__cote[disabled] { color: var(--bord); }
.feuilleter__n {
  font: 500 13px/1 var(--f-ui); letter-spacing: .06em;
  text-transform: uppercase; color: var(--encre-3);
  font-variant-numeric: tabular-nums;
}
/* Le glissement du doigt : court, et muet si le système demande moins de
   mouvement — la règle générale de l'app le neutralise déjà. */
#recette { transition: transform .12s ease-out; }

/* L'aide au feuilletage : elle se dit une fois, en bas, et s'efface dès que
   le geste est fait. Ni bulle, ni flèche animée — une phrase. */
.feuilleter__aide {
  padding: 0 var(--marge) 30px; margin-top: -22px;
  font: 400 13px/1.45 var(--f-ui); color: var(--encre-3);
  background: var(--carte); text-align: center;
}

/* UNE CARTE PAR PAGE, ET ELLE PREND LA PAGE — le dessin des applications de
   rencontre, qui vaut ici pour la même raison : on juge une chose à la fois,
   en grand, sans rien d'autre à l'écran.
   ⚠️ Le texte n'est JAMAIS sur l'image : « on ne voit pas le texte » avait
   tranché la question. L'image prend le haut, le nom vit dessous, sur le
   papier — et il reste lisible quelle que soit la photo. */
#feuillet { display: flex; padding: 0; }
.feuillet__plein {
  display: grid; grid-template-rows: 1fr auto auto;
  flex: 1; min-height: 0; margin: 12px var(--marge) 0;
  background: var(--carte); border: 0;
  border-radius: var(--r-carte); overflow: hidden; box-shadow: var(--ombre-2);
}
.feuillet__vue {
  display: grid; place-items: center; min-height: 0;
  background: var(--bande); color: var(--sauge); overflow: hidden;
}
.feuillet__vue img { width: 100%; height: 100%; object-fit: cover; display: block; }
.feuillet__dire { padding: 18px 18px 6px; }
.feuillet__nom { font: 600 22px/1.15 var(--f-titre); color: var(--encre); margin: 0; }
.feuillet__mes { font: 400 15px/1.4 var(--f-ui); color: var(--encre-3); margin: 6px 0 0; }
.feuilleter-entree { padding: 12px var(--marge) 0; }
.feuilleter-entree .b { width: 100%; }

/* ⚠️ SANS PHOTO, LA ZONE VISUELLE NE PREND PAS L'ÉCRAN. La plupart des
   recettes n'ont pas d'image — c'est la réalité de ce projet, pas un manque
   temporaire — et une gravure étirée sur six cents pixels donne un grand
   aplat vert au milieu duquel flotte un petit dessin. Elle garde une hauteur
   d'objet, et le nom remonte à sa rencontre. */
.feuillet__plein[data-sans-photo] { grid-template-rows: 300px auto auto; }
.feuillet__plein[data-sans-photo] .feuillet__dire { padding-top: 22px; }

/* LA RANGÉE DE GESTES AU POUCE. Cinq disques en bas de la carte, comme dans
   les applications de rencontre — la forme est excellente : le pouce y arrive
   sans que la main bouge, et chaque geste garde sa place d'une carte à
   l'autre. Ce qui n'est pas repris, c'est la MORALE : ni croix ni cœur, rien
   à accepter ni à rejeter. */
.feuilleter { justify-content: space-around; }
.geste-rond {
  width: 52px; height: 52px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--carte); color: var(--sauge);
  border: 0; box-shadow: var(--ombre-1);
}
.geste-rond svg { width: 22px; height: 22px; }
.geste-rond[disabled] { color: var(--bord); box-shadow: none; }
/* Celui du milieu est le geste principal — ouvrir — et il se voit. */
.geste-rond--large { width: 64px; height: 64px; background: var(--sauge); color: var(--blanc);
  border-color: var(--sauge); }
.geste-rond--large svg { width: 26px; height: 26px; }
/* Le retour du geste : le disque se remplit une seconde, sans texte qui
   pousse ses voisins. */
.geste-rond--fait { background: var(--sauge); color: var(--blanc); border-color: var(--sauge); }
/* Les icônes de commande prennent la taille de leur cible, pas celle du
   texte : elles ne sont plus des caractères. */
.carnet__fin svg, .carnet__bouge svg, .feuilleter__cote svg { width: 20px; height: 20px; }
.cuisson__ou svg, .ligne-etape__monte svg { width: 20px; height: 20px; }

/* ⚠️ UN SVG SANS TAILLE PREND TOUTE LA PLACE QU'ON LUI DONNE. Les chevrons
   des étapes remplaçaient un caractère, qui prenait la taille du texte ; le
   SVG, lui, s'est étiré à la hauteur du bouton — deux flèches géantes en bas
   de l'écran de cuisson. Les icônes de commande ont désormais une taille dans
   TOUS les boutons qui en portent, celui-ci compris. */
.cuisson__page svg { width: 26px; height: 26px; }

/* La recette dépliée sous sa carte : le MÊME rendu que la fiche, dans un
   autre contenant — un seul endroit à corriger le jour où la fiche change. */
#feuillet { display: block; overflow-y: auto; }
.feuillet__lire { padding: 0 0 30px; }
.feuillet__lire .fiche__corps { margin-top: 0; border-radius: 0; }
.feuillet__lire .b--large { width: calc(100% - var(--marge) * 2); margin: 0 var(--marge); }

/* La flèche de dépliage, posée sur la carte à la façon des applications de
   rencontre : elle dit le SENS — le contenu vient d'en dessous — et elle se
   retourne pour replier. Elle chevauche la limite entre l'image et le nom,
   là où le pouce la trouve sans masquer ni l'un ni l'autre. */
.feuillet__plein { position: relative; }
/* ⚠️ LE BOUTON DIT CE QU'IL OUVRE. C'était un disque sauge anonyme à
   chevron, jugé ambigu par la revue du 12/08 — surtout posé sur la vignette
   sans photo, où le vert du disque se fondait dans le vert de la gravure.
   Un bouton qui porte du texte écrit blanc sur basalte, comme partout. */
.feuillet__ouvrir {
  position: absolute; right: 16px; z-index: 2;
  min-height: 44px; padding: 0 16px; border-radius: var(--r-bouton);
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--encre); color: var(--blanc);
  font: 600 15px/1 var(--f-ui);
  box-shadow: 0 6px 16px -6px rgba(30, 27, 24, .5);
}
.feuillet__ouvrir svg { width: 18px; height: 18px; }
/* ⚠️ La zone visuelle rogne ce qui dépasse (overflow: hidden), sinon une
   photo déborderait de la carte arrondie. Le bouton ne peut donc pas y vivre
   à cheval : il est posé sur la CARTE, à la hauteur du bas de l'image. */
.feuillet__plein[data-sans-photo] .feuillet__ouvrir { top: 276px; }
.feuillet__ouvrir { top: auto; bottom: 128px; }

/* L'ÉQUIPEMENT, en gélules : ce sont des objets qu'on a ou qu'on n'a pas, pas
   des choses à faire dans l'ordre — une liste numérotée mentirait sur leur
   nature. Elles reprennent l'allure des étiquettes, qui font déjà ce travail
   ailleurs : rien de nouveau à apprendre. */
.ustensiles { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.ustensile {
  display: inline-flex; align-items: center; min-height: 36px; padding: 0 14px;
  border-radius: var(--r-pilule); background: var(--creme); color: var(--encre-2);
  font: 400 15px/1.3 var(--f-ui);
}

/* CE QU'ON A CHEZ SOI. Une ligne par chose, le nom d'abord et ce qu'on en sait
   dessous — jamais un chiffre de stock, que l'application ne peut pas
   connaître. Les entamées s'écrivent en gris : elles sont là, mais on ne
   compte plus dessus. */
.provisions { padding: 0 var(--marge); }
.provision { padding: 11px 4px 12px; }
/* LA PLANCHE D'ÉTAGÈRE. « On parle de placard, de tiroirs, mais on ne les
   retrouve jamais dans les designs » (Félix) : le meuble doit se VOIR.
   Chaque provision est POSÉE sur une planche — un trait épais aux bouts
   arrondis qui déborde un peu du texte, pas un filet de tableau. */
.pot .provisions .provision { position: relative; margin-inline: 2px; }
.pot .provisions .provision::after { content: ''; position: absolute;
  left: -6px; right: -6px; bottom: 0; height: 4px;
  border-radius: 2px; background: var(--bord); }
/* Hors des pots (vieille liste plate éventuelle), le filet sobre reste. */
.provisions > .provision:not(.pot .provisions .provision) { border-bottom: 1px solid var(--trait); }
.provision__nom { display: block; font: 500 17px/1.3 var(--f-ui); color: var(--encre); }
.provision__dit { display: block; margin-top: 3px;
  font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }
.provision--entame .provision__nom { color: var(--encre-3); }
#provisions .volets { margin-top: 4px; }
#provisions .aide { padding: 0 var(--marge); }

/* LE PLAN DE LA MAISON. Quatre portes qu'on pousse, pas quatre onglets :
   un placard et une cave sont des ENDROITS, et l'écran doit le dire avant
   d'être utile. Deux colonnes, comme la mosaïque d'accueil — c'est la même
   grammaire, et rien de nouveau à apprendre. */
.maison { display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  padding: 16px var(--marge) 0; }
.piece {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  min-height: 132px; padding: 16px 14px; text-align: left;
  background: var(--carte); border: 0;
  border-radius: var(--r-carte); box-shadow: var(--ombre-1); color: var(--encre);
}
.piece__vue { width: 30px; height: 30px; color: var(--sauge); margin-bottom: 6px; }
.piece__nom { font: 600 17px/1.2 var(--f-titre); }
.piece__quoi { font: 400 13px/1.35 var(--f-ui); color: var(--encre-3); }
/* Ce qui presse se voit depuis le plan : c'est la seule raison d'ouvrir une
   porte plutôt qu'une autre. */
.badge--presse { margin-top: auto; background: var(--feu); color: var(--sur-feu); }
.piece__retour { margin: 8px var(--marge) 4px; padding: 0; }

/* LA LISTE DE COURSES : la quantité EN TÊTE, en gras, comme sur une liste
   qu'on écrit à la main. On lit la colonne des chiffres sans lire les noms —
   c'est ce qu'on fait devant un rayon. */
.course__quoi { display: block; min-width: 0; }
.course__nom { display: block; }
.course__q { font-variant-numeric: tabular-nums; }
.course__dou { display: block; margin-top: 2px;
  font: 400 13px/1.3 var(--f-ui); color: var(--encre-3); }
.course.fait .course__dou { opacity: .7; }

/* ============================================================== 12. LA CAVE
   Des casiers nommés par soi, et une place par bouteille.

   ⚠️ AUCUNE COULEUR NE CLASSE ICI. Les bouteilles sont toutes à la sauge et
   c'est la FORME — le diamètre du rond, qui est celui du format réel — qui
   distingue une champenoise d'une canette. Les encres --p0..--p6 sont celles
   des PERSONNES ; les employer pour des types de boissons aurait cassé le
   seul code couleur du projet. Trois états, jamais des catégories :
   sauge = elle est là, pâle = hors du filtre posé, et rien d'autre. */
.cave__somme {
  margin: 10px var(--marge) 0; font: 400 13px/1.4 var(--f-ui); color: var(--encre-2);
}
.cave__entamees {
  font: inherit; color: var(--encre); background: none; border: 0;
  padding: 0; border-bottom: 1px solid var(--trait);
}
.cave__quoi {
  margin: 22px var(--marge) 10px; font: 600 12px/1 var(--f-ui);
  letter-spacing: .12em; text-transform: uppercase; color: var(--encre-2);
}
/* La bascule d'affichage : deux gravures dans la ligne de titre. ⚠️ PAS une
   bande segmentée pleine largeur — elle pesait autant que les filtres, alors
   qu'elle ne change pas la réponse, seulement la façon de la lire. */
/* ⚠️ DEUX LIGNES EN POCHE. Le compte, la recherche et la bascule sur une
   seule rangée : à 430 px le libellé se cassait en deux et venait toucher
   le champ (vu à la capture). Le compte prend donc sa ligne, les outils la
   leur ; au large, tout revient sur une rangée. */


/* La recherche et la bascule voyagent ENSEMBLE : sans ce groupe, elles se
   séparaient à l'étroit et la bascule tombait seule contre le bord. */


@media (min-width: 700px) {
  /* ⚠️ LA RECHERCHE RESTE DU CÔTÉ DE LA LISTE. Poussée à droite par le
     `space-between`, elle venait se poser au-dessus du panneau de détail
     et semblait lui appartenir. Elle suit le compte, elle appartient aux
     bouteilles. */



}





/* LES PORTES DES CASIERS. Même forme que les pièces de la réserve : on
   reconnaît un casier à sa vignette comme une pièce à son plan. */
.casiers { display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  padding: 0 var(--marge); }
/* La case-porte encore vide : la grammaire de la place libre. */
.bento__case--libre { box-shadow: none; background: transparent;
  border: 1px dashed var(--bord); align-items: center; text-align: center;
  padding-top: 14px; }
.bento__case--libre .bento__nom, .bento__case--libre .bento__quoi { padding: 0 12px; }
.case-libre__rond { width: 44px; height: 44px; color: var(--trait); }
.bento__case--libre:active .case-libre__rond { color: var(--sauge); }
.badge--allume { background: var(--sauge); color: var(--blanc); }
.casier {
  display: flex; gap: 14px; align-items: center; width: 100%; text-align: left;
  background: var(--blanc); border: 1px solid var(--trait);
  border-radius: 18px; padding: 14px; min-height: var(--touche);
}
.casier__vue { color: var(--sauge); flex: none; }
.casier__vue rect { color: var(--encre); stroke: currentColor; }
.casier__vue circle { fill: var(--sauge); }
.casier__corps { flex: 1; min-width: 0; }
.casier__nom { display: block; font: 600 16px/1.2 var(--f-ui); }
.casier__ou, .casier__compte {
  display: block; font: 400 13px/1.4 var(--f-ui); color: var(--encre-2);
}
.casier__ou { color: var(--encre-3); }
.casier__compte--allume { color: var(--sauge); }
.casier__fin { color: var(--encre-3); display: grid; place-items: center; }
.casier__fin svg { width: 18px; height: 18px; }

/* ⚠️ Le pointillé n'est pas décoratif : c'est celui des cases libres. Dans
   cette cave, un trait pointillé veut dire une seule chose — il y a de la
   place ici. Et la carte est EN BAS : en tête, elle proposerait d'en créer un
   avant même d'avoir regardé ceux qu'on a. */
.casier--neuf { background: transparent; border-style: dashed; border-width: 1.5px; }
.casier__plus {
  display: grid; place-items: center; width: 62px; height: 47px; flex: none;
  border-radius: 10px; color: var(--sauge);
  background: color-mix(in srgb, var(--sauge) 10%, transparent);
}
.casier__plus svg { width: 20px; height: 20px; }

/* LA GRILLE. ⚠️ Elle ne se recompose JAMAIS : les cases hors filtre pâlissent
   sur place. Une grille qui se réorganise à chaque filtre détruit ce qu'on
   vient y chercher — la mémoire de l'endroit. */
.casier-plan {
  display: grid; grid-template-columns: 18px repeat(var(--col), 1fr);
  align-items: center; gap: 4px; margin: 12px var(--marge) 0;
  /* LA MATIÈRE DE LA HOME ici aussi : pas de conteneur, les cases
     portent la carte — le bento appliqué au plan de la cave. */
}
.casier__col-n, .casier__rang-n {
  font: 400 12px/1 var(--f-ui); color: var(--encre-3); text-align: center;
}
.case {
  display: grid; place-items: center; aspect-ratio: 1;
  min-height: var(--touche); background: var(--carte);
  border: 0; box-shadow: var(--ombre-1);
  border-radius: var(--r-carte); color: var(--sauge);
}
.case--libre { background: transparent; box-shadow: none; }
.case--libre { color: var(--trait); }
.cave__rond { width: 100%; height: 100%; }
.cave__rond--hors { color: color-mix(in srgb, var(--sauge) 34%, var(--blanc)); }

/* Une bouteille en ligne : le rond à son calibre, le nom, et SA PLACE. Une
   liste de cave sans position est une liste, pas une cave. */



/* Le titre du casier a sa propre classe : .entete__titre sert ailleurs, et
   deux écrans qui partagent une classe rendent le repérage ambigu — pour les
   tests comme pour qui navigue au lecteur d'écran. */
.cave__nom {
  margin: 14px var(--marge) 0; font: 600 22px/1.15 var(--f-titre);
}
.cave__ou {
  display: block; font: 400 13px/1.4 var(--f-ui); color: var(--encre-3);
}

/* LES FILTRES DE LA CAVE : SIX SILHOUETTES, PAS SIX MOTS.
   ⚠️ Six mots à 16 px ne tenaient pas dans 430 px et débordaient à droite.
   Le remède n'est pas de rapetisser le texte : ici, LA FORME DIT DÉJÀ LE
   TYPE — c'est le principe du dessin de toute cette cave. Une champenoise,
   une chope et un verre bas se distinguent en noir et blanc (RGAA 3.1), et
   le mot reste en aria-label, parce qu'une gravure ne se prononce pas. */
.familles {
  display: grid; grid-template-columns: repeat(6, 1fr);
  gap: 6px; padding: 12px var(--marge) 0;
}
/* La famille de la cave EST une pilule (mêmes métriques que .rapide) :
   un seul composant dans toute l'app. */
.famille {
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; min-height: var(--touche); padding: 0;
  border: 1px solid var(--bord); border-radius: var(--r-pilule);
  background: var(--carte); color: var(--encre-2);
}
.famille.on {
  background: var(--sauge); border-color: var(--sauge); color: var(--blanc);
}
.famille__vue { width: 26px; height: 26px; }
/* Le mot revient EN TOUTES LETTRES dès qu'un filtre est posé : une gravure
   allumée dit qu'on filtre, elle ne dit pas sur quoi. */
.familles__dit {
  margin: 8px var(--marge) 0; font: 500 13px/1.4 var(--f-ui); color: var(--sauge);
}

/* La boîte de choix : on touche ce qu'on reconnaît. Les silhouettes y sont
   les mêmes que dans les filtres — ce qu'on voit ici se retrouve là-haut. */
.boite__choix { display: grid; gap: 8px; margin: 4px 0 10px; }
.boite__choix .b {
  display: flex; align-items: center; gap: 12px; justify-content: flex-start;
  text-align: left;
}
.boite__choix .famille__vue { width: 24px; height: 24px; color: var(--sauge); flex: none; }

/* ⚠️ LES CASES LIBRES APPELLENT quand une bouteille attend sa place. Sans ce
   signal, l'écran est identique à celui qu'on vient de quitter et on ne
   comprend pas qu'il attend un geste. */
.case--appelle { color: var(--sauge); }
.case--appelle svg circle { stroke-dasharray: 3 3; }
/* LA CAISSE : le rabat du carton, retenu sur planche le 11/08. ⚠️ Le carré nu
   ressemblait à un bouton — le plein sauge veut dire « choisi » dans cette
   application, et une caisse n'est pas une sélection ; l'échancrure du pack
   était plus juste comme objet mais se perdait à 44 px. Le pli suffit, et il
   tient à toutes les tailles. */
.cave__caisse text { fill: currentColor; }
/* ⚠️ GARDE-FOU. `.choix` est une grille « 44px 1fr » : un enfant unique tombe
   dans la colonne étroite et le texte se lit un mot par ligne — la panne a
   frappé deux fois, une fois avec .ligne-ing utilisée pour lire, une fois
   ici. Une rangée sans gravure occupe désormais toute la largeur, au lieu de
   se laisser écraser en silence. */
.choix > span:only-child { grid-column: 1 / -1; }
/* ⚠️ Le nom et le décompte d'un carnet n'avaient AUCUN style : deux span
   restés en ligne, donc un titre et son compte qui se télescopent au milieu
   d'une phrase. Le corps de la carte est une colonne, et chaque chose y tient
   sa ligne. */
.carnet__nom {
  display: block; font: 600 19px/1.2 var(--f-titre);
}
.carnet__compte {
  display: block; margin-top: 2px;
  font: 400 13px/1.4 var(--f-ui); color: var(--encre-3);
}

/* ========================================== 13. L'ÉCRAN LARGE — tablette et ordinateur
   ⚠️ CETTE INTERFACE EST DESSINÉE POUR 430 px, ET ELLE LE RESTE. Étalée sur
   un écran d'ordinateur, une carte de recette fait un mètre de large : le
   titre nage, la photo se déforme, et la colonne d'ingrédients devient
   illisible parce que l'œil ne retrouve plus le début de la ligne suivante.

   On ne redessine donc pas une version « bureau » — on pose la même colonne
   au milieu, comme un téléphone posé sur la table. C'est ce que fait déjà
   l'aperçu avec son cadre, et ce que font les applications qui existent des
   deux côtés. Le jour où un vrai écran large méritera sa mise en page — la
   relecture des scans côte à côte, l'édition du livre — ce sera un écran
   CONÇU pour ça, pas celui-ci étiré.

   Trois paliers, et chacun ne fait qu'une chose :
     560   la colonne se centre, le reste de l'écran devient la table
     900   la barre d'onglets quitte le bas pour un RAIL à gauche — au
           paysage, le pouce n'a plus le monopole, et une barre de cinq mots
           étirée sur trente centimètres se lit comme un pied de page de site
    1200   la colonne respire à 560 px — PAS plus : les grilles de la cave
           sont calibrées pour six colonnes tactiles — et la liste de la
           collection passe en deux colonnes de cartes.

   La COLONNE est une variable (--colonne) : tout ce qui se cale sur elle —
   feuilles, voile, disque d'ajout — la lit au lieu de recopier 430. */
@media (min-width: 560px) {
  :root { --colonne: 430px; }
  body { background: #E9E6DF; }

  .ecrans {
    /* ⚠️ `position: relative` est indispensable : les écrans sont en position
       ABSOLUE et, sans ancre, ils se calent sur la fenêtre — la colonne était
       bien posée mais le contenu passait par-dessus. */
    position: relative;
    max-width: var(--colonne); margin-inline: auto;
    background: var(--blanc);
    box-shadow: 0 0 60px -12px rgba(20, 19, 15, .28);
    min-height: 100vh;
  }

  /* ⚠️ Les éléments en position fixe se calent sur la FENÊTRE, pas sur la
     colonne : sans ces trois règles, la barre d'onglets s'étalerait sur toute
     la largeur et le disque d'ajout partirait se coller au bord droit de
     l'écran, à vingt centimètres du contenu. */
  .onglets, .feuille, .voile {
    left: 50%; right: auto; width: var(--colonne); transform: translateX(-50%);
  }
  /* ⚠️ Une partie des écrans vit HORS de .ecrans dans le HTML (la semaine,
     la réserve, les carnets, les familles…) : leur `position: absolute;
     inset: 0` se cale alors sur la fenêtre, faute d'ancêtre positionné, et
     la réserve s'étalait sur toute la largeur de l'écran pendant que la
     collection restait sagement dans sa colonne. On les rhabille comme la
     colonne — même largeur, même fond, même ombre — au lieu de toucher au
     HTML. */
  body > .ecran {
    left: 50%; right: auto; width: var(--colonne); transform: translateX(-50%);
    background: var(--blanc);
    box-shadow: 0 0 60px -12px rgba(20, 19, 15, .28);
  }
  .feuille { transform: translateX(-50%) translateY(100%); }
  .feuille.on { transform: translateX(-50%); }
  /* ⚠️ Les feuilles fermées sont GARÉES sous l'écran (translateY(100 %)),
     mais leurs ombres, elles, remontent : plusieurs feuilles superposées
     projetaient une tache sombre au pied de la colonne, visible dès que le
     fond de table apparaît. L'ombre n'appartient qu'à la feuille levée. */
  .feuille:not(.on) { box-shadow: none; }
  .poser { left: 50%; right: auto;
    margin-left: calc(var(--colonne) / 2 - 56px - var(--marge)); }
}

/* LE RAIL. Les mêmes cinq lieux, les mêmes gravures, le même vocabulaire
   d'état — trait de 2 px, graisse, sauge — mais posés à gauche, où vivent
   les navigations de tablette. La barre ne change pas de nature : elle dit
   toujours OÙ L'ON EST, et le disque d'ajout dit toujours ce qu'on fait,
   près du contenu et jamais au bord de l'écran. */
@media (min-width: 900px) {
  :root { --rail: 72px; }

  /* Le rail se réserve sa bande : la colonne se centre dans ce qui RESTE.
     C'est le remplissage du body qui fait ce partage — les .ecrans gardent
     leur margin-inline auto et se centrent dans la boîte de contenu. */
  body { padding-left: var(--rail); }
  /* Les écrans de porte (bienvenue, compte) n'ont pas de navigation : sans
     rail, pas de bande réservée, sinon la porte serait décentrée. */
  body.sans-barre { padding-left: 0; }

  .onglets {
    left: 0; right: auto; top: 0; bottom: 0;
    width: var(--rail); height: auto; transform: none;
    /* Une pile, plus une rangée : les cellules reprennent leur hauteur de
       barre (80 px) et se centrent dans la hauteur de l'écran, comme les
       rails d'iPad — en haut, elles se disputeraient l'angle avec l'ours. */
    grid-auto-flow: row; grid-auto-rows: var(--barre);
    align-content: center; row-gap: 6px;
    padding: 0;
    border-top: 0; border-right: 1px solid var(--trait);
    box-shadow: none;
  }
  /* Le trait d'état bascule avec la barre : au-dessus il disait « cet onglet
     est sous ton pouce », à gauche il longe le bord d'où vient le rail. */
  .onglet.on::before {
    top: 50%; left: 0; transform: translateY(-50%);
    width: 2px; height: 22px;
  }

  /* Le point de fuite des éléments fixes se décale d'une demi-bande : 50 %
     de la fenêtre n'est plus le centre de l'espace de contenu. Les écrans
     hors de .ecrans suivent : leur bloc englobant est la fenêtre, pas le
     body rembourré. */
  .feuille, .voile, body > .ecran { left: calc(50% + var(--rail) / 2); }
  body.sans-barre > .ecran { left: 50%; }
  /* La boîte de dialogue aussi : centrée sur la fenêtre, elle flotterait
     36 px à gauche de la colonne qu'elle interroge. */
  .boite { left: calc(50% + var(--rail) / 2); }
  .poser {
    left: calc(50% + var(--rail) / 2);
    /* Plus de barre en bas : le disque descend à hauteur de pouce de souris,
       et le défilement n'a plus à réserver la hauteur de la barre. */
    bottom: calc(var(--sous) + 28px);
  }
  .defile {
    padding-bottom: calc(var(--sous) + 122px);
    scroll-padding-bottom: calc(var(--sous) + 28px);
  }
}

/* L'ORDINATEUR. La colonne s'élargit d'un cran — 560 px, le seuil du premier
   palier, PAS plus : les grilles de la cave (six familles, les casiers en
   colonnes tactiles) sont calibrées pour cette largeur, et une ligne de
   texte y reste sous les 70 caractères où l'œil retrouve son début de
   ligne. La place gagnée sert à UNE chose : la liste de la collection en
   deux colonnes. */
@media (min-width: 1200px) {
  /* ============ LA VRAIE INTERFACE WEB — plus de téléphone posé au milieu.
     « En version web il faut une vraie interface, même si on doit
     redessiner » (Félix, 12/08). La charte ne bouge pas d'un pixel ; c'est
     L'ESPACE qui change de logique : chaque écran emploie la largeur à SA
     façon — la collection en grille de magazine, la fiche en double page de
     livre ouvert, la semaine en sept vraies colonnes. Les écrans de LECTURE
     s'élargissent ; les écrans de SAISIE (compte, profil, légal) gardent
     leur colonne : un formulaire large est un formulaire illisible. */

  /* Les feuilles et boîtes restent des modales de 560 : une feuille qui
     s'étire sur 1400 px n'est plus une feuille, c'est un écran. */
  :root { --colonne: 560px; }

  /* ⚠️ PLUS DE CADRE, PLUS DE BANDES : l'application EST la page. « Sur un
     24 pouces j'ai encore des bandes grises — il faut des sites différents,
     pas une adaptation » (Félix). Le fond redevient la crème partout, le
     contenu remplit l'écran moins le rail, et chaque grille se calcule en
     colonnes fluides. Seuls les écrans de SAISIE gardent leur colonne. */
  body { background: var(--blanc); }
  .ecrans { max-width: none; background: transparent; box-shadow: none; }
  body > #ecran-semaine, body > #ecran-provisions, body > #ecran-carnets,
  body > #ecran-courses, body > #ecran-livres, body > #ecran-recherche,
  body > #ecran-familles {
    left: var(--rail, 0px); right: 0; width: auto; transform: none;
    background: transparent; box-shadow: none;
  }
  .defile { padding-inline: clamp(32px, 4vw, 96px); }
  /* La fiche reste une double page de LECTURE : généreuse, jamais infinie —
     et centrée sur un fond continu, il n'y a plus de bande à voir. */
  .fiche { max-width: 1680px; margin-inline: auto; }

  /* L'en-tête : la marque à gauche, les portes GROUPÉES à droite — des
     icônes éparpillées sur deux mètres ne se trouvent pas. Le
     `margin-left: auto` mobile poussait CHAQUE porte, donc les répartissait :
     seule la première garde la poussée, les autres se serrent. */
  .ours { gap: 14px; }
  .ours .ours__porte { margin-left: 0; }
  .ours .ours__porte:first-of-type { margin-left: auto; }

  /* LA VUE « LES MAINS » : une personne n'est pas une bande d'un mètre —
     c'est une carte, et les cartes se rangent en grille fluide comme la
     collection. Les rubriques « Rapporté du web » et « D'où ça vient »
     gardent la pleine largeur : ce sont des rangées de gélules. */
  [data-vue="mains"] {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: 14px 16px; align-items: stretch;
  }
  [data-vue="mains"] .vue__titre,
  [data-vue="mains"] .bloc[data-role="source"],
  [data-vue="mains"] .bloc[data-role="aire"] { grid-column: 1 / -1; }
  [data-vue="mains"] .bloc[data-role="personne"] {
    margin-inline: 0; min-height: 128px; border-radius: var(--r-carte);
    background: var(--carte); box-shadow: var(--ombre-1);
  }

  /* LA COLLECTION EN GRILLE DE MAGAZINE : des colonnes FLUIDES — trois sur
     un portable, cinq sur un 24 pouces, sans changer de règle. */
  #ecran-collection .bloc[data-forme="liste"] .bloc__corps {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 14px 16px; padding-bottom: 6px;
  }
  #ecran-collection .bloc[data-forme="liste"] .plat {
    padding: 18px 20px; min-height: 96px;
    background: var(--carte); border-radius: var(--r-carte);
    box-shadow: var(--ombre-1);
    grid-template-columns: 40px 1fr; align-items: start;
  }
  #ecran-collection .bloc[data-forme="liste"] .plat + .plat::before { display: none; }
  #ecran-collection .bloc[data-forme="liste"] .plat__t {
    grid-column: 2; grid-row: 3; justify-self: start; margin-top: 6px;
  }
  /* LE BENTO, LE VRAI. auto-fit alignait sept cases identiques sur une
     ligne, avec deux orphelines dessous — « on peut pas avoir de truc dans
     une ligne, on fait du bento réactif » (Félix). Une mosaïque a des cases
     de TAILLES INÉGALES qui s'emboîtent : la une occupe 2×2, les deux
     recettes montent sur deux rangées, les quatre portes se logent dans les
     cellules restantes (flux dense). À quatre colonnes le compte tombe
     juste : 4 + 2 + 2 + 4 = 12 cellules, aucune orpheline. */
  .bento { grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-auto-rows: minmax(150px, auto); grid-auto-flow: dense; gap: 14px; }
  /* Les rubriques suivent la même règle de mosaïque que le bento : la
     vedette « pleine largeur » est une règle de téléphone — sur 24 pouces
     elle faisait une carte d'un mètre. Vedette 2×2, les quatre cartes de
     la suite autour : 4 + 4 = 8 cellules, deux rangées pleines. */
  .bloc[data-forme="rubrique"] .bloc__corps {
    grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
  .bloc[data-forme="rubrique"] .carte:first-child { grid-column: span 2; grid-row: span 2; }
  .bloc[data-forme="rubrique"] .carte:first-child .carte__dit {
    height: auto; flex: 1; min-height: 172px; }
  .bloc[data-forme="rubrique"] .carte:first-child .carte__dit[data-image="photo"] img {
    height: 100%; }
  .bento .une { grid-column: span 2; grid-row: span 2; }
  /* Les cartes restent d'UNE rangée : les zones pâles géantes des cases
     étirées noyaient la gravure (« reprends la home », 13/08). Avec les
     portes Cave et Carnets, les 12 cellules tombent juste : une 2×2 +
     2 cartes + 6 portes. */
  .bento__case { min-height: 0; }

  /* LA FICHE EN DOUBLE PAGE. Ce qu'il faut à gauche — mesures, ustensiles,
     ingrédients — et la façon de faire à droite, comme dans tous les livres
     de cuisine. La colonne de gauche reste sous les yeux pendant qu'on
     défile les gestes. */
  .fiche__corps {
    display: grid; grid-template-columns: 400px 1fr;
    gap: 0 64px; align-items: start;
  }
  .fiche__avant { position: sticky; top: 12px; }
  .fiche__faire .fiche__section:first-child { margin-top: 0; }

  /* LA SEMAINE EN SEMAINE : sept colonnes, comme sur un calendrier de
     cuisine. Chaque jour est une carte ; « rien de prévu » se voit d'un
     coup d'œil sur la semaine entière. */
  #semaine .semaine__jours {
    /* minmax(0,1fr) : sans le 0, un long titre pousse sa colonne et la
       semaine gondole à 1366 (revue visuelle). */
    display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 12px; align-items: stretch; padding-inline: 8px;
    min-height: 60vh;
  }
  #semaine .jour {
    background: var(--carte); border-radius: var(--r-carte);
    box-shadow: var(--ombre-1); padding: 16px 14px;
    margin: 0; border-bottom: 0;
    display: flex; flex-direction: column;
  }
  /* Aujourd'hui : le trait sauge en tête de colonne — le même vocabulaire
     que l'onglet actif du rail. */
  #semaine .jour--la { box-shadow: inset 0 3px 0 var(--sauge), var(--ombre-1); }
  #semaine .jour__nom { flex-direction: column; align-items: flex-start; gap: 2px; }
  #semaine .jour__repas {
    background: var(--blanc); border: 1px solid var(--trait);
    border-radius: 12px; padding: 8px 4px 8px 10px; margin-top: 10px;
  }
  /* La place libre vit TOUJOURS au même endroit : sous le dernier repas,
     ou sous la date d'un jour vide. Le « + » qui flottait au pied des
     colonnes vides changeait de place selon la journée (revue visuelle). */
  #semaine .jour__prevoir { margin-top: 12px; }
  #semaine .semaine__tete { padding-inline: 8px; }
  #semaine .vide-doux { grid-column: 1 / -1; }

  /* LA CAVE : les portes des casiers en deux colonnes, et la grille d'un
     casier plafonnée — des cases de 90 px ne sont plus des cases, ce sont
     des assiettes. */
  #provisions .casiers { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
  #provisions .casier-plan { max-width: 680px; margin-inline: auto; }
  #provisions .piece__retour, #provisions .cave__nom, #provisions .cave__somme {
    max-width: 680px; margin-inline: auto; }
  #provisions .maison { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }

  /* LES COURSES EN RAYONS CÔTE À CÔTE : chaque rayon est une carte, la
     liste entière se lit d'un regard — c'est le tableau accroché à la porte
     du frigo, pas un ticket de caisse. */
  #courses .courses-rayons { columns: 340px; column-gap: 16px;
    margin-inline: 0; padding-top: 8px; }
  #courses .course-rayon { break-inside: avoid; margin: 0 0 16px;
    background: var(--carte); border-radius: var(--r-carte);
    box-shadow: var(--ombre-1); padding: 14px 18px 10px; }
  #courses .course-rayon__nom { margin-top: 0; }

  /* LES FAMILLES : la tête en pleine largeur, puis une carte par personne —
     le même pas de grille que les personnes de la vue Par personne. */
  /* ⚠️ align-CONTENT aussi : sans lui la grille étire ses rangées sur la
     hauteur de l'écran et une bande vide s'ouvre sous la tête. La grille
     vit PAR FAMILLE : chaque famille est une section pleine largeur — on
     appartient à plusieurs familles en même temps, on ne bascule pas. */
  #familles .familles-liste { grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
    gap: 16px; align-content: start; }
  #familles .famille-groupe { display: grid;
    grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
    gap: 16px; align-items: start; align-content: start; }
  #familles .famille-tete { grid-column: 1 / -1; margin: 0; }
  #familles .membre { margin: 0; padding: 20px 20px 12px;
    background: var(--carte); border-radius: var(--r-carte);
    box-shadow: var(--ombre-1); }
  /* Des cartes dans la carte : les carnets s'aplatissent en lignes à
     filet, sinon l'ombre porte sur l'ombre. */
  #familles .membre .carnet { box-shadow: none; }

  /* Les volets en segments TEXTE (courses, réserve) gardent une largeur de
     main : la contrainte vivait sur .choix-vue et a suivi le sélecteur de
     la collection quand il est passé en icônes (revue du 12/08). */
  .volets { padding-inline: 0; }

  /* LA CAVE : six boutons étirés à 270 px pour une icône de 16 px ne se
     lisent pas. Les familles reprennent leur taille et disent leur MOT —
     la place ne manque pas ici (revue visuelle + parcours). */
  #provisions .familles { display: flex; flex-wrap: wrap;
    grid-template-columns: none; }
  #provisions .famille { display: inline-flex; align-items: center; gap: 8px;
    flex: 0 0 auto; padding: 0 16px; }
  #provisions .famille__mot { display: inline; font: 500 14px/1 var(--f-ui); }

  /* Les écrans de SAISIE et de LISTE COURTE gardent une colonne de lecture :
     un champ de recherche d'1,6 m et des cartes d'un mètre quatre-vingts
     sont des zones mortes, pas des dessins (revue visuelle). */
  #ecran-recherche .defile { max-width: 760px; margin-inline: auto; }
  #ecran-livres .defile { max-width: 900px; margin-inline: auto; }

  /* LES CARNETS EN CLASSEUR PLEINE LARGEUR — « carnet est pas en pleine
     largeur sur ordi » (Félix). La grille alterne déjà carte / flèches :
     le repeat par PAIRES (carte extensible + colonne de rangement fixe)
     pose plusieurs carnets par rangée sans toucher au markup. */
  #carnets .carnets--classables {
    grid-template-columns: repeat(auto-fill, minmax(380px, 1fr) 48px);
    gap: 14px 10px; align-items: center;
  }
  #carnets .carnets--classables .carnet { grid-column: auto; }
  /* Les entrées seules (Les familles, Sans carnet) restent des cartes de
     lecture, pas des bannières d'un mètre quatre-vingts. */
  #carnets .choix, #carnets > .carnet { max-width: 640px; }

  /* Le compte d'une section reste À CÔTÉ de son titre : poussé au bord
     droit, à 1 400 px du mot qu'il compte, il ne se lit plus. */
  #ecran-collection .bloc[data-forme="liste"] .bloc__tete .bloc__compte {
    margin-left: 10px; margin-right: auto; }

  /* Le meuble de la pièce se centre, comme le plan de la cave. */
  /* La scène du placard : le meuble à gauche, le détail à droite — le
     même partage qu'à la cave. */
  #provisions .piece-scene { display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
    gap: 28px; align-items: start; }

  /* Le meuble emploie TOUT l'écran (les colonnes suivent, calculées au
     rendu) ; les alvéoles gardent leur taille de main. */
  #provisions .meuble-p { max-width: none; margin-inline: 0; padding: 6px 14px 14px; }
  #provisions .meuble-p .case-p svg { width: 34px; height: 34px; }
  #provisions .meuble-p .case-p--libre svg { width: 100%; height: 100%; }
  #provisions .casier-p--liste { columns: 340px; column-gap: 18px; }

  /* LA SCÈNE DU CASIER : le plan à gauche, « Dedans » à droite — l'écran
     sert à lire, pas à encadrer du vide. */
  #provisions .casier-scene { display: grid;
    grid-template-columns: minmax(0, 720px) minmax(280px, 1fr);
    gap: 28px; align-items: start; }
  #provisions .casier-plan { max-width: none; margin-inline: var(--marge) 0; }
  #provisions .piece__retour, #provisions .cave__nom, #provisions .cave__somme {
    max-width: none; margin-inline: 0; }

  /* Le disque d'ajout se cale au coin du contenu, pas de l'écran. */
  .poser { left: auto; right: 64px; margin-left: 0; transform: none; }
  .ours__compte { display: inline; }

  /* LES FEUILLES DEVIENNENT DES BOÎTES. Un tiroir qui monte du bas est un
     geste de pouce ; à la souris, sur 24 pouces, c'était une bande de
     téléphone posée au milieu de la page, voile compris (« filtre s'adapte
     pas à l'écran », Félix). Le voile reprend TOUT l'écran, la feuille se
     centre comme une boîte de dialogue, et la croix remplace la poignée. */
  .voile { left: 0; right: 0; width: auto; transform: none; }
  .feuille {
    left: calc(50% + var(--rail) / 2); right: auto; top: 50%; bottom: auto;
    width: min(600px, calc(100vw - 96px));
    max-height: min(84vh, 900px);
    border-radius: 16px; padding-bottom: 24px;
    box-shadow: 0 24px 80px -20px rgba(23, 21, 15, .45);
    /* ⚠️ FERMÉE = GARÉE HORS ÉCRAN PAR TRANSFORM, comme sur téléphone.
       La première version s'effaçait sur place (opacity + visibility) et
       le compositeur peignait des calques périmés : huit feuilles fantômes
       superposées au milieu de l'écran, alors que getComputedStyle jurait
       « hidden ». Un transform qui sort du viewport n'a pas ce piège — et
       c'est déjà le mécanisme du mobile, éprouvé. Elle glisse du bas. */
    transform: translate(-50%, 150vh);
  }
  .feuille.on { transform: translate(-50%, -50%); }
  body.sans-barre .feuille { left: 50%; }
  .feuille__poignee { display: none; }
  /* Collante : elle reste sous la souris même quand la liste des champs
     défile. La marge négative rend sa place au flux — le titre ne descend
     pas d'une ligne pour elle. */
  .feuille__croix { position: sticky; top: 12px; z-index: 2;
    display: grid; place-items: center;
    width: 44px; height: 44px; margin: 12px 0 -48px auto;
    border: 0; padding: 0; border-radius: 50%;
    background: var(--bande); color: var(--encre-2); cursor: pointer; }
  .feuille__croix:hover { color: var(--encre); }
  .feuille__croix svg { width: 20px; height: 20px; }
}

/* ===================================== 14. LA REVUE DE COHÉRENCE (nuit du 11)
   Correctifs mécaniques de la revue multi-largeurs. Les arbitrages de design
   (unification des titres de vues, contraste du feu en petit corps) attendent
   Félix — un agent ne retouche pas la charte à l'aveugle en pleine nuit. */

/* ⚠️ [hidden] BATTU PAR LE CSS, troisième fois que cette panne mord (après
   .nav__action). Toute règle qui pose un display doit céder devant l'attribut :
   l'étiquette « fichier de recette » et le bloc sauvegarde de la maquette
   s'affichaient en préprod alors que le HTML les déclarait cachés. */
.champ__etiquette[hidden], .sauvegarde[hidden] { display: none; }

/* La porte d'entrée ne réserve plus la moitié de l'écran à une photo qui
   n'existe pas encore : la zone se replie, le signet respire au centre, et
   le jour où la photo de famille arrive (--fond-accueil), elle reprend de
   la hauteur d'elle-même. */
.bienvenue__vue { height: clamp(120px, 24vh, 220px); }

/* Le compte de l'ours se tait quand la colonne est étroite : il vit déjà
   sous les gélules de vue, et « Mijote 5 rece… » tronqué en permanence est
   pire qu'absent. */
.ours__compte { display: none; }

/* Les gravures sont vertes, PARTOUT — les familles de la cave étaient les
   seules à l'encre, et la charte ne connaît pas d'exception. */
.famille { color: var(--sauge); }

/* La paire courses/réserve retrouve la marge que la réserve avait déjà. */


/* L'étiquette qui suit un titre ou un paragraphe reprend son souffle. */
.feuille h2 + .collerlien .champ__etiquette,
.form .entete__sous + .fiche-bloc .champ-nom { margin-top: 6px; }
.feuille h2 { margin-bottom: 14px; }

/* Les cibles tactiles remontent à 44 px sans changer le dessin : les flèches
   de rangement des carnets et la case du consentement gardaient leur taille
   de trait, pas leur taille de doigt (RGAA 14.1). */
.carnet__bouge { min-width: 44px; min-height: 44px; display: grid; place-items: center; }
.consent { min-height: 44px; align-items: center; }
.consent input { width: 24px; height: 24px; flex: none; }

/* LE SÉLECTEUR DE COUVERTS, dans le cartouche de mesures. Pas de popup : le
   − et le + vivent sur la fiche et recalculent la colonne machine — la
   parole ne bouge jamais (règle du plan, motif retenu de l'analyse Gronda).
   Les boutons font la cible tactile pleine (44 px) sans grossir le dessin. */
.mesure--pers { display: flex; flex-direction: column; align-items: center; }
.mesure__ligne { display: inline-flex; align-items: center; gap: 2px; }
.mesure__pas {
  width: 44px; height: 44px; padding: 0; border: 0; background: none;
  font: 400 22px/1 var(--f-ui); color: var(--sauge);
  display: grid; place-items: center; border-radius: 50%;
}
.mesure__pas:active { background: color-mix(in srgb, var(--sauge) 12%, transparent); }
.mesure--pers .mesure__n { white-space: nowrap; }

/* ================================= 15. LE TRÈS GRAND ÉCRAN (1600 et plus)
   Six colonnes : la mosaïque de l'accueil remplit la largeur sans étirer
   ses cases — 4 + 2 + 2 + 4 = 12 cellules, deux rangées pleines, rien
   d'orphelin à aucune largeur. */
@media (min-width: 1600px) {
  .bento { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  /* La rubrique aussi : vedette double largeur et les quatre cartes à sa
     suite — 2 + 4 = 6, une rangée pleine, toutes à la même hauteur. */
  .bloc[data-forme="rubrique"] .bloc__corps { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .bloc[data-forme="rubrique"] .carte:first-child { grid-row: auto; }
}

/* L'ÉTIQUETTE DANS LE PANNEAU — « il manque toujours l'étiquette, on
   l'ajoute où ? » (Félix, 13/08). La photo se prend après coup, bouteille
   en main, et elle se regarde ici : c'est elle qui dit ce que c'est tant
   que la fiche est vide. Hauteur bornée pour qu'une photo de téléphone ne
   pousse jamais le nom hors du panneau. */
.casier-cote__etiquette {
  display: block;
  width: 100%;
  max-height: 210px;
  object-fit: cover;
  margin-bottom: 12px;
  box-shadow: 0 2px 10px rgba(43, 38, 32, .16);
}

/* ------------------------------------------- ce que la maison a déjà
   « Le cellier évite de gaspiller en vérifiant toujours ce qui est dispo à
   la maison ; en passant sur une recette on pourrait avoir un affichage
   dynamique. Une personne qui dit "tiens, il me manque juste des tomates"
   va être tentée de la faire et de l'ajouter à son calendrier » (Félix,
   13/08).

   ⚠️ ON NE MARQUE QUE CE QUI MANQUE. Marquer aussi ce qu'on a couvrait la
   liste de pastilles : sur dix ingrédients dont on possède huit, huit
   marques vertes cachent les deux qui comptent. La phrase de la bande dit
   le positif, la ligne dit le négatif — et l'œil ne cherche qu'une chose.

   ⚠️ LE REPÈRE EST UN MOT, PAS UNE COULEUR (RGAA 3.1) : « à prendre » se
   lit en noir et blanc comme en couleur. */
/* ⚠️ ELLE S'APPELLE .chez-nous, PAS .maison — `.maison` EXISTE DÉJÀ (le
   plan des pièces de la réserve, une grille à deux colonnes). Ma bande en
   héritait : le texte à gauche, les deux pilules empilées à droite, vu à
   la capture. C'est la troisième collision de noms entre les feuilles de
   ce projet ; avant de nommer une classe, on la cherche. */
.chez-nous {
  margin: 0 0 14px; padding: 12px 14px;
  background: var(--carte); box-shadow: var(--ombre-1);
}
.chez-nous__mot { font: 500 15px/1.35 var(--f-ui); color: var(--encre); }
.chez-nous--complete .chez-nous__mot { color: var(--sauge); }
.chez-nous__gestes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.parole[data-chez="manque"] .parole__dit::after {
  content: "à prendre"; display: inline-block; margin-left: 8px;
  padding: 1px 8px; border-radius: var(--r-pilule);
  background: var(--feu-clair); color: var(--feu-texte);
  font: 600 12px/1.5 var(--f-ui); vertical-align: 1px;
}

/* ============================================ LA RÉSERVE, UN SEUL SYSTÈME
   « Le placard c'est pareil, design différent » (Félix, 14/08). La cave et
   le cellier partagent désormais le même en-tête, les mêmes cartes et la
   même fiche : ce qui suit habille les deux, il n'y a plus de feuille
   « cave » et de feuille « placard ». */

/* Les quatre portes, toujours sous la main : on passe du placard à la cave
   sans ressortir sur le plan de la maison. */
/* Le compte, sous la barre : il informe, il ne se manipule pas — il n'a
   donc rien à faire dans une barre d'outils collante. */
.reserve-compte { padding: 12px var(--marge) 10px; font: 600 12px/1 var(--f-ui);
  letter-spacing: .12em; text-transform: uppercase; color: var(--encre-2); }
/* ⚠️ Le champ tient sur la MÊME rangée que les trois lectures : un <input>
   revendique une vingtaine de caractères, ce qui suffisait à faire passer
   la barre à la ligne en poche. Il doit pouvoir rétrécir. */
/* ⚠️ `.outils .rapides` est plus spécifique : sans reprendre les deux
   classes, le flex-basis restait `auto` et la barre passait quand même à
   la ligne. */
.outils .rapides--cherche { display: flex; align-items: center; flex: 1 1 120px; }
.rapides--cherche .cherche { min-width: 0; }
.rapides--lieux { padding: 8px var(--marge) 0; }

/* Les deux lectures. La ligne dit CE QU'ON A, la case dit d'un coup d'œil
   ce qu'il y a ; les deux ouvrent la même fiche. */
.lignes-p { display: flex; flex-direction: column; gap: 8px; padding: 0 var(--marge); }
.ligne-p { display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 12px 14px; min-height: var(--touche); text-align: left;
  border: 0; background: var(--carte); box-shadow: var(--ombre-1); }
.ligne-p__vue { flex: 0 0 auto; display: grid; place-items: center;
  width: 34px; height: 34px; color: var(--sauge); }
.ligne-p__vue svg { width: 26px; height: 26px; }
.ligne-p__corps { flex: 1 1 auto; min-width: 0; }
.ligne-p__nom { display: block; font: 600 15px/1.2 var(--f-ui); color: var(--encre); }
.ligne-p__dit { display: block; font: 400 13px/1.4 var(--f-ui); color: var(--encre-3); }
.ligne-p__place { flex: 0 0 auto; font: 600 13px/1.2 var(--f-ui);
  color: var(--encre-2); white-space: nowrap; }
/* ⚠️ PAS DE TEXTE BARRÉ pour un produit entamé — « pourquoi il est barré,
   c'est chelou » (Félix). Entamé n'est pas supprimé : c'est un point. */
.ligne-p--entame .ligne-p__vue { position: relative; }
.ligne-p--entame .ligne-p__vue::after { content: ""; position: absolute;
  top: -1px; right: -1px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--feu); }

.cases-p { display: grid; gap: 10px; padding: 0 var(--marge);
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); }

/* ---------------------------------------------------------- la fiche
   Un tableau d'intitulés, pas une phrase à points médians : c'est ce qui
   rend utile de remplir les champs, et c'est ce qui manquait. */
.fiche-p__etiquette { display: block; width: 100%; max-height: 220px;
  object-fit: cover; margin-bottom: 14px; box-shadow: var(--ombre-1); }
.fiche-p__sans { display: grid; place-items: center; height: 96px;
  margin-bottom: 14px; background: var(--sauge-clair); color: var(--sauge); }
.fiche-p__sans svg { width: 44px; height: 44px; }
.fiche-p__tete { display: flex; align-items: baseline; gap: 10px; }
.fiche-p__nom { font: 600 22px/1.15 var(--f-titre); color: var(--encre); }
.fiche-p__nb { flex: 0 0 auto; padding: 2px 9px; border-radius: var(--r-pilule);
  background: var(--sauge-clair); color: var(--sauge); font: 600 12px/1.5 var(--f-ui); }
.fiche-p__sous { margin-top: 3px; font: 400 15px/1.35 var(--f-ui); color: var(--encre-3); }
.fiche-p__entame { display: inline-block; margin-top: 8px; padding: 1px 9px;
  border-radius: var(--r-pilule); background: var(--feu-clair);
  color: var(--feu-texte); font: 600 12px/1.6 var(--f-ui); }
.fiche-p__lignes { margin-top: 14px; }
.fiche-p__l { display: grid; grid-template-columns: 92px minmax(0, 1fr);
  gap: 12px; padding: 8px 0; border-top: 1px solid var(--bord); }
.fiche-p__l dt { font: 400 13px/1.35 var(--f-ui); color: var(--encre-3); }
.fiche-p__l dd { font: 500 15px/1.35 var(--f-ui); color: var(--encre); }
.fiche-p__parole { margin-top: 12px; font: 400 15px/1.5 var(--f-titre);
  font-style: italic; color: var(--encre-2); }
.fiche-p__rien, .fiche-p__attente { margin-top: 10px;
  font: 400 15px/1.45 var(--f-ui); color: var(--encre-3); }
.fiche-p__gestes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }

/* ⚠️ EN POCHE, PAS DE COLONNE : la fiche s'ouvre en feuille (voir
   montrerProduit). Le panneau resterait un bloc de texte sous la liste,
   qu'on n'irait jamais chercher. */
.reserve-scene > .casier-cote { display: none; }
@media (min-width: 1000px) {
  .reserve-scene { display: grid; align-items: start; gap: 28px;
    grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
    padding-right: var(--marge); }
  .reserve-scene > .reserve-corps { min-width: 0; }
  .reserve-scene > .casier-cote { display: block; position: sticky; top: 14px; }
}

/* ================================================ LE LIVRE, SUITE : LES LISTES

   Le passage au papier ne suffisait pas : une liste de blocs cernés reste
   une liste de boîtes, et c'est exactement ce que Félix refusait depuis le
   début. Dans un livre, une liste n'a pas de boîtes — elle a des FILETS et
   du rythme. Les lignes reposent sur la page, séparées par un trait, avec
   assez d'air pour qu'on lise plutôt qu'on scanne.

   ⚠️ La règle de forme tient toujours : ce qui se LIT n'a pas de cadre, ce
   qui se TOUCHE en a un (les pilules, le disque, les champs). Une ligne de
   liste se touche aussi, mais son cadre serait un cadre par ligne : c'est
   le filet qui fait la séparation, et le fond au survol qui fait la cible. */
.lignes-p { gap: 0; padding: 0; }
.ligne-p {
  background: none; box-shadow: none;
  border-bottom: 1px solid var(--trait);
  padding: 15px var(--marge);
}
.lignes-p .ligne-p:last-child { border-bottom: 0; }
.ligne-p:active { background: var(--carte); }

/* L'intitulé d'une section porte son filet, comme un titre courant de page. */
.reserve-compte {
  display: flex; align-items: center; gap: 12px;
  padding: 18px var(--marge) 10px;
}
.reserve-compte::after {
  content: ""; flex: 1 1 auto; height: 1px; background: var(--trait);
}

/* La barre d'outils vit SUR la page, elle n'est pas une bande rapportée. */
.outils { background: var(--blanc); border-bottom-color: var(--trait); }

/* Les cases gardent leur cadre : ce sont des CASES, l'idée même du casier,
   et le pointillé du vide n'a de sens que dans une grille de cadres. */

/* Les champs, sur le papier. Le fond gris plein faisait une dalle au milieu
   de la page ; un champ de livre, c'est une ligne à remplir : du papier, un
   filet, et le filet devient vert quand on y est. */
.champ, #recherche, #f-url, .boite__champ, input[type="text"], input[type="url"],
input[type="search"], textarea, select {
  background: var(--carte); border-color: var(--trait);
}
.champ:focus, #recherche:focus, #f-url:focus, .boite__champ:focus,
input[type="text"]:focus, input[type="url"]:focus, input[type="search"]:focus,
textarea:focus, select:focus { border-color: var(--sauge); background: var(--carte); }

/* La colonne de droite est une PAGE EN REGARD : un filet vertical l'ouvre,
   comme la gouttière d'un livre. Pas d'ombre, pas de carte flottante. */
@media (min-width: 1000px) {
  .reserve-scene > .casier-cote {
    padding-left: 28px; border-left: 1px solid var(--trait);
    min-height: 60vh;
  }
}

/* ====================================== LE TITRE COURANT, PRIS AUX KITS

   Trois UI kits de recettes ouverts dans Figma (14/08). Leur habillage est
   exactement celui qu'on vient de refuser — boîtes rondes, accent saturé,
   boutons pleine largeur — mais les trois font une chose que Mijote ne
   faisait pas, et qui est juste : LE TITRE DE LA PAGE EST GROS, EN HAUT,
   À GAUCHE. Chez nous il était minuscule et centré dans la barre, comme un
   titre de fenêtre système. Un livre met son titre courant en haut de page,
   dans sa vraie taille ; c'est la même règle, et elle rend enfin l'écran
   identifiable d'un coup d'œil.

   ⚠️ Le titre reste DANS la barre plutôt qu'ajouté au contenu de chaque
   écran : une seule règle CSS habille les quinze écrans, et le jour où l'on
   en ajoute un, il est coiffé sans qu'on y pense. */
.nav { grid-template-columns: auto 1fr auto; }
.nav__titre {
  justify-self: start; text-align: left;
  font: 600 22px/1.15 var(--f-titre); color: var(--encre);
  letter-spacing: -.004em;
}
/* Quand un retour existe (les écrans de second niveau), il passe devant le
   titre, en petit : on est dans une sous-page, la hiérarchie doit se voir. */
.nav__retour + .nav__titre { padding-left: 2px; }

/* ============================== LE GABARIT D'ÉCRAN : UNE PILE DE SECTIONS

   « Je parle pas de couleur, je parle de la mise en page globale »
   (Félix, 14/08), avec quatre kits à l'appui. Ce qu'ils partagent tous et
   que Mijote n'avait pas, c'est un GABARIT : un écran n'est pas une liste,
   c'est une pile de sections, et chaque section porte le même en-tête —
   un intitulé à gauche, son compte, un filet qui va jusqu'au bord.

   Mijote avait SIX habillages pour ce même objet : le rayon des courses,
   le seuil du meuble, le titre de bloc de l'accueil, le nom de casier, le
   jour de la semaine, le compte de la réserve. Six dessins pour une seule
   idée : c'est très exactement ce qui faisait « pas cohérent ».
   Un seul, désormais, et il vaut pour l'écran qu'on ajoutera demain. */
.course-rayon__nom,
.meuble-p__seuil,
.casier-p__tete,
.reserve-compte {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0; padding: 22px var(--marge) 10px;
  font: 600 12px/1.2 var(--f-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--encre-2);
}
.course-rayon__nom > *, .meuble-p__seuil > *, .casier-p__tete > * {
  font: inherit; letter-spacing: inherit; text-transform: inherit;
}
/* Le compte se dit en clair, jamais en pastille : dans un livre, un chiffre
   de section est un chiffre, pas un badge de notification. */
.course-rayon__n, .meuble-p__seuil small, .casier-p__n {
  font: 400 12px/1.2 var(--f-ui); letter-spacing: .06em; color: var(--encre-3);
  background: none; padding: 0; min-width: 0;
}
/* LE FILET QUI VA JUSQU'AU BORD. C'est lui qui fait tenir la pile : sans
   trait, deux sections successives se lisent comme une seule liste. */
.course-rayon__nom::after, .meuble-p__seuil::after,
.casier-p__tete::after, .reserve-compte::after {
  content: ""; flex: 1 1 auto; height: 1px; border: 0;
  background: var(--trait); align-self: center;
}

/* ========================================== LES COMMANDES, UNE SEULE FAMILLE

   « Nos boutons, nos filtres, c'est pas aussi fluide que le design »
   (Félix, 14/08). Il a raison, et le diagnostic est précis :

   1. DEUX DESSINS CÔTE À CÔTE. Dans la même barre d'outils, la gélule de
      filtre était sur --carte et le sélecteur de vue sur --blanc, avec
      deux jeux de bordures. Deux fonds à trois pixels l'un de l'autre, ça
      ne se nomme pas mais ça se voit — c'est ça, « pas fluide ».
   2. AUCUN ÉTAT. Ni survol, ni pression, ni focus clavier sur les gélules,
      les vues, les volets. Seul .b avait un scale au doigt. Une commande
      qui ne répond pas au toucher paraît morte.
   3. AUCUNE TRANSITION. Tout basculait d'un coup. « Fluide » est
      littéralement ce mot-là.

   Une seule base, un seul jeu d'états, une seule durée. ⚠️ Toute nouvelle
   commande s'ajoute à cette liste de sélecteurs plutôt que de se redessiner
   à côté : c'est la règle qui empêche le désordre de revenir. */
:root {
  --c-pas: .16s;
  --c-courbe: cubic-bezier(.2, .7, .3, 1);
}

.rapide, .choix-vue__on, .vue__pion, .filtrable, .reserve-vue,
.suggere, .trouve, .facette__val {
  background: var(--carte); border: 1px solid var(--bord); color: var(--encre-2);
  transition: background-color var(--c-pas) var(--c-courbe),
              border-color var(--c-pas) var(--c-courbe),
              color var(--c-pas) var(--c-courbe),
              transform .1s var(--c-courbe);
}
/* Le survol existe sur l'ordinateur, la pression sur le doigt : les deux
   disent la même chose et méritent le même dessin. */
@media (hover: hover) {
  .rapide:hover, .choix-vue__on:hover, .vue__pion:hover, .filtrable:hover,
  .suggere:hover, .facette__val:hover { background: var(--bande); border-color: var(--encre-3); }
}
.rapide:active, .choix-vue__on:active, .vue__pion:active, .filtrable:active,
.suggere:active, .facette__val:active, .ligne-p:active, .case-p:active,
.onglet:active, .poser:active { transform: scale(.96); }
.poser { transition: transform .1s var(--c-courbe), box-shadow var(--c-pas) var(--c-courbe); }
.poser:active { box-shadow: 0 4px 10px -6px rgba(245, 143, 31, .7); }

/* ⚠️ L'ANNEAU DE FOCUS EST OBLIGATOIRE (RGAA 10.7) et il n'existait nulle
   part sur ces commandes : au clavier, on ne savait pas où l'on était. */
.rapide:focus-visible, .choix-vue__on:focus-visible, .vue__pion:focus-visible,
.filtrable:focus-visible, .ligne-p:focus-visible, .case-p:focus-visible,
.onglet:focus-visible, .poser:focus-visible, .b:focus-visible,
.nav__action:focus-visible, .nav__retour:focus-visible {
  outline: 2px solid var(--sauge); outline-offset: 2px;
}

/* LE SÉLECTEUR DE LECTURES DEVIENT UN VRAI SEGMENTÉ : un seul cadre, des
   segments dedans. Trois pastilles séparées dont une pleine se lisaient
   comme trois boutons sans rapport ; soudées, elles disent « une seule
   question, trois réponses ». */
/* ⚠️ PAS DE PADDING SUR LE GROUPE : chaque pixel pris au cadre est pris au
   segment, et un segment sous 44 x 44 casse le critère tactile 14.1 (vu à
   l'audit, 46 x 36). Le cadre serre les segments, les segments gardent leur
   taille de doigt. */
.outils .choix-vue--icones {
  gap: 0; padding: 0; margin-right: 8px;
  background: var(--carte); border: 1px solid var(--bord);
  border-radius: var(--r-pilule);
  border-right: 1px solid var(--bord);
}
.choix-vue--icones .choix-vue__on {
  width: 48px; min-height: var(--touche); border: 0; background: none;
  border-radius: var(--r-pilule);
}
.choix-vue--icones .choix-vue__on.on { background: var(--sauge); }
.choix-vue--icones .choix-vue__on .gr { width: 19px; height: 19px; }

/* La rangée s'aligne sur le groupe segmenté, qui porte un cadre en plus. */
.outils .rapide, .outils .cherche { min-height: 46px; }

/* Les champs répondent aussi : le cadre se colore avant même la frappe. */
.champ, #recherche, #f-url, .boite__champ, input[type="text"], input[type="url"],
input[type="search"], textarea, select {
  transition: border-color var(--c-pas) var(--c-courbe),
              background-color var(--c-pas) var(--c-courbe);
}

/* ⚠️ ET RIEN NE BOUGE POUR QUI L'A DEMANDÉ (RGAA 13.8, prefers-reduced-motion).
   Une animation qu'on ne peut pas couper est une barrière, pas une finition. */
@media (prefers-reduced-motion: reduce) {
  .rapide, .choix-vue__on, .vue__pion, .filtrable, .reserve-vue, .suggere,
  .trouve, .facette__val, .poser, .champ, input, textarea, select {
    transition: none;
  }
  .rapide:active, .choix-vue__on:active, .vue__pion:active, .filtrable:active,
  .suggere:active, .facette__val:active, .ligne-p:active, .case-p:active,
  .onglet:active, .poser:active { transform: none; }
}

/* ============================ LA PASSE COMPLÈTE : LE RESTE DES ÉCRANS

   « Je parle pas que des commandes, je parle de l'app complète — attention
   tu limites à chaque fois ton travail » (Félix, 14/08). Juste. Ce qui suit
   fait entrer dans le système les écrans que je n'avais pas repassés :
   l'accueil, la semaine, les carnets, les courses, la fiche, les feuilles
   et les dialogues. Deux familles, et rien d'autre :

     — LA FAMILLE « EN-TÊTE DE SECTION » : intitulé en capitales espacées,
       compte en clair, filet jusqu'au bord.
     — LA FAMILLE « COMMANDE » : même base, mêmes états, même durée.       */

/* ---- 1. tous les en-têtes de section restants -------------------------- */
.bloc__tete, .jour__nom, .famille-tete__quoi + *, .courses-rayons > :first-child {
  align-items: baseline;
}
.bloc__tete, .jour__nom {
  display: flex; align-items: baseline; gap: 10px;
  padding-top: 18px; padding-bottom: 8px;
}
/* ⚠️ .bloc__titre RESTE ÉDITORIAL. Je l'avais fait passer en intitulé de
   section : « Qu'est-ce qu'on mange ce matin ? » en capitales de 12 px, ça
   tuait la voix de l'accueil. Unifier n'est pas aplatir — un titre de bloc
   de la une est un TITRE, pas une étiquette de rayon. Seul son en-tête
   prend le filet commun. */
.jour__nom {
  font: 600 12px/1.2 var(--f-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--encre-2);
}

/* ⚠️ DEUX RÔLES DE TITRE, PAS TROIS (RGAA 9.1, COHER-3 de l'audit). Les
   titres éditoriaux étaient à deux graisses de Playfair selon l'écran —
   500 ici, 600 là — ce qui comptait pour deux rôles à lui seul. Une seule
   graisse : le second rôle est celui des intitulés de section. */
h1, h2 { font-weight: 600; }
.jour__date, .bloc__no {
  font: 400 12px/1.2 var(--f-ui); letter-spacing: .06em; color: var(--encre-3);
}
.bloc__tete::after, .jour__nom::after {
  content: ""; flex: 1 1 auto; height: 1px; background: var(--trait);
  align-self: center;
}
/* Le jour courant reste marqué, mais par l'ENCRE et non par une teinte
   seule : sur un intitulé de section, la couleur seule ne suffit pas. */
.jour--la .jour__nom { color: var(--encre); }
.jour--la .jour__nom::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--sauge); align-self: center; flex: 0 0 auto;
}

/* ---- 2. la liste de courses ------------------------------------------- */
/* Une ligne de courses est une LIGNE DE LISTE, comme partout ailleurs :
   filet dessous, de l'air, et la quantité en colonne à droite. */
.course-rayon__lignes { padding: 0 var(--marge); }
.course { gap: 14px; padding: 13px 0; border-bottom: 1px solid var(--trait); }
.course-rayon__lignes .course:last-child { border-bottom: 0; }
.course__quoi { flex: 1 1 auto; min-width: 0; }
.course__q {
  flex: 0 0 auto; font: 600 15px/1.2 var(--f-ui); color: var(--encre-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* ⚠️ ICI LE BARRÉ EST JUSTE, et c'est la seule liste où il l'est. Sur la
   réserve, un produit entamé barré était « chelou » (Félix) parce qu'il
   existe encore. Aux courses, l'article est FAIT : le barré dit une tâche
   accomplie, pas un objet supprimé. */
.course.fait .course__nom { text-decoration: line-through; }
.course.fait .course__q { color: var(--encre-3); }

/* ---- 3. les tuiles de geste de la fiche, dans la famille commande ------ */
.choix {
  background: var(--carte); border: 1px solid var(--bord);
  border-radius: var(--r-carte); box-shadow: none;
  transition: background-color var(--c-pas) var(--c-courbe),
              border-color var(--c-pas) var(--c-courbe),
              transform .1s var(--c-courbe);
}
.choix:active { transform: scale(.97); }
.choix:focus-visible { outline: 2px solid var(--sauge); outline-offset: 2px; }
@media (hover: hover) { .choix:hover { background: var(--bande); border-color: var(--encre-3); } }

/* ---- 4. les feuilles et les dialogues --------------------------------- */
/* Ils RECOUVRENT la page : eux ont droit à une ombre, et c'est la seule
   chose qui les distingue du papier. Mais leurs boutons sont des boutons
   de l'app, pas des boutons de système. */
.boite, .feuille { border-radius: var(--r-carte); }
.boite__action {
  min-height: var(--touche); border-radius: var(--r-bouton);
  transition: background-color var(--c-pas) var(--c-courbe),
              transform .1s var(--c-courbe);
}
.boite__action:active { transform: scale(.97); }
.boite__action:focus-visible { outline: 2px solid var(--sauge); outline-offset: 2px; }

/* ---- 5. l'exergue et les encarts -------------------------------------- */
.exergue { border-radius: var(--r-carte); }

/* ---- 6. la barre du bas ----------------------------------------------- */
.onglet { transition: color var(--c-pas) var(--c-courbe); }

/* ===================== ÉTAPE 2 DE LA V2 : LA BARRE ET LES LIGNES

   « Mais ça ressemble pas du tout à ça » (Félix, maquette à l'appui).
   Juste : l'étape 1 n'avait changé que les couleurs, et une palette ne
   fait pas une mise en page. Ce qui reste à descendre de la maquette,
   c'est la STRUCTURE. Deux morceaux ici, choisis parce qu'ils touchent
   les quinze écrans d'un coup.

   1. LA BARRE DU HAUT devient une barre d'OUTILS : la marque au centre en
      petites capitales, les gestes de part et d'autre. Plus de gros titre
      qui redit le nom de l'écran — « quand je parle de réflexe web c'est
      ça, tu veux mettre des titres partout ».
   2. LES LIGNES DE LISTE passent à la serif, avec les points de conduite
      et la quantité en petites capitales à droite. C'est ce qui donne le
      sommaire de livre plutôt que la liste d'application.               */

/* ---- 1. la barre ------------------------------------------------------ */
.nav { grid-template-columns: auto 1fr auto; gap: 6px; min-height: 52px;
  padding: max(6px, var(--sur)) 10px 6px; }
.nav__titre {
  justify-self: center; text-align: center;
  font: 500 12px/1 var(--f-ui); letter-spacing: .24em;
  text-transform: uppercase; color: var(--encre); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* ⚠️ LE RETOUR ET L'ACTION GARDENT LEURS MOTS. J'avais essayé de les
   réduire à des carrés de 44 avec `font-size: 0` pour cacher le texte,
   comme les actes de la maquette. Deux raisons de renoncer :
   1. `font-size: 0` crée un CORPS DE ZÉRO dans l'échelle typographique,
      que l'audit compte comme un neuvième palier (COHER-4). Un contrôle
      qui existe pour empêcher le désordre a raison de refuser ça.
   2. Surtout, ces boutons portent des mots qu'on ne peut pas dessiner :
      « Vider le panier », « Enregistrer », « Modifier ». Un pictogramme
      inventé pour eux serait une devinette.
   Ils restent donc des mots, en petit et en retrait. */
.nav__retour, .nav__action {
  min-height: 44px; padding: 0 10px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  border-radius: var(--r-bouton);
  font: 500 13px/1 var(--f-ui); letter-spacing: .02em; color: var(--encre-2);
  transition: background-color var(--c-pas) var(--c-courbe);
}
.nav__retour svg, .nav__action svg { width: 18px; height: 18px; flex: 0 0 auto; }
@media (hover: hover) {
  .nav__retour:hover, .nav__action:hover { background: var(--bande); }
}

/* ---- 2. les lignes de liste ------------------------------------------ */
.ligne-p { padding: 14px var(--marge); gap: 14px; }
.ligne-p__nom {
  font: 400 19px/1.2 var(--f-titre); letter-spacing: -.01em; color: var(--encre);
}
.ligne-p__dit {
  font: 400 10px/1.3 var(--f-ui); letter-spacing: .1em;
  text-transform: uppercase; color: var(--encre-3);
}
.ligne-p__place {
  font: 400 10px/1 var(--f-ui); letter-spacing: .1em;
  text-transform: uppercase; color: var(--encre-3);
}
/* LES POINTS DE CONDUITE, le détail qui dit « sommaire » plus fort que
   n'importe quel choix de couleur. Ils poussent la quantité contre le bord
   droit, donc la colonne des chiffres s'aligne enfin d'une ligne à
   l'autre — ce qu'on lit vraiment devant un rayon. */
.ligne-p__corps { display: flex; align-items: baseline; gap: 10px; }
.ligne-p__corps::after {
  content: ""; flex: 1 1 auto; min-width: 14px; height: 1px;
  margin-bottom: 4px; border-bottom: 1.5px dotted var(--trait);
}
.ligne-p__nom { flex: 0 1 auto; }
.ligne-p__dit { order: 3; flex: 0 0 auto; }

/* ⚠️ LE DÉTAIL ET LA PLACE SE CHEVAUCHAIENT à la cave (« Delamotte · entamé
   · hier » par-dessus « Cave du Salon · A1 »), parce que le corps de la
   ligne pousse sans limite quand son contenu déborde. Il doit pouvoir
   rétrécir, et le détail se couper proprement : la place, elle, ne se
   coupe jamais — c'est l'information qu'on vient chercher. */
.ligne-p__corps { min-width: 0; }
.ligne-p__dit {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ligne-p__place { padding-left: 10px; }

/* ============ ÉTAPE 3 DE LA V2 : LES COMMANDES PRENNENT LA FORME DESSINÉE

   « Non mais c'est pas le design de ce qu'on vient de dessiner » (Félix).
   Exact, et je vois enfin où ça coince : les couleurs et les lignes sont
   passées, LES COMMANDES non. Ce sont elles qui trahissent l'ancienne app,
   parce que ce sont les objets les plus visibles de l'écran.

   Trois écarts avec la maquette, corrigés ici :
   — le rail des lieux était une rangée de GROSSES PILULES VERTES ; dans la
     maquette ce sont des ONGLETS SOULIGNÉS, discrets, où seul le trait vert
     dit où l'on est ;
   — le sélecteur de lectures était un groupe à fond vert plein ; il devient
     un segmenté neutre, l'actif en vert pâle ;
   — la recherche était une dalle grise sans nom ; elle devient une ligne
     de serif sous son intitulé, comme dans la maquette.                 */

/* ---- le rail des lieux : des onglets soulignés ------------------------ */
.rapides--lieux { gap: 22px; padding: 10px var(--marge) 0;
  border-bottom: 1px solid var(--trait); }
.rapides--lieux .rapide {
  width: auto; min-height: 42px; padding: 0 0 8px;
  background: none; border: 0; border-bottom: 2px solid transparent;
  border-radius: 0; color: var(--encre-3);
}
.rapides--lieux .rapide .rapide__dit {
  font: 500 12px/1 var(--f-ui); letter-spacing: .16em; text-transform: uppercase;
}
.rapides--lieux .rapide.on {
  background: none; color: var(--encre); border-bottom-color: var(--sauge);
}
@media (hover: hover) {
  .rapides--lieux .rapide:hover { background: none; color: var(--encre); }
}

/* ---- le segmenté : neutre, l'actif en vert pâle ----------------------- */
.outils .choix-vue--icones { background: none; }
/* ⚠️ L'ÉTAT COURANT NE PEUT PAS TENIR À LA SEULE TEINTE (RGAA 3.2, relevé
   par l'audit dès la première tentative). Le vert pâle de la maquette ne
   contraste pas assez avec le fond transparent des autres segments : il
   faut un repère de FORME. Le segment actif reçoit donc son cadre vert en
   plus du fond — c'est le dessin de la maquette, avec ce qu'il lui
   manquait pour être utilisable. */
.choix-vue--icones .choix-vue__on.on {
  background: var(--sauge-clair); box-shadow: inset 0 0 0 1.5px var(--sauge);
}
.choix-vue--icones .choix-vue__on.on .gr { color: var(--sauge); }

/* ---- la recherche : une ligne de serif, sous son nom ------------------ */
/* ⚠️ L'INTITULÉ EST PERMANENT. Le texte d'invite seul ne dit pas à quoi
   sert le champ — il disparaît à la première frappe (charte du projet, et
   Félix l'a relevé sur la maquette). Il est posé par le JS ; ici l'habit. */
.outils .rapides--cherche { flex-direction: column; align-items: stretch; gap: 0; }
.outils .rapides--cherche .cherche {
  min-height: 40px; padding: 0 0 6px;
  background: none; border: 0; border-bottom: 1px solid var(--trait);
  border-radius: 0; font: 400 17px/1 var(--f-titre); color: var(--encre);
}
.outils .rapides--cherche .cherche:focus { border-bottom-color: var(--sauge); }
.outils .rapides--cherche .cherche::placeholder {
  font: 400 15px/1 var(--f-ui); color: var(--encre-3);
}

/* ============================ ÉTAPE 4 DE LA V2 : LE BENTO DE L'ACCUEIL

   Les tuiles de site/essai-4.css, portées dans l'app. Deux colonnes, des
   rangées de 112 px, et des tuiles qui occupent 1, 2 ou 4 cases.

   ⚠️ AUCUNE RANGÉE ORPHELINE : le serveur ne renvoie une tuile que si elle
   a du contenu (accueil.mjs), donc la grille se ferme d'elle-même. Une case
   vide dans un bento se lit comme une panne, pas comme du vide. */
.bento-a {
  display: grid; grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 112px; gap: 10px;
  padding: 12px var(--marge) 4px;
}
.bento-a:empty { display: none; }
.tuile {
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 14px; text-align: left; background: var(--carte);
  border: 1px solid var(--trait); border-radius: var(--r-carte);
  color: inherit; font: inherit; overflow: hidden;
  transition: border-color var(--c-pas) var(--c-courbe), transform .1s var(--c-courbe);
}
.tuile:active { transform: scale(.985); }
.tuile:focus-visible { outline: 2px solid var(--sauge); outline-offset: 2px; }
/* Le haut de la grande tuile est la place de la photo du plat. Le dégradé
   le dit sans mentir : on voit qu'il manque une image, pas qu'on a oublié. */
.tuile--haute {
  grid-column: span 2; grid-row: span 2; justify-content: space-between;
  background: linear-gradient(var(--sauge-clair) 0 54%, var(--carte) 54%);
}
.tuile--large {
  grid-column: span 2; flex-direction: row; align-items: center;
  justify-content: space-between; gap: 12px;
}
.tuile__sur {
  display: block; font: 500 12px/1 var(--f-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--encre-3);
}
.tuile__mot {
  display: block; margin-top: 8px;
  font: 400 19px/1.08 var(--f-titre); letter-spacing: -.02em; color: var(--encre);
}
.tuile--haute .tuile__mot { font-size: 22px; }
.tuile__dit {
  display: block; margin-top: 6px; font: 400 12px/1.3 var(--f-ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--encre-3);
}
.tuile__n { margin-left: auto; font: 400 22px/1 var(--f-titre); color: var(--encre); }

/* ⚠️ LA VALEUR RÉSERVE DEUX LIGNES sur les demi-largeurs : « 2 choses »
   tient sur une ligne et « Sauce au chocolat » sur deux, et deux cartes
   côte à côte dont le texte ne démarre pas à la même hauteur se lisent
   comme un défaut d'alignement (Félix, sur la maquette). */
.tuile--avant .tuile__mot, .tuile--pret .tuile__mot {
  display: flex; align-items: flex-end; min-height: calc(2 * 1.08em);
}
/* Promue en pleine largeur pour fermer la rangée, la demi-tuile redevient
   une ligne : plus besoin de réserver deux lignes à sa valeur.
   ⚠️ La valeur reste À GAUCHE, sous son étiquette : poussée à droite, elle
   se lisait comme un compteur alors que c'est la phrase qui compte. */
.tuile--large.tuile--avant, .tuile--large.tuile--pret { flex-direction: column;
  align-items: flex-start; justify-content: flex-end; }
.tuile--large.tuile--avant .tuile__mot, .tuile--large.tuile--pret .tuile__mot {
  min-height: 0; margin-top: 8px; margin-left: 0;
}
/* ⚠️ BLANC SUR ORANGE VIF — DÉCISION DE FÉLIX, RÉPÉTÉE TROIS FOIS.
   « Blanc sur orange, vert sur blanc, tu es revenu en arrière. »

   Ce que la mesure dit, et je le laisse écrit pour qu'on sache : le blanc
   sur #E8720C tient 3,07:1, quand le référentiel en demande 4,5 pour du
   texte. J'ai proposé les deux sorties possibles — assombrir l'orange, ou
   écrire à l'encre — et Félix a choisi de garder l'orange vif ET le blanc.
   C'est son projet et sa palette : orange vif, vert, charbon, blanc.

   Deux garde-fous conservés : l'étiquette n'est plus en opacité réduite
   (elle tomberait plus bas encore), et le corps est monté pour rester du
   « grand texte » au sens du référentiel, où le seuil est de 3:1. */
/* ⚠️ LA TUILE DES COURSES PREND LA DOMINANTE, PAS L'ACCENT. « Je vois pas la
   palette qu'on vient de construire » (Félix, 19/08) — mesuré sur l'accueil :
   49 % de blanc pur, 5 % d'ambre, et **0 % de forêt**. La palette était bien
   appliquée, mais l'écran n'avait aucune surface pour la porter : une seule
   tuile colorée sur dix.

   Dans la maquette, le grand pavé est la LISTE DE COURSES et il porte la
   dominante ; l'ambre y est l'accent, sur une petite tuile et sur le bouton
   rond. C'est cette distribution-là qu'on rétablit : le forêt porte le blanc
   à 7,79, et les signes gardent l'accord de la planche — l'ambre sur le
   forêt, 3,53, au-dessus du seuil des éléments d'interface. */
.tuile--avant { background: var(--sauge); border-color: var(--sauge); }
/* ⚠️ L'EXCEPTION DISPARAÎT. Ces trois lignes portaient du blanc sur la
   tangerine, à 3,07 — la seule faute de contraste qui restait dans l'app, et
   elle était assumée faute de mieux. Sur l'ambre, l'encre de la planche donne
   7,50 : il n'y a plus rien à assumer. */
.tuile--avant .tuile__sur { color: var(--blanc); opacity: 1; font-size: 13px; }
.tuile--avant .tuile__mot { color: var(--blanc); }
.tuile--avant .tuile__vue { color: var(--signe-sur-sauge); }
.tuile--pret { background: var(--sauge-clair); border-color: var(--sauge-clair); }

/* ⚠️ LES COULEURS DOIVENT SE RÉPONDRE, PAS SE SUCCÉDER. « Le vert gagne et
   l'orange disparaît, tu ne joues pas entre les couleurs » (Félix, 19/08).
   Juste, et c'est le défaut de ma propre règle : une dominante à 65 % et une
   vive à 11 %, appliquées à un écran qui n'a qu'UNE tuile colorée, donnent un
   vainqueur et un figurant. La maquette, elle, alterne — un pavé sombre, un
   pavé chaud, un pavé pâle, et du blanc entre les deux.

   Trois tuiles portent donc trois rôles, et l'alternance se lit d'un coup
   d'œil :
     · les COURSES     → la dominante  (ce qui structure)
     · À FINIR         → la vive       (ce qui demande une décision)
     · les CARNETS     → la douce      (ce qui se consulte, la mémoire)
   Le reste garde le blanc : sans blanc entre elles, trois couleurs ne se
   répondent plus, elles se cognent. */
.tuile[data-va-reserve] { background: var(--feu); border-color: var(--feu); }
.tuile[data-va-reserve], .tuile[data-va-reserve] * { color: var(--sur-feu); }
.tuile[data-va-reserve] .tuile__vue, .tuile[data-va-reserve] .tuile__vue * {
  color: var(--signe-sur-feu);
}
/* ⚠️ « TEXTE NOIR DANS LA TUILE ? » puis « ET LES AUTRES COULEURS DE LA
   PALETTE ? » (Félix, 19/08). Les six sont mesurées sur l'ambre #F39A41 :

     encre     #211E1D  7,50   texte oui
     forêt     #3B5943  3,53   grand texte seulement
     crème     #FFFEF2  2,18   non          blanc  2,21  non
     vermillon #F15B22  1,52   non          blush  1,47  non

   Donc l'ambre n'accepte QUE deux encres, et une seule pour du petit texte.
   Le titre passe au forêt — la réponse de la planche — en le mettant en gras :
   à 19 px gras il relève du « grand texte », seuil 3,0, et 3,53 le passe. Pas
   de corps nouveau dans la charte, et le noir quitte la ligne la plus lue.
   Le surtitre de 12 px, lui, garde l'encre : à cette taille rien d'autre ne
   tient, et c'est une limite de la couleur, pas un réglage. */
.tuile[data-va-reserve] .tuile__mot {
  color: var(--signe-sur-feu); font-weight: 700;
}
.tuile[data-va-carnets] { background: var(--feu-clair); border-color: var(--feu-clair); }
.tuile[data-va-carnets], .tuile[data-va-carnets] * { color: var(--encre); }
/* ⚠️ SUR LE BLUSH, LA PLANCHE PROPOSE LE VERMILLON — ET IL NE TIENT PAS :
   2,23, sous le seuil des signes lui-même. C'est le seul des trois accords de
   la planche qui ne survive pas à la mesure. Le forêt prend le relais (5,17),
   et l'encre écrit (10,99). Une planche d'accords imprimés n'est pas une
   grille de lisibilité : là où elle se tait, on mesure. */
.tuile[data-va-carnets] .tuile__vue, .tuile[data-va-carnets] .tuile__vue * {
  color: var(--sauge);
}
.tuile--pret .tuile__sur { color: var(--sauge); }

@media (prefers-reduced-motion: reduce) {
  .tuile { transition: none; }
  .tuile:active { transform: none; }
}

/* ⚠️ LE BENTO EST UNE COMPOSITION DE TÉLÉPHONE. Étiré sur un écran
   d'ordinateur, il devient trois bandeaux de 1 700 px de large : la tuile
   « il te manque » ressemble à une alerte système, et plus rien ne se lit
   d'un coup d'œil — vu sur la capture de Félix. Il garde donc sa largeur
   de main et se centre, comme une page. */
@media (min-width: 760px) {
  .bento-a { max-width: 620px; margin-inline: auto; grid-auto-rows: 120px; }
}

/* Les dessins des tuiles : ils permettent de reconnaître une tuile sans la
   lire, ce qui est très exactement ce que le bento sert à faire. Verts
   comme partout ailleurs — sauf sur l'aplat orange, où c'est l'encre. */
.tuile__vue { display: block; width: 24px; height: 24px; color: var(--sauge);
  margin-bottom: auto; }
.tuile__vue svg { width: 24px; height: 24px; }
.tuile--large .tuile__vue { margin-bottom: 0; margin-right: 12px; flex: 0 0 auto; }
/* ⚠️ RÈGLE MORTE, ÉCRITE APRÈS CELLE QUI VOULAIT DU BLANC — donc c'est elle
   qui gagnait, et l'icône restait sombre sur l'orange (« pourquoi dans le
   widget icône et checkbox sont noirs ? », Félix). C'est le prix d'une
   feuille où l'on empile des correctifs : la dernière écrite l'emporte,
   même quand elle est la plus ancienne dans l'intention. Supprimée. */
.tuile--large.tuile--avant .tuile__vue, .tuile--large.tuile--pret .tuile__vue {
  margin-bottom: 8px;
}

/* ⚠️ UNE DEMI-TUILE NE S'ÉTIRE PAS, ELLE COUPE. « Il te manque pâte brisée,
   bonne noix de beurre » prenait trois lignes dans une case de 112 px : la
   valeur remontait par-dessus son étiquette, les deux se chevauchaient, et
   l'icône était rognée en haut (vu à la capture). Deux lignes au plus, puis
   les points de suspension — la tuile donne l'idée, la fiche donne le
   détail. */
.bento-a .tuile:not(.tuile--haute):not(.tuile--large) .tuile__mot {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 0; margin-top: 6px;
}
.bento-a .tuile:not(.tuile--haute) .tuile__vue { margin-bottom: 6px; }
/* La valeur se cale en bas de la case, l'étiquette juste au-dessus : les
   deux demi-tuiles d'une rangée s'alignent alors sur la même ligne de base
   même quand l'une tient sur une ligne et l'autre sur deux. */
.bento-a .tuile:not(.tuile--haute):not(.tuile--large) .tuile__sur { margin-top: auto; }

/* Le dessin du plat, dans la zone claire de la grande tuile — là où la
   photo se posera quand il y en aura une. */
.tuile__plat { display: grid; place-items: center; height: 54%;
  color: var(--sauge); margin: -14px -14px 0; }
.tuile__plat svg { width: 44px; height: 44px; }
.tuile--haute { justify-content: flex-end; }
.tuile--haute .tuile__sur { margin-top: 12px; }

/* ⚠️ UN BENTO JOUE AVEC LES TAILLES. « Là c'est trop basique et on profite
   pas du bento » (Félix) : huit demi-cases identiques empilées en quatre
   rangées, c'est une grille, pas un bento. Trois formats désormais — la
   grande (2 × 2), la HAUTE (1 × 2, une colonne sur deux rangées) et la
   large (2 × 1) — et le reste en demies. C'est l'inégalité qui fait qu'on
   balaie l'écran au lieu de le lire ligne à ligne. */
.tuile--haute2 { grid-row: span 2; justify-content: flex-end; }
.bento-a { grid-auto-flow: dense; }
/* La tuile haute a de la place : sa valeur peut prendre trois lignes. */
.bento-a .tuile--haute2 .tuile__mot { -webkit-line-clamp: 3; }
.bento-a .tuile--haute2 .tuile__vue { margin-bottom: auto; }

/* La tuile des courses : une liste qu'on coche sans quitter l'accueil. */
.tuile--liste { position: relative; justify-content: flex-start; }
.tuile-liste { list-style: none; margin: 8px 0 0; padding: 0; }
.tuile-liste li + li { margin-top: 2px; }
.tuile-liste__coche {
  display: flex; align-items: center; gap: 9px; width: 100%;
  min-height: 32px; padding: 0; background: none; border: 0; text-align: left;
}
.tuile-liste__rond {
  flex: 0 0 auto; width: 17px; height: 17px; border-radius: 50%;
  border: 2px solid var(--bord); background: var(--carte);
  transition: background-color var(--c-pas) var(--c-courbe),
              border-color var(--c-pas) var(--c-courbe);
}
.tuile-liste__coche:active .tuile-liste__rond {
  background: var(--sauge); border-color: var(--sauge);
}
.tuile-liste__mot {
  font: 400 15px/1.25 var(--f-titre); color: var(--encre);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tuile-liste__reste {
  display: block; margin-top: 6px; font: 400 12px/1 var(--f-ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--encre-3);
}
/* ⚠️ Le « + » fait 44 x 44 comme toute cible (critère tactile 14.1), même
   s'il n'a l'air que d'un signe. */
.tuile-liste__plus {
  position: absolute; right: 6px; bottom: 6px;
  width: 44px; height: 44px; display: grid; place-items: center;
  background: none; border: 0; border-radius: var(--r-bouton);
  /* ⚠️ 22 ET NON 24 : la charte compte huit corps, et ce « + » en inventait
     un neuvième. Il passait inaperçu parce que l'audit ne voyait pas encore
     l'accueil. */
  font: 400 22px/1 var(--f-ui); color: var(--sauge);
  transition: background-color var(--c-pas) var(--c-courbe);
}
@media (hover: hover) { .tuile-liste__plus:hover { background: var(--sauge-clair); } }
.tuile-liste__plus:focus-visible { outline: 2px solid var(--sauge); outline-offset: -3px; }

/* Sur la tuile orange, la coche devient un « + » : le geste n'est pas
   « c'est fait » mais « note-le ». */
.tuile-liste__rond--plus {
  display: grid; place-items: center; border-color: currentColor;
  background: none; color: inherit;
  font: 400 13px/1 var(--f-ui);
}
/* ⚠️ TOUT CE QUI ÉTAIT BLANC SUR LA TANGERINE PASSE À L'ENCRE. Sur l'ambre
   le blanc tombe à 2,21 ; l'encre de la planche y tient à 7,50. Les anneaux
   et les contours suivent : ce sont des éléments d'interface, seuil 3,0. */
.tuile--avant .tuile-liste__mot { color: var(--blanc); }
.tuile--avant .tuile-liste__reste { color: var(--blanc); opacity: .8; }
.tuile--avant .tuile-liste__plus { color: var(--blanc); }
.tuile--avant .tuile-liste__coche:active .tuile-liste__rond--plus {
  background: #FFFFFF; color: var(--feu);
}


/* ⚠️ CETTE RÈGLE FOURRE-TOUT ANNULAIT LES AUTRES. Elle datait de la
   tangerine, où le blanc était la moins mauvaise option ; posée en fin de
   feuille et avec `*`, elle repeignait en blanc tout ce que les règles
   précédentes venaient de régler — c'est elle qui gardait l'icône du panier
   en blanc alors que son conteneur était déjà passé au forêt.

   Sur l'ambre, le dessus est l'ENCRE (7,50). Les signes qui doivent porter
   l'accord de la planche — pictogramme, anneaux, contours — sont nommés
   juste en dessous, et cette fois ils gagnent, parce qu'ils sont plus
   spécifiques ET postérieurs. */
/* ⚠️ LE BLANC PUR N'APPARTIENT PAS À LA PLANCHE. « Tu ne respectes pas les
   couleurs inversables partout » (Félix, 19/08) — et l'endroit exact où je ne
   les respectais pas, c'est ici : le texte de la tuile forêt était en #FFFFFF,
   qui n'est aucune des six couleurs. Le crème de la gamme donne 7,68 au lieu
   de 7,79 : la différence est invisible à l'œil, et la palette redevient
   fermée sur elle-même. */
.tuile--avant, .tuile--avant * { color: var(--blanc); }
.tuile--avant .tuile__vue, .tuile--avant .tuile__vue * { color: var(--signe-sur-sauge); }
.tuile--avant .tuile__sur { opacity: 1; }
.tuile--avant .tuile-liste__rond--plus { border-color: var(--signe-sur-sauge); }
.tuile--avant .tuile-liste__coche:active .tuile-liste__rond--plus {
  background: #FFFFFF; color: var(--feu);
}

/* ⚠️ LE « + » ÉTAIT BLANC SUR BLANC. Depuis que la tuile des courses est
   passée à l'orange et celle des manques au papier, la règle « tout ce qui
   est sur l'orange est blanc » ne couvrait plus la seconde : son bouton
   gardait un blanc hérité et disparaissait sur le fond clair. On nomme donc
   les deux cas au lieu d'en déduire un de l'autre. */
.tuile--liste:not(.tuile--avant) .tuile-liste__plus { color: var(--sauge); }
.tuile--liste:not(.tuile--avant) .tuile-liste__rond--plus {
  border-color: var(--sauge); color: var(--sauge);
}
.tuile--liste:not(.tuile--avant) .tuile-liste__mot { color: var(--encre); }

/* ============================ LA COCHE, UN SEUL DESSIN POUR TOUTE L'APP

   « Tu as mieux comme bouton radio ? » (Félix). Oui, et il existait déjà :
   celui de l'écran des courses — 22 px, une couronne qui se remplit et un
   ✓ qui apparaît. Le mien, sur la tuile, en faisait 17 avec un simple
   remplissage. Même geste, deux dessins, exactement le défaut qu'on a
   passé la journée à corriger ailleurs. C'est l'ancien qui gagne : il est
   plus gros, donc plus facile à viser, et il DIT ce qu'il fait.

   ⚠️ Et le survol laissait un carré blanc : le fond de survol était plus
   grand que le rond et n'était pas arrondi. Il est rond et suit la
   couleur de sa tuile. */
.tuile-liste__rond {
  width: 22px; height: 22px; display: grid; place-items: center;
  border-width: 2px; font: 600 12px/1 var(--f-ui);
}
/* ⚠️ ET LE CENTRE EST VIDE DES DEUX CÔTÉS. « C'est pas le même bouton »
   (Félix, capture à l'appui) : les deux faisaient pourtant 22 px et 2 px de
   trait. Ce qui les séparait, c'est que la coche de l'orange gardait un fond
   crème — donc un DISQUE PLEIN sur l'aplat, quand celle du papier n'est
   qu'un anneau. Au-delà de l'incohérence, le disque plein ment : il a
   exactement l'allure d'une case déjà cochée, c'est-à-dire l'inverse de ce
   qu'il demande. Un seul dessin, donc : l'anneau. Il ne se remplit qu'au
   doigt, et seul le signe au centre distingue les deux gestes — rien pour
   « je l'ai pris », un « + » pour « note-le ». */
.tuile-liste__rond { background: none; }
/* ⚠️ ET SUR L'ORANGE, LE TRAIT EST BLANC. « Le bouton pour dire qu'on a le
   produit, en orange, fond gris, c'est chelou non » (Félix) : en vidant
   l'anneau de son fond crème, j'ai découvert son TRAIT, resté au beige de la
   charte. Un anneau beige sur un aplat orange se lit gris. La règle générale
   « tout ce qui est sur l'orange est blanc » ne l'attrapait pas : elle porte
   sur `color`, et une bordure a sa propre couleur. Le contour de focus avait
   le même défaut, en charbon sur l'orange. */
.tuile--avant .tuile-liste__rond { border-color: var(--signe-sur-sauge); }
.tuile--avant .tuile-liste__coche:focus-visible { outline-color: var(--signe-sur-sauge); }

/* ============================ LA LISTE OCCUPE LA TUILE

   « On peut mettre plus d'ingrédients et jouer avec les tailles de police
   pour que la liste prenne bien toute la tuile » (Félix, 15/08). La tuile
   réservait la hauteur d'une photo qu'elle n'a pas : trois lignes serrées en
   bas, un grand vide au-dessus.

   Elle emporte donc cinq articles au lieu de trois.

   ⚠️ AUCUN NOUVEAU CORPS DE TEXTE. La charte en compte huit et le contrôle
   COHER-4 refuse le neuvième. On alterne donc entre deux tailles qui
   existent déjà, 15 et 13, selon le nombre d'articles.

   ⚠️ ET LA HAUTEUR VIENT DU NOMBRE, PAS D'UN ÉTIREMENT. « C'est moche
   d'avoir deux lignes avec un espace comme ça » (Félix, capture à l'appui) :
   j'avais fait se PARTAGER la hauteur aux lignes, ce qui remplit bien à cinq
   et devient absurde à deux — deux articles écartés d'un trou. Les lignes
   gardent donc leur hauteur naturelle et se rangent sous leur titre ; c'est
   la LISTE qui remplit la tuile quand elle est longue, pas chaque ligne qui
   s'étire quand elle est courte. */
.tuile-liste { display: flex; flex-direction: column; }
.tuile-liste li { display: flex; align-items: center; }
.tuile-liste[data-n="3"] .tuile-liste__mot,
.tuile-liste[data-n="4"] .tuile-liste__mot { font-size: 13px; }

/* ⚠️ LE NOM ENTIER, PAS SON DÉBUT. « On voit pas les ingrédients complets
   sur mobile » (Félix, 15/08) : la ligne était tenue sur UNE ligne avec des
   points de suspension, donc « poignée de ciboules... » — et une liste de
   courses qui ne dit pas quoi acheter ne sert à rien devant le rayon. Elle
   passe donc à la ligne. Deux lignes quand la tuile en a la hauteur, une
   seule quand elle est réduite à une case : la limite suit la tuile, elle
   n'est plus un chiffre fixe. */
.tuile-liste__mot {
  white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 1; line-clamp: 1; overflow: hidden;
}
.tuile--haute2 .tuile-liste__mot { -webkit-line-clamp: 2; line-clamp: 2; }

/* ⚠️ SUR UNE SEULE CASE, L'ICÔNE REJOINT LE TITRE. « Sucre vanillé est
   coupé » (Félix) : une case fait 112 px, dont 28 de marges. L'icône empilée
   au-dessus du titre en mangeait 24 de plus, et le deuxième article passait
   sous le bord — la tuile le coupait sans le dire, ce qui est pire que de ne
   pas l'afficher. Couchée sur la ligne du titre, elle ne coûte plus rien en
   hauteur et reste présente, comme demandé pour toutes les cartes. */
.tuile--liste:not(.tuile--haute2) {
  display: grid; grid-template-columns: auto 1fr;
  align-content: start; column-gap: 8px; padding: 12px;
}
.tuile--liste:not(.tuile--haute2) .tuile__vue { margin-bottom: 0; }
.tuile--liste:not(.tuile--haute2) .tuile__tete { align-self: center; }
.tuile--liste:not(.tuile--haute2) .tuile-liste { grid-column: 1 / -1; margin-top: 4px; }
.tuile--liste:not(.tuile--haute2) .tuile-liste__coche { min-height: 28px; }

/* La tête : le titre à gauche, ce qui dépasse à droite, sur la même ligne. */
.tuile__tete { display: flex; align-items: baseline; gap: 8px; }
.tuile__tete .tuile__sur {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tuile__tete .tuile-liste__reste {
  margin: 0 0 0 auto; display: block; white-space: nowrap;
}
.tuile-liste__coche:active .tuile-liste__rond::after { content: "✓"; color: var(--blanc); }
.tuile--avant .tuile-liste__coche:active .tuile-liste__rond {
  background: #FFFFFF; border-color: var(--blanc);
}
.tuile--avant .tuile-liste__coche:active .tuile-liste__rond::after { color: var(--sauge); }

/* Le survol : un rond, jamais un carré, et de la couleur de la tuile. */
.tuile-liste__plus { border-radius: 50%; }
@media (hover: hover) {
  .tuile--liste:not(.tuile--avant) .tuile-liste__plus:hover { background: var(--sauge-clair); }
  .tuile--avant .tuile-liste__plus:hover { background: rgba(255, 254, 242, .22); }
}
.tuile-liste__plus:focus-visible { outline-offset: -2px; border-radius: 50%; }

/* L'étiquette de la tuile des manques porte un nom de plat : elle peut être
   longue, elle se coupe sur une ligne plutôt que de pousser la liste. */
.tuile--liste:not(.tuile--avant) .tuile__sur {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;
}

/* La quantité en tête de ligne, en chiffres tabulaires : c'est le premier
   mot qu'on cherche devant un rayon. */
.tuile-liste__qte { font: 600 13px/1 var(--f-ui); font-variant-numeric: tabular-nums; }


/* ============================================================ LE MOUVEMENT

   Ajouté le 19/08 après que Félix a montré motion-primitives et
   watermelon-platform. Les deux sont React + Tailwind + Framer Motion : rien
   n'en est réutilisable ici tel quel. Ce qui l'est, c'est l'INTENTION — un
   écran qui se substitue, une grille qui se pose au lieu d'apparaître d'un
   bloc — et le navigateur sait déjà faire les deux, sans dépendance.

   ⚠️ RIEN NE BOUGE POUR QUI A DEMANDÉ MOINS D'ANIMATION. Le bloc est écrit
   à l'envers exprès : le mouvement est déclaré DANS la requête « pas de
   préférence », donc l'absence de mouvement est le défaut, pas l'exception.
   C'est la seule écriture qui ne peut pas être oubliée. */
@media (prefers-reduced-motion: no-preference) {

  /* 1. LE CHANGEMENT D'ÉCRAN. L'ancien s'efface, le nouveau monte de trois
     pixels — assez pour qu'on lise une substitution, pas assez pour qu'on
     attende. 180 ms : au-delà, une application de cuisine devient lente. */
  ::view-transition-old(root) {
    animation: mij-sort 120ms var(--c-courbe) both;
  }
  ::view-transition-new(root) {
    animation: mij-entre 180ms var(--c-courbe) both;
  }
  @keyframes mij-sort { to { opacity: 0; } }
  @keyframes mij-entre {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
  }

  /* 2. LE BENTO SE POSE TUILE PAR TUILE. Le décalage est de 35 ms : dix
     tuiles se posent en 350 ms, ce qui reste sous le seuil où l'œil
     commence à attendre. ⚠️ Pas de translation horizontale — sur une
     grille, elle donne l'impression d'un défilement raté. */
  .bento-a .tuile {
    animation: mij-pose 260ms var(--c-courbe) both;
  }
  @keyframes mij-pose {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
    to   { opacity: 1; transform: none; }
  }
  .bento-a .tuile:nth-child(1)  { animation-delay:   0ms; }
  .bento-a .tuile:nth-child(2)  { animation-delay:  35ms; }
  .bento-a .tuile:nth-child(3)  { animation-delay:  70ms; }
  .bento-a .tuile:nth-child(4)  { animation-delay: 105ms; }
  .bento-a .tuile:nth-child(5)  { animation-delay: 140ms; }
  .bento-a .tuile:nth-child(6)  { animation-delay: 175ms; }
  .bento-a .tuile:nth-child(7)  { animation-delay: 210ms; }
  .bento-a .tuile:nth-child(8)  { animation-delay: 245ms; }
  .bento-a .tuile:nth-child(9)  { animation-delay: 280ms; }
  .bento-a .tuile:nth-child(n+10) { animation-delay: 315ms; }
}
