/* ═══════════════════════════════════════════════════════════════════════
   Maryse — styles communs aux pages index.php, generer.php et profil.php
   Palette de couleurs (clair/sombre), reset de base, header + navigation.
   Chaque page ajoute ensuite ses propres styles spécifiques dans son <style>.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: light dark;
  --safe-top: env(safe-area-inset-top, 0px);

  --green-mid: #7aA855;
  --green-btn: #5a8040;

/* ── `--sur-vert` : ce qu'on POSE SUR le vert de la maison ─────────────────
   En sombre, `--green-btn` est un vert CLAIR (#7ab850) : du blanc dessus donne
   2,39:1, sous les 4,5 exigés d'un texte. Au survol c'est pire — le vert
   s'éclaircit encore (#8fce63) et le blanc tombe à 1,88:1, donc viser le bouton
   l'efface. Le fond de page, lui, donne 7,64:1 au repos et 9,71:1 au survol :
   le survol AMÉLIORE, cette fois.
   Un seul jeton pour les trente-quatre surfaces concernées. Aucune couleur de
   FOND ne bouge, donc rien ne change de taille, de forme ni de position — et
   le thème clair est strictement identique à ce qu'il était. */
  --sur-vert: #fff;
/* Et ses deux sœurs, pour les deux autres accents pleins.
   L'AMBRE est le cas à part : elle ne change pas avec le thème. Son vert
   équivalent (#b8882a) est déjà si clair qu'un texte blanc n'y donne que
   3,19:1 EN THÈME CLAIR — le défaut n'attendait donc pas le mode sombre, il
   était là depuis toujours, et il tombait à 1,59:1 en sombre. Une encre brune
   très foncée tient les deux : 5,15:1 sur l'ambre clair, 10,33:1 sur l'ambre
   sombre. Une seule valeur, pas de bascule.
   Le BLEU, lui, se comporte comme le vert : blanc en clair (5,45:1), fond de
   page en sombre — #9ecbf5 avec du blanc dessus ne donnerait que 1,5:1. */
  /* Le plein AMBRE et son encre. `--amber-badge-text` reste ce qu'il est — une
     ENCRE, posée sur des fonds pâles. Rempli, il ne portait pas le blanc :
     3,19:1 mesuré, sous le seuil, et c'est pour ça que l'encre y était sombre.
     Adam veut du blanc dessus ; le plein descend donc d'un cran, à 5,41:1.
     En nuit, l'ambre clair porte l'encre de la page : 11,50:1. */
  --amber-plein: #8a6318;
  --sur-ambre: #fff;
  /* Le plein ROUGE d'un « Supprimer ». En clair, --danger est sombre et le
     blanc s'y pose bien. En sombre, --danger devient #e8847e — un rouge CLAIR :
     du blanc dessus tombait sous le seuil. Même mécanique que --sur-vert. */
  --sur-danger: #fff;
  --sur-bleu: #fff;
  --green-btn-hover: #4a6e32;
  /* Le vert du « + » AU REPOS : moins soutenu que `--green-btn`, sans être
     plus clair. C'est la seule commande pleine du bandeau ; au vert franc
     elle criait plus fort que le menu qu'elle sert à créer. Le survol la
     ramène à `--green-btn`.
     Les deux conditions sont MESURÉES, pas estimées : saturation 23 contre 33,
     luminance 0,168 contre 0,180. Un premier essai (#6b8a56) désaturait bien
     mais éclaircissait de 22 %, et faisait tomber la croix blanche de 4,57 à
     3,89 — sous le seuil. Celle-ci la remonte à 4,82. */
  --green-doux: #5d7a4d;
  --green-light: #eaf3de;
  --green-border: #d0e8bc;
  --green-strip: #f2f8ec;
  --green-strip-border: #deeece;

  --amber-bg: #fdf5e8;
  /* L'ambre DILUÉ des rangées « à revoir » (C1, validé le 25/08). `--amber-bg`
     n'est qu'à ΔE 7,9 de la carte — aussi discret qu'un fond d'en-tête, et
     c'est ce qu'Adam trouvait trop faible. Celui-ci est à 12,8, sans rien
     coûter à la lecture : le nom du plat y tient 4,77:1 en `--amber-plein`. */
  --amber-doux: #fcefda;
  --amber-border: #ecd9a8;
  --amber-text: #7a5820;
  --amber-badge-bg: #fdeec8;
  --amber-badge-text: #b8882a;

  /* L'ambre portait TROIS sens à la fois : le week-end, un créneau libre, et
     une alerte qui attend quelque chose de toi. Trois messages, une couleur —
     on ne pouvait plus les distinguer d'un coup d'œil. L'ambre ne garde donc
     que le troisième, et les deux autres reçoivent leur propre famille.
     Le week-end passe en PRUNE : une teinte de repos, qui ne réclame rien. */
  --wknd-bg: #f6eef8;
  --wknd-border: #e0cae6;
  --wknd-text: #6b4278;        /* 6,92:1 sur --wknd-bg */
  --wknd-strong: #f0e2f4;      /* la bande soutenue de la fenêtre du jour */
  --wknd-past: #efe9f1;        /* un week-end passé, désaturé comme les autres */

  --wknd-doux: #faf4fb;        /* la prune, diluée à mi-chemin du blanc */
  --sur-wknd: #fff;            /* l'encre qui se pose sur la prune pleine */

  /* ── La journée donne sa couleur à ce qu'elle contient ────────────────────
     « Libre » n'a PAS de teinte à lui. Chaque journée porte la sienne — verte
     en semaine, prune le week-end — et un créneau libéré ne fait que la
     reprendre, en moins prononcé. Six variables portées par la CARTE : tout
     son contenu en hérite, y compris les fenêtres qu'elle ouvre. Les valeurs
     ci-dessous sont celles de la SEMAINE ; `.weekend` les remplace en bloc.

     Elles vivent sur `:root` et pas seulement sur `.day-card` pour que les
     quelques commandes « libérer » posées hors d'une journée résolvent quand
     même quelque chose, au lieu de tomber sur du transparent. */
  --j-bande: var(--green-strip);        /* la bande de l'en-tête */
  --j-bord:  var(--green-strip-border);
  --j-encre: var(--green-btn-hover);    /* UNE seule encre : titre, panier, icônes */
  --j-doux:  #f8fcf5;                   /* le fond d'un créneau libre */
  /* Le bouton « libérer » ALLUMÉ prend l'encre de la journée, pleine. Un
     troisième vert (--green-btn) y donnait 4,57:1 pour le blanc posé dessus —
     ça passe, de justesse ; l'encre du jour monte à 5,89, et « une seule
     encre » veut dire une, pas deux.
     Écrire `--j-plein: var(--j-encre)` ici NE MARCHE PAS, et je l'ai vérifié
     à la sonde après l'avoir cru : une variable qui en appelle une autre est
     résolue sur l'élément où elle est DÉCLARÉE, puis héritée comme une valeur
     figée. Posée sur `:root`, elle vaut le vert partout — le week-end avait
     beau remplacer `--j-encre`, le bouton d'un samedi restait vert. La valeur
     est donc répétée là où l'encre est remplacée. */
  --j-plein: var(--green-btn-hover);
  --sur-j:   var(--sur-vert);
  --j-fort:  var(--green-light);        /* la bande plus soutenue des fenêtres */
  --j-fort-bord: var(--green-border);

  --blue-bg: #e8f0fd;
  --blue-border: #a8c8ec;
  --blue-text: #2a5a8a;
  --blue-badge-bg: #d8e8fc;
  --blue-badge-text: #2a6ab8;

  --page-bg: #f5f3ef;
  --card-bg: #ffffff;
  /* LA COULEUR QU'iOS LIT SOUS LA BARRE D'ÉTAT (v380) : celle du fond des
     voiles — voir `.mc-overlay`. Claire ici, sombre en mode sombre. */
  --voile-teinte: rgba(255,255,255,.12);
  --text: #3a3a2a;
  /* ⚠️ B.6 (Adam, 01/10) : « augmente la visibilité et le contraste, sur tout
     le site ». #8a8a78 ne donnait que 3,5:1 sur la carte et 3,2:1 sur le fond
     de page — sous le seuil AA de 4,5:1 pour du texte courant, et c'est le gris
     de TOUS les textes secondaires (comptes, « + Nouvelle liste », sous-titres,
     petites capitales). Même teinte chaude, plus sombre : 5,1:1 sur la carte,
     4,6:1 sur le fond de page, 4,5:1 sur le vert pâle. Le thème sombre passait
     déjà (6,3:1) et ne bouge pas. */
  --text-muted: #706f5e;
  /* Gris chaud assombri : l'ancien #c0bead ne donnait que 1,7:1 sur fond clair
     (seuil WCAG AA : 4,5:1) — placeholders et compteurs étaient quasi illisibles
     en extérieur. Teinte conservée, luminosité réduite -> 4,6:1. */
  --text-faint: #6f6e64;
  --border: #e8e4dc;
  --row-border: #f2efe9;
  /* L'AIR DU BAS (B.8, Adam 02/10) : ce qui reste entre le dernier bouton
     d'une feuille posée en bas (téléphone) et le bord de l'écran — EN PLUS de
     la barre gestuelle. 12 px collaient « Valider » au bord. Une seule
     valeur pour toutes les feuilles, et pour le bas des pages. */
  --air-bas: 24px;
  /* La barre gestuelle, sous une FEUILLE. Une variable, et non `env()` écrit
     en dur, pour qu'une fenêtre CLAVIER OUVERT puisse l'annuler : le clavier
     couvre la barre, et la web app gardait 34 px de blanc sous le pied
     (Adam, 02/10 — « Nouvelle liste », champ Nom). `.clavier-ouvert` est posée
     par `suivreZoneVisible`. */
  --sur-bas: env(safe-area-inset-bottom, 0px);

  --libre-text: #6a6a5a;
  --meal-type-text: #c0bead;

  --danger: #b71c1c;
  --danger-bg: #fce8e6;
  --danger-border: #f0c4c0;

  /* Hérités d'un ancien écran de suivi ; conservés, sans effet ailleurs */
  --done-bg: #fafdf8;
  --done-text: #5a8040;
  --rec-dot: #c02020;
  --block-text: #b71c1c;
  --block-bg: #fce8e6;
  --toggle-track-off: #d1d1d6;
  --toggle-track-on: #5a8040;
}

/* La bascule. Une seule règle décide de tout : le week-end remplace en bloc les
   variables de `:root`, et tout ce que la journée contient suit — l'en-tête, le
   panier, l'icône « journée libre », le fond d'un créneau libéré, le bouton
   allumé, et les fenêtres qu'elle ouvre.
   Elle vit dans la feuille COMMUNE parce que la fenêtre « Libérer ce repas » y
   vit aussi : posée dans index.php seulement, la fenêtre d'un samedi restait
   verte devant une carte prune. */
/* La fenêtre « Personnaliser » et la fenêtre « Libérer » ne sont PAS dans
   cette liste, et c'est un choix : leurs commandes — journée libre, repas
   libre, plat libre, « Rétablir », le rond de « Rien à prévoir » — restent au
   vert standard de l'application. Une commande n'est pas une marque de
   journée ; c'est la CARTE qui porte la couleur, pas ce qu'on lui fait. */
.day-card.weekend,.mc-card.weekend{
  --j-bande: var(--wknd-bg);
  --j-bord:  var(--wknd-border);
  --j-encre: var(--wknd-text);
  --j-plein: var(--wknd-text);
  --j-doux:  var(--wknd-doux);
  --sur-j:   var(--sur-wknd);
  --j-fort:  var(--wknd-strong);
  --j-fort-bord: var(--wknd-border);
}

@media (prefers-color-scheme: dark) {
  :root {
    --green-mid: #86c25f;
    --green-btn: #7ab850;
    --sur-vert: var(--page-bg);
    --sur-bleu: var(--page-bg);
    --sur-danger: var(--page-bg);
    --amber-plein: var(--amber-badge-text);
    --sur-ambre: var(--page-bg);
    --green-btn-hover: #8fce63;
    --green-doux: #6f9e56;   /* saturation 30 contre 42, luminance 0,285 contre 0,390 */
    --green-light: #23301c;
    --green-border: #33461f;
    --green-strip: #202a19;
    --green-strip-border: #2c3a20;

    --amber-bg: #2c2416;
    --amber-doux: #332a17;   /* ΔE 10,9 de la carte ; le nom y tient 8,92:1 */
    --amber-border: #4a3a1c;
    --amber-text: #e0b869;
    --amber-badge-bg: #4a3a1c;
    --amber-badge-text: #f0c878;

    --wknd-bg: #241a28;
    --wknd-border: #3d2c44;
    --wknd-text: #cba8d6;      /* 8,07:1 sur --wknd-bg */
    --wknd-strong: #3d2c44;
    --wknd-past: #1f1a22;

    --wknd-doux: var(--wknd-bg);
    --sur-wknd: var(--page-bg);

    /* Diluer en sombre : mesuré, et pas avec le bon instrument du premier coup.
       Un rapport de contraste ne mesure que la LUMINANCE — il annonçait 1,02
       pour la prune diluée, donc « invisible », alors que l'œil voit très bien
       le violet. En distance de COULEUR (ΔE) la prune diluée est à 13,8 du fond :
       tout sauf invisible. Le vrai point faible est le VERT dilué en sombre,
       à ΔE 3,9 quand la bande pleine est à 7,2. D'où ce choix : dilué en clair,
       la bande telle quelle en sombre. */
    --j-encre: var(--green-mid);
    --j-plein: var(--green-mid);
    --j-doux:  var(--green-strip);

    --blue-bg: #182432;
    --blue-border: #2c4a6a;
    --blue-text: #8fc0f0;
    --blue-badge-bg: #223850;
    --blue-badge-text: #9ecbf5;

    --page-bg: #14160f;
    --card-bg: #1e2118;
    --voile-teinte: rgba(20,22,15,.12);
    --text: #e8e6dc;
    --text-muted: #a2a294;
    --text-faint: #9a9a8c; /* 2,7:1 -> 5,7:1 sur les fonds sombres */
    --border: #2e3226;
    --row-border: #262a20;

    --libre-text: #9a9a8a;
    --meal-type-text: #6e6e60;

    --danger: #e8847e;
    --danger-bg: #3a201c;
    --danger-border: #4a2420;

    --done-bg: #1a2416;
    --done-text: #8fce63;
    --rec-dot: #e8605a;
    --block-text: #e8847e;
    --block-bg: #3a201c;
    /* Contraste renforcé pour le switch en mode sombre : trop proche du fond sinon */
    --toggle-track-off: #6b6e5e;
    --toggle-track-on: #8fce63;
  }
}

*{box-sizing:border-box;margin:0;padding:0;}
/* Le fond va sur <html> AUSSI, et pas seulement sur <body>. Tout ce qui dépasse
   de la boîte du body (rebond de défilement, zone de sécurité sous l'écran d'un
   iPhone, page plus courte que l'écran) est peint par <html> : sans cette règle
   il est BLANC par défaut. C'est la bande claire qu'on voyait en bas des modales
   sur Profil et Générer. index.php avait la règle dans son <style> ; les deux
   autres pages ne l'avaient pas. */
html{background:var(--page-bg);}
/* ⚠️ LA WEB APP iOS (Adam, 02/10 — la bande beige sous les fenêtres) : avec
   la barre d'état `black-translucent`, le `100%` de la page s'arrête à l'écran
   moins la barre d'état, et sur une page qui ne défile pas iOS ne montre rien
   de la page au-delà. La racine fait donc au moins l'écran entier. Classe posée
   par maryse-common.js, dans la web app SEULEMENT : Safari ne bouge pas. */
html.web-app{min-height:calc(100% + env(safe-area-inset-top, 0px));}
/* Si une bande subsiste malgré tout (mesurée à chaque fenêtre ouverte), elle
   prend la couleur de la feuille : on la lit comme le bas de la fenêtre. */
html.web-app.comble-blanc{background:var(--card-bg);}
/* Clavier ouvert, la barre gestuelle est sous le clavier : rien à lui laisser.
   Le pied garde son air, comme dans Safari. */
.clavier-ouvert{--sur-bas:0px;}
/* ⚠️ `100vh` FAIT DÉFILER UNE PAGE VIDE SUR TÉLÉPHONE (Adam, 4 sept. : « il y a
   quand même un scroll dans la page alors qu'il faut que ce soit vide »).
   Sur mobile, `vh` vaut le GRAND viewport — celui qu'on aurait si la barre
   d'adresse était rétractée — et pas ce qu'on voit. Le corps est donc toujours
   plus haut que l'écran de la hauteur de cette barre, et la page défile de
   soixante à cent pixels même quand elle ne contient qu'une carte.
   `svh` est le PETIT viewport, barres affichées : c'est ce qu'on voit
   réellement, et il ne change pas quand les barres se rétractent — contrairement
   à `dvh`, qui ferait bouger la mise en page pendant le défilement.
   Les deux lignes se suivent exprès : un navigateur qui ne connaît pas `svh`
   ignore la seconde et garde l'ancienne. */
body{font-family:-apple-system,BlinkMacSystemFont,'Helvetica Neue',Arial,sans-serif;background:var(--page-bg);color:var(--text);min-height:100vh;min-height:100svh;}

/* ── Header + navigation (communs aux 3 pages) ───────────────── */
/* ⚠️ `.safe-top-fill` A VÉCU (ménage du 25/09) — le bandeau vert qui bouchait
   l'encoche au-dessus du header. Le header blanc (`.app-header`, plus bas) porte
   lui-même son `env(safe-area-inset-top)`. `.sticky`, juste en dessous, reste. */
.sticky{position:sticky;top:var(--safe-top);z-index:50;}
/* ⚠️ `.top-bar`, `.logo`, `.nav-tabs` ET `.nav-tab` ONT VÉCU (ménage du 25/09)
   — LE PREMIER HEADER de Maryse : un bandeau vert avec le nom à gauche, et des
   onglets pleine largeur en dessous, soulignés à l'actif. Six règles, aucun
   poseur dans les six pages qui chargent cette feuille. Ce qui les remplace est
   écrit dix lignes plus bas : `.app-header` et son `.brand`, blanc et fixe,
   posés par index, profil, favoris et admin. */

/* ── Header/navigation blanc, FIXE (ne disparaît plus au scroll) ─────────────
   Maryse (logo, gauche) — Menus · Générer (centre) — Profil (droite).
   Sert de barre de navigation principale sur toutes les pages. ── */
/* Conteneur collant qui regroupe le bandeau de notification ET l'en-tête :
   un message pousse ainsi la barre de navigation vers le bas au lieu de la
   recouvrir — on peut continuer à naviguer pendant qu'il est affiché.
   (Auparavant display:contents + header sticky ; le bandeau, fixé par-dessus,
   masquait la navigation.) */
.topstack{position:sticky;top:0;z-index:51;}
.app-header{background:var(--card-bg);border-bottom:1px solid var(--border);box-shadow:0 1px 3px rgba(0,0,0,.06);display:flex;align-items:center;gap:10px;padding:9px 16px;padding-top:calc(9px + env(safe-area-inset-top));}
.app-header .brand{display:flex;align-items:center;gap:7px;font-size:16px;font-weight:600;color:var(--text);text-decoration:none;flex-shrink:0;}
.app-header .brand svg{display:block;}
.app-nav{flex:1;display:flex;justify-content:center;gap:18px;}
/* Onglets : pas de trait sous les actifs, la couleur verte suffit. */
.app-nav-link{font-size:13px;font-weight:600;color:var(--text-muted);text-decoration:none;padding:5px 2px;white-space:nowrap;}
.app-nav-link.active{color:var(--green-btn);font-weight:700;}
.app-nav-profile{flex-shrink:0;}
/* L'EN-TÊTE SOUS 357 PX (point 6 de N, Adam, 10/10 : « l'option A ») : deux
   paliers posés par `majEntete` (maryse-common.js), MESURÉS — 1. resserrer :
   l'air de la page (12 px), les onglets à 12 px d'écart ; 2. la feuille
   seule : le mot « Maryse » s'efface (le lien garde son nom). */
.app-header.serre{padding-left:12px;padding-right:12px;gap:8px;}
.app-header.serre .app-nav{gap:12px;}
.app-header.feuille .brand-mot{display:none;}
@media (hover:hover){.app-nav-link:hover{color:var(--text);}.app-nav-link.active:hover{color:var(--green-btn);}}

/* ── Champs texte communs (toutes les pages), sauf les boutons-champs comme "+Plus"
   pour le nombre de personnes (classe .opt-btn) : fond transparent pour se fondre dans
   la section plutôt que de créer un encart contrasté, juste un contour. On retire le
   focus par défaut du navigateur au profit d'un contour plus foncé. ── */
/* `type="search"` est inclus depuis le 06/08 : il ne l'était pas, et le champ de
   recherche des préférences affichait le contour bleu du navigateur alors que
   tout le reste de l'appli passe au liseré vert. Le type reste `search` et non
   `text` : sur mobile, il donne une touche « Rechercher » au clavier. */
input:not([type]):not(.opt-btn), input[type="text"]:not(.opt-btn), input[type="search"]:not(.opt-btn), textarea:not(.opt-btn) {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  outline: none;
  box-shadow: none;
  -webkit-tap-highlight-color: transparent;
}
input:not([type]):not(.opt-btn):not([readonly]):focus, input[type="text"]:not(.opt-btn):not([readonly]):focus, input[type="search"]:not(.opt-btn):not([readonly]):focus, textarea:not(.opt-btn):not([readonly]):focus,
input:not([type]):not(.opt-btn):not([readonly]):focus-visible, input[type="text"]:not(.opt-btn):not([readonly]):focus-visible, input[type="search"]:not(.opt-btn):not([readonly]):focus-visible, textarea:not(.opt-btn):not([readonly]):focus-visible {
  outline: none;
  box-shadow: none;
  border-color: var(--green-btn);
}
/* ⚠️ LE FOCUS NE SE MONTRE QU'AU CLAVIER (Adam, 13/10). D'abord « enlève-le
   complètement » (capture : un carré bleu autour du « ⋮ », de « ••• » et
   d'autres boutons, quand le focus revient au bouton d'ouverture, une
   fenêtre refermée) ; puis, le prix mesuré (au clavier, plus rien ne
   montrait où est le focus) : « oui mais que au clavier ». Donc :
   - plus JAMAIS le contour du navigateur, ni au clic, ni quand une fenêtre
     rend le focus ;
   - un anneau VERT dès qu'on navigue au clavier, sous `html.nav-clavier`
     (posée par Tab ou une flèche, ôtée au premier toucher ou clic :
     `suivreLeClavier`, commun) ;
   - les repères propres à une classe (le fond d'une option, l'anneau d'une
     tuile) suivent la même loi : `:where(html.nav-clavier)` devant eux.
   Sans le JS commun (connexion, inscription, admin), pas de
   `html.clavier-suivi` : l'anneau s'en remet à `:focus-visible`.
   `:where()` : ces règles d'ensemble ne pèsent rien face à celles d'une
   classe, qui gardent leur repère à elles. */
:where(button, a, summary, select, input[type="checkbox"], input[type="radio"], [role="button"], [tabindex]):focus {
  outline: none;
}
:where(html.nav-clavier) :where(button, a, summary, select, input[type="checkbox"], input[type="radio"], [role="button"], [tabindex]:not([tabindex="-1"])):focus,
:where(html:not(.clavier-suivi)) :where(button, a, summary, select, input[type="checkbox"], input[type="radio"], [role="button"], [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: 2px solid var(--green-btn);
  outline-offset: 2px;
}
/* Un champ en lecture seule (profil non édité) ne doit jamais afficher de contour de focus,
   même s'il est cliqué (un <input readonly> reste focusable). */
input[readonly]:focus, input[readonly]:focus-visible, textarea[readonly]:focus, textarea[readonly]:focus-visible {
  outline: none;
  box-shadow: none;
  border-color: transparent;
}

/* ═══ Bandeau de notification unique ═══════════════════════════════════
   Placé DANS LE FLUX, en tête du conteneur collant : il pousse la barre de
   navigation vers le bas plutôt que de la recouvrir. Pas d'animation :
   l'apparition doit être immédiate, pas spectaculaire. */
.mt-banner{
  display:none;
  padding:calc(9px + env(safe-area-inset-top)) 18px 9px;
  font-size:13px;font-weight:600;line-height:1.4;text-align:center;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.mt-banner.show{display:block;}
.mt-banner.mt-ok{background:var(--green-btn);color:var(--sur-vert);}
.mt-banner.mt-warn{background:var(--amber-plein);color:var(--sur-ambre);}
.mt-banner.mt-info{background:var(--green-light);color:var(--green-btn);}
.mt-banner.mt-error{background:var(--danger);color:#fff;}
/* Le bandeau occupe déjà la zone sûre : l'en-tête ne doit pas la reprendre. */
.mt-banner.show + .app-header{padding-top:9px;}

/* ═══ Modale partagée (shadowbox) : confirmations + éditeurs ═══════════════ */
/* ⚠️ LE VOILE DIT « CLAIR » À iOS, ET SE PEINT SOMBRE PAR-DESSUS (v380 —
   Adam, 27/10, captures de l'iPhone : « quand une modale en bas apparaît,
   l'ombre derrière fait basculer l'heure et le wifi en blanc […] il faut que
   ce soit noir tout le temps en version claire, et tout le temps blanc en
   version sombre »). iOS 26 choisit la couleur du texte de la barre d'état
   d'après l'élément FIXE qu'il trouve au bord du haut, au milieu : la PREMIÈRE
   couleur de fond pleine de cet élément, lue à pleine force dès que son alpha
   passe 0,1 (dessous, il l'ignore) — et il ne la relit qu'à certains moments
   (un fondu qui passe 0,1, un défilement). Un voile `rgba(0,0,0,.45)` se
   lisait NOIR : texte blanc. La fiche flottante, sans fondu, n'était pas
   relue : texte noir — d'où l'un et l'autre selon la fenêtre, et après
   « Renommer » (sa carte blanche en haut, pleine hauteur), le noir qui
   restait. Ici, la couleur de fond est celle de la page à 12 % (`--voile-teinte`,
   claire ; sombre en mode sombre) : c'est elle qu'iOS lit, à pleine force. Le
   voile, lui, est une IMAGE (un dégradé uni) — une image ne compte pas — peinte
   par-dessus : à l'œil, rien ne change (un noir sous le voile passe de 0 à
   6 %). Même règle pour `.modal-overlay` et `.rx-chat-ov`. */
.mc-overlay{position:fixed;inset:0;z-index:1000;overscroll-behavior:contain;background:linear-gradient(rgba(0,0,0,.45),rgba(0,0,0,.45)) var(--voile-teinte);display:flex;align-items:flex-start;justify-content:center;padding:calc(24px + env(safe-area-inset-top, 0px)) 14px calc(24px + env(safe-area-inset-bottom, 0px));overflow-y:auto;-webkit-overflow-scrolling:touch;}
.mc-card{width:100%;max-width:440px;background:var(--card-bg);border-radius:14px;border:1px solid var(--border);padding:18px;display:flex;flex-direction:column;gap:12px;margin:auto;}
.mc-head{display:flex;align-items:center;justify-content:space-between;gap:10px;}
.mc-title{font-size:17px;font-weight:700;color:var(--text);}
/* LA MÊME CROIX POUR TOUTES LES FENÊTRES, petites et grandes. Elles étaient
   deux règles distinctes, et elles avaient divergé : `.mc-close` portait un
   socle `var(--page-bg)` permanent — un carré gris visible en permanence, plus
   sombre que la carte en mode sombre —, là où `.modal-close` n'avait que sa
   croix. Une croix n'a pas besoin de socle : le fond n'apparaît qu'au survol,
   et seulement là où un pointeur existe.
   Un seul jeu de déclarations pour les deux sélecteurs, sinon elles
   redivergeront — elles l'ont déjà fait une fois. */
.mc-close,.modal-close{width:32px;height:32px;flex-shrink:0;border:none;border-radius:8px;
            background:none;color:var(--text-muted);font-size:20px;cursor:pointer;line-height:1;
            display:flex;align-items:center;justify-content:center;}
@media (hover:hover){
  .mc-close:hover,.modal-close:hover{background:var(--border);color:var(--text);}
}
.mc-msg{font-size:13.5px;color:var(--text-muted);line-height:1.5;white-space:pre-line;}
.mc-actions{display:flex;gap:8px;margin-top:4px;}
.mc-actions button{flex:1;padding:11px;border-radius:8px;font-size:13.5px;font-weight:700;cursor:pointer;}
.mc-cancel{border:1px solid var(--border);background:none;color:var(--text-muted);}
@media (hover:hover){.mc-cancel:hover{background:var(--page-bg);}}
.mc-confirm{border:none;background:var(--danger);color:var(--sur-danger);}
/* La couleur du texte vient de `.mc-confirm`, qui a un fond ROUGE : elle est
   donc blanche, et elle le restait quand le modificateur repeignait le fond en
   vert. Trois modificateurs, trois encres — chacun la sienne. */
.mc-confirm.mc-primary{background:var(--green-btn);color:var(--sur-vert);}
@media (hover:hover){.mc-confirm:hover{opacity:.88;}}
@media (min-width:560px){.mc-actions{justify-content:flex-end;}.mc-actions button{flex:0 0 auto;min-width:120px;}}

/* ── LA FENÊTRE QUI ANNONCE ───────────────────────────────────────────────
   ⚠️ LE BANDEAU DE TITRE TEINTÉ A VÉCU ICI (13/08 → 18/09). Adam : « même
   style que supprimer, en-tête noir, pas de couleur dans la bannière de tête,
   même si c'est ambiancé en ambre ». Une fenêtre qui change de contour et de
   titre selon son sujet devient une FAMILLE de fenêtres, à retenir en plus des
   autres. Il n'y en a qu'une, et c'est son BOUTON qui porte la couleur : les
   trois teintes vivent dans le bloc de la cohérence, plus bas.

   CE QUI SURVIT DU BANDEAU : la largeur des boutons. « Générer quand même » à
   parts égales passait sur deux lignes ; l'action prend donc la largeur de son
   texte et « Annuler » occupe le reste. Donner plus de place au retrait qu'à
   l'action, sur un avertissement, n'est pas une maladresse — c'est le bon sens.
   La règle suit maintenant la TEINTE, seule trace qu'il reste de ces
   fenêtres-là. */
.mc-card.cp-ambre .mc-actions button,
.mc-card.cp-bleu .mc-actions button,
.mc-card.cp-rouge .mc-actions button{white-space:nowrap;}
.mc-card.cp-ambre .mc-actions:not(.seul) .mc-confirm,
.mc-card.cp-bleu .mc-actions:not(.seul) .mc-confirm,
.mc-card.cp-rouge .mc-actions:not(.seul) .mc-confirm{flex:0 0 auto;}

/* UN SEUL BOUTON : la fenêtre ANNONCE, elle ne demande pas. Il se pose EN BAS
   À DROITE avec un plancher de largeur, et ne s'étire plus d'un bord à l'autre
   (Adam, 18/09) : « Compris » pleine largeur avait l'air d'un engagement, alors
   qu'il n'y a rien à engager — ce que la fenêtre raconte a déjà eu lieu. */
.mc-actions.seul{justify-content:flex-end;}
.mc-actions.seul .mc-confirm{flex:0 0 auto;min-width:132px;}

/* (« Je n'ai pas pu tout garder » et sa liste des recettes écartées, `.rt-*`,
   sont partis avec U.4, 02/11 : un favori se pose sur son créneau.) */

/* ═══════════════════════════════════════════════════════════════════════
   MARYSE-CSS-VERSION: 155
   Ouvre https://maryse.app/sources/css/maryse-common.css et cherche cette
   ligne : elle dit quelle version est RÉELLEMENT en ligne. Le ?v= de l'URL ne
   prouve rien — le serveur renvoie le même fichier quel que soit ce paramètre.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════
   Champ « Allergies et intolérances » — commun à profil.php et à la modale
   de generer.php. Voir createAllergyField() dans maryse-common.js.

   ENTIÈREMENT AUTONOME, et c'est le point. La première version réutilisait
   les classes .infield-* … qui n'existent QUE dans le <style> de profil.php.
   Dans la modale de Générer le champ se retrouvait sans aucune règle : input
   large de quelques centimètres, puces écrasées à « Glu × » et « Pai × ».
   Un composant partagé qui dépend du CSS de sa page d'accueil n'est pas
   partagé. Tout ce dont .af-* a besoin est ici.

   DEUX NATURES DE PUCE, ET LA DIFFÉRENCE DOIT SE VOIR :
   ─ codée  : fond plein. Maryse la reconnaît, elle filtrera la base de
              recettes dessus, sans appel au modèle.
   ─ libre  : contour POINTILLÉ. Maryse ne la connaît pas, elle la transmet
              comme contrainte forte et vérifie plat par plat.

   Les puces libres ne sont PAS atténuées : même couleur, même graisse.
   Atténuer suggérerait « moins important », alors que c'est seulement
   « moins automatisable ». Les deux sont dans le champ allergies, les deux
   priment sur les précisions.
   ═══════════════════════════════════════════════════════════════════════ */
.af-wrap{display:flex;flex-direction:column;gap:8px;min-width:0;}
.af-chips{display:flex;flex-wrap:wrap;gap:7px;align-items:center;min-width:0;}
.af-empty{font-size:12px;color:var(--text-faint);}

/* La puce ne rétrécit JAMAIS : elle passe à la ligne. Sans flex:0 0 auto, le
   nœud de texte devient un élément flexible anonyme que le navigateur comprime
   avant de retourner à la ligne — c'est ce qui coupait « Gluten » en « Glu ».
   Le libellé est dans son propre <span> pour la même raison. */
.af-chip{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;max-width:100%;
         font-size:12px;font-weight:600;padding:5px 6px 5px 11px;border-radius:20px;
         background:var(--danger-bg);color:var(--danger);border:1.5px solid transparent;}
.af-chip.free{background:transparent;border:1.5px dashed var(--danger);}
.af-chip .af-chip-label{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.af-chip button{flex:0 0 auto;border:none;background:none;color:var(--danger);cursor:pointer;
                font-size:14px;line-height:1;padding:0 2px;}

/* Saisie + liste de suggestions ancrée dessous. */
.af-field{position:relative;min-width:0;}
.af-input{width:100%;box-sizing:border-box;padding:11px 46px 11px 12px;border:1px solid var(--border);
          border-radius:8px;font-size:16px;background:transparent;color:var(--text);font-family:inherit;}
.af-input::placeholder{color:var(--text-faint);}
.af-ok{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:32px;height:32px;padding:0;
       border:none;border-radius:7px;background:var(--green-btn);color:var(--sur-vert);cursor:pointer;
       display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;transition:opacity .15s ease;}
.af-ok svg{width:16px;height:16px;display:block;}
.af-field.dirty .af-ok{visibility:visible;opacity:1;}
@media (hover:hover){.af-ok:hover{background:var(--green-btn-hover);}}

.af-sugg{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:40;
         background:var(--card-bg,var(--page-bg));border:1px solid var(--border);border-radius:10px;
         box-shadow:0 8px 24px rgba(0,0,0,.16);overflow:hidden;display:none;}
.af-sugg.open{display:block;}
.af-sugg button{display:flex;width:100%;align-items:baseline;gap:7px;padding:10px 12px;
                border:none;background:none;text-align:left;cursor:pointer;font-size:13.5px;color:var(--text);}
.af-sugg button+button{border-top:1px solid var(--border);}
.af-sugg button.on,.af-sugg button:hover{background:var(--danger-bg);}
.af-sugg .af-sugg-label{font-weight:700;}
/* « tu as tapé "yaourt" » — c'est ce qui explique pourquoi la suggestion apparaît. */
.af-sugg .af-sugg-why{font-size:11.5px;color:var(--text-faint);}

/* Accès direct aux plus courantes : personne ne connaît la nomenclature. */
.af-common{display:flex;flex-wrap:wrap;gap:6px;}
.af-common button{font-size:11.5px;font-weight:600;padding:5px 10px;border-radius:20px;white-space:nowrap;
                  border:1px solid var(--border);background:none;color:var(--text-muted);cursor:pointer;}
.af-common button:disabled{opacity:.35;cursor:default;}
@media (hover:hover){.af-common button:not(:disabled):hover{border-color:var(--danger);color:var(--danger);}}
.af-hint{font-size:11px;color:var(--text-faint);line-height:1.45;}

/* ── Verrou de défilement ────────────────────────────────────────────────
   `overflow:hidden` sur la racine (et plus sur le corps : voir juste dessous).
   `overscroll-behavior:none` empêche en plus le rebond de fin de course, qui
   laissait apparaître le fond derrière la page.

   C'est exactement le procédé qui tourne déjà dans index.php depuis le lot 3.
   Une première version posait `position:fixed` sur le body : ça bloque aussi le
   défilement, mais ça raccourcit le document, et le bas de l'écran se remplit
   alors du fond de <html>. Mieux vaut reprendre un procédé éprouvé de l'appli
   que d'en inventer un second qui se comporte autrement. */
/* ⚠️ `overflow:hidden` SUR LA RACINE SEULE, JAMAIS SUR LE CORPS (Adam, 05/10,
   capture « Détails » de la page invitée : l'en-tête et la bande remontés
   hors de l'écran). C'était LA cause de tous les « ça pousse la navigation
   vers le haut » — Favoris (02/10), « Nouvelle liste », « Détails » — et on ne
   la voyait pas : on soignait ses effets un par un.
   Quand la racine a son propre `overflow`, c'est ELLE qui le donne à la
   fenêtre ; celui du corps s'applique alors AU CORPS, qui devient un
   périmètre défilant à lui tout seul — jamais défilé. Tout ce qui colle s'y
   cale aussitôt et retombe à sa place dans le flux : hors de l'écran dès qu'on
   était descendu. Mesuré dans Chromium ET dans WebKit, sans le gel : avec le
   corps, l'en-tête passe de 0 à −400 ; sans lui, il ne bouge pas, et la page
   ne défile pas davantage (molette, programme).
   « Selon le navigateur, c'est html ou body qui défile » ne vaut qu'en mode
   quirks ; nos pages sont toutes en `<!DOCTYPE html>`.
   `overscroll-behavior` reste sur les deux : il ne crée pas de périmètre.
   ⚠️ ET PAS SUR ORDINATEUR (Adam, 05/10 : « elle rend invisible l'ascenseur,
   peu importe la modale ») : `overflow:hidden` retire la barre. Avec une
   souris, le script pose `garde-script` et refuse lui-même le défilement
   (`_poserGardes`, maryse-common.js) ; la racine garde alors sa barre. */
html.scroll-locked:not(.garde-script){overflow:hidden;}
html.scroll-locked,body.scroll-locked{overscroll-behavior:none;}
/* … et pendant ce verrou, TOUT CE QUI COLLE est figé à sa place — par le
   script, pas par une règle par élément : voir `_gelerCollants()` dans
   maryse-common.js. Depuis que le corps n'est plus un périmètre (ci-dessus),
   il ne fait plus que redire ce que le collant fait déjà : c'est le filet,
   plus le remède.
   Il ne reste ici qu'une chose que le script ne peut pas faire seul : rendre au
   corps la GOUTTIÈRE de la barre de défilement, que `overflow:hidden` vient de
   supprimer. Sans elle, la fenêtre s'élargit de treize pixels et tout ce qui
   est centré glisse vers la droite à chaque ouverture de fenêtre.
   Seulement quand la barre s'en va vraiment : sous `garde-script` elle reste,
   et la rendre une seconde fois décalerait tout de treize pixels à gauche. */
html.scroll-locked:not(.garde-script) body{padding-right:var(--gel-droite,0px);}

/* ═══════════════════════════════════════════════════════════════════════
   Sélecteur de tags — 7 en vue, le reste dans un modal.
   Voir createTagField() et openTagPicker() dans maryse-common.js.
   ═══════════════════════════════════════════════════════════════════════ */
.tf-wrap{display:flex;flex-direction:column;gap:9px;min-width:0;}
.tf-row{display:flex;flex-wrap:wrap;gap:7px;align-items:center;}
/* Fond BLANC au repos, et non transparent : posées sur le beige de la page ces
   pastilles se lisaient comme du texte souligné d'un trait vert. Le blanc les
   pose sur le fond comme les cartes.

   Le contour au repos est celui du champ de recherche et des tuiles, --border :
   elles vivent côte à côte sous le même champ, et deux gris différents à trois
   pixels l'un de l'autre se voient. Le vert ne sert plus qu'à dire ce qui est
   ALLUMÉ — au repos il annonçait un état que la pastille n'avait pas. Il
   revient au survol, pour dire où l'on va sans le dire trop tôt. */
.tf-tog{flex:0 0 auto;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
        font-size:12px;font-weight:600;padding:7px 12px;border-radius:20px;
        border:1px solid var(--border);background:var(--card-bg);
        color:var(--green-btn);cursor:pointer;transition:border-color .15s, background .15s;}
.tf-tog.on{background:var(--green-btn);color:var(--sur-vert);border-color:var(--green-btn);}
.tf-tog:disabled{cursor:default;opacity:.35;}
@media (hover:hover){
  .tf-tog:not(.on):not(:disabled):hover{background:var(--green-light);border-color:var(--green-border);}
}
/* Le lien « Voir tous les tags » est en gras : c'est le vrai chemin vers les 83,
   les sept boutons ne sont qu'un raccourci. */
.tf-more{align-self:flex-start;border:none;background:none;padding:2px 0;cursor:pointer;
         font-size:12.5px;font-weight:700;color:var(--text);text-decoration:underline;text-underline-offset:3px;}
/* Le compteur vit dans le libellé du champ : même corps, même famille, mais
   pas la graisse du titre, sinon deux choses se disputent le regard. */
.tf-count{font-weight:400;color:var(--text-faint);margin-left:6px;}
.tf-count.full{color:var(--danger);}

/* ── Le modal ─────────────────────────────────────────────────────────── */
/* Le remplissage tient compte des zones de sécurité de l'iPhone : sans ça, le
   pied de page du modal passe sous la barre d'accueil. Même calcul que la
   modale de jour dans index.php. */
/* HAUTEUR DÉFINIE, et pas seulement un max-height. C'est ce qui manquait à la
   première version : la carte n'avait qu'un max-height, et .tp-stage — dont TOUS
   les enfants sont en position:absolute — n'a aucune hauteur intrinsèque. Son
   flex:1 1 auto se résolvait donc à zéro, et le modal s'affichait réduit à son
   en-tête et son pied de page, sans la liste.
   Les tests jsdom ne pouvaient pas l'attraper : jsdom ne calcule pas de mise en
   page. Vérifié désormais par une assertion sur la feuille de style. */
/* La croix à DROITE, la flèche retour à gauche. Le titre étant en position
   absolue, il ne prend pas de place dans le flux : c'est ce margin-left qui
   repousse la croix, et lui seul. */

/* Deux écrans qui coulissent dans le MÊME modal : la liste, et la fiche d'un
   tag. Pas de seconde couche empilée, pas d'ombre par-dessus une ombre, et le
   geste de retour est celui que tout le monde connaît sur mobile. */
/* DEUX GARANTIES PLUTÔT QU'UNE. La hauteur de .tp-card suffirait en théorie,
   mais cette zone est le cœur du modal : si elle se résout à zéro, il ne reste
   qu'un en-tête et un pied de page, et l'écran n'a plus aucun sens. Le
   min-height la fait donc tenir debout SEULE, même si la règle de .tp-card
   venait à sauter ou à ne pas être servie.
   Une zone qui, vide, rend l'écran incompréhensible mérite deux garanties. */
/* .tp-pane : positionnement délégué à .sl-pane, posée par le tiroir. */
/* Le panneau porte le fond de PAGE, pas celui d'une carte. La zone de recherche
   en hérite : elle était blanche, et cassait en deux un écran que la liste de
   courses montre d'un seul tenant. Les cartes vertes se posent dessus, comme un
   rayon se pose sur la page Courses. */
.tp-pane{background:var(--page-bg);}

/* LE CHAMP ET L'INTERRUPTEUR DANS LA MÊME GÉLULE. Ils règlent la même chose —
   ce qu'on voit dans la liste — et deux contenants côte à côte les faisaient
   passer pour deux outils sans rapport. C'est donc la RANGÉE qui porte le
   contour, le fond blanc et l'arrondi ; le champ, à l'intérieur, n'a plus ni
   bord ni fond à lui.

   Le contour est celui des cartes et des tuiles, --border. Il avait dû passer
   au gris franc quand le champ était TRANSPARENT : posé sur le beige de la
   page, un trait aussi clair se confondait avec le fond et le champ n'existait
   plus. Depuis qu'il a son propre fond blanc, le contraste vient du fond et
   non du trait — et le gris franc, lui, sonnait trop fort pour un contenant au
   repos.

   ARRONDI COMPLET, à la différence des boutons et des cartes qui gardent leurs
   8 px : la forme dit ce qu'on attend. Une gélule se remplit d'un mot qu'on
   tape puis qu'on efface, un rectangle arrondi contient une valeur qu'on
   enregistre. */
.tp-search{margin:12px 12px 10px;display:flex;align-items:center;gap:10px;
           padding-right:14px;background:var(--card-bg);
           border:1px solid var(--border);border-radius:999px;}
/* Le liseré de focus passe sur la GÉLULE : posé sur le champ, il aurait dessiné
   un second contour à l'intérieur du premier.
   Et il est tourné vers l'INTÉRIEUR (`inset`). Vers l'extérieur, il débordait
   d'un pixel hors de la gélule — et dans la page Favoris, ce pixel tombait pile
   sous la rangée de filtres, qui est collante et peint le fond de la page
   par-dessus tout ce qui a un plan inférieur. Le bas du contour se retrouvait
   rogné de moitié pendant que les côtés restaient pleins. Un liseré intérieur
   ne dépasse d'aucun bord : aucun voisin ne peut plus le manger, ici comme
   dans les fenêtres de choix. */
.tp-search:focus-within{border-color:var(--green-btn);box-shadow:inset 0 0 0 1px var(--green-btn);}
.tp-search input{flex:1;min-width:0;box-sizing:border-box;padding:9px 4px 9px 16px;
                 border:none;border-radius:999px;font-size:16px;
                 background:none;color:var(--text);font-family:inherit;
                 -webkit-appearance:none;appearance:none;}
.tp-search input:focus{outline:none;box-shadow:none;}

/* ⚠️ `.tp-filtres` A VÉCU (ménage du 25/09) — la rangée de filtres par rang du
   sélecteur de plat, avec l'interrupteur « Groupes » poussé à droite. Deux
   règles ; `.tp-search`, `.tp-toggle` et le reste du sélecteur restent. */

/* L'interrupteur « Groupes ». Deux boutons côte à côte auraient fait croire à
   deux actions ; c'est un seul réglage à deux états, et un interrupteur le dit
   sans mot de plus. */
.tp-toggle{flex:none;display:flex;align-items:center;gap:7px;cursor:pointer;user-select:none;
           -webkit-user-select:none;-webkit-tap-highlight-color:transparent;}
.tp-toggle-label{font-size:12px;font-weight:700;color:var(--text-faint);transition:color .15s;}
.tp-toggle input{position:absolute;opacity:0;width:0;height:0;}
.tp-switch{position:relative;width:40px;height:23px;flex:none;border-radius:20px;
           background:var(--border);transition:background .18s;}
.tp-switch::after{content:'';position:absolute;top:2px;left:2px;width:19px;height:19px;border-radius:50%;
                  background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.28);transition:transform .18s;}
.tp-toggle.on .tp-switch{background:var(--green-btn);}
.tp-toggle.on .tp-switch::after{transform:translateX(17px);}
.tp-toggle.on .tp-toggle-label{color:var(--green-btn);}
:where(html.nav-clavier) .tp-toggle input:focus-visible+.tp-switch{outline:2px solid var(--green-btn);outline-offset:2px;}

/* La zone qui défile prend le fond de PAGE, pas celui d'une carte : les groupes
   y sont des cartes posées dessus, exactement comme les rayons de la liste de
   courses. C'est ce qui fait qu'on reconnaît l'écran sans l'avoir appris. */
/* 28 px sous la dernière ligne, et non 18 : depuis que le pied suit dans les
   sous-sections et dans la fiche, il y a TOUJOURS une barre au bas de la
   fenêtre. Ce dégagement est ce qui garantit que la dernière préférence d'une
   liste s'en détache franchement au lieu de la frôler — c'est de l'espace de
   défilement, il ne coûte rien à la mise en page. */
.tp-list{background:var(--page-bg);padding:12px 12px 28px;}
/* Un conteneur PAR FAMILLE. Sans lui, les cinq titres collants se colleraient
   tous au même endroit et se recouvriraient : un élément `position:sticky` reste
   collé tant que son parent est à l'écran. Avec un parent par famille, chaque
   titre est poussé dehors par le suivant. */
/* Une carte ordinaire : bordure d'un pixel, coins arrondis, et AUCUN découpage.
   Ni `overflow:clip` ni `overflow:hidden` — trois passes ont été perdues à
   essayer de découper les enfants pour qu'ils ne dépassent pas des angles :
   `hidden` annule position:sticky, et `clip` découpe à la boîte de rembourrage,
   donc les enfants s'arrêtent un pixel avant le bord et la carte montre son
   propre contour au-dessus du bandeau collé.
   Le rayon des courses règle ça autrement, et depuis toujours : la carte ne
   découpe rien, c'est le BANDEAU qui se charge de masquer ce qui défile derrière
   lui, avec deux pseudo-éléments qui débordent légèrement de la carte. Rien à
   découper, donc rien à rogner. */
/* L'écart entre deux cartes doit être PLUS GRAND que le débordement du bandeau
   suivant : ce débordement peint le fond de page, et il mangeait la dernière
   ligne de la carte d'au-dessus — six pixels rognés, très visibles sur un plat
   dont il ne restait plus qu'un demi-texte. En mode « Groupes », c'était le bas
   des pastilles vertes. Seize pixels pour treize de débordement, un de plus pour
   le bandeau qui déborde d'un pixel sous sa carte : de la marge, pas un calcul
   au ras. */
.tp-group{position:relative;background:var(--card-bg);border:1px solid var(--border);
          border-radius:11px;margin-bottom:16px;}

/* Bandeau de famille, sur le modèle des rayons de la liste de courses : fond
   vert clair, pleine largeur, collant en haut de la zone qui défile.
   `top:0` suffit à le caler juste sous « Types de cuisine » : la zone qui défile
   commence déjà sous l'en-tête du modal, il n'y a donc aucune hauteur à
   retrancher, et rien à recalculer si l'en-tête change de taille.
   Le texte est en --green-btn-HOVER et non en --green-btn : sur le fond vert
   clair, ce dernier ne donnait que 4,00:1, sous le seuil WCAG AA de 4,5:1. La
   variante foncée monte à 5,15:1 en clair et 7,40:1 en sombre. Vérifié au
   calcul, pas à l'œil : deux verts proches se distinguent mal sur un écran de
   bureau bien réglé, et très mal dehors en plein soleil. */
/* L'en-tête est un BOUTON : le toucher ne déplie plus, il fait glisser vers ce
   groupe seul. Un groupe ouvert et un groupe fermé montraient la même flèche
   pour deux gestes différents ; il n'y a plus qu'un geste.
   Les marges négatives font mordre l'en-tête sur la bordure de la carte, pour
   que le vert aille bien jusqu'au bord arrondi.
   La hauteur est celle d'avant (11px de marge intérieure) : c'est le style qui
   change, pas la densité de la liste.
   Le texte est en --green-btn-HOVER et non en --green-btn : sur le fond vert
   clair, ce dernier ne donnait que 4,00:1, sous le seuil WCAG AA de 4,5:1. La
   variante foncée monte à 5,15:1 en clair et 7,40:1 en sombre. Vérifié au
   calcul, pas à l'œil. */
/* width:100% : un <button> se rétrécit à son contenu, et le bandeau vert ne
   faisait que la largeur du titre au lieu de courir d'un bord à l'autre comme
   les rayons de la liste de courses. */
/* Construit exactement comme .cat-head dans la liste de courses, et pour la même
   raison : un en-tête collant à coins arrondis laisse voir, DERRIÈRE ses angles,
   le contenu blanc qui défile dessous. Deux pseudo-éléments règlent ça —
   ::before peint le fond de page au-dessus et sur les côtés, ::after porte le
   vert, la bordure et les arrondis. L'en-tête lui-même reste transparent. */
/* top:12px, la MÊME valeur que le remplissage haut de .tp-list : le bandeau ne
   bouge donc pas d'un pixel entre le repos et le collage, et l'air au-dessus de
   lui est le même sur tous les écrans qui l'emploient — la racine de la fenêtre,
   une famille montrée seule, la fiche d'un (i), le tiroir de la page Profil.
   Le décalage ne coûte plus rien depuis que ::before déborde vers le haut : il
   peint le fond de page dans cet interstice, là où sans lui la carte montrerait
   son contour et les lignes défileraient à découvert. */
.tp-fam{position:sticky;top:12px;z-index:5;width:100%;background:transparent;
        padding:11px 14px;border:none;border-radius:0;
        display:flex;align-items:center;gap:8px;text-align:left;cursor:pointer;
        font-family:inherit;font-size:12.5px;font-weight:700;color:var(--green-btn-hover);
        -webkit-tap-highlight-color:transparent;}
/* Les deux pseudo-éléments des rayons de la liste de courses, aux mêmes valeurs.
   ::before peint le fond de PAGE, et il DÉBORDE : 3 px de chaque côté, 13 px
   au-dessus — un de plus que l'écart du collage, pour couvrir l'interstice
   jusqu'au bord de la zone qui défile, et pas davantage : chaque pixel de trop
   est un pixel rogné sur la carte du dessus. Ce débordement est tout l'intérêt — il recouvre la bordure de la
   carte et les lignes blanches qui défilent derrière les angles du bandeau. Rien
   ne dépasse donc du titre pendant le défilement, ni au-dessus ni sur les côtés.
   ::after repose par-dessus le vert ET la bordure, calée à -1 px : elle retombe
   exactement sur celle de la carte, qu'on vient de masquer. Le petit contour
   autour du titre est là, et il est le seul. */
.tp-fam::before{content:'';position:absolute;left:-3px;right:-3px;top:-13px;bottom:0;
                background:var(--page-bg);z-index:-2;}
.tp-fam::after{content:'';position:absolute;left:-1px;right:-1px;top:0;bottom:0;
               background:var(--green-light);border:1px solid var(--border);border-bottom:none;
               border-radius:11px 11px 0 0;z-index:-1;}
/* En mode « Groupes », le bandeau EST la carte : son fond descend jusqu'au bord
   exact (-1 px en haut comme en bas) et referme sa bordure. Même règle que
   `.cat-card.collapsed` dans les courses. */
.tp-group.seul .tp-fam::after{border-radius:11px;top:-1px;bottom:-1px;
                              border-bottom:1px solid var(--border);}
.tp-fam-nom{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* La mention « 2 au maximum » est une précision, pas un titre : même vert, moins
   de gras, et elle s'efface avant le nom si la place manque. */
.tp-fam-max{flex:none;font-weight:600;opacity:.75;}
.tp-fam-go{flex:none;font-size:15px;line-height:1;opacity:.75;}
@media (hover:hover){.tp-fam:hover{background:var(--green-strip);}}
.tp-item{display:flex;align-items:center;gap:12px;padding:12px 14px;border-bottom:1px solid var(--row-border);
         background:var(--card-bg);}
.tp-item:last-child{border-bottom:none;}
/* Les coins du BAS sont portés par la dernière ligne, la carte ne découpant
   rien. Le fond de la ligne et celui de la carte sont le même blanc : l'écart
   d'un pixel entre les deux arcs ne se voit pas, là où sous le bandeau VERT il
   sautait aux yeux — d'où les pseudo-éléments en haut et rien en bas.
   En PROPRIÉTÉS LONGUES, et pas en raccourci : un élément qui est à la fois le
   premier et le dernier (une liste à un seul plat) recevait deux raccourcis
   dont le second remettait à zéro les coins du premier. C'est ce qui donnait
   des cartes au bas carré dès qu'elles avaient plus d'une ligne, et rondes
   quand elles n'en avaient qu'une. */
.tp-group > *:last-child{border-bottom-left-radius:11px;border-bottom-right-radius:11px;}
/* Une famille montrée SEULE n'a pas d'en-tête — son nom est le titre de la
   fenêtre. C'est donc sa première ligne qui porte les coins du haut. */
.tp-group > .tp-item:first-child{border-top-left-radius:11px;border-top-right-radius:11px;}
/* Rien à effacer sur la carte en mode « Groupes » : le ::after du bandeau la
   recouvre déjà d'un bord à l'autre, bordure comprise. */
.tp-item.off{opacity:.4;}
.tp-pick{flex:1 1 auto;display:flex;align-items:center;gap:12px;min-width:0;
         border:none;background:none;padding:0;text-align:left;cursor:pointer;color:var(--text);}
.tp-item.off .tp-pick{cursor:default;}
.tp-box{flex:0 0 20px;width:20px;height:20px;border-radius:5px;border:1.5px solid var(--border-strong,var(--border));
        display:flex;align-items:center;justify-content:center;color:transparent;font-size:13px;font-weight:700;}
.tp-item.on .tp-box{background:var(--green-btn);border-color:var(--green-btn);color:var(--sur-vert);}
.tp-name{flex:1 1 auto;min-width:0;font-size:14px;}
.tp-why{display:block;font-size:11px;color:var(--text-faint);margin-top:2px;}
/* Le (i) est TOUT À DROITE et hors du bouton de sélection : le toucher ne doit
   jamais cocher le tag par accident. */
.tp-info{flex:0 0 auto;border:none;background:none;padding:0;width:34px;height:34px;cursor:pointer;
         color:var(--text-faint);display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:700;}
.tp-info span{width:20px;height:20px;border:1.5px solid currentColor;border-radius:50%;
              display:flex;align-items:center;justify-content:center;font-size:12px;font-style:italic;}
@media (hover:hover){.tp-info:hover{color:var(--text);}}
.tp-empty{padding:28px 16px;text-align:center;font-size:13px;color:var(--text-faint);}

/* ═══════════════════════════════════════════════════════════════════════
   RANGER DES CARTES : ce que la liste de courses et la page Favoris partagent

   Ces règles vivaient dans le <style> d'index.php. Elles en sortent le 09/08,
   quand la page Favoris a eu besoin des mêmes : recopier vingt lignes de CSS,
   c'est se donner rendez-vous dans six mois avec deux versions qui ont divergé.

   Rien n'a changé au passage — mêmes valeurs, même rendu. Ce qui change est
   qu'il n'y a plus qu'un endroit où les modifier.
   ═══════════════════════════════════════════════════════════════════════ */

/* Le carré « + » de « Nouvelle liste » (« Ajouter à »). Le séparateur
   « (+) Nouveau rayon —— » qui le portait aussi est parti : entre deux rayons
   de la liste du menu avec R, au pied d'une journée avec S (15/10 : l'en-tête
   d'un rayon plié, `.jour-nouveau`). */
.cat-insert-btn{width:24px;height:24px;flex-shrink:0;border-radius:6px;background:none;
                border:1px solid var(--text-muted);color:var(--text-muted);display:flex;
                align-items:center;justify-content:center;padding:0;cursor:pointer;transition:all .12s;}
.cat-insert-btn svg{width:14px;height:14px;display:block;}

/* La poignée. touch-action:none est ce qui permet de glisser au doigt sans que
   le navigateur croie qu'on veut faire défiler la page. */
.grip-btn{width:30px;height:30px;flex-shrink:0;border:1px solid var(--green-border);border-radius:6px;
          background:var(--card-bg);color:var(--green-btn);cursor:grab;display:flex;align-items:center;
          justify-content:center;padding:0;touch-action:none;-webkit-tap-highlight-color:transparent;}
.grip-btn svg{width:15px;height:15px;display:block;}
.grip-btn:active{cursor:grabbing;}
.grip-btn:disabled{opacity:.35;cursor:default;}
@media (hover:hover){.grip-btn:not(:disabled):hover{background:var(--green-light);}}
/* Poignée d'une ligne (« Nouveau rayon », R) : ni contour ni fond, juste un
   changement de teinte. Répétée à chaque ligne, un cadre ferait une grille. */
.item-grip{border:none;background:none;color:var(--text-faint);}
@media (hover:hover){.item-grip:hover{background:none;color:var(--text-muted);}}

/* Le champ qui nommait un rayon DANS son en-tête vert (`.cat-name-input`, sa
   coche dedans) est parti avec S (15/10) : un rayon se nomme dans un tiroir,
   partout (« + Nouveau rayon », « Renommer », la fenêtre d'une journée). */

/* ⚠️ LA FENÊTRE « RANGER CE PLAT » A VÉCU (ménage du 25/09) — `.rg-stage`,
   `.rg-dish`, `.rg-list`, `.rg-pick` et son bouton radio, puis `.rg-ok`.
   ⚠️ ET LE PETIT MENU QUI L'AVAIT REMPLACÉE AUSSI (B.6, 01/10) : le rangement
   est une MODALE, plus bas (`.rg-ov`, `.rg-carte`). Sont partis avec lui
   `.rg-new`, `.rg-new-ok` (la petite fenêtre « Nouvelle liste »),
   `.rg-neuve-card`, `.rg-neuve-aide`, `.rg-menu-neuve`, `.rg-menu-fin` et
   `.rg-menu-eclipse`.
   ⚠️ ET LE TIROIR D'ACTIONS `.rg-menu` AUSSI (B.7 suite, 01/10) : le « ••• »
   d'un menu ouvre la fenêtre à tiroirs (`.ot-*`, plus bas). Partis avec lui :
   `.rg-menu`, `.rg-voile`, `.rg-menu-titre`, `.rg-menu-sep`. */

/* ── Les lignes de « Ajouter à » ─────────────────────────────────────────
   `.rg-menu-item`, `.rg-case`… : une liste se coche de la même façon partout. */
.rg-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:8px 9px;border:none;
              background:none;font-family:inherit;font-size:13.5px;color:var(--text);
              text-align:left;cursor:pointer;border-radius:8px;
              -webkit-tap-highlight-color:transparent;}
@media (hover:hover){.rg-menu-item:hover{background:var(--green-light);}}
.rg-menu-nom{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.rg-menu-item.on .rg-menu-nom{font-weight:700;color:var(--green-btn-hover);}
.rg-menu-cnt{flex:none;font-size:11.5px;font-weight:600;color:var(--text-muted);}
/* Une CASE, pas une pastille ronde : un plat peut vivre dans plusieurs listes,
   et un rond dit qu'il faut choisir. La coche n'apparaît qu'une fois cochée. */
.rg-case{flex:none;width:19px;height:19px;border-radius:5px;box-sizing:border-box;
         border:1.5px solid var(--border);background:var(--card-bg);color:#fff;
         display:flex;align-items:center;justify-content:center;transition:.12s ease;}
.rg-case svg{width:13px;height:13px;display:block;opacity:0;transform:scale(.7);
             transition:opacity .12s ease, transform .12s ease;}
.rg-menu-item.on .rg-case{background:var(--green-btn);border-color:var(--green-btn);}
.rg-menu-item.on .rg-case svg{opacity:1;transform:none;}

/* ── La modale « Ajouter à » (B.6, validée le 30/09 et le 01/10) ──────────
   Le cœur d'un plat (Menus) et « ••• » d'une tuile (Favoris) l'ouvrent.
   FLOTTANTE sur ordinateur, FIGÉE EN BAS sur téléphone. Elle prend la hauteur
   de ses listes, jusqu'au plafond de la liste de courses d'une journée
   (`.mc-card.day-modal` : mêmes valeurs, 16 px de marge) ; au-delà, SEULE la
   zone des listes défile — en-tête et pied restent, aucun fondu.
   ⚠️ Les poids sont choisis contre la coquille commune, qui est écrite PLUS
   BAS : `.modal-card` (marge, plafond, remplissage), `.modal-foot .modal-save`
   (qui étire le bouton) et le bloc téléphone (`.modal-card:not(.pleine)`). */
.modal-overlay.rg-ov{align-items:center;
  padding:calc(16px + env(safe-area-inset-top, 0px)) 12px calc(16px + env(safe-area-inset-bottom, 0px));}
.modal-overlay .rg-carte.modal-card{max-width:380px;margin:0 auto;gap:0;padding:0;overflow:hidden;outline:none;
  max-height:calc(100vh - 32px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  max-height:calc(100dvh - 32px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));}
.rg-carte .modal-head{flex:none;padding:15px 16px 12px;border-bottom:1px solid var(--border);}
.rg-listes{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;padding:8px 8px 6px;}
/* Le nom d'une liste en demi-gras, sur LES DEUX pages : la page Menus le pose
   dans sa propre feuille (`.rg-menu-item .rg-menu-nom`, pour son tiroir
   d'actions) et Favoris ne l'avait pas — la même modale y sortait en maigre
   (vu dans Chromium, 01/10). */
.rg-carte .rg-menu-nom{font-weight:600;}
/* ⚠️ PAS D'ANNEAU BLEU DU NAVIGATEUR DANS LA MODALE (Adam, 01/10, capture
   Safari). Une liste qu'on vient de créer reçoit le focus — c'est ce qui garde
   le clavier dans la fenêtre — et Safari l'entourait de son anneau bleu, même
   créée à la souris. Une ligne de liste dit son focus comme son survol : le
   fond vert pâle. Les boutons du pied, un liseré vert. */
.rg-carte .rg-menu-item:focus,.rg-carte .rg-neuve:focus,.rg-carte .rg-fin:focus,
.rg-carte .rg-champ-ok:focus,.rg-carte .modal-close:focus{outline:none;}
:where(html.nav-clavier) .rg-carte .rg-menu-item:focus-visible{background:var(--green-light);}
:where(html.nav-clavier) .rg-carte .rg-neuve:focus-visible .cat-insert-btn,
:where(html.nav-clavier) .rg-carte .rg-fin:focus-visible,:where(html.nav-clavier) .rg-carte .modal-close:focus-visible{box-shadow:0 0 0 2px var(--green-border);}
/* La ligne qu'on écrit : sa case est cochée d'avance, elle n'est pas un bouton. */
.rg-fantome{cursor:default;padding-top:5px;padding-bottom:5px;}
@media (hover:hover){.rg-fantome:hover{background:none;}}
/* Le champ « libre » : la coche vit DANS le champ et n'apparaît qu'une fois un
   nom tapé (`.dirty`), comme « Précisions » d'un profil. */
.rg-champ-l{position:relative;display:block;flex:1 1 auto;min-width:0;}
/* ⚠️ LE NOM S'ÉCRIT À LA TAILLE OÙ IL S'AFFICHERA (Adam, 01/10). Le champ
   était à 16 px — le réflexe anti-zoom d'iOS — au milieu de noms à 13,5 px
   demi-gras : la ligne qu'on écrit ne ressemblait pas à la liste qu'elle
   allait devenir. Même taille, même graisse que `.rg-menu-nom` dans la
   modale. Le zoom d'iOS au foyer n'est pas à craindre : les pages qui
   l'ouvrent (Menus, Favoris) posent `maximum-scale=1`. */
.rg-champ-l .rg-champ{width:100%;box-sizing:border-box;padding:6px 34px 6px 10px;
  border:1px solid var(--border);border-radius:8px;font:inherit;font-size:13.5px;font-weight:600;
  color:var(--text);background:var(--card-bg);}
.rg-champ-l .rg-champ::placeholder{color:var(--text-faint);font-weight:400;}
/* La coche a la taille de celle d'un champ « libre » (`.fr-ok`, la raison
   d'un repas libre) : 22 px, dessin de 14, à 5 px du bord (Adam, 01/10). */
.rg-champ-ok{position:absolute;right:5px;top:50%;transform:translateY(-50%);
  width:22px;height:22px;padding:0;border:none;border-radius:6px;
  background:var(--green-btn);color:var(--sur-vert);cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  visibility:hidden;opacity:0;transition:opacity .15s ease;}
.rg-champ-ok svg{width:14px;height:14px;display:block;}   /* 22 − 14 = 8, PAIR */
.rg-champ-l.dirty .rg-champ-ok{visibility:visible;opacity:1;}
@media (hover:hover){.rg-champ-ok:hover{background:var(--green-btn-hover);}}
/* Le pied : « + Nouvelle liste » à gauche, toujours visible ; « Terminer » à
   droite, éteint tant que rien n'a changé. */
.rg-carte .rg-pied{flex:none;padding:12px 16px;border-top:1px solid var(--border);justify-content:space-between;}
.rg-neuve{display:inline-flex;align-items:center;gap:8px;padding:8px 6px 8px 0;border:none;background:none;
  font:inherit;font-size:13.5px;font-weight:700;color:var(--text-muted);cursor:pointer;
  -webkit-tap-highlight-color:transparent;}
.rg-neuve .cat-insert-btn{width:24px;height:24px;}
@media (hover:hover){
  .rg-neuve:not(:disabled):hover{color:var(--green-btn);}
  .rg-neuve:not(:disabled):hover .cat-insert-btn{background:var(--green-btn);border-color:var(--green-btn);color:var(--sur-vert);}
}
.rg-neuve:disabled{opacity:.4;cursor:default;}
.rg-carte .rg-pied .rg-fin{flex:0 0 auto;margin:0;padding:11px 22px;}
.rg-carte .rg-pied .rg-fin:disabled{background:var(--border);color:var(--text-faint);cursor:default;}
@media (max-width:520px){
  /* Figée en bas, comme la liste du jour : plafond à 90 %, coins du haut
     seulement, le dégagement du pouce dans le pied. */
  .modal-overlay.rg-ov{align-items:flex-end;padding:calc(16px + env(safe-area-inset-top, 0px)) 0 0;}
  .modal-overlay .rg-carte.modal-card{max-width:none;margin:0;padding:0;
    border-radius:14px 14px 0 0;border-bottom:none;
    max-height:90vh;
    max-height:min(90dvh, calc(100dvh - 16px - env(safe-area-inset-top, 0px)));}
  .rg-carte .rg-pied{padding-bottom:calc(var(--air-bas) + var(--sur-bas));}
}

/* La fiche d'une préférence est du texte, pas une liste : elle garde le fond
   d'une carte, posée sur le fond de page comme tout le reste. */
.tp-detail-body{margin:12px 12px 28px;background:var(--card-bg);border:1px solid var(--border);
                border-radius:11px;padding:18px 18px 24px;display:flex;flex-direction:column;}

/* ══ LA FENÊTRE À TIROIRS (B.7 suite, 01/10) ══════════════════════════════
   `ouvrirTiroirs` (maryse-common.js) : la coquille de « Ajouter à »
   (`.rg-ov` / `.rg-carte`), des OPTIONS, puis un TIROIR par option. */
.modal-overlay .ot-carte.rg-carte.modal-card{display:flex;flex-direction:column;overflow:hidden;overflow:clip;}
@media (min-width:560px){.modal-overlay .ot-carte.ot-large.rg-carte.modal-card{max-width:520px;}}
/* Clavier ouvert, la carte ne dépasse plus la zone visible (v367, relecture
   adverse de v366) : son plafond (`100dvh`) ne connaît pas le clavier. Au
   téléphone couché, la carte de « Minuterie » débordait la fenêtre repliée
   et « Pour quoi ? » restait à moitié sous le clavier — iOS le faisait
   glisser en vue, il ne le fait plus (`champsSansGlissement`). Son corps
   défile, le pied (« Lancer ») reste en vue. */
.modal-overlay.rg-ov.clavier-ouvert > .rg-carte.modal-card{max-height:100%;}
.ot-carte .modal-head{gap:6px;}
.ot-titres{flex:1 1 auto;min-width:0;text-align:center;}
.ot-titres .modal-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
/* R : le nom d'un menu (jusqu'à 60 signes) passe sur deux lignes plutôt que
   de finir en « … » sur un petit téléphone (`deuxLignes`). */
.ot-carte.ot-deux-lignes .ot-titres .modal-title{white-space:normal;line-height:1.25;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;}
.ot-sous{font-size:12px;font-weight:600;color:var(--text-muted);margin-top:2px;}
.ot-carte .modal-back{visibility:hidden;}
@media (hover:hover){.ot-carte .modal-back:hover{background:var(--page-bg);}}
.ot-carte .modal-back:focus,.ot-carte .ot-option:focus{outline:none;}
:where(html.nav-clavier) .ot-carte .modal-back:focus-visible,:where(html.nav-clavier) .ot-carte .ot-option:focus-visible{background:var(--green-light);}
/* La scène DÉCOUPE sans jamais défiler (`clip`) : voir `ouvrirTiroirs`. */
.ot-scene{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden;overflow:clip;}
.ot-pane{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;background:var(--card-bg);}
.ot-pane[hidden]{display:none;}
.ot-pane.ot-hors{position:absolute;top:0;left:0;right:0;flex:none;}
/* LE RELAIS DU CLAVIER (v380, `relaisClavier`) : un champ qui tient le focus
   le temps d'une animation, sans clavier. Invisible, mais pas caché — un champ
   caché (`display:none`, `visibility:hidden`) ne prend pas le focus ; 16 px :
   sous 16 px, iOS agrandit la page au focus d'un champ. */
.ot-relais{position:absolute;top:0;left:0;width:1px;height:1px;margin:0;padding:0;border:0;
  opacity:0;font-size:16px;pointer-events:none;caret-color:transparent;}
/* Le glissement de côté : la courbe et la durée de `.sl-pane` (« Nouveau menu »). */
.ot-scene.ot-anime .ot-pane{transition:transform .28s cubic-bezier(.4,0,.2,1);}
.ot-corps{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;}
.ot-pane > .ot-pied{flex:none;}
/* Le bouton du pied d'un tiroir prend toute la largeur. */
.ot-carte .ot-pied .rg-fin{flex:1 1 auto;width:100%;margin:0;}
.ot-carte .ot-pied .rg-fin.ot-fin-danger{background:var(--danger);color:var(--sur-danger);}
@media (hover:hover){.ot-carte .ot-pied .rg-fin.ot-fin-danger:hover{background:var(--danger);opacity:.88;}}
/* Les options : une ligne, une icône, un mot, un chevron. */
.ot-options{padding:8px 8px calc(8px + env(safe-area-inset-bottom, 0px));}
/* Sur téléphone, la feuille touche le bord : l'air du bas. La dernière
   option porte déjà 12 px sous son mot, d'où le − 8. ⚠️ APRÈS la règle de
   base, qui l'écraserait sinon (même poids). */
@media (max-width:520px){
  .ot-options{padding-bottom:calc(var(--air-bas) - 8px + var(--sur-bas));}
}
.ot-option{display:flex;align-items:center;gap:10px;width:100%;padding:12px 10px;border:none;
  background:none;font-family:inherit;font-size:14px;color:var(--text);text-align:left;
  cursor:pointer;border-radius:8px;-webkit-tap-highlight-color:transparent;}
.ot-option + .ot-option{margin-top:17px;}
@media (hover:hover){.ot-option:hover{background:var(--green-light);}
  .ot-option.ot-danger:hover{background:var(--danger-bg);}}
.ot-ico{flex:none;width:18px;height:18px;display:flex;align-items:center;justify-content:center;color:var(--text-muted);}
.ot-ico svg{width:18px;height:18px;display:block;}
.ot-mot{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;}
.ot-chevron{flex:none;width:16px;height:16px;color:var(--text-faint);}
.ot-chevron svg{width:16px;height:16px;display:block;}
.ot-option.ot-danger,.ot-option.ot-danger .ot-ico{color:var(--danger);}
.ot-option.ot-danger .ot-chevron{color:var(--danger);opacity:.6;}

/* ── B.8 : UN FILET ENTRE DEUX OPTIONS (Adam, 02/10) ──────────────────────
   Les options « ••• » (Favoris et Menus) et les listes de « Ajouter à ».
   Rien au-dessus de la première, rien sous la dernière : le filet ne vit
   qu'ENTRE deux lignes (`X + X::before`). Il va de l'icône — ou de la case —
   jusqu'au chevron — ou au nombre de plats : il reprend le retrait latéral
   de la ligne.
   ⚠️ IL NE S'EFFACE PAS AU SURVOL (Adam, 02/10, seconde passe). Le fond vert
   du survol ne doit simplement JAMAIS le toucher : 17 px d'écart entre deux
   lignes — 8 d'air, le trait, 8 d'air. Couleur des filets de « Réordonner »
   (`.ml-ligne`). */
.ot-option,.rg-carte .rg-menu-item{position:relative;}
.rg-carte .rg-menu-item + .rg-menu-item{margin-top:17px;}
.ot-option + .ot-option::before,.rg-carte .rg-menu-item + .rg-menu-item::before{content:'';position:absolute;
  top:-9px;height:1px;background:var(--row-border);pointer-events:none;}
.ot-option + .ot-option::before{left:10px;right:10px;}
.rg-carte .rg-menu-item + .rg-menu-item::before{left:9px;right:9px;}
/* Une option ÉTEINTE (B.8 : « Supprimer » sur la dernière liste) : BIEN
   grise — mot, icône et chevron —, sans survol, sans phrase (Adam, 02/10). */
.ot-option:disabled{cursor:default;}
.ot-option.ot-eteinte,.ot-option.ot-eteinte .ot-ico,.ot-option.ot-eteinte .ot-chevron{color:var(--text-faint);}
.ot-option.ot-eteinte{opacity:.5;}
@media (hover:hover){.ot-option:disabled:hover,.ot-option.ot-danger:disabled:hover{background:none;}}
/* Une fenêtre LOGÉE (`logerDansTiroir`, page Menus) : sa carte vit dans le
   tiroir, sans cadre, sans ombre, sans plafond à elle — c'est la modale qui
   plafonne ; son en-tête est caché, son titre est dans celui de la modale.
   `!important` : elle arrive avec ses propres règles de fenêtre (position,
   largeur, plafond, entrée animée), écrites pour un écran entier. */
/* L'overlay d'origine, DÉPLACÉ dans le tiroir : plus rien de ce qui le posait
   à l'écran (fixe, plein écran, voile, marges). */
.ot-carte .ot-greffe{position:static !important;inset:auto !important;z-index:auto !important;
  background:none !important;padding:0 !important;margin:0 !important;backdrop-filter:none !important;
  animation:none !important;flex:1 1 auto;min-height:0;display:flex !important;flex-direction:column;}
.ot-carte .ot-greffe .ot-greffon{position:static !important;inset:auto !important;transform:none !important;
  width:auto !important;max-width:none !important;max-height:none !important;height:auto !important;
  margin:0 !important;border:0 !important;border-radius:0 !important;box-shadow:none !important;
  flex:1 1 auto;min-height:0;display:flex;flex-direction:column;animation:none !important;}
.ot-greffon > .modal-head,.ot-greffon > .mc-head{display:none !important;}
/* Une confirmation logée : pas d'« Annuler » (← en tient lieu), et son
   bouton prend toute la largeur, comme partout dans un tiroir. */
.ot-greffon.mc-card{padding:16px 18px;}
.ot-greffon.mc-card .mc-cancel{display:none;}
.ot-carte .ot-greffon.mc-card .mc-actions{justify-content:stretch;}
.ot-carte .ot-greffon.mc-card .mc-actions .mc-confirm{flex:1 1 auto;min-width:0;}
/* Sur téléphone, une fenêtre logée finit au bord de l'écran : elle prend
   L'AIR DU BAS comme les autres tiroirs (Adam, 02/10 — « Détails » et
   « Supprimer » du « ••• » d'un menu collaient au bord, 16 et 18 px). */
@media (max-width:520px){
  .ot-carte .ot-greffon.mc-card{padding-bottom:calc(var(--air-bas) + var(--sur-bas));}
  .ot-carte .ot-greffon .fd-corps{padding-bottom:calc(var(--air-bas) + var(--sur-bas));}
}
/* La croix qui vide un champ (`ajouterEffacer`) : à droite DANS le champ,
   visible tant qu'il a du texte. */
.ot-efface-l{position:relative;display:block;}
.ot-efface-l > .ot-efface-champ{width:100%;box-sizing:border-box;padding-right:42px !important;}
.ot-effacer{position:absolute;right:9px;top:50%;transform:translateY(-50%);width:24px;height:24px;padding:0;
  border:none;border-radius:50%;background:var(--border);color:var(--text-muted);cursor:pointer;
  display:none;align-items:center;justify-content:center;-webkit-tap-highlight-color:transparent;}
.ot-effacer svg{width:12px;height:12px;display:block;}
/* « RENOMMER » (`tiroirNom`, B.9) : un champ plus grand, sous son titre
   « Nom », la croix dedans. Ces règles vivaient dans favoris.php ; le tiroir
   sert désormais aussi la page Menus, et une pièce qui voyage emporte ses
   règles. `.ml-bloc` habille aussi « Supprimer la liste » (Favoris). */
.ml-bloc{padding:16px 16px 14px;}
.ml-lab{display:block;font-size:12px;font-weight:700;letter-spacing:.02em;color:var(--text-muted);margin:0 0 7px 2px;}
.ml-champ{display:block;width:100%;box-sizing:border-box;font:inherit;font-size:16px;font-weight:600;
  color:var(--text);padding:12px 42px 12px 12px;border:1px solid var(--border);border-radius:10px;
  background:var(--card-bg);outline:none;}
.ml-champ:focus{border-color:var(--green-btn);}
/* ⚠️ LE ROUGE DU REFUS DOIT GAGNER AUSSI AU FOCUS — et le champ refusé A le
   focus (`refuser` l'y remet). La règle commune des champs au focus pèse
   0,4,1 (`input[type="text"]:not(.opt-btn):not([readonly]):focus`) : écrite
   seule, `.ml-bloc.refuse .ml-champ` (0,3,0) ne gagnait jamais, et le champ
   refusé restait VERT sous sa phrase rouge. Classe doublée + l'élément :
   0,5,1. (B.9, vu en capture le 03/10.) */
.ml-bloc.refuse .ml-champ,
.ml-bloc.refuse input.ml-champ.ml-champ:focus{border-color:var(--danger);}
.ml-refus{display:none;margin:8px 2px 0;font-size:12.5px;font-weight:600;color:var(--danger);}
.ml-bloc.refuse .ml-refus{display:block;}
/* « NOM ET DESCRIPTION » (T, 27/10) : la description sous le nom, trois
   lignes, « facultatif » à côté de son titre, son compte dessous. */
.ml-lab-desc{margin-top:16px;}
.ml-opt{font-weight:500;letter-spacing:0;color:var(--text-faint);}
.ml-bloc textarea.ml-champ-desc{resize:none;min-height:84px;font-size:15px;font-weight:500;line-height:1.4;padding:10px 12px;}
.ml-desc-cpt{margin:6px 2px 0;font-size:12px;color:var(--text-faint);text-align:right;}
/* RANGER (R, 13/10) : le tiroir de « Réordonner » et de « Réorganiser »
   (`tiroirRanger`), descendu de favoris.php avec ses règles. Une ligne par
   élément (filets) : l'émoji, le nom, la quantité, la poignée ≡. */
.ml-listes{padding:8px 8px 6px;}
.ml-corps{display:flex;flex-direction:column;}
.ml-ligne{display:flex;align-items:center;min-height:44px;padding:2px 4px 2px 9px;
  background:var(--card-bg);position:relative;}
.ml-ligne ~ .ml-ligne{border-top:1px solid var(--row-border);}      /* « ~ » : le filet survit à une note glissée devant */
.ml-nom{flex:1 1 auto;min-width:0;margin-right:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:13.5px;font-weight:600;color:var(--text);}
.ml-emo{margin-right:7px;}
/* La quantité d'un article (« Réorganiser ») : à droite du nom, sans le pousser. */
.ml-qte{flex:none;max-width:44%;margin-right:8px;font-size:12.5px;color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* La poignée : le ≡ d'un plat. touch-action:none, sinon le doigt fait défiler. */
.ml-poignee{flex:none;width:34px;height:34px;padding:0;border:none;background:none;border-radius:8px;
  color:var(--text-muted);display:flex;align-items:center;justify-content:center;
  cursor:grab;touch-action:none;-webkit-tap-highlight-color:transparent;}
.ml-poignee svg{width:20px;height:20px;display:block;}
@media (hover:hover){.ml-poignee:hover{color:var(--text);background:var(--page-bg);}}
:where(html.nav-clavier) .ml-poignee:focus-visible{background:var(--green-light);}
/* Glisser : la ligne tenue pâlit, le trait vert commun court d'un bord à
   l'autre, comme les filets. */
.ml-listes.glisse{cursor:grabbing;}
.ml-ligne.dragging{opacity:.4;}
.ml-ligne.drag-over-top::before,.ml-ligne.drag-over-bottom::after{left:0;right:0;}
/* DES GROUPES (S, 15/10 : « Réordonner les rayons » d'un menu et de ses
   carnets à part) : chacun son titre, À L'APLOMB des lignes (leur marge,
   9 px), et le TRAIT NOIR entre eux — celui de « Des courses en plus ? ». Un
   groupe qui ne recevra pas la ligne tenue s'éteint ; un groupe vide reste,
   et reçoit ; la phrase d'un refus se lit sous sa ligne. */
.ml-listes.ml-en-groupes{display:flex;flex-direction:column;gap:14px;}
.ml-groupe{transition:opacity .15s ease;}
.ml-groupe + .ml-groupe{border-top:1px solid var(--text);padding-top:14px;}
.ml-groupe.ml-eteint{opacity:.35;}
.ml-groupe-titre{font-size:14.5px;font-weight:700;color:var(--text);margin:0 0 4px;padding-left:9px;}
.ml-vide{display:flex;align-items:center;justify-content:center;min-height:44px;border:1px dashed var(--border);
  border-radius:8px;font-size:13px;color:var(--text-muted);}
.ml-vide.ml-cible{border-color:var(--green-btn);background:var(--green-light);color:var(--green-btn);}
.ml-note{margin:-2px 0 6px 9px;font-size:12px;line-height:1.4;color:var(--text-muted);}
.ot-efface-l.plein .ot-effacer{display:flex;}
@media (hover:hover){.ot-effacer:hover{background:var(--text-faint);color:var(--card-bg);}}
@media (prefers-reduced-motion:reduce){.ot-scene.ot-anime .ot-pane{transition:none;}}
/* .tp-detail-fam a disparu le 07/08 : la famille est le TITRE de la fenêtre,
   elle n'a plus à être répétée au-dessus du nom. */
.tp-detail-name{font-size:19px;font-weight:700;color:var(--text);margin:0 0 14px;}
.tp-detail-desc{font-size:14px;line-height:1.65;color:var(--text-muted);margin:0 0 20px;}
/* Réserve médicale : détachée par un vrai vide, et par un filet. Ce n'est pas
   une phrase de plus sur la cuisine, c'est un avertissement d'une autre nature,
   et il doit se lire comme tel avant même d'être lu. */
.tp-detail-caveat{margin:22px 0 20px;padding-top:14px;border-top:1px solid var(--border);
                  font-size:12.5px;line-height:1.6;color:var(--text-faint);}
.tp-detail-block{font-size:12.5px;line-height:1.5;color:var(--danger);background:var(--danger-bg);
                 border-radius:9px;padding:11px 13px;margin:0 0 18px;}
/* Un cran plus discret : il conclut une lecture, il ne lance pas une action
   engageante. Il n'a pas besoin de la présence d'un bouton de validation. */
.tp-detail-btn{align-self:flex-start;padding:10px 20px;border:none;border-radius:8px;
               background:var(--green-btn);color:var(--sur-vert);font-size:13.5px;font-weight:700;
               cursor:pointer;font-family:inherit;}
.tp-detail-btn.remove{background:none;border:1px solid var(--border);color:var(--text-muted);}
.tp-detail-btn:disabled{opacity:.4;cursor:default;}

/* Le compteur du panneau, dans le pied, à gauche de « Terminer » : c'est là
   qu'on regarde en finissant. Toute la mention est en gras et d'une SEULE
   couleur, nombre et mot : ils forment une phrase, pas deux informations.
   Le nombre garde son élément pour que la traduction puisse le déplacer, mais
   il n'a plus de couleur propre, et la limite les colore tous les deux. */
.tp-foot-count{font-size:13px;font-weight:700;color:var(--text);}
.tp-foot-num{font-weight:700;}
.tp-foot-count.full{color:var(--danger);}
/* Mêmes formes que partout ailleurs : coins à 8 px, vert plein, 14 px gras.
   Le bouton en gélule venait de la maquette de référence, pas de l'appli. */
.tp-save{margin-left:auto;border:none;border-radius:8px;background:var(--green-btn);color:var(--sur-vert);
         padding:12px 22px;font-size:14px;font-weight:700;cursor:pointer;font-family:inherit;}
@media (hover:hover){.tp-save:hover{background:var(--green-btn-hover);}}

/* ═══════════════════════════════════════════════════════════════════════
   La modale de profil

   Ces règles vivaient dans le <style> de generer.php. La modale est partagée
   avec la page Profil depuis le 06/08 : laisser son habillage dans une seule
   des deux pages, c'était refaire l'erreur des classes .infield-*, où le champ
   allergies se retrouvait sans aucun style dans la modale de Générer.
   ═══════════════════════════════════════════════════════════════════════ */
.modal-overlay{position:fixed;inset:0;z-index:100;overscroll-behavior:contain;background:linear-gradient(rgba(0,0,0,.45),rgba(0,0,0,.45)) var(--voile-teinte);display:flex;align-items:flex-start;justify-content:center;padding:calc(24px + env(safe-area-inset-top, 0px)) 14px calc(24px + env(safe-area-inset-bottom, 0px));overflow-y:auto;-webkit-overflow-scrolling:touch;}
/* HAUTEUR FIXE, portée par la carte et non par son contenu : identique sur les
   trois écrans, formulaire, liste et fiche.
   Elle retranche le remplissage de l'ovelay ET les zones de sécurité, sinon la
   carte dépasse le cadre : sur téléphone, elle sortait par le bas et le bouton
   « Créer le profil » se retrouvait sous l'écran, à aller chercher en faisant
   défiler l'arrière-plan.
   Le plafond descend sur petit écran : une fenêtre haute de presque tout
   l'écran ne se lit plus comme une fenêtre, et son pied est trop bas pour le
   pouce. */
/* Par défaut, une fenêtre fait la taille de son contenu : une fenêtre d'info de
   quatre paragraphes n'a aucune raison d'occuper 760 px de haut avec du vide
   sous son bouton. Ce qui dépasse fait défiler l'overlay. */
.modal-card{--modal-max:760px;width:100%;max-width:520px;
            background:var(--card-bg);border-radius:14px;border:1px solid var(--border);
            padding:18px;display:flex;flex-direction:column;gap:11px;margin:auto;}
/* Sauf celles qui portent un TIROIR : là, la hauteur doit être la même du
   formulaire à la liste puis à la fiche, sans quoi le sol bouge sous chaque
   pas. Elle se mesure sur l'OVERLAY et non sur l'écran, lui seul connaissant
   son propre remplissage, les zones de sécurité qu'il retranche déjà, et la
   hauteur que suivreZoneVisible() lui laisse quand le clavier est ouvert.
   `100dvh` ne bouge pas d'un pixel quand le clavier surgit. */
.modal-card.fixe{height:min(var(--modal-max), 100%);}

/* Une fenêtre PLEINE : son contenu va d'un bord à l'autre. La carte porte
   normalement 18 px de remplissage, ce qui laissait une bande blanche tout
   autour d'une liste rangée par cartes — le fond beige s'arrêtait avant les
   bords et l'écran ne ressemblait plus à la liste de courses.
   Le remplissage descend donc dans l'en-tête, le pied et la racine, qui en ont
   chacun besoin d'un différent. */
.modal-card.pleine{padding:0;gap:0;overflow:hidden;}
.modal-card.pleine .modal-head{padding:15px 18px;border-bottom:1px solid var(--border);}
.modal-card.pleine .modal-foot{padding:12px 16px;border-top:1px solid var(--border);}
.modal-card.pleine .modal-root{padding:14px 18px 18px;}
/* LE PLAFOND EST UN POURCENTAGE, plus un nombre de pixels. 620 px, c'était 73 %
   d'un iPhone 14 mais 93 % d'un iPhone SE : le même réglage donnait une fenêtre
   sur le grand téléphone et presque une page entière sur le petit — exactement
   à l'envers de ce qu'on veut, puisque c'est le petit écran qui a le plus besoin
   qu'on voie la page derrière.
   90 % : le maximum de contenu, la page derrière n'étant plus qu'un rappel
   qu'on est dans une fenêtre et non sur un écran de plus.
   Sur un téléphone à encoche, le remplissage du haut de l'overlay et la zone
   de sécurité peuvent rogner un point ou deux — et c'est très bien, ce sont
   eux qui empêchent la carte de passer sous l'heure et les icônes réseau.
   `vh` d'abord comme repli : sur les navigateurs sans `dvh`, il vaut la hauteur
   barre d'adresse rétractée, donc un peu trop — c'est le `min(…, 100%)` de
   `.fixe` qui rattrape, l'overlay connaissant seul sa hauteur réelle. */
@media (max-width:520px){
  .modal-card{--modal-max:90vh;padding:16px;border-radius:12px;}
  @supports (height:1dvh){.modal-card{--modal-max:90dvh;}}
}
.modal-head{display:flex;align-items:center;justify-content:space-between;gap:10px;}
.modal-title{font-size:16px;font-weight:700;color:var(--text);}
/* `.modal-close` est déclarée AVEC `.mc-close`, plus haut dans cette feuille :
   les deux fenêtres portent la même croix, et deux règles séparées avaient déjà
   divergé une fois. */
.modal-label{font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--text-faint);margin-top:4px;}
.modal-name-input{width:100%;padding:11px 12px;border:1px solid var(--border);border-radius:8px;font-size:15px;background:var(--page-bg);color:var(--text);}
/* ⚠️ `.modal-input` A VÉCU (ménage du 25/09) — le grand champ multiligne de
   l'ancienne modale de profil, dont les deux dernières règles étaient GROUPÉES
   avec `.modal-name-input`, bien vivante : on a découpé les listes au lieu de
   supprimer les règles. */
.modal-name-input::placeholder{color:var(--text-faint);}
.modal-save{margin-top:6px;padding:12px;border:none;border-radius:8px;background:var(--green-btn);color:var(--sur-vert);font-size:14px;font-weight:700;cursor:pointer;}
@media (hover:hover){.modal-save:hover{background:var(--green-btn-hover);}}

/* ── Le tiroir ─────────────────────────────────────────────────────────
   Un panneau par écran, empilés dans le même cadre. `sl-pane` est posée par
   createSlider() sur tout ce qu'on y pousse. */
.sl-pane{position:absolute;inset:0;overflow-y:auto;-webkit-overflow-scrolling:touch;
         overscroll-behavior:contain;background:var(--card-bg);
         transition:transform .28s cubic-bezier(.4,0,.2,1);}
/* Déclaré APRÈS .sl-pane, sinon celui-ci l'emporte et repeint le panneau en
   blanc : une liste rangée par cartes prend le fond de PAGE, comme la liste de
   courses, et les cartes se posent dessus. */
.tp-pane, .sl-pane.tp-pane{background:var(--page-bg);}
/* ⚠️ LA MOSAÏQUE EN TIROIR NE DÉFILE PAS COMME UN PANNEAU ORDINAIRE. `.sl-pane`
   défile d'un bloc ; celle-ci a une barre de recherche et de tri qui doit
   RESTER en haut pendant qu'on parcourt la liste. On lui rend donc sa colonne,
   et le défilement redescend dans `.nl-corps`, à qui il appartient. Déclaré
   après `.sl-pane` pour la même raison que la ligne au-dessus : à poids égal,
   c'est l'ordre qui tranche. */
.nl-pane, .sl-pane.nl-pane{display:flex;flex-direction:column;overflow:hidden;
                           background:var(--page-bg);}
/* ⚠️ ET SON PIED REND SON DÉCOR À SON HÔTE. `.nl-pied` était le bas d'une
   CARTE : filet, remplissage, coins arrondis. Posé dans le `.modal-foot` d'une
   fenêtre qui a déjà les siens, ça faisait deux traits l'un sur l'autre — et un
   bouton qui n'atteignait plus le bord droit, faute d'occuper toute la largeur.
   Il ne garde ici que sa rangée ; `.nl-compte` (flex:1) repousse « Valider » à
   droite, exactement comme dans la fenêtre autonome. */
.modal-foot .nl-pied{flex:1 1 auto;width:100%;padding:0;background:none;
                     border-top:none;border-radius:0;}
/* La scène ne défile PAS : c'est le panneau qu'elle contient (`.sl-pane`) qui
   s'en charge. Elle ne fait que découper le cadre et masquer ce qui glisse.
   Et elle n'a PAS de hauteur propre : elle prend ce que la carte lui laisse.
   C'est ce qui garde la fenêtre rigoureusement de la même taille du formulaire
   à la liste, puis à la fiche — une fenêtre qui change de taille à chaque
   écran donne l'impression que le sol bouge. */
.modal-stage{position:relative;flex:1 1 auto;min-height:0;overflow:hidden;}
/* La racine défile comme un panneau. Elle vivait dans le flux, donc sa hauteur
   était celle de son contenu : dans un cadre de hauteur fixe, la fin du
   formulaire passait sous le bord et devenait inatteignable sur téléphone.
   Elle garde son propre remplissage : les panneaux poussés par-dessus ont le
   leur, souvent différent (une liste colle ses lignes aux bords). */
.modal-root{position:absolute;inset:0;overflow-y:auto;-webkit-overflow-scrolling:touch;
            overscroll-behavior:contain;
            padding:2px 2px 16px;display:flex;flex-direction:column;gap:11px;}
/* AUCUN enfant ne se comprime. Par défaut, un enfant de colonne flex accepte de
   descendre sous sa hauteur de contenu : la zone de texte « Précisions » se
   retrouvait haute d'une ligne, avec son propre texte qui débordait. */
.modal-root > *{flex:0 0 auto;}
.modal-foot{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;
            gap:12px;padding-top:12px;border-top:1px solid var(--border);}
.modal-foot .modal-save{flex:1 1 auto;}
/* La flèche garde toujours sa place, même invisible : le titre est centré
   ENTRE elle et la croix, et la faire disparaître le décalait d'un demi-bouton
   à chaque changement d'écran. */
.modal-back{width:32px;height:32px;flex-shrink:0;border:none;border-radius:8px;
            background:none;color:var(--text);font-size:20px;cursor:pointer;line-height:1;
            display:flex;align-items:center;justify-content:center;visibility:hidden;}
.modal-title{flex:1 1 auto;text-align:center;}

/* Nom du profil et étoile « par défaut », sur une seule ligne. */
.modal-name-row{display:flex;align-items:center;gap:8px;}
.modal-name-row .modal-name-input{flex:1 1 auto;min-width:0;}
.modal-star{width:38px;height:38px;flex-shrink:0;border:1px solid var(--green-border);
            border-radius:8px;background:none;color:var(--green-btn);cursor:pointer;
            display:flex;align-items:center;justify-content:center;padding:0;}
.modal-star svg{width:17px;height:17px;display:block;}
.modal-star.on{background:var(--green-btn);border-color:var(--green-btn);color:var(--sur-vert);}
@media (hover:hover){.modal-star:not(.on):hover{background:var(--green-light);}}

/* ── Le champ « Précisions » ────────────────────────────────────────────
   Une ligne au départ, qui grandit avec le texte. Le bouton de validation
   n'apparaît que tant que la saisie n'est pas confirmée : un bouton toujours
   visible ne dit rien, un bouton qui apparaît dit « il reste quelque chose à
   faire ». Repris de la carte de profil.php, où il vivait seul. */
.pf-wrap{display:flex;flex-direction:column;gap:2px;min-width:0;}
.pf-row{position:relative;}
.pf-input{width:100%;box-sizing:border-box;padding:11px 12px;border:1px solid var(--border);
          border-radius:8px;font-size:16px;background:transparent;color:var(--text);
          font-family:inherit;line-height:1.4;resize:none;overflow:hidden;display:block;}
.pf-input::placeholder{color:var(--text-faint);}
/* En lecture seule (carte de profil repliée sur elle-même), le champ redevient
   du texte : un cadre autour de quelque chose qu'on ne peut pas modifier
   promet une action qui n'existe pas. */
.pf-input[readonly]{background:none;border-color:transparent;padding-left:0;}
/* On réserve la place du bouton : à droite sur une ligne, en bas à droite dès
   que le texte passe à la ligne — au milieu, il recouvrirait le texte. */
.pf-row.editable .pf-input{padding-right:46px;}
.pf-row.editable.multiline .pf-input{padding-right:12px;padding-bottom:44px;}
.pf-ok{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:32px;height:32px;
       padding:0;border:none;border-radius:7px;background:var(--green-btn);color:var(--sur-vert);cursor:pointer;
       display:flex;align-items:center;justify-content:center;
       visibility:hidden;opacity:0;transition:opacity .15s ease;}
.pf-row.multiline .pf-ok{top:auto;bottom:6px;transform:none;}
.pf-ok svg{width:16px;height:16px;display:block;}
.pf-row.dirty .pf-ok{visibility:visible;opacity:1;}
@media (hover:hover){.pf-ok:hover{background:var(--green-btn-hover);}}

/* ══ LA COHÉRENCE D'UN PROFIL ═════════════════════════════════════════════
   Ce que Maryse dit quand un profil se contredit. Trois teintes, déjà dans la
   feuille et déjà porteuses de sens : AMBRE ce qui attend quelque chose de toi,
   BLEU je n'ai pas compris, ROUGE la sécurité. Rien de neuf n'est inventé ici.

   Le message a DEUX hôtes :
     · page Profil  — une FENÊTRE, la même que « Supprimer le profil », un seul
       bouton « Compris ». Posé dans la page, le message naissait hors de
       l'écran sur une liste de sept profils.
     · page Générer — une carte teintée au bas du formulaire, SANS bouton : le
       pied de la fenêtre porte déjà « Continuer ».

   Et il ne demande JAMAIS rien : Maryse ne tient pas compte de l'incohérence,
   il n'y a donc rien à arbitrer. Ce qui reste en régime permanent, c'est la
   mention sous le champ.
   ══════════════════════════════════════════════════════════════════════════ */

/* La teinte voyage avec le message, quel que soit son hôte : c'est d'elle que
   la pastille du texte de l'utilisateur tire sa couleur. */
.cp-msg{border:1px solid var(--cp-bord);background:var(--cp-fond);border-radius:11px;
  padding:13px 15px;}
.cp-msg, .cp-texte{display:flex;flex-direction:column;gap:8px;}
.cp-msg.ambre, .cp-texte.ambre{--cp-fond:var(--amber-bg);--cp-bord:var(--amber-border);
  --cp-encre:var(--amber-text);--cp-plein:var(--amber-plein);--cp-sur:var(--sur-ambre);}
.cp-msg.rouge, .cp-texte.rouge{--cp-fond:var(--danger-bg);--cp-bord:var(--danger-border);
  --cp-encre:var(--danger);--cp-plein:var(--danger);--cp-sur:var(--sur-danger);}
.cp-msg.bleu, .cp-texte.bleu{--cp-fond:var(--blue-bg);--cp-bord:var(--blue-border);
  --cp-encre:var(--blue-text);--cp-plein:var(--blue-badge-text);--cp-sur:var(--sur-bleu);}

/* La fenêtre est la carte ORDINAIRE, celle de « Supprimer le profil ». La
   couleur n'y passe que par le BOUTON : ni filet teinté ni titre coloré, sinon
   une fenêtre de plus dans la famille, à retenir en plus des autres.
   Les classes gardent leur préfixe `cp-`. `.mc-card.ambre` peignait un BANDEAU
   d'en-tête jusqu'au 18/09 ; elle n'existe plus, mais le préfixe reste : c'est
   sous ce nom que le JS les pose, et renommer n'apporterait qu'un risque. */
.mc-card.cp-ambre .mc-confirm{background:var(--amber-plein);color:var(--sur-ambre);}
.mc-card.cp-rouge .mc-confirm{background:var(--danger);color:var(--sur-danger);}
.mc-card.cp-bleu .mc-confirm{background:var(--blue-badge-text);color:var(--sur-bleu);}

.cp-titre{font-size:14px;font-weight:700;color:var(--cp-encre);line-height:1.3;}

/* TROIS PARAGRAPHES, ET UNE LIGNE SAUTÉE ENTRE EUX. Un message dit toujours la
   même chose dans le même ordre : le FAIT, CE QUE JE FAIS (c'est ce
   paragraphe-là qui porte la pastille), LA SUITE. Une phrase par paragraphe et
   pas de virgule : quand une phrase en réclame une, elle porte deux idées, et
   la seconde a droit à sa ligne. */
.cp-p{font-size:13px;line-height:1.5;color:var(--text);}
.cp-p + .cp-p{margin-top:6px;}

/* La pastille : le bout de SA phrase dont le message parle. Une seule forme
   pour les trois teintes et pour les deux sens — ce qui est écarté et ce qui
   est retenu. C'est la PHRASE qui dit lequel des deux, pas la couleur.
   ⚠️ Pas de guillemets en plus : la pastille EST la citation.
   ⚠️ `inline` et non `inline-block` : une boîte en ligne pousse la hauteur de
   SA ligne et désaligne le paragraphe autour. `box-decoration-break:clone`
   rend ses bouts arrondis à chaque morceau quand elle passe à la ligne. */
.cp-p .cp-cite{display:inline;background:var(--cp-plein);color:var(--cp-sur);
  font-weight:700;font-style:normal;border-radius:20px;padding:2px 9px;
  box-decoration-break:clone;-webkit-box-decoration-break:clone;}

/* ── LES MENTIONS SOUS LE CHAMP ───────────────────────────────────────────
   Elles portent l'information en régime permanent, quand le message a disparu
   depuis longtemps. Une LIGNE, jamais un pavé.
   ⚠️ UNE SEULE MISE EN FORME POUR TOUT CE QUI SE DIT SOUS UN CHAMP :
   partiellement appliquée, non prise en compte, langue inattendue. Trois
   tailles et trois encres pour trois phrases au même endroit, ça fait trois
   voix. Seule l'allergie change de couleur, parce qu'elle change de nature. */
.cp-mention, .cp-langue{font-size:11.5px;font-weight:600;line-height:1.4;
  margin-top:5px;color:var(--amber-text);}
.cp-mention.rouge{color:var(--danger);}
/* Une précision écartée en ENTIER est barrée ; une précision partiellement
   appliquée ne l'est pas. Sans cette différence, on ne sait pas si sa phrase
   est morte ou seulement arbitrée. */
.pf-input.cp-barre{text-decoration:line-through;color:var(--text-faint);}

/* La confirmation d'enregistrement, dans la fenêtre de la page Générer. Un
   message qui paraît juste après « Enregistrer » se lit comme un ÉCHEC : la
   confirmation et la remarque s'affichent donc EN MÊME TEMPS. */
.cp-ok{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:700;
  color:var(--green-btn);margin-top:6px;}
.cp-ok svg{width:15px;height:15px;display:block;}
.cp-fermeture{font-size:12px;color:var(--text-faint);}

/* ══ ATTENDRE, ET LE DIRE ═════════════════════════════════════════════════
   Un enregistrement de profil part au serveur ET fait relire le texte par le
   modèle : un seul appel, mais il peut durer. Pendant ce temps, le bouton qu'on
   vient de cliquer devient le témoin, et tout le reste s'éteint.

   ⚠️ MONTÉ D'`index.php` LE 18/09, où il servait déjà la fenêtre d'une journée.
   Ne pas le redéclarer là-bas : deux exemplaires du même bouton divergent.

   ⚠️ ET IL PORTE SON ENCRE LUI-MÊME. Il ne déclarait que son FOND, ce qui
   suffisait sur `.btn-jour-ok` — dont le `:disabled` ne touche pas à la
   couleur. Posé sur `.modal-save`, dont le `:disabled` repeint le texte en
   `--text-faint`, le mot tombait à 1,07:1 sur le vert adouci. Mesuré.

   ⚠️ DEUX VERTS, DEUX SENS. Le vert ADOUCI dit « j'attends », le vert PLEIN dit
   « c'est fait ». Un seul vert pour les deux, et le bouton ne raconte plus rien
   en changeant d'état. Et un `opacity:.5` sur un vert plein le rend grisâtre —
   d'où un vert amorti écrit en clair plutôt qu'une transparence. */
/* ⚠️ `button.` N'EST PAS UNE COQUETTERIE : IL FAIT GAGNER LA CASCADE. Trois
   règles de même poids (0,2,0) peignent ces mêmes boutons et sont écrites
   APRÈS — `.modal-save:disabled` plus bas dans cette feuille,
   `.btn-jour-ok:disabled` et `.btn-edit-toggle.is-saving` dans les `<style>`
   des pages, servis après le `<link>`. Sans l'élément, le témoin était peint en
   gris de bouton mort, le ✓ aussi, et le bouton d'une journée retombait à
   `opacity:.5`. Mesuré en résolvant la cascade, pas en lisant les règles. */
button.btn-jour-attente,button.btn-jour-attente:disabled{background:var(--green-doux);
  color:var(--sur-vert);border-color:var(--green-doux);opacity:1;
  display:flex;align-items:center;justify-content:center;gap:9px;}
/* Le rond suit L'ENCRE DU BOUTON, il ne la redit pas. Il était écrit en blanc
   en dur : sur le vert adouci du thème sombre, il tournait à 3,13:1 pendant que
   le mot d'à côté, lui, était passé au fond de page. `currentColor` le rend
   solidaire une fois pour toutes.
   La piste est déclarée DEUX fois : la version `rgba` d'abord, pour les moteurs
   qui ignorent `color-mix`, la version calculée ensuite. */
.jour-spin{width:14px;height:14px;border-radius:50%;flex:0 0 auto;
  border:2px solid rgba(127,127,127,.4);
  border:2px solid color-mix(in srgb, currentColor 42%, transparent);
  border-top-color:currentColor;
  animation:jour-tourne .8s linear infinite;}
@keyframes jour-tourne{to{transform:rotate(360deg);}}
/* Mouvement réduit : on ARRÊTE le rond. Le ralentir laissait une boucle infinie,
   ce qui est précisément ce que le réglage demande d'éviter — et le mot
   « Enregistrement… » dit déjà tout ce que le rond disait. */
@media (prefers-reduced-motion:reduce){.jour-spin{animation:none;opacity:.55;}}

/* ⚠️ LA CONFIRMATION VIT DANS LE BOUTON QU'ON VIENT DE CLIQUER (Adam, 18/09) :
   « ce message apparaît quelques secondes dans le CTA avant de se dissiper ».
   Sur la page Profil c'est un gain sec — la confirmation y vivait dans
   `#saved-msg`, à l'intérieur d'un conteneur `display:none`, et ne s'était
   jamais vue. Le bouton reste ÉTEINT pendant ces secondes : ce qu'il annonce a
   déjà eu lieu, il n'y a rien à recliquer. */
button.btn-jour-fait,button.btn-jour-fait:disabled{background:var(--green-btn);
  color:var(--sur-vert);border-color:var(--green-btn);opacity:1;
  display:flex;align-items:center;justify-content:center;gap:8px;}
.btn-jour-fait svg{width:15px;height:15px;display:block;flex:0 0 auto;}

/* ⚠️ RIEN NE DISPARAÎT, TOUT S'ÉTEINT. Un bouton qui s'en va laisse croire
   qu'on aurait pu s'en servir ; un bouton éteint dit « pas maintenant ». C'est
   déjà la règle du pied de la fenêtre d'une journée. Et un lien de navigation
   éteint garde SA PLACE : la barre du haut ne doit pas se réorganiser parce
   qu'un enregistrement est parti.
   `pointer-events` ET `disabled` : le premier couvre les `<a>`, qui ne
   connaissent pas le second. */
.att-eteint,button.att-eteint{opacity:.45;pointer-events:none;cursor:default;}
a.att-eteint{text-decoration:none;}

/* ⚠️ ET LE CLIC À CÔTÉ CESSE D'ÊTRE UNE PORTE. La classe ne peint rien : elle
   marque la fenêtre pour le code, et rend le curseur honnête. Sans ce verrou,
   un clic distrait sur le fond emporte un formulaire déjà parti au serveur, et
   la page reste avec un profil que le serveur a rangé. */
.mc-overlay.att-sourd,.modal-overlay.att-sourd{cursor:default;}

/* ── LES MARQUEURS DU SÉLECTEUR DE PROFIL ─────────────────────────────────
   Le point de couleur est TOUJOURS doublé d'un libellé, et il paraît DANS la
   liste — pas seulement sur le profil retenu, sinon on découvre le problème
   après avoir choisi.
   ⚠️ L'encre est `--amber-plein` et non `--amber-badge-text` : le couple de
   badge ne donne que 2,77:1 en clair, mesuré. Il passe pour un chiffre
   décoratif, pas pour un libellé de 10,5 px qui porte un avertissement. */
.cp-marq{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;
  font-size:10.5px;font-weight:700;border-radius:20px;padding:3px 8px;
  background:var(--amber-badge-bg);color:var(--amber-plein);white-space:nowrap;}
.cp-marq.rouge{background:var(--danger-bg);color:var(--danger);}
.cp-marq.gris{background:var(--page-bg);color:var(--text-faint);}
.cp-marq .cp-pt{width:7px;height:7px;border-radius:50%;background:currentColor;flex:0 0 auto;}
/* (Le déclencheur PLEIN de la liste déroulante du profil, où la pastille
   s'inversait, a vécu : S, 14/10 — une ligne et un tiroir, sur fond blanc.) */

/* En-tête d'une carte : ses boutons font STRICTEMENT la même taille, quel que
   soit leur style. (Le crayon et la corbeille d'un rayon sont partis avec R :
   son « ••• » les ouvre.) */
.cat-actions{display:flex;align-items:center;gap:6px;flex-shrink:0;}
.cat-actions>button{width:30px;height:30px;flex:none;box-sizing:border-box;padding:0;}
.cat-actions>button svg{width:15px;height:15px;display:block;}

/* Le repère de dépose ENTRE deux lignes : un TRAIT vert, plein, posé au bord
   de la ligne visée — un trait dit où la chose va atterrir. La règle est
   générique (elle ne nomme aucune liste). */
.drag-over-top::before,.drag-over-bottom::after{content:'';position:absolute;left:14px;right:12px;
  height:3px;background:var(--green-btn);border-radius:2px;z-index:4;}
.drag-over-top::before{top:-1px;}
.drag-over-bottom::after{bottom:-1px;}

/* ══════════════════════════════════════════════════════════════════════════
   LA MOSAÏQUE ET SA FENÊTRE DE CHOIX
   Descendues du <style> de favoris.php le 11/08 : la page Générer choisit
   désormais ses plats dans le même écran, et la page Inspiration le fera
   demain. Deux copies de ces règles, c'était deux mosaïques qui divergeaient
   au premier réglage — on a déjà payé ça pour le champ de recherche.
   ══════════════════════════════════════════════════════════════════════════ */
/* Le retour, dans la rangée des filtres — là où l'on règle déjà ce qu'on voit.
   Il ouvre la rangée, avant les rangs : on sort d'un cran avant de trier ce
   qu'il reste.
   C'est une PASTILLE, la vraie : il porte .tf-tog et n'ajoute que la flèche. Il
   a d'abord eu ses propres couleurs, vert clair au repos et vert plein au
   survol — deux boutons voisins qui ne réagissent pas pareil au même geste, et
   la rangée cessait d'être une rangée. */
.fv-retour{display:inline-flex;align-items:center;gap:7px;}
.fv-retour svg{width:14px;height:14px;display:block;flex:none;}

/* PAS D'OMBRE SOUS LES TUILES (N, Adam, 10/10). Leur ombre portée (6 px plus
   bas, 18 px de flou) débordait de la carte : quand le titre collant d'une
   liste arrivait au bout de ses tuiles, elle dépassait SOUS sa bande — une
   traînée grise entre deux listes. Une photo n'a pas besoin d'ombre pour se
   détacher ; la tuile garde son liseré (`.fv-ring`). */
.fv-tile{position:relative;overflow:hidden;border-radius:var(--fv-radius);
         background:var(--row-border);cursor:pointer;isolation:isolate;
         transition:box-shadow .14s ease;}
.fv-photo{position:absolute;inset:0;}
.fv-tile img{width:100%;height:100%;object-fit:cover;display:block;}
/* LE SURVOL (T, Adam, 27/10 : « pas un effet de zoom […] mais bien un
   contour vert AUTOUR de la tuile » ; puis « le même contour vert [que] quand
   on survole une liste, mêmes épaisseurs, même petite marge et animation ») :
   l'anneau de la tuile d'une liste (`.lt-carte`), au trait près — 2 px du
   fond de page, 2 px de vert, en 0,14 s. Sur la page (chez soi, chez
   l'invité), dans la fenêtre de choix, pendant « Réorganiser ». Le NOM d'un
   plat, lui, ne change pas de couleur (celui d'une liste, si). Pas sur une
   tuile que le plafond éteint. Le clavier montre le même. */
@media (hover:hover){
  .fv-tile:not(.off):hover{box-shadow:0 0 0 2px var(--page-bg), 0 0 0 4px var(--green-btn);}
}
:where(html.nav-clavier) .fv-tile:focus-visible{outline:none;box-shadow:0 0 0 2px var(--page-bg), 0 0 0 4px var(--green-btn);}
/* PAS DE VOILE EN HAUT DES TUILES (N, Adam, 10/10) : le dégradé sombre qui
   assombrissait le haut de la photo sous le cœur est parti avec sa règle. Le
   cœur se lit seul, cerné de blanc (plus bas). */
.fv-ring{position:absolute;inset:0;border-radius:inherit;pointer-events:none;
         box-shadow:inset 0 0 0 .5px rgba(255,255,255,.16), inset 0 0 0 1px rgba(0,0,0,.05);}

/* Le bouton de la tuile — le cœur, seul depuis B.7 — respire : collé au coin,
   il se lirait comme posé SUR le bord de la tuile plutôt que dedans. */
.fv-tile-actions{position:absolute;top:13px;right:13px;display:flex;gap:7px;z-index:4;}
/* LE CŒUR SANS ROND (N, Adam, 10/10) : plus de pastille de verre autour, le
   cœur seul, cerné d'un CONTOUR BLANC qui le détache de n'importe quelle
   photo. Le bouton garde sa zone de 30 px (le doigt vise la même chose), elle
   est seulement invisible. Sur une tuile blanche, le contour se fond dans la
   carte. */
.fv-gbtn{width:30px;height:30px;border-radius:999px;display:flex;align-items:center;
         justify-content:center;padding:0;cursor:pointer;
         background:none;border:none;color:#fff;
         -webkit-tap-highlight-color:transparent;
         transition:transform .12s ease;}
.fv-gbtn svg{width:21px;height:21px;display:block;}
@media (hover:hover){.fv-gbtn:hover{transform:scale(1.1);}}
.fv-gbtn:focus{outline:none;}
:where(html.nav-clavier) .fv-gbtn:focus-visible{outline:2px solid rgba(255,255,255,.9);outline-offset:-2px;}
/* Le tracé du cœur n'est pas centré dans sa grille de 24 : sa boîte tombe un
   demi-pixel trop bas et un tiers de pixel trop à droite. On le recale, et on
   le remonte d'un cheveu de plus : une pointe en bas et deux lobes en haut se
   lisent comme trop basses même quand la boîte, elle, est centrée.
   Le recalage voyage avec la pression du doigt, sinon le cœur se replacerait
   au moment où on appuie dessus.
   Le CONTOUR est le trait du tracé, peint SOUS le remplissage
   (`paint-order`) : seule sa moitié extérieure se voit. */
.fv-heart svg{transform:translate(-0.4px, -0.42px);}
.fv-heart svg path{fill:#e0556b;stroke:#fff;stroke-width:3.5;stroke-linejoin:round;
                   paint-order:stroke fill;}
.fv-heart:active svg{transform:translate(-0.4px, -0.42px) scale(.85);}

/* LA PASTILLE DU RANG des petits carrés à photo est partie avec sa règle (N,
   10/10) : le nom s'y affiche désormais, l'icône du rang devant lui. */

/* Le bandeau du nom. Le flou naît du BORD BAS de la tuile et se dissout vers
   le haut : un rectangle net posé sur la photo se lirait comme une étiquette
   collée dessus. Il porte lui-même les deux coins du bas — un élément à
   backdrop-filter crée son propre contexte et ne suit pas l'overflow de la
   tuile. */
.fv-legende{position:absolute;left:0;right:0;bottom:0;z-index:4;pointer-events:none;
            padding:54px 16px 15px;color:#fff;
            border-radius:0 0 var(--fv-radius) var(--fv-radius);
            background:linear-gradient(to top, rgba(10,10,8,.16), rgba(10,10,8,.05) 50%, rgba(10,10,8,0));
            backdrop-filter:blur(26px) saturate(1.25) brightness(.70);
            -webkit-backdrop-filter:blur(26px) saturate(1.25) brightness(.70);
            -webkit-mask-image:linear-gradient(to top, #000 0%, #000 34%, rgba(0,0,0,.62) 62%, rgba(0,0,0,.22) 84%, transparent 100%);
            mask-image:linear-gradient(to top, #000 0%, #000 34%, rgba(0,0,0,.62) 62%, rgba(0,0,0,.22) 84%, transparent 100%);}
/* PAS D'OMBRE SOUS LES TEXTES (N, Adam, 10/10) : le nom du plat et son rang
   (« Entrée », « Plat », « Dessert », l'icône comprise) s'écrivent nets. Sur
   une photo, c'est la bande floutée et assombrie du bas qui les rend
   lisibles. */
.fv-rang{display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:700;
         letter-spacing:.06em;text-transform:uppercase;opacity:.95;margin-bottom:5px;}
.fv-rang .fv-emoji{font-size:13px;line-height:1;}
.fv-dish{font-size:16px;font-weight:700;line-height:1.28;letter-spacing:-.01em;
         display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.fv-tile.fv-etroite .fv-legende{padding:44px 12px 12px;}
.fv-tile.fv-etroite .fv-rang{font-size:10.5px;letter-spacing:.05em;margin-bottom:3px;}
.fv-tile.fv-etroite .fv-dish{font-size:13.5px;line-height:1.25;-webkit-line-clamp:3;}

/* LES PETITS CARRÉS À PHOTO DISENT LEUR NOM (N, Adam, 10/10). Ils le
   cachaient, et ne montraient que la pastille du rang. Désormais : le bandeau
   des autres tuiles, et dedans UNE ligne — l'icône du rang, une espace, le
   nom —, sur toute la largeur moins la marge intérieure, terminée par « … »
   si elle ne tient pas. L'icône vient de `data-emo` (posé par le script sur
   `.fv-dish`) : pas de rond autour, pas de mot « PLAT ». */
.fv-tile.fv-petite.fv-avec-photo .fv-legende{padding:40px 12px 11px;}
.fv-tile.fv-petite.fv-avec-photo .fv-rang{display:none;}
.fv-tile.fv-petite.fv-avec-photo .fv-dish{display:block;font-size:13.5px;line-height:1.25;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-line-clamp:unset;}
.fv-tile.fv-petite.fv-avec-photo .fv-dish[data-emo]::before{content:attr(data-emo);margin-right:.38em;}
.fv-tile.fv-petite .fv-gbtn{width:26px;height:26px;}
.fv-tile.fv-petite .fv-gbtn svg{width:19px;height:19px;}
.fv-tile.fv-petite .fv-tile-actions{top:10px;right:10px;}

/* ── La tuile sans photo ────────────────────────────────────────────────── */
/* C'est l'état normal tant que le générateur d'images n'est pas là. Elle
   devient une carte comme les autres cartes de l'application, et le nom prend
   toute la place : il n'y a plus rien d'autre à regarder. Le nom s'affiche sur
   TOUTES les tailles, petits carrés compris. */
.fv-tile.fv-vide{background:var(--card-bg);}
.fv-tile.fv-vide .fv-ring{box-shadow:inset 0 0 0 1px var(--border);}
.fv-tile.fv-vide .fv-legende{position:absolute;top:0;width:auto;height:auto;margin:0;
    clip-path:none;white-space:normal;overflow:hidden;
    display:flex;flex-direction:column;justify-content:center;
    padding:14px;color:var(--text);background:none;
    backdrop-filter:none;-webkit-backdrop-filter:none;
    -webkit-mask-image:none;mask-image:none;}
.fv-tile.fv-vide .fv-rang{color:var(--text-muted);opacity:1;}
.fv-tile.fv-vide .fv-dish{-webkit-line-clamp:4;}
.fv-tile.fv-vide.fv-grande .fv-dish{font-size:19px;line-height:1.3;-webkit-line-clamp:3;}
.fv-tile.fv-vide.fv-grande .fv-rang{margin-bottom:7px;}
.fv-tile.fv-vide.fv-etroite .fv-dish{font-size:15px;}
.fv-tile.fv-vide.fv-petite .fv-legende{padding:12px;}
.fv-tile.fv-vide.fv-petite .fv-dish{font-size:13.5px;-webkit-line-clamp:4;}
/* Le cœur d'une tuile blanche : sans rond lui aussi (N). Son contour prend la
   couleur de la CARTE — il « se fond dans la carte », en sombre aussi, où un
   blanc se verrait (relecture, 10/10) ; et son liseré de focus passe au vert,
   un blanc ne se verrait pas sur la carte claire. */
.fv-tile.fv-vide .fv-heart svg path{stroke:var(--card-bg);}
.fv-tile.fv-vide .fv-gbtn:focus-visible{outline-color:var(--green-btn);}

/* ══ LA TUILE D'UNE LISTE (T, Adam, 27/10) ════════════════════════════════
   Un carré — les quatre premiers plats de la liste, chacun sa photo ou sa
   carte blanche — et le nom dessous. Commune : la page Favoris en fait sa
   page d'accueil, la fenêtre de choix sa vue des listes (`tuileListe`). Ses
   tailles suivent sa largeur (`cqi`) : la même tuile sert trois colonnes sur
   ordinateur, deux au téléphone. */
.lt-carte{min-width:0;cursor:pointer;-webkit-tap-highlight-color:transparent;outline:none;}
.lt-tuile{position:relative;aspect-ratio:1;border-radius:var(--fv-radius, 16px);overflow:hidden;
  background:var(--row-border);isolation:isolate;container-type:inline-size;transition:box-shadow .14s ease;}
/* Ses quatre premiers plats : 1, 2, 3 ou 4 cases, un filet entre elles. */
.lt-mos{position:absolute;inset:0;display:grid;gap:clamp(2px, .9cqi, 3px);}
.lt-mos.n1{grid-template:1fr / 1fr;}
.lt-mos.n2{grid-template:1fr / 1fr 1fr;}
.lt-mos.n3{grid-template:1fr 1fr / 1fr 1fr;}
.lt-mos.n3 > .lt-cel:first-child{grid-row:1 / 3;}
.lt-mos.n4{grid-template:1fr 1fr / 1fr 1fr;}
.lt-cel{position:relative;overflow:hidden;min-width:0;min-height:0;background:var(--card-bg);}
.lt-cel img{width:100%;height:100%;object-fit:cover;display:block;}
/* Une case SANS photo : la carte blanche, l'émoji du rang et le nom du plat
   (la tuile blanche de la page, en petit). */
.lt-cel.sans{display:flex;flex-direction:column;justify-content:flex-end;gap:4px;
  padding:clamp(8px, 4cqi, 14px);}
.lt-emo{font-size:clamp(13px, 5cqi, 18px);line-height:1;}
.lt-pnom{font-size:clamp(11px, 4.1cqi, 15px);font-weight:700;line-height:1.24;letter-spacing:-.01em;
  color:var(--text);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;}
.lt-mos.n1 .lt-pnom{font-size:clamp(15px, 7cqi, 22px);}
.lt-mos.n2 .lt-pnom,.lt-mos.n3 > .lt-cel:first-child .lt-pnom{font-size:clamp(12px, 5cqi, 17px);-webkit-line-clamp:4;}
/* Vide : la tuile le dit. */
.lt-tuile.lt-vide{background:var(--card-bg);}
.lt-rien{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:9px;
  color:var(--text-faint);font-size:clamp(11.5px, 4.2cqi, 14px);font-weight:600;text-align:center;padding:10px;}
.lt-rien svg{width:clamp(24px, 12cqi, 40px);height:auto;display:block;}
/* Le liseré : celui des tuiles de plats, intérieur. */
.lt-ring{position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:3;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.07);}
.lt-tuile.lt-vide .lt-ring{box-shadow:inset 0 0 0 1px var(--border);}
/* Le nom dessous, coupé « … » ; l'icône lien si la liste est partagée ; le
   NOMBRE de plats à droite, une marge entre les deux. */
.lt-pied{display:flex;align-items:baseline;gap:6px;min-width:0;margin:10px 2px 0;}
.lt-nom{flex:0 1 auto;min-width:0;font-size:16px;font-weight:700;letter-spacing:-.012em;line-height:1.25;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .14s ease;}
.lt-lien{flex:none;display:flex;align-self:center;color:var(--green-btn);}
.lt-lien svg{width:16px;height:16px;display:block;}
.lt-n{flex:none;margin-left:auto;padding-left:12px;font-size:13px;font-weight:600;color:var(--text-muted);white-space:nowrap;}
/* LE SURVOL : un anneau vert AUTOUR de la tuile, détaché d'elle par un filet
   du fond de page, et le NOM passe au vert. Le clavier montre le même. */
@media (hover:hover){
  .lt-carte:hover .lt-tuile{box-shadow:0 0 0 2px var(--page-bg), 0 0 0 4px var(--green-btn);}
  .lt-carte:hover .lt-nom{color:var(--green-btn);}
}
:where(html.nav-clavier) .lt-carte:focus-visible .lt-tuile{box-shadow:0 0 0 2px var(--page-bg), 0 0 0 4px var(--green-btn);}
:where(html.nav-clavier) .lt-carte:focus-visible .lt-nom{color:var(--green-btn);}
/* Les plats choisis de cette liste (la fenêtre de choix) : en haut à gauche,
   là où les tuiles de plats portent leur rond. */
.lt-pris{position:absolute;top:10px;left:10px;z-index:5;min-width:27px;height:27px;padding:0 8px;border-radius:14px;
  box-sizing:border-box;display:flex;align-items:center;justify-content:center;background:var(--green-btn);color:var(--sur-vert);
  font-size:13px;font-weight:800;box-shadow:0 0 0 2px rgba(255,255,255,.92);}
/* La grille : trois par rangée (la page Favoris en pose deux au téléphone). */
.lt-grille{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:26px 18px;}

/* ══════════════════════════════════════════════════════════════════════════
   LA FENÊTRE « NOUVELLE LISTE »
   Créer une liste et la remplir sont le même geste : on nomme « Le dimanche »
   parce qu'on a déjà en tête les quatre plats qui vont dedans. Le champ de nom
   posé seul obligeait à créer, puis à retrouver chaque plat, puis à le ranger
   un par un.
   ══════════════════════════════════════════════════════════════════════════ */
/* Le nom EST l'en-tête : on n'écrit pas « Nom de la liste » au-dessus d'un champ
   qui le dit déjà, et le titre de la fenêtre est justement ce qu'on décide.
   Mais un titre ne se tape pas : sans rien pour le dire, le champ passait pour
   une étiquette. Il porte donc les marques d'un champ — un fond légèrement
   creusé, un liseré, et le crayon de l'application — sans rien perdre de la
   typographie d'un titre. */
.nl-head{position:relative;z-index:2;
         display:flex;align-items:center;gap:10px;padding:11px 11px 11px 12px;
         border-bottom:1px solid var(--row-border);background:var(--card-bg);
         border-radius:16px 16px 0 0;flex:none;}
/* LE CACHE. Huit pixels de fond de page posés juste sous l'en-tête, PAR-DESSUS
   la liste et HORS du défileur. Au repos ils ne couvrent rien — la barre de tri
   commence par douze pixels de vide. Mais ils avalent ce qu'un titre collant
   laisse passer quand il retarde d'une image pendant une inertie, chose qu'on
   ne voit que sur un téléphone. Hors du défileur, il ne suit pas le contenu :
   il ne peut donc rien recouvrir qui ne soit pas déjà sous l'en-tête. */
.nl-head::after{content:'';position:absolute;left:0;right:0;top:100%;height:8px;
                background:var(--page-bg);pointer-events:none;}
/* Quand l'en-tête n'est PAS un champ — Générer ne nomme rien, il choisit dans
   une liste qui existe — le titre garde la même typographie, sans le fond ni le
   liseré qui disent « tape ici ». Un titre habillé en champ serait un mensonge
   de plus par écran. */
.nl-titre-fixe{flex:1;min-width:0;padding:8px 2px 8px 6px;
               font-size:20px;font-weight:800;letter-spacing:-.022em;color:var(--text);
               overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.nl-champ{flex:1;min-width:0;display:flex;align-items:center;gap:8px;
          padding:7px 12px;border-radius:10px;
          background:var(--page-bg);border:1px solid var(--border);
          transition:border-color .15s ease, box-shadow .15s ease;}
.nl-champ:focus-within{border-color:var(--green-btn);
                       box-shadow:inset 0 0 0 1px var(--green-btn);}
.nl-nom{flex:1;min-width:0;border:none;background:none;padding:2px 0;
        font-family:inherit;font-size:20px;font-weight:800;letter-spacing:-.022em;
        color:var(--text);outline:none;}
.nl-nom::placeholder{color:var(--text-faint);font-weight:800;}
.nl-nom:focus{outline:none;box-shadow:none;}
/* Le crayon s'efface dès qu'on écrit : il a dit ce qu'il avait à dire. */
.nl-crayon{flex:none;width:17px;height:17px;color:var(--text-faint);
           transition:opacity .15s ease;}
.nl-crayon svg{width:100%;height:100%;display:block;}
.nl-champ:focus-within .nl-crayon{opacity:0;}
/* UN NOM DÉJÀ PRIS (10/10) : le champ rougit, le pied dit pourquoi — la règle
   de « Renommer » (`.ml-bloc.refuse`). */
.nl-champ.refuse,.nl-champ.refuse:focus-within{border-color:var(--danger);
                                               box-shadow:inset 0 0 0 1px var(--danger);}

/* La barre de tri, exactement celle de la page : même gélule, mêmes pastilles.
   Deux recherches de formes différentes pour le même geste, dans la même
   application, c'est une de trop.
   Elle DÉFILE avec le reste : elle a d'abord été escamotable — elle partait en
   descendant, revenait en remontant — et ce mouvement, qui gagnait cent pixels,
   coûtait plus qu'il ne rapportait : deux choses bougeaient à chaque geste au
   lieu d'une, et on ne savait plus si la page avait défilé ou si la barre s'était
   déplacée. Le titre de liste, lui, colle : c'est le seul repère dont on a
   besoin en cochant. */
.nl-tri{padding:12px 0 2px;background:var(--page-bg);}
.nl-tri .tp-search{margin:0 0 10px;padding-right:16px;}
.nl-filtres{display:flex;flex-wrap:nowrap;align-items:center;gap:7px;padding:0 0 12px;}
/* T (relecture adverse) : UNE ligne — [← Retour] [les filtres] [Tous les
   plats]. Les filtres dans leur bande, qui défile de côté quand la place
   manque (`nl-defile`, posé par le script) ; un fondu à droite dit qu'il y en
   a d'autres. Au clavier, l'anneau d'une pastille se pose EN DEDANS : la
   bande le couperait. */
.nl-zone{display:flex;gap:7px;flex:0 1 auto;min-width:0;overflow:hidden;}
.nl-zone.nl-defile{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;overscroll-behavior-x:contain;
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 18px),transparent);
          mask-image:linear-gradient(to right,#000 calc(100% - 18px),transparent);}
.nl-zone.nl-defile::-webkit-scrollbar{display:none;}
.nl-zone.nl-defile::after{content:'';flex:none;width:14px;height:1px;}
:where(html.nav-clavier) .nl-zone .tf-tog:focus{outline-offset:-3px;}
:where(html.nav-clavier) .nl-zone .tf-tog.on:focus{outline-color:var(--sur-vert);}

/* --fv-radius et --fv-u vivent sur .fv-list : les tuiles posées ici n'en
   héritaient donc pas, et `border-radius: var(--fv-radius)` sans valeur ne
   donne pas un arrondi par défaut, il n'en donne AUCUN. Les carrés étaient à
   angles droits au milieu d'une page tout en coins ronds.
   Pas de `padding-top` ici : le navigateur réduit le rectangle de collage d'un
   défileur de son propre remplissage, et le titre de liste se retrouverait cloué
   d'autant trop bas. Mesuré au navigateur le 11/08. */
/* PAS de `-webkit-overflow-scrolling: touch` ici. La propriété ne sert plus à
   rien depuis iOS 13 — le défilement à inertie est natif —, mais elle continue
   d'envoyer le défileur sur une couche de composition à part, et les enfants
   collants y RETARDENT pendant l'inertie : le titre de liste flottait un instant
   sous le bord, laissant voir une lame de carrés au-dessus de lui. Invisible sur
   un écran d'ordinateur, criant sur un iPhone. */
.nl-corps{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
          background:var(--page-bg);padding:0 12px 16px;
          --fv-radius:13px;}
/* Le titre de la liste ouverte colle SOUS l'en-tête de la fenêtre : on coche
   en défilant, et il faut savoir dans quelle liste on pioche sans avoir à
   remonter.

   Sa bande est PLEINE, comme celle de la page — jamais de fondu. Un dégradé
   posé là s'est vu tout de suite : il laisse une zone laiteuse sous le titre,
   qui se lit comme un défaut d'affichage plutôt que comme une transition.
   Et SERRÉE, pour la même raison que sur la page : un titre qui flotte à 24 px
   de ses carrés n'a plus l'air de les nommer, et l'écran perd une rangée à
   chaque liste. */
.nl-titre{position:sticky;top:0;z-index:5;background:transparent;
          padding:10px 2px 8px;display:flex;align-items:center;}
/* La bande s'arrête AU RAS du titre, en haut comme en bas. Elle a débordé de
   14 px vers le haut, pour masquer ce que le collage laissait passer sur
   iPhone : hors de l'écran ces 14 px étaient bien découpés par le défileur,
   mais un titre au MILIEU de la liste, lui, n'est pas collé — et son
   débordement peignait le fond par-dessus la dernière rangée du groupe
   précédent. On rattrapait une fuite d'un côté en coupant les carrés de
   l'autre. Ce qui masque la fuite est désormais un cache posé hors du
   défileur, sous l'en-tête : il ne bouge pas avec le contenu, donc il ne peut
   rien recouvrir qui ne soit pas déjà sous l'en-tête. */
.nl-titre::before{content:'';position:absolute;left:-12px;right:-12px;top:0;bottom:0;
                  background:var(--page-bg);z-index:-1;}
.nl-titre-nom{font-size:19px;font-weight:800;letter-spacing:-.022em;color:var(--text);}
.nl-titre-cnt{font-size:13px;font-weight:600;color:var(--text-muted);margin-left:9px;}
/* Les tuiles : celles de la page, mais UNIQUEMENT en carré 1×1. On coche dans
   une grille régulière ; un pavage à tuiles inégales ferait chercher des yeux
   la case suivante à chaque fois. */
.nl-grille{display:grid;gap:8px;grid-template-columns:repeat(2, 1fr);}
.nl-tuile{aspect-ratio:1;}
/* Le nom se pose EN BAS, comme sur les tuiles de la page : au centre, il venait
   buter dans le rond de sélection posé en haut à gauche. Le bandeau flou reste
   celui de .fv-legende — le jour où les photos arrivent, il s'applique ici sans
   qu'on ait à y toucher. */
.fv-tile.fv-vide.nl-tuile .fv-legende{justify-content:flex-end;padding:40px 13px 12px;}
/* DEUX lignes, avec les points de suspension. Trois ne tiennent pas dans un
   carré de cette taille : le navigateur les affichait quand même et coupait la
   dernière en deux dans le sens de la hauteur, ce qui se lit comme un défaut. */
.fv-tile.fv-vide.fv-petite.nl-tuile .fv-dish{font-size:14.5px;line-height:1.28;
                                             -webkit-line-clamp:2;}
.fv-tile.fv-vide.fv-petite.nl-tuile .fv-rang{font-size:10.5px;letter-spacing:.05em;
                                             margin-bottom:4px;}

/* Le rond de sélection, EN HAUT À GAUCHE — le seul coin qu'aucune tuile de la
   page n'occupe : à droite vivent le cœur et les trois points, en bas le nom. */
.nl-rond{position:absolute;top:11px;left:11px;z-index:5;width:27px;height:27px;
         border-radius:999px;display:flex;align-items:center;justify-content:center;
         background:rgba(255,255,255,.94);border:1.5px solid var(--row-border);
         box-shadow:0 1px 3px rgba(30,28,20,.12);color:transparent;
         transition:background .14s ease, border-color .14s ease,
                    color .14s ease, transform .12s ease;}
.nl-rond svg{width:15px;height:15px;display:block;}
.fv-tile.nl-tuile{cursor:pointer;}
.fv-tile.nl-tuile.on .nl-rond{background:var(--green-btn);border-color:var(--green-btn);color:var(--sur-vert);}
.fv-tile.nl-tuile:active .nl-rond{transform:scale(.9);}
/* Le liseré d'une tuile cochée est INTÉRIEUR.
   Vers l'extérieur, il débordait de deux pixels dans la gouttière — et c'est là
   que les titres de liste posent leur bande de fond. Le liseré du haut passait
   sous le titre de sa propre liste, celui du bas sous le titre de la suivante :
   une tuile cochée se retrouvait cerclée sur trois côtés et tranchée net en
   haut comme en bas. Un liseré tourné vers le dedans ne sort d'aucun bord ;
   aucun voisin ne peut plus le manger. Même leçon que la gélule de recherche.
   Il est porté par .fv-ring, qui hérite déjà de l'arrondi de la tuile — deux
   pixels au lieu du filet gris du repos, et rien d'autre ne bouge : un voile
   vert sur la photo cacherait justement ce qu'on regarde pour choisir. */
.fv-tile.nl-tuile.on .fv-ring{box-shadow:inset 0 0 0 2px var(--green-btn);}
/* ⚠️ ET IL PASSE AU-DESSUS DU NOM (Adam, 18/10 : « tout autour, pas juste au
   dessus ») : le bandeau du nom (`.fv-legende`, z-index 4) couvrait le bas
   du liseré — la tuile cochée n'était cerclée qu'en haut et sur les côtés. */
.fv-tile.nl-tuile .fv-ring{z-index:6;}
/* Un carré que le PLAFOND interdit : le menu n'a plus de place pour ce rang-là.
   Il reste lisible — on doit pouvoir constater ce qu'on ne peut pas prendre —
   mais il ne réagit plus au doigt. Le grisé dit « plus de place », pas
   « indisponible » : décocher un autre plat du même rang le rallume. */
.fv-tile.nl-tuile.off{opacity:.42;cursor:default;}
.fv-tile.nl-tuile.off .nl-rond{opacity:.5;}
/* U.4 (Adam, 3ᵉ passe de la simulation, 02/11) : UN plat pour un créneau —
   la coche suffit, pas d'anneau vert. */
.nl-un .fv-tile.nl-tuile.on .fv-ring{box-shadow:none;}
/* ⚠️ AU SURVOL AUSSI (relecture adverse du lot B, 02/11, point 9) : l'anneau
   du survol de T (`.fv-tile:not(.off):hover`, sur la TUILE, pas sur
   `.fv-ring`) revenait sur le plat coché dès le clic, le pointeur resté
   dessus. Coché, il n'y a que la coche. Le repère du CLAVIER, lui, reste. */
@media (hover:hover){
  .nl-un .fv-tile.nl-tuile.on:hover{box-shadow:none;}
}
/* G : un favori DÉJÀ au menu le dit, sur sa tuile éteinte (`.off`), au-dessus
   du nom. */
.nl-pris-mot{position:absolute;top:11px;right:11px;z-index:5;padding:3px 9px;border-radius:20px;
  background:var(--card-bg);color:var(--text);font-size:11px;font-weight:700;line-height:1.3;}

.nl-vide{padding:34px 4px;text-align:center;color:var(--text-muted);font-size:14px;}

/* « TOUS LES PLATS » (T, Adam, 27/10) : tout à droite des filtres, à part —
   le bouton de la page Favoris, coché d'entrée ici. Au téléphone, l'icône
   seule, dans un rond de la hauteur d'une pastille (posée par le script) ;
   dans une liste, « ← Retour » de même si la rangée passe à la ligne. */
.nl-filtres .nl-tous-btn{margin-left:auto;display:inline-flex;align-items:center;gap:6px;}
.nl-filtres .nl-tous-btn svg{width:13px;height:13px;display:block;flex:none;}
.nl-filtres .nl-tous-btn.en-rond{padding:0;justify-content:center;gap:0;}
.nl-filtres .nl-tous-btn.en-rond .nl-tous-mot{display:none;}
.nl-filtres .nl-tous-btn.en-rond svg{width:14px;height:14px;}
.nl-filtres .fv-retour.en-rond{padding:0;justify-content:center;gap:0;}
.nl-filtres .fv-retour.en-rond .nl-retour-mot{display:none;}
/* Les listes en tuiles : deux par rangée, trois sur un grand écran. */
.nl-corps .lt-grille.nl-listes{grid-template-columns:repeat(2, minmax(0, 1fr));gap:16px 10px;padding-top:4px;}
.nl-corps .lt-nom{font-size:14.5px;}
.nl-corps .lt-pied{margin-top:8px;}
.nl-corps .lt-n{font-size:12.5px;padding-left:10px;}

/* Le pied : ce qu'on a pris à gauche, ce qu'on en fait à droite. */
.nl-pied{display:flex;align-items:center;gap:12px;padding:12px 14px;flex:none;
         background:var(--card-bg);border-top:1px solid var(--row-border);
         border-radius:0 0 16px 16px;}
.nl-compte{flex:1;min-width:0;font-size:12.5px;font-weight:700;color:var(--text-muted);}
.nl-compte.rien{color:var(--text-faint);font-weight:600;}
.nl-compte.refus{color:var(--danger);}
.nl-ok{flex:none;padding:11px 20px;border-radius:8px;border:none;cursor:pointer;
       background:var(--green-btn);color:var(--sur-vert);font-family:inherit;
       font-size:14px;font-weight:700;}
.nl-ok:disabled{background:var(--row-border);color:var(--text-faint);cursor:default;}

/* ── Grand écran ────────────────────────────────────────────────────────── */
@media (min-width: 720px) {
  .nl-corps{padding:0 16px 18px;--fv-radius:16px;}
  .nl-grille{grid-template-columns:repeat(4, 1fr);gap:11px;}
  .nl-corps .lt-grille.nl-listes{grid-template-columns:repeat(3, minmax(0, 1fr));gap:18px 12px;}
  .nl-tri{padding:14px 0 2px;}
  .nl-titre::before{left:-16px;right:-16px;}
}

/* ══════════════════════════════════════════════════════════════════════════
   TÉLÉPHONE : LA FENÊTRE SE POSE EN BAS, D'UN BORD À L'AUTRE
   Le seuil est celui qui existe déjà pour --modal-max : un seuil de plus, et
   deux réglages du même écran basculeraient à deux largeurs différentes.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width:520px){

  /* ── La grande coquille : profil, tags, pioche, nouvelle liste ─────────── */
  /* Le remplissage LATÉRAL tombe à zéro — c'est lui qui laissait les 14 px de
     fond noir sur les côtés — et celui du BAS aussi. Celui du HAUT reste : sans
     lui, une carte haute viendrait mordre l'encoche. */
  .modal-overlay{align-items:flex-end;
                 padding:calc(24px + env(safe-area-inset-top, 0px)) 0 0;}

  /* ── … SAUF UNE FENÊTRE D'INFORMATION ──────────────────────────────────
     Une feuille de travail monte du bas : on va y agir, elle occupe la main.
     Une fiche de DÉTAILS ne demande rien — on la lit, on la referme. Elle garde
     donc son comportement d'ordinateur sur les deux formats, comme la
     confirmation juste en dessous : flottante, centrée, la taille de son
     contenu. `.flottant` remet exactement ce que le bloc ci-dessus retire —
     jamais une valeur nouvelle, sinon les deux se mettraient à diverger. */
  .modal-overlay.flottant{align-items:flex-start;
                          padding:calc(24px + env(safe-area-inset-top, 0px)) 14px
                                  calc(24px + env(safe-area-inset-bottom, 0px));}
  .modal-overlay.flottant .modal-card{max-width:520px;margin:auto;
                                      border-radius:12px;border-bottom:1px solid var(--border);}

  /* `margin:auto` centrait la carte dans l'overlay : il annulerait le calage en
     bas. Le plafond de 520 px saute, et les deux coins du bas avec — un arrondi
     sur un bord d'écran ne se voit pas, il laisse juste passer le fond noir. */
  .modal-card{max-width:none;margin:0;
              border-radius:14px 14px 0 0;border-bottom:none;}

  /* LA BARRE GESTUELLE passe sous la fenêtre : sans ce dégagement, « Valider »
     tombe à l'endroit exact où le doigt remonte pour quitter l'application.
     On l'AJOUTE au remplissage existant plutôt que de le remplacer — le pied
     garde ses 12 px de respiration au-dessus du bouton. */
  .modal-card.pleine .modal-foot{
    padding-bottom:calc(var(--air-bas) + var(--sur-bas));}
  .modal-foot{padding-bottom:var(--sur-bas);}
  /* La mosaïque a son PROPRE pied — `.nl-pied`, pas `.modal-foot`. L'oublier
     laissait « Valider » à l'endroit exact de la barre gestuelle : c'est le
     seul défaut qu'a montré la simulation, et il ne se voyait qu'une fois la
     fenêtre posée en bas. */
  .nl-pied{padding-bottom:calc(var(--air-bas) + var(--sur-bas));}
  /* Une carte SANS pied — « Nouvelle liste » du menu Ranger — porte son propre
     remplissage : c'est lui qu'on allonge. */
  .modal-card:not(.pleine){
    padding-bottom:calc(var(--air-bas) + var(--sur-bas));}

  /* Le corps de la mosaïque s'arrête au ras du bord : ses 16 px du bas étaient
     calculés pour une carte qui flottait au-dessus du vide. */
  .nl-corps{padding-bottom:10px;}

  /* EN PLEINE HAUTEUR (`ov-plein`, Adam, 18/10 : « Nouvelle liste »,
     « Nouveau profil », « Nouveau menu » standard et express doivent « faire
     toute la hauteur de la page avec le haut de la modale figé » ; puis
     « renommer rayon », « nouveau rayon », « la modale minuterie » — toute
     fenêtre à tiroirs le temps d'un tiroir où l'on écrit) : d'un bord à
     l'autre, sans plafond, l'en-tête sous l'encoche. Clavier ouvert, l'overlay prend la zone visible d'en haut
     (`suivreZoneVisible`) : l'en-tête ne bouge pas, seul le bas remonte. */
  .modal-overlay.ov-plein{padding:0;}
  .modal-overlay.ov-plein > .modal-card{height:100%;max-height:none;border-radius:0;border:none;}
  .modal-overlay.ov-plein .nl-head{border-radius:0;padding-top:calc(11px + env(safe-area-inset-top, 0px));}
  .modal-overlay.ov-plein > .modal-card.pleine > .modal-head{padding-top:calc(15px + env(safe-area-inset-top, 0px));}
  /* La fenêtre à tiroirs, le temps d'un tiroir où l'on écrit (`ouvrirTiroirs`,
     v365 : « Renommer » un rayon, « Nouveau rayon », « Minuterie »…). Le
     tiroir qui glisse occupe toute la scène (`bottom:0`) : sans ça, son pied
     courait sous son contenu le temps du glissement, puis tombait en bas. */
  .modal-overlay.ov-plein > .ot-carte > .modal-head{padding-top:calc(15px + env(safe-area-inset-top, 0px));}
  .modal-overlay.ov-plein .ot-pane.ot-hors{bottom:0;}

  /* LA CONFIRMATION NE DESCEND PAS. Essayée en bas le 12/08 puis ramenée au
     centre : une question fermée n'est pas une feuille de travail, elle
     interrompt. Elle garde donc son comportement d'ordinateur sur les deux
     formats — flottante, centrée, la taille de son contenu — et sans
     dégagement de barre gestuelle, puisqu'elle ne touche plus le bas de
     l'écran. Aucune règle ici : c'est l'absence de règle qui la laisse
     tranquille. */
}

/* ELLES MONTENT DU BAS (Adam, 18/10 : « toutes les modales sur mobile qui
   partent du bas doivent s'animer depuis le bas, comme “Cuisiner avec
   Maryse” ») : la carte glisse depuis le bord, le voile se pose. Une fenêtre
   FLOTTANTE (une fiche, une confirmation) ne vient pas du bas : elle ne bouge
   pas. La liste du jour (`.day-overlay`) fait de même, dans sa page. */
@media (max-width:520px) and (prefers-reduced-motion:no-preference){
  .modal-overlay:not(.flottant){animation:rx-ov-fondu .2s ease both;}
  .modal-overlay:not(.flottant) > .modal-card{animation:rx-glisse-b .28s cubic-bezier(.2,.8,.2,1);}
}
@keyframes rx-glisse-b{from{transform:translateY(100%);}to{transform:none;}}
/* POSÉE, ELLE NE REMONTE PLUS (v380, `logerDansTiroir`) : la fenêtre à
   tiroirs qui loge une fenêtre perd un instant `modal-overlay` — la règle du
   dessus repartait de zéro, et la fenêtre entière remontait du bas de
   l'écran. Écrite APRÈS elle, et d'un cran plus précise. */
.modal-overlay.ot-ov.ot-posee,.modal-overlay.ot-ov.ot-posee > .modal-card{animation:none;}

/* DE L'AIR AVANT LES BOUTONS D'UNE CONFIRMATION, sur les deux formats.
   Une question de trois mots donnait une carte si basse qu'elle se lisait comme
   une barre de boutons collée sous une phrase, et non comme une question qu'on
   nous pose. On n'impose PAS de hauteur minimale : la carte garde la taille de
   son contenu, on ne fait qu'écarter les boutons du dernier paragraphe.
   20 px ici, PLUS les 12 px de `gap` de la carte : 32 px de blanc au total, et
   c'est ce total qui a été réglé à l'œil — la valeur seule ne veut rien dire
   tant qu'on oublie le gap. */
.mc-actions{margin-top:20px;}

/* ══ FENÊTRE DE CHOIX — « Changer ce plat », « Libérer ce repas » ═══════════
   Elle remplace les décomptes de trois secondes. Ceux-ci faisaient décider
   PENDANT qu'ils tournaient, sans jamais dire ce qu'ils allaient faire ; et un
   compte à rebours qu'on n'a pas demandé se lit comme une menace. La fenêtre
   pose la question avant d'agir, et rien ne part tant qu'on n'a pas validé.
   Elle emprunte la coquille commune (.modal-*) et n'ajoute que les cartes. */
.rc-choix{display:flex;flex-direction:column;gap:10px;}
.rc-carte{border:1px solid var(--border);border-radius:11px;background:var(--card-bg);
          overflow:hidden;transition:border-color .15s ease, box-shadow .15s ease;}
.rc-carte.on{border-color:var(--green-btn);box-shadow:inset 0 0 0 1px var(--green-btn);}
.rc-tete{display:flex;align-items:center;gap:11px;padding:13px 14px;cursor:pointer;
         width:100%;background:none;border:none;text-align:left;font:inherit;color:var(--text);}
/* Au clavier, la CARTE s'entoure, comme au survol (relecture adverse, 13/10) :
   l'anneau d'ensemble, posé autour de `.rc-tete`, tombait sous le
   `overflow:hidden` de la carte — on ne voyait rien. */
.rc-tete:focus{outline:none;}
:where(html.nav-clavier) .rc-carte:has(.rc-tete:focus-visible){border-color:var(--green-btn);box-shadow:0 0 0 2px var(--green-btn);}
.rc-rond{flex:none;width:20px;height:20px;border-radius:50%;border:2px solid var(--border);
         display:flex;align-items:center;justify-content:center;transition:border-color .15s;}
.rc-carte.on .rc-rond{border-color:var(--green-btn);}
.rc-rond::after{content:'';width:10px;height:10px;border-radius:50%;background:var(--green-btn);
                transform:scale(0);transition:transform .15s ease;}
.rc-carte.on .rc-rond::after{transform:scale(1);}
.rc-txt{flex:1;min-width:0;}
.rc-nom{display:block;font-size:14.5px;font-weight:700;line-height:1.3;}
.rc-sous{display:block;font-size:12px;color:var(--text-muted);margin-top:3px;line-height:1.4;}
.rc-champ{padding:0 14px 13px;}
/* Le champ n'apparaît qu'une fois la carte cochée : deux champs ouverts sous
   deux choix exclusifs demandent de lire avant de comprendre lequel compte. */
.rc-carte:not(.on) .rc-champ{display:none;}
/* ⚠️ PLUS D'EXCEPTION (Adam, 22/09). « Libérer ce repas » gardait son champ
   ouvert d'emblée — une seule carte, rien à départager. Mais la carte porte
   désormais une flèche qui promet un dépli, et un champ déjà ouvert sous une
   flèche fermée ne tenait pas cette promesse. La règle est partie avec
   l'option `champToujours` qui la déclenchait : une classe sans feuille et une
   feuille sans classe sont deux pièges, pas un demi-réglage. */

/* ── LES CARTES DE « NOUVEAU MENU » ─────────────────────────────────────
   ⚠️ CET HABILLAGE VIT ICI, PAS DANS UNE PAGE. `ouvrirFenetreChoix` sert
   plusieurs écrans : des règles posées dans le `<style>` d'index.php
   laisseraient les mêmes cartes nues ailleurs. C'est l'erreur des
   `.infield-*`, qui avaient laissé le champ allergies sans style dans la
   fenêtre de Générer — et un test la garde depuis.
   ⚠️ PLUS DE BOUTON RADIO (16 sept. 2026) : le contour vert dit déjà lequel
   est choisi, et le rond ne faisait que le redire à dix pixels. Il reste dans
   le markup — le rôle `radio` du bouton doit rester vrai pour qui ne voit pas
   la couleur — mais il ne se peint plus. L'icône prend sa place, 28 px,
   centrée sur la hauteur de la carte. Une carte SANS icône retrouve son rond
   sans qu'on y pense. */
.rc-carte .rc-rond{display:none;}
.rc-carte:not(:has(.rc-ic)) .rc-rond{display:block;}
.rc-ic{flex:none;width:28px;height:28px;display:flex;align-items:center;
  justify-content:center;color:var(--green-btn);align-self:center;}
.rc-ic svg{width:28px;height:28px;display:block;}
.rc-carte .rc-tete{gap:14px;align-items:center;}
/* ⚠️ L'ICÔNE EST À L'ENCRE AU REPOS (Adam, 21/09). Le gris disait « éteint » :
   dans « + Nouveau menu » les cartes sont des chemins à égalité tant
   qu'on n'a pas choisi, et dans « Changer ce plat » elles sont deux. Grises,
   elles se lisaient comme des propositions désactivées. Le gris ne désignait
   rien puisqu'il les couvrait toutes ; c'est le vert qui désigne — au survol et
   une fois pris. */
.rc-carte:not(.on) .rc-ic{color:var(--text);}
/* ⚠️ LA CARTE RÉPOND AU SURVOL, TOUT ENTIÈRE (Adam, 16 sept. au soir). Depuis
   que la carte EST l'action — plus de bouton « Continuer », un clic déclenche —
   elle doit dire qu'elle est visée AVANT qu'on clique : le contour, l'icône et
   le titre prennent le vert ensemble. Séparément, ils laisseraient croire à
   trois cibles ; ensemble, ils disent « toute cette carte ».
   `@media (hover:hover)` : sur téléphone le survol reste COLLÉ après le tap, et
   la carte resterait allumée derrière l'écran suivant. */
@media (hover:hover){
  /* ⚠️ LE CONTOUR EST CELUI D'UNE JOURNÉE ACTUELLE, au trait près (Adam,
     16 sept. au soir) : `.day-today{box-shadow:0 0 0 2px var(--green-btn)}`.
     Le vert CLAIR (`--green-border`) du premier jet se confondait avec une
     bordure ordinaire — sur une carte qui est le bouton, l'anneau doit se
     voir. La bordure passe au même vert que l'anneau, sinon un liseré pâle
     reste coincé dessous.
     ⚠️ DEHORS, pour toutes les cartes de choix (« + Nouveau menu »…). EN
     DEDANS dans la seule fenêtre « Choisir » / « Autre chose » (Adam, 02/11) :
     la règle vit avec elle, sous `.ch-corps` (index.php). */
  .rc-carte:hover{border-color:var(--green-btn);
    box-shadow:0 0 0 2px var(--green-btn);}
  .rc-carte:hover .rc-ic,
  .rc-carte:hover .rc-nom{color:var(--green-btn);}
}

/* U.4 : une carte qui POUSSE un tiroir (« Choisir dans mes favoris ») ne se
   coche pas — elle a la flèche d'une porte, au bout. */
.rc-chev{flex:none;margin-left:auto;font-size:19px;line-height:1;color:var(--text-faint);}
.rc-carte.rc-pousse .rc-tete{align-items:center;}
/* U.6 : le message de Maryse, DANS la carte, sous le champ qu'il juge. */
.rc-verdict{margin-top:10px;}
.rc-input{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--border);
          border-radius:8px;font-size:16px;background:var(--page-bg);color:var(--text);font-family:inherit;}
.rc-input::placeholder{color:var(--text-faint);}

/* ── LA BOÎTE BEIGE : de quoi parle cette fenêtre ? ────────────────────────
   La boîte d'un repas dans la fenêtre d'une journée, et l'en-tête de « Autre
   chose » (index.php). Son bas — `.rc-bas`, le RAPPEL d'une fenêtre de choix
   (« Déjeuner du mardi… », puis le plat en gras) — est parti le 05/11 avec
   le rappel lui-même : « Autre chose » a son en-tête (le rang, ses couverts,
   la ligne du plat), et les boîtes des repas leur liste (`.rq-liste`). */
.rc-quoi{margin:0 0 14px;padding:12px 13px;background:var(--page-bg);
         border:1px solid var(--border);border-radius:9px;}
.rc-haut{display:block;line-height:1.4;}
.rc-quoi.rc-quoi .rc-haut{font-size:13px;color:var(--text-muted);}

/* La carte ambre du repas libre reprend la couleur du bouton qui l'ouvre. */
.modal-save:disabled{background:var(--row-border);color:var(--text-faint);cursor:default;}
/* La fenêtre fait la HAUTEUR DE SON CONTENU : deux choix n'ont pas besoin des
   90 % d'une liste de préférences, et le vide sous le bouton se lirait comme un
   chargement qui n'arrive pas. Le plafond reste au cas où. */
.modal-card.rc{max-height:min(var(--modal-max), 100%);}
.rc-corps{overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
          padding:14px 16px 16px;}
/* « Nouveau menu » n'a pas de pied : sur téléphone, c'est son corps qui
   touche le bord. Il prend L'AIR DU BAS, et passe la barre gestuelle en
   blanc dans la web app (Adam, 02/10 — la dernière carte y collait au bord). */
@media (max-width:520px){
  .rc-corps{padding-bottom:calc(var(--air-bas) + var(--sur-bas));}
}

/* ══ LE LIEN D'UN PARTAGE (D.2, 04/10) ════════════════════════════════════════
   Le champ du lien, « Copier » dedans, le bouton à trois visages
   (`partageMecanique`, maryse-common.js) — nés dans la fenêtre « Partager le
   menu » d'index.php le 17 sept., communs depuis qu'une liste de favoris se
   partage avec la même fenêtre. La coquille, elle, reste à chaque page : la
   fenêtre d'un menu (`.pt-card`, index.php), le tiroir d'une liste
   (`.pt-tiroir`, ci-dessous).
   ═════════════════════════════════════════════════════════════════════════════ */
.pt-lbl{font-size:15px;font-weight:700;color:var(--text);margin-bottom:9px;}
.pt-lien{display:flex;align-items:center;gap:10px;padding:11px 13px;border:1px solid var(--border);
  border-radius:10px;background:var(--page-bg);font-size:14.5px;color:var(--text);overflow:hidden;
  user-select:none;-webkit-user-select:none;
  transition:border-color .2s ease, background .2s ease, box-shadow .2s ease;}
.pt-lien .pt-ico{display:flex;flex:none;}
.pt-lien svg{width:18px;height:18px;flex:none;color:var(--text-muted);transition:color .2s ease;}
.pt-url{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  font-variant-numeric:tabular-nums;letter-spacing:.2px;}
.pt-url b{font-weight:700;}
.pt-vide{letter-spacing:1.5px;color:var(--text-faint);}
.pt-lien.eteint{color:var(--text-faint);}
.pt-lien.copie{border-color:var(--green-btn);background:var(--green-light);box-shadow:0 0 0 2px var(--green-btn);}
.pt-lien.copie svg{color:var(--green-btn);}
/* Le bouton du pied a TROIS visages dans la même boîte : le mot se remplace,
   la largeur ne bouge pas. */
.pt-copier{position:relative;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;}
.pt-copier svg{width:18px;height:18px;flex:none;}
.pt-copier .pt-face{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  transition:transform .28s cubic-bezier(.3,.7,.3,1), opacity .2s ease;}
.pt-copier .pt-face.b,.pt-copier .pt-face.c{position:absolute;inset:0;transform:translateY(100%);opacity:0;}
.pt-a-creer,.pt-a-stop{display:none;align-items:center;gap:8px;}
.pt-copier.a-creer .pt-a-creer,.pt-copier.a-stop .pt-a-stop{display:inline-flex;}
/* UN SEUL BOUTON, DEUX MÉTIERS. Au repos, avec un lien, il DÉSACTIVE : il
   parle rouge, sans remplir — c'est un geste qu'on ne fait pas souvent, et
   il est immédiat. L'annonce qui suit (« Partage désactivé ») est neutre :
   ce n'est ni une réussite à fêter, ni une erreur. */
.pt-copier.a-stop:not(.fait):not(.attente){background:var(--card-bg);color:var(--danger);
  box-shadow:inset 0 0 0 1px var(--border);}
@media (hover:hover){.pt-copier.a-stop:not(.fait):not(.attente):hover{box-shadow:inset 0 0 0 1px currentColor;}}
.pt-copier.fait.fait-stop{background:var(--text-muted);color:var(--page-bg);animation:none;}
/* « Copier le lien », DANS le champ, à droite. Le champ entier est la cible. */
.pt-lien:not(.eteint){cursor:pointer;padding-right:5px;}
/* Repli quand le presse-papiers refuse : le lien SE SÉLECTIONNE, pour un copier à la main. */
.pt-lien.a-selectionner .pt-url{user-select:all;-webkit-user-select:all;}
/* Le survol, le contour du clic : vert FONCÉ, même épaisseur (Adam, 05/10 ;
   il était vert clair et fin). */
@media (hover:hover){.pt-lien:not(.eteint):not(.copie):hover{border-color:var(--green-btn);box-shadow:0 0 0 2px var(--green-btn);}}
.pt-copie{flex:none;position:relative;display:inline-flex;align-items:center;justify-content:center;
  height:32px;padding:0 11px;border:1px solid var(--border);border-radius:8px;background:var(--card-bg);
  color:var(--text);font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer;overflow:hidden;}
.pt-copie[hidden]{display:none;}
.pt-copie svg{width:15px;height:15px;flex:none;color:currentColor;}
.pt-copie-a{display:inline-flex;align-items:center;gap:6px;transition:opacity .15s ease;}
.pt-copie-b{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;
  color:var(--green-btn);transition:opacity .15s ease;}
.pt-copie-b svg{width:18px;height:18px;}
.pt-copie.fait{border-color:var(--green-btn);}
.pt-copie.fait .pt-copie-a{opacity:0;}
.pt-copie.fait .pt-copie-b{opacity:1;}
.pt-copie.fait .pt-coche path{animation:ptTrace .35s .05s ease-out forwards;}
@media (hover:hover){.pt-copie:hover{border-color:var(--green-btn);color:var(--green-btn);}}
/* Sur téléphone, « Copier » suffit : le CODE ne doit jamais être abrégé. */
@media (max-width:480px){
  .pt-copie-fin{display:none;}
  .pt-lien{gap:8px;padding-left:11px;font-size:14px;}
  .pt-url{letter-spacing:0;}
  .pt-copie{padding:0 9px;}
}
/* Plus étroit encore : le bouton garde son dessin, et au-dessous la chaîne
   de gauche s'efface — le code d'abord, entier. */
@media (max-width:380px){
  .pt-copie-mot{display:none;}
  .pt-copie{width:34px;padding:0;}
}
@media (max-width:340px){ .pt-lien .pt-ico{display:none;} }
/* UN PRÉFIXE LONG (une liste : « maryse.app/favoris?F= », D.2) : là où la
   fenêtre est étroite (la feuille du téléphone, et la carte de 380 px entre
   521 et 559 px), « Copier » n'est plus que son icône et le texte est un peu
   plus petit et plus serré — sinon le code s'abrégeait, et le code, lui, ne
   s'abrège jamais. Sous 380 px, le petit bouton s'efface : le champ ENTIER
   copie toujours, et le grand bouton dit « Lien copié ». Sur ordinateur, la
   fenêtre des favoris a la largeur de celle des menus (520 px, Adam, 05/10) :
   « Copier le lien » y garde son mot. Mesuré sous Chromium de 320 à 1100 px,
   avec une marge d'au moins 9 % pour la police du système (plus large que
   celle du banc). */
.pt-lien.pt-long .pt-url{letter-spacing:0;}
@media (max-width:559px){
  .pt-lien.pt-long{font-size:12.5px;}
  .pt-lien.pt-long .pt-copie-mot{display:none;}
  .pt-lien.pt-long .pt-copie{width:34px;padding:0;}
}
@media (max-width:380px){ .pt-lien.pt-long .pt-copie{display:none;} }
.pt-copier.fait .pt-face.a,.pt-copier.attente .pt-face.a{transform:translateY(-100%);opacity:0;}
.pt-copier.fait .pt-face.b,.pt-copier.attente .pt-face.c{transform:none;opacity:1;}
.pt-coche path{stroke-dasharray:22;stroke-dashoffset:22;}
.pt-copier.fait .pt-coche path{animation:ptTrace .35s .12s ease-out forwards;}
.pt-copier.fait{animation:ptPouls .45s ease-out;}
@keyframes ptTrace{to{stroke-dashoffset:0;}}
@keyframes ptPouls{0%{box-shadow:0 0 0 0 rgba(90,128,64,.45);}100%{box-shadow:0 0 0 12px rgba(90,128,64,0);}}
/* L'attente parle la langue de la maison : le vert amorti et le rond de
   `.btn-jour-attente` / `.jour-spin`. */
.pt-copier.attente,.pt-copier.attente:disabled{background:var(--green-doux);color:var(--sur-vert);opacity:1;cursor:default;}
@media (prefers-reduced-motion:reduce){
  .pt-copier .pt-face{transition:none;}
  .pt-copier.fait,.pt-copier.fait .pt-coche path{animation:none;stroke-dashoffset:0;}
}
.pt-occupe .pt-lien{pointer-events:none;}
/* LE TIROIR « PARTAGER » D'UNE LISTE (`tiroirPartage`) : l'air de la fenêtre
   d'un menu (`.pt-card .pdf-corps`), et le bouton sur toute la largeur du
   pied, comme tout bouton de pied de tiroir. */
.pt-tiroir{padding:20px 18px 18px;}
.ot-carte .ot-pied .pt-copier{flex:1 1 auto;width:100%;margin:0;}

/* ══ LA PORTE D'ENTRÉE DE L'INVITÉ (17 sept. ; commune depuis D.2) ═════════════
   Le bandeau « … créé avec Maryse · Créer le tien » et la page du lien mort :
   la page d'un menu partagé (`/?M=`) et celle d'une liste partagée
   (`/favoris?F=`) les portent tous deux. Où le bandeau se POSE reste à chaque
   page. ⚠️ `invite` et `lien-mort` sont des classes du BODY. */
/* Pas de compte, pas de navigation : la marque seule, au centre. */
body.invite .app-header{justify-content:center;}
.inv-promo{display:none;}
body.invite .inv-promo{display:flex;align-items:center;justify-content:space-between;gap:10px;min-width:0;
  box-sizing:border-box;height:44px;margin-bottom:10px;padding:0 5px 0 12px;border:1px solid var(--border);
  border-radius:12px;background:var(--card-bg);}
.inv-feuille{display:inline-flex;color:#3a6a22;flex:none;}
.inv-feuille svg{width:18px;height:18px;display:block;}
.inv-promo-txt{display:flex;align-items:center;gap:8px;min-width:0;font-size:13px;color:var(--text-muted);
  white-space:nowrap;overflow:hidden;}
.inv-promo-txt b{color:var(--text);}
.inv-promo-cta{display:inline-flex;align-items:center;justify-content:center;flex:none;box-sizing:border-box;
  height:34px;padding:0 13px;border-radius:8px;background:var(--green-btn);
  color:var(--sur-vert);font-size:13px;font-weight:700;text-decoration:none;white-space:nowrap;}
@media (hover:hover){.inv-promo-cta:hover{background:var(--green-btn-hover);}}
.inv-mort{position:fixed;left:0;right:0;bottom:0;top:var(--header-h,52px);z-index:5;overflow-y:auto;
  background:var(--page-bg);display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding:24px 16px 40px;box-sizing:border-box;}
.inv-mort > *{max-width:420px;}
.inv-mort-i{width:44px;height:44px;color:var(--text-faint);opacity:.55;margin-bottom:13px;flex:none;}
.inv-mort-i svg{width:44px;height:44px;}
.inv-mort-t{font-size:18px;font-weight:700;color:var(--text);}
.inv-mort-p{font-size:14px;color:var(--text-muted);margin-top:8px;line-height:1.5;}
.inv-mort-pub{margin-top:30px;width:100%;flex:none;box-sizing:border-box;padding:24px 20px 22px;
  border-radius:16px;background:var(--green-light);border:1px solid var(--green-border);}
.inv-mort-pub-t{font-size:17px;font-weight:700;color:var(--text);margin-top:10px;}
.inv-mort-pub-p{font-size:13.5px;color:var(--text-muted);margin-top:6px;line-height:1.5;}
.inv-mort-cta{display:flex;align-items:center;justify-content:center;margin-top:18px;height:50px;
  border-radius:25px;background:var(--green-btn);color:var(--sur-vert);font-size:15.5px;font-weight:700;
  text-decoration:none;box-shadow:0 6px 18px rgba(58,106,34,.25);}
@media (hover:hover){.inv-mort-cta:hover{background:var(--green-btn-hover);}}
.inv-feuille.grande svg{width:34px;height:34px;}

/* ══ LES MINUTERIES (point 4 de « 3 + 5 », 17/10) ═══════════════════════════
   Communes à toutes les pages : une minuterie lancée sur la page d'un plat
   sonne aussi sur les Favoris (maryse-common.js, `minLancer`). */
/* Une ligne : l'icône, son nom (et son plat), le temps, pause et arrêter. */
.rx-minuteries{display:flex;flex-direction:column;}
.rx-minuteries:empty{display:none;}
.rx-min{display:flex;align-items:center;gap:9px;padding:7px 0;border-top:1px solid var(--row-border);color:var(--text);}
.rx-min:first-child{border-top:none;}
.rx-min-ic{width:18px;height:18px;color:var(--green-btn);flex:none;}
.rx-min-ic svg{width:100%;height:100%;display:block;}
.rx-min-mot{flex:1;min-width:0;font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.rx-min-plat{display:block;font-size:11px;font-weight:600;color:var(--text-muted);line-height:1.25;overflow:hidden;text-overflow:ellipsis;}
.rx-min.sonne .rx-min-plat{color:inherit;opacity:.8;}
.rx-min-t{font-size:16px;font-weight:800;font-variant-numeric:tabular-nums;}
.rx-min button{flex:none;width:30px;height:30px;border-radius:50%;border:1px solid var(--border);background:var(--card-bg);padding:0;
  color:var(--text);cursor:pointer;display:flex;align-items:center;justify-content:center;font-family:inherit;font-size:18px;line-height:1;}
.rx-min button svg{width:13px;height:13px;}
@media (hover:hover){.rx-min:not(.sonne) button:hover{border-color:var(--green-border);color:var(--green-btn);}}
.rx-min.pause .rx-min-t{color:var(--text-muted);}
/* Elle sonne : la ligne passe au vert plein et tremble ; « OK » l'arrête. */
.rx-min.sonne{margin:2px -8px;padding:7px 8px;border-radius:9px;border-top-color:transparent;background:var(--green-btn);color:var(--sur-vert);
  animation:rx-sonne 1s ease-in-out infinite;}
.rx-min.sonne + .rx-min{border-top-color:transparent;}
.rx-min.sonne .rx-min-ic{color:var(--sur-vert);}
.rx-min.sonne button{background:var(--sur-vert);color:var(--green-btn);border-color:transparent;width:auto;padding:0 14px;border-radius:15px;font-size:13px;font-weight:800;}
@keyframes rx-sonne{0%,100%{transform:translateX(0);}20%{transform:translateX(-2px);}40%{transform:translateX(2px);}60%{transform:translateX(-1px);}}
:where(html.nav-clavier) .rx-min button:focus-visible{outline:2px solid var(--green-btn);outline-offset:2px;}

/* ── QUAND ÇA SONNE : LA FENÊTRE FLOTTANTE (Adam, 16/10) ─────────────────
   Au milieu, téléphone comme ordinateur, au-dessus de toute autre fenêtre.
   VERT CLAIR (4ᵉ passe) ; la photo du plat dans le rond, la minuterie en
   badge qui tremble, trois ondes (8ᵉ passe). « + 1 min » et « Arrêter ». */
.mc-overlay.rx-sonne-ov{z-index:1100;align-items:center;animation:rx-sonne-fondu .2s ease both;}
@keyframes rx-sonne-fondu{from{opacity:0;}to{opacity:1;}}
.rx-sonne{width:min(330px, 100%);margin:auto 0;background:var(--green-light);border:1px solid var(--green-border);color:var(--text);border-radius:20px;
  padding:26px 22px 20px;text-align:center;box-shadow:0 24px 60px -18px rgba(0,0,0,.45);animation:rx-monte .32s cubic-bezier(.2,.9,.25,1.1) both;
  outline:none;}
@keyframes rx-monte{from{transform:translateY(14px) scale(.96);opacity:0;}to{transform:none;opacity:1;}}
.rx-sonne-ic{position:relative;width:112px;height:112px;margin:0 auto 14px;}
.rx-sonne-rond{position:absolute;inset:14px;border-radius:50%;background:var(--green-btn);color:var(--sur-vert);overflow:hidden;
  display:flex;align-items:center;justify-content:center;z-index:1;}
.rx-sonne-rond:not(.photo) svg{width:38px;height:38px;animation:rx-tremble .9s ease-in-out infinite;transform-origin:50% 60%;}
.rx-sonne-rond.photo{background:var(--green-strip);box-shadow:0 0 0 3px var(--green-btn);}
.rx-sonne-rond.photo img{width:100%;height:100%;object-fit:cover;display:block;}
.rx-sonne-badge{position:absolute;right:8px;bottom:8px;z-index:2;width:32px;height:32px;border-radius:50%;background:var(--green-btn);color:var(--sur-vert);
  display:flex;align-items:center;justify-content:center;box-shadow:0 0 0 3px var(--green-light);}
.rx-sonne-badge svg{width:18px;height:18px;animation:rx-tremble .9s ease-in-out infinite;transform-origin:50% 60%;}
.rx-sonne-rond:not(.photo) + .rx-sonne-badge{display:none;}
@keyframes rx-tremble{0%,55%,100%{transform:rotate(0);}5%{transform:rotate(-14deg);}12%{transform:rotate(12deg);}19%{transform:rotate(-10deg);}26%{transform:rotate(8deg);}33%{transform:rotate(-4deg);}40%{transform:rotate(2deg);}}
.rx-onde{position:absolute;inset:14px;border-radius:50%;border:2px solid var(--green-btn);opacity:0;animation:rx-onde 1.8s ease-out infinite;}
.rx-onde.d2{animation-delay:.6s;} .rx-onde.d3{animation-delay:1.2s;}
@keyframes rx-onde{0%{transform:scale(1);opacity:.55;}100%{transform:scale(1.55);opacity:0;}}
.rx-sonne-titre{font-size:20px;font-weight:800;color:var(--green-btn);}
.rx-sonne-mot{margin-top:4px;font-size:15px;font-weight:600;color:var(--text);}
.rx-sonne-plat{margin-top:2px;font-size:12.5px;font-weight:600;color:var(--text-muted);}
.rx-sonne-plat:empty,.rx-sonne-reste:empty{display:none;}
.rx-sonne-reste{margin-top:6px;font-size:12px;color:var(--text-muted);}
.rx-sonne-boutons{display:flex;gap:10px;margin-top:20px;}
.rx-sonne-boutons button{flex:1 1 0;height:44px;border-radius:22px;font:inherit;font-size:15px;font-weight:800;cursor:pointer;}
.rx-sonne-plus{border:1px solid var(--green-border);background:var(--card-bg);color:var(--green-btn);}
.rx-sonne-stop{border:1px solid var(--green-btn);background:var(--green-btn);color:var(--sur-vert);}
:where(html.nav-clavier) .rx-sonne-boutons button:focus-visible{outline:2px solid var(--text);outline-offset:2px;}
@media (prefers-reduced-motion:reduce){.rx-sonne-rond svg,.rx-sonne-badge svg,.rx-onde,.rx-min.sonne,.rx-sonne{animation:none;}}

/* ══ « EN COURS » ET LE GUIDE « CUISINER AVEC MARYSE » (point 4 de « 3 + 5 ») ═
   Communs aux pages : un plat qu'on cuisine depuis sa page se voit sur le
   menu, les Carnets et les Favoris, et son guide s'y ouvre
   (maryse-common.js, `guideOuvrir`). Tranchés sur la simulation, seize
   passes : Dev/memoire/project_maryse_page_recette.md */

/* ── « EN COURS » : une section par plat, ses minuteries dessous (7ᵉ passe) ── */
.rx-ec-sections{display:flex;flex-direction:column;}
.rx-ec-sec{padding:4px 0 6px;}
.rx-ec-sec + .rx-ec-sec{border-top:1px solid var(--border);padding-top:8px;}
.rx-ec-sec:last-child{padding-bottom:0;}
.rx-ec-sec > .rx-minuteries .rx-min:first-child{padding-top:4px;}
.rx-ec-plat{display:flex;align-items:center;gap:11px;width:100%;padding:4px 0;border:none;
  background:none;font:inherit;color:var(--text);text-align:left;cursor:pointer;border-radius:0;}
.rx-ec-ph{position:relative;flex:none;width:42px;height:42px;border-radius:8px;overflow:hidden;
  background:var(--green-strip);box-shadow:inset 0 0 0 1px rgba(0,0,0,.06);}
.rx-ec-ph > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
.rx-ec-modale .rx-ec-ph{width:46px;height:46px;}
.rx-ec-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;}
.rx-ec-txt b{font-size:14px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.rx-ec-ou{font-size:12px;font-weight:600;color:var(--text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.rx-ec-ou:empty{display:none;}
.rx-ec-ou em{font-style:normal;color:var(--green-btn);}
.rx-ec-barre{display:flex;gap:3px;margin-top:2px;}
.rx-ec-barre i{flex:1 1 0;height:4px;border-radius:2px;background:var(--green-strip-border);max-width:28px;}
.rx-ec-barre i.faite{background:var(--green-btn);}
.rx-ec-chev{flex:none;width:16px;height:16px;color:var(--text-muted);}
.rx-ec-chev svg{width:100%;height:100%;display:block;}
@media (hover:hover){.rx-ec-plat:hover b{color:var(--green-btn);}}
:where(html.nav-clavier) .rx-ec-plat:focus-visible{outline:2px solid var(--green-btn);outline-offset:2px;border-radius:8px;}
/* Sa marge est à elle : la fenêtre s'ouvre aussi sur Favoris, qui n'a pas
   la coquille de « Télécharger » (`.pdf-corps`, index.php). */
.rx-ec-modale{padding:22px 18px;}
/* Au téléphone, la fenêtre touche le bas de l'écran : l'air du bas des autres
   tiroirs, la barre gestuelle comprise (Adam, 19/10 : « il faut plus d'espace
   en bas pour la modale "en cours" sur mobile »). Elle n'a pas de pied pour
   le porter. */
@media (max-width:520px){.rx-ec-modale{padding-bottom:calc(var(--air-bas) + var(--sur-bas));}}
.rx-ec-vide{margin:6px 0 4px;font-size:14px;color:var(--text-muted);}
.rx-ec-vide[hidden]{display:none;}
/* ══ LE RANGEUR : « RÉORGANISER » EN TUILES (v383 — sorti de favoris.php :
   les Carnets s'en servent aussi ; le geste, `rangeurOuvrir`, commun) ══
   Deux couches : les PLACES (la page les dessine, vides) et, dessus, les
   TUILES posées en absolu (`.ro-couche`), qui GLISSENT d'une place à l'autre. */
.ro-couche{position:absolute;inset:0;pointer-events:none;}
.ro-couche > .ro-tuile{position:absolute;margin:0;pointer-events:auto;
  transition:left .34s cubic-bezier(.2,.8,.2,1),top .34s cubic-bezier(.2,.8,.2,1),
             width .34s cubic-bezier(.2,.8,.2,1),height .34s cubic-bezier(.2,.8,.2,1),
             box-shadow .14s ease;}
.ro-fige .ro-tuile{transition:none !important;}
.ro-tuile:focus{outline:none;}
/* LE FRÉMISSEMENT : à peine (~1 px aux coins, `--tr` posé selon la largeur),
   jamais deux tuiles en mesure (`--td`, `.ro-inverse`). */
@keyframes ro-fremir{from{transform:rotate(calc(var(--tr, .6deg) * -1));}to{transform:rotate(var(--tr, .6deg));}}
.ro-tuile{animation:ro-fremir .14s ease-in-out infinite alternate;animation-delay:var(--td, 0s);}
.ro-tuile.ro-inverse{animation-direction:alternate-reverse;}
/* PORTÉE : un petit carré qui pend au pointeur — une ombre la détache, elle
   ne frémit plus. Position, taille, inclinaison : le script, à chaque image
   (aucune transition ici, elle traînerait derrière le ressort). */
.ro-couche > .ro-tuile.ro-leve{position:fixed;z-index:60;animation:none;pointer-events:none;
  box-shadow:0 26px 48px -12px rgba(28,26,18,.38),0 8px 18px -6px rgba(28,26,18,.22);
  transition:box-shadow .16s ease;}
/* POSÉE : elle rejoint sa place (position, taille) et redescend. */
.ro-couche > .ro-tuile.ro-pose{position:fixed;z-index:60;animation:none;pointer-events:none;
  transform:none;box-shadow:none;
  transition:left .26s cubic-bezier(.2,.8,.2,1),top .26s cubic-bezier(.2,.8,.2,1),
             width .26s cubic-bezier(.2,.8,.2,1),height .26s cubic-bezier(.2,.8,.2,1),
             transform .26s ease, box-shadow .26s ease;}
@media (prefers-color-scheme:dark){
  .ro-couche > .ro-tuile.ro-leve{box-shadow:0 26px 48px -10px rgba(0,0,0,.7),0 8px 18px -6px rgba(0,0,0,.5);}
}
@media (prefers-reduced-motion:reduce){
  .ro-tuile{animation:none;}
  .ro-couche > .ro-tuile,.ro-couche > .ro-tuile.ro-pose{transition:none;}
}
/* Le calque qui capte le pointeur pendant le geste (la tuile tenue change de
   parent de positionnement sous le doigt). */
.ro-calque{position:fixed;inset:0;z-index:9998;cursor:grabbing;touch-action:none;display:none;}
html.ro-glisse, html.ro-glisse *{cursor:grabbing !important;-webkit-user-select:none;user-select:none;}

/* LE BOUTON FLOTTANT « En cours » (v383, Adam, 29/10 : « dans un cercle en
   bas à droite de l'écran ») : la casserole dans un rond vert clair qui flotte
   (une ombre), son chiffre au coin ; plein quand ça sonne. Au-dessus de la
   page, de ses bandes collantes et du pied de la génération (z 55) ; sous ce
   qui flotte en geste (une tuile qu'on porte, z 60), le guide (90), les
   fenêtres (100, 1000) et les bulles (120). Un pied collé en bas de l'écran
   le soulève : `--ec-bas`, sa hauteur (la page la pose). */
.ec-flottant{position:fixed;z-index:58;right:calc(16px + env(safe-area-inset-right, 0px));
  bottom:max(calc(16px + env(safe-area-inset-bottom, 0px)), calc(var(--ec-bas, 0px) + 14px));
  width:54px;height:54px;padding:0;border-radius:50%;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--green-border);background:var(--green-light);color:var(--green-btn);cursor:pointer;
  box-shadow:0 10px 26px -8px rgba(28,26,18,.34),0 3px 8px -2px rgba(28,26,18,.16);
  -webkit-tap-highlight-color:transparent;transition:background .15s ease,border-color .15s ease,color .15s ease;
  animation:ec-arrive .26s cubic-bezier(.2,.9,.25,1.2) both;}
.ec-flottant[hidden]{display:none;}
.ec-flottant > svg{width:26px;height:26px;flex:none;}
.ec-flottant.sonne{background:var(--green-btn);border-color:var(--green-btn);color:var(--sur-vert);}
@media (hover:hover){.ec-flottant:not(.sonne):hover{border-color:var(--green-btn);}}
:where(html.nav-clavier) .ec-flottant:focus-visible{outline:2px solid var(--green-btn);outline-offset:3px;}
@keyframes ec-arrive{from{transform:scale(.6);opacity:0;}to{transform:none;opacity:1;}}
@media (prefers-color-scheme:dark){.ec-flottant{box-shadow:0 10px 26px -8px rgba(0,0,0,.7),0 3px 8px -2px rgba(0,0,0,.45);}}
@media (prefers-reduced-motion:reduce){.ec-flottant{animation:none;}}
.rx-rond-n{position:absolute;top:-4px;right:-4px;min-width:17px;height:17px;padding:0 4px;box-sizing:border-box;border-radius:9px;
  background:var(--green-btn);color:var(--sur-vert);font-size:10.5px;font-weight:800;line-height:17px;text-align:center;box-shadow:0 0 0 2px var(--page-bg);}
.rx-rond-n[hidden]{display:none;}
.ec-flottant.sonne .rx-rond-n{background:var(--card-bg);color:var(--green-btn);}
/* Tant qu'il est là, le bas de la page lui fait de la place : on peut faire
   remonter la dernière ligne au-dessus de lui. */
html.ec-vu body::after{content:'';display:block;height:calc(54px + 26px + env(safe-area-inset-bottom, 0px));}
/* La vapeur monte, doucement, tant que quelque chose est en cours. */
.rx-vap{animation:rx-vap 2.4s ease-in-out infinite;}
.rx-vap.v2{animation-delay:.5s;}
.rx-vap.v3{animation-delay:1s;}
@keyframes rx-vap{0%,100%{opacity:.25;transform:translateY(1px);}50%{opacity:1;transform:translateY(-1px);}}
@media (prefers-reduced-motion:reduce){.rx-vap{animation:none;opacity:1;}}

/* ── LE GUIDE : UNE MODALE (5ᵉ passe) ────────────────────────────────────
   Un voile sur toute la page, menu du haut compris. Rien n'y défile de côté
   (13ᵉ passe).
   ⚠️ IL PREND TOUTE LA HAUTEUR (Adam, 18/10 : « toute la hauteur de la page
   avec le haut de la modale figé ») : au téléphone, tout l'écran — l'en-tête
   sous l'encoche (`env(safe-area-inset-top)`) ; clavier ouvert, il prend la
   zone visible D'EN HAUT (`suivreZoneVisible`) : le haut ne bouge pas, seul
   le bas remonte. À partir de 600 px, un panneau de 440 px contre le bord
   droit.
   ⚠️ ET IL ARRIVE COMME IL PART (Adam, 18/10, v365 — il venait de la droite
   partout depuis v364) : au téléphone, il MONTE du bas et redescend en se
   fermant, comme les autres fenêtres du téléphone ; sur ordinateur, il
   arrive de la droite et y repart. La sortie, c'est `.part`
   (`guidePartir`, commun). */
.rx-chat-ov{position:fixed;inset:0;z-index:90;background:linear-gradient(rgba(0,0,0,.28),rgba(0,0,0,.28)) var(--voile-teinte);animation:rx-ov-fondu .2s ease both;}
@keyframes rx-ov-fondu{from{opacity:0;}to{opacity:1;}}
.rx-chat{position:fixed;z-index:91;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;
  background:var(--card-bg);color:var(--text);outline:none;overflow:hidden;
  box-shadow:-10px 0 30px -12px rgba(0,0,0,.25);animation:rx-glisse-b .28s cubic-bezier(.2,.8,.2,1);}
.rx-chat.clavier-ouvert{bottom:auto;}
/* Au téléphone, clavier ouvert (v366) : le guide se replie d'un coup à la
   zone visible, et le clavier met deux dixièmes de seconde à monter. Entre
   les deux, le voile : il prend la couleur du guide, qui semble descendre
   jusqu'au clavier — plus de page assombrie qui passe sous le champ. */
@media (max-width:599.98px){.rx-chat-ov:has(> .rx-chat.clavier-ouvert){background:var(--card-bg);}}
/* Sous le guide, la page n'a plus d'ascenseur (v366, Adam, 18/10 : « deux
   scroll verticaux, celui de la page doit être invisible ») : il ne reste que
   celui de la conversation. La racine perd sa barre MÊME sous `garde-script`
   (le verrou de l'ordinateur la garde, voir `.scroll-locked`), et le corps
   reprend la gouttière qu'il a mesurée : rien ne bouge derrière le voile, et
   le panneau touche le bord de l'écran. Posée et ôtée par le guide
   (`guideDom`, `guideQuitterLaPage`, commun). */
html.sans-ascenseur{overflow:hidden;}
html.sans-ascenseur body{padding-right:var(--gel-droite,0px);}
@keyframes rx-glisse-d{from{transform:translateX(100%);}to{transform:none;}}
/* Il part : le voile s'efface, le guide descend (téléphone) ou repart à
   droite (ordinateur). Inerte pendant ce temps (`guidePartir`). */
.rx-chat-ov.part{pointer-events:none;animation:rx-ov-part .24s ease both;}
@keyframes rx-ov-part{from{opacity:1;}to{opacity:0;}}
.rx-chat-ov.part > .rx-chat{animation:rx-part-b .24s cubic-bezier(.4,0,1,1) both;}
@keyframes rx-part-b{from{transform:none;}to{transform:translateY(100%);}}
@keyframes rx-part-d{from{transform:none;}to{transform:translateX(100%);}}
/* Le panneau, dès que l'écran le permet ; sur ordinateur, de l'air
   (« L'air du guide », plus bas : APRÈS les règles qu'il élargit, à poids
   égal la dernière gagne). */
@media (min-width:600px){
  .rx-chat{left:auto;width:440px;border-left:1px solid var(--border);animation-name:rx-glisse-d;}
  .rx-chat-ov.part > .rx-chat{animation-name:rx-part-d;}
}
@media (prefers-reduced-motion:reduce){.rx-chat,.rx-chat-ov,.rx-chat-ov.part > .rx-chat{animation:none;}}
.rx-chat-tete{flex:none;display:flex;align-items:center;gap:10px;padding:calc(14px + env(safe-area-inset-top, 0px)) 14px 12px 16px;border-bottom:1px solid var(--border);}
.rx-chat-tete .rx-avatar{width:34px;height:34px;}
.rx-chat-titres{flex:1;min-width:0;}
/* 8ᵉ passe : le nom du plat sorti en défilant, le titre de la fenêtre le prend. */
.rx-chat-titre{position:relative;font-size:15.5px;font-weight:800;line-height:1.3;height:1.3em;overflow:hidden;}
.rx-chat-titre > span{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:transform .26s cubic-bezier(.2,.8,.2,1),opacity .2s ease;}
.rx-ct-b{position:absolute;left:0;right:0;top:0;transform:translateY(100%);opacity:0;}
.rx-chat-titre.plat .rx-ct-a{transform:translateY(-100%);opacity:0;}
.rx-chat-titre.plat .rx-ct-b{transform:none;opacity:1;}
.rx-chat-x{flex:none;width:34px;height:34px;border-radius:50%;border:none;background:none;color:var(--text);font-family:inherit;font-size:24px;line-height:1;cursor:pointer;}
@media (hover:hover){.rx-chat-x:hover{background:var(--page-bg);}}
.rx-avatar{flex:none;width:28px;height:28px;border-radius:50%;background:var(--green-light);color:var(--green-btn);display:flex;align-items:center;justify-content:center;}
.rx-avatar svg{width:70%;height:70%;}
/* La bande des minuteries du plat, en haut (sauf celle que montre la carte). */
.rx-chat > .rx-g-bande{flex:none;padding:6px 14px 2px;border-bottom:1px solid var(--row-border);}
.rx-chat > .rx-g-bande:empty{display:none;}
.rx-g-corps{--g-haut:16px;--g-droite:16px;--g-gauche:16px;
  position:relative;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:var(--g-haut) var(--g-droite) 22px var(--g-gauche);overscroll-behavior:contain;}
/* Le fil d'une étape : ses bulles, ce qu'on peut demander. */
.rx-fil{display:flex;flex-direction:column;gap:10px;margin-top:16px;}
.rx-fil:empty{display:none;}
.rx-msg{display:flex;gap:8px;align-items:flex-end;max-width:92%;}
.rx-msg.moi{align-self:flex-end;}
.rx-bulle{padding:9px 12px;border-radius:14px;font-size:13.5px;line-height:1.5;white-space:pre-line;overflow-wrap:anywhere;}
.rx-msg.maryse .rx-bulle{background:var(--green-strip);border:1px solid var(--green-strip-border);border-bottom-left-radius:4px;}
.rx-msg.moi .rx-bulle{background:var(--green-btn);color:var(--sur-vert);border-bottom-right-radius:4px;}
/* Elle garde la main (un autre sujet) : une bulle bleue ; une panne : grise. */
.rx-msg.garde .rx-bulle{background:var(--blue-bg);border-color:var(--blue-border);}
.rx-msg.rate .rx-bulle{background:var(--page-bg);border-color:var(--border);color:var(--text-muted);}
.rx-msg.rx-g-attente .rx-bulle{display:flex;align-items:center;gap:9px;}
.rx-tape .rx-bulle{display:flex;gap:4px;padding:12px;}
.rx-tape i{width:6px;height:6px;border-radius:50%;background:var(--text-muted);animation:rx-tape 1s infinite ease-in-out;}
.rx-tape i:nth-child(2){animation-delay:.15s;}
.rx-tape i:nth-child(3){animation-delay:.3s;}
@keyframes rx-tape{0%,80%,100%{opacity:.25;transform:none;}40%{opacity:1;transform:translateY(-3px);}}
/* Tranché (6ᵉ passe) : à droite, en pointillé, COMME TES RÉPONSES ; touchée, elle devient ta bulle. */
.rx-proposes{display:flex;flex-direction:column;align-items:flex-end;gap:6px;margin-top:2px;}
.rx-proposes-t{font-size:11px;font-weight:700;color:var(--text-muted);margin-right:4px;}
.rx-propose{max-width:85%;padding:8px 12px;border-radius:14px;border-bottom-right-radius:4px;border:1.5px dashed var(--green-border);
  background:var(--card-bg);color:var(--green-btn);font:inherit;font-size:13.5px;font-weight:600;text-align:left;cursor:pointer;}
@media (hover:hover){.rx-propose:hover{border-style:solid;background:var(--green-strip);}}
.rx-pied{flex:none;padding:10px 12px calc(12px + env(safe-area-inset-bottom, 0px));border-top:1px solid var(--border);}
.rx-chat.clavier-ouvert .rx-pied{padding-bottom:10px;}
/* L'air du guide sur ordinateur (Adam, 18/10 : « c'est un peu serré »). */
@media (min-width:820px){
  .rx-chat-tete{padding:calc(18px + env(safe-area-inset-top, 0px)) 22px 14px 22px;}
  .rx-chat > .rx-g-bande{padding:8px 26px 4px 22px;}
  .rx-g-corps{--g-haut:20px;--g-droite:28px;--g-gauche:22px;padding-bottom:32px;}
  .rx-pied{padding:14px 28px calc(24px + env(safe-area-inset-bottom, 0px)) 22px;}
  .rx-chat.clavier-ouvert .rx-pied{padding-bottom:24px;}
}
/* v385 (Adam, 30/10) : le champ grandit avec le texte jusqu'à cinq lignes,
   puis il défile (`guideChampTaille` pose sa hauteur) — le bouton reste EN
   BAS, à côté de la dernière ligne, comme dans une messagerie. Une ligne :
   20 px + 2 × 10 px de marge + le bord = les 42 px du bouton. */
.rx-saisie{display:flex;align-items:flex-end;gap:8px;}
/* v385 : ce que la dictée a à dire quand le champ porte déjà des mots (son texte d'attente ne se voit plus). */
.rx-dictee-note{font-size:12.5px;font-weight:600;line-height:1.35;color:var(--text-muted);padding:0 8px 8px;}
.rx-champ{flex:1;min-width:0;display:flex;align-items:flex-end;min-height:42px;border-radius:21px;border:1px solid var(--border);
  background:var(--page-bg);padding:0 16px;transition:border-color .15s ease,background .15s ease;}
.rx-champ:focus-within{border-color:var(--green-btn);background:var(--card-bg);}
.rx-champ textarea{flex:1;min-width:0;display:block;height:40px;max-height:120px;margin:0;border:none;background:none;padding:10px 0;
  font:inherit;font-size:16px;line-height:20px;color:var(--text);resize:none;overflow-y:hidden;}
/* Le texte d'attente (« Une question avant de commencer… ») tient sur UNE
   ligne et finit par « … » s'il ne tient pas (320 px). ⚠️ Sur le champ VIDE
   ET sur `::placeholder` : l'un sans l'autre, Chromium coupe au milieu d'une
   lettre, sans « … » (mesuré deux fois, deux bancs différents). */
.rx-champ textarea:placeholder-shown{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.rx-champ textarea::placeholder{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.rx-champ textarea:focus{outline:none;}
.rx-envoyer{flex:none;width:42px;height:42px;border-radius:50%;border:none;background:var(--green-btn);color:var(--sur-vert);cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0;}
.rx-envoyer svg{width:18px;height:18px;}
/* v383 (Adam, 29/10) : rien d'écrit, rien à envoyer — la bulle de « Cuisiner
   avec Maryse », en gris, éteinte ; un mot écrit, la flèche verte, avec le
   battement de « Nouveau carnet » ⇄ « Nouveau rayon » (`vuePop`). */
.rx-envoyer:disabled{background:var(--border);color:var(--text-faint);cursor:default;}
.rx-envoyer:disabled svg{width:20px;height:20px;}
.rx-envoyer svg.env-pop{animation:vuePop .3s ease-out;}
@media (prefers-reduced-motion:reduce){.rx-envoyer svg.env-pop{animation:none;}}
/* v384 (Adam, 30/10) : le champ vide, le MICRO (allumé, vert) ; on parle, l'ARRÊT,
   le bouton bat doucement comme une onde et le champ se borde de vert. */
.rx-envoyer[data-ic="micro"] svg{width:20px;height:20px;}
.rx-envoyer.ecoute{animation:ecouteOnde 1.5s ease-out infinite;}
.rx-champ.ecoute{border-color:var(--green-btn);background:var(--card-bg);}
/* L'ONDE DE LA VOIX (Adam, 03/11 au soir : « quelque chose qui indique que ça
   bouge », à l'intensité de la parole) : cinq barres au bout du champ, sur la
   ligne du bas, seulement pendant qu'on parle (`.rx-champ.ecoute`). La page
   pose leur hauteur à chaque tampon du micro (`dicteeOnde`) ; la transition
   lisse entre deux tampons (85 ms). */
.rx-voix{display:none;flex:none;align-self:flex-end;align-items:center;gap:3px;height:20px;margin:0 0 10px 10px;}
.rx-champ.ecoute .rx-voix{display:flex;}
.rx-voix i{display:block;width:3px;height:20px;border-radius:2px;background:var(--green-btn);transform:scaleY(.18);
  transform-origin:center;transition:transform 90ms linear;}
@media (prefers-reduced-motion:reduce){.rx-voix i{transition:none;}}
@keyframes ecouteOnde{0%{box-shadow:0 0 0 0 rgba(90,128,64,.45)}70%{box-shadow:0 0 0 10px rgba(90,128,64,0)}100%{box-shadow:0 0 0 0 rgba(90,128,64,0)}}
@media (prefers-reduced-motion:reduce){.rx-envoyer.ecoute{animation:none;box-shadow:0 0 0 3px rgba(90,128,64,.35);}}
/* v385 : arrêtée, la dictée rend encore son texte (« Je retranscris… ») —
   le micro gris, éteint, qui respire. */
.rx-envoyer[data-ic="attente"] svg{width:20px;height:20px;animation:dicteeAttente 1.2s ease-in-out infinite;}
@keyframes dicteeAttente{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){.rx-envoyer[data-ic="attente"] svg{animation:none;}}
/* Le battement d'une icône qui change (la vue des Courses, les boutons des
   Carnets, « Envoyer ») : petite, puis un peu trop grande, puis elle. */
@keyframes vuePop{0%{transform:scale(.55);opacity:.25}65%{transform:scale(1.14);opacity:1}100%{transform:none;opacity:1}}

/* ── LE MODE CUISINE : une étape à la fois, en grand ────────────────────── */
.rx-g-grand{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;height:48px;padding:0 16px;border-radius:24px;border:none;
  background:var(--green-btn);color:var(--sur-vert);font:inherit;font-size:15.5px;font-weight:800;cursor:pointer;white-space:nowrap;}
.rx-g-grand svg{width:18px;height:18px;flex:none;}
@media (hover:hover){.rx-g-grand:hover{background:var(--green-btn-hover);}}
/* Le plat, en tête : sa photo, son nom ; plus basse pendant les étapes. */
.rx-g-hero{margin:0 0 16px;}
.rx-g-hero-ph{position:relative;display:flex;align-items:center;justify-content:center;height:172px;border-radius:14px;overflow:hidden;background:var(--green-strip);}
.rx-g-hero.court .rx-g-hero-ph{height:116px;}
.rx-g-hero-ph > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;transition:transform .35s ease;}
.rx-g-sans{width:auto;height:40%;max-height:64px;overflow:visible;color:var(--green-btn);}
.rx-g-sans path{fill:currentColor;}
.rx-g-hero-bas{display:flex;align-items:flex-start;gap:12px;margin-top:11px;}
.rx-g-hero-txt{flex:1;min-width:0;}
/* 9ᵉ passe : « Terminé », à droite du nom — on arrête tout. */
.rx-g-termine{flex:none;display:inline-flex;align-items:center;gap:5px;height:34px;padding:0 14px 0 11px;border-radius:17px;
  border:1px solid var(--green-border);background:var(--green-light);color:var(--green-btn);font:inherit;font-size:13px;font-weight:800;cursor:pointer;}
.rx-g-termine svg{width:15px;height:15px;}
@media (hover:hover){.rx-g-termine:hover{background:var(--green-btn);border-color:var(--green-btn);color:var(--sur-vert);}}
.rx-g-hero-t{display:block;font-size:19px;font-weight:800;line-height:1.2;color:var(--text);}
/* Le dernier mot et son chevron, d'un seul tenant (Adam, 19/10 : sur deux
   lignes, le chevron partait au bout de la boîte, loin du mot). */
.rx-g-hero-fin{white-space:nowrap;}
.rx-g-hero-t svg{display:inline-block;width:17px;height:17px;margin-left:3px;vertical-align:-2px;color:var(--text-muted);}
.rx-g-hero-s{display:block;font-size:12.5px;font-weight:600;color:var(--text-muted);margin-top:4px;line-height:1.5;}
/* La photo et le nom mènent à la fiche du plat (8ᵉ passe) — sauf sur la fiche. */
.rx-g-fiche{display:block;width:100%;padding:0;border:none;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;}
.rx-g-fiche-t{display:inline-block;width:auto;}
@media (hover:hover){
  .rx-g-fiche-ph:hover img{transform:scale(1.03);}
  .rx-g-fiche-t:hover .rx-g-hero-t,.rx-g-fiche-t:hover .rx-g-hero-t svg{color:var(--green-btn);}
}
/* Le RAPPEL de l'étape (7ᵉ passe) : il colle en haut quand l'étape est partie ; un toucher y ramène.
   ⚠️ D'UN BORD À L'AUTRE, ET COLLÉ SOUS L'EN-TÊTE (Adam, 19/10, capture de
   l'ordinateur) : il reprend le remplissage du corps PAR SES VARIABLES. Écrit
   en dur (16 px), il valait pour le téléphone ; sur ordinateur, où le corps
   prend de l'air (20 · 28 · 22), il flottait à 4 px sous l'en-tête et laissait
   12 px à droite, 6 à gauche. */
.rx-g-rappel-z{position:sticky;top:calc(-1 * var(--g-haut));height:0;z-index:5;margin:0 calc(-1 * var(--g-droite)) 0 calc(-1 * var(--g-gauche));}
.rx-g-rappel{position:absolute;left:0;right:0;top:0;display:flex;align-items:center;gap:10px;padding:9px 14px 9px 16px;border:none;
  border-bottom:1px solid var(--green-strip-border);background:var(--green-strip);color:var(--text);font:inherit;text-align:left;cursor:pointer;
  transform:translateY(-104%);opacity:0;pointer-events:none;transition:transform .22s ease,opacity .22s ease;box-shadow:0 8px 16px -12px rgba(0,0,0,.4);}
.rx-g-rappel.vu{transform:none;opacity:1;pointer-events:auto;}
.rx-g-rappel-n{flex:none;width:26px;height:26px;border-radius:50%;background:var(--green-btn);color:var(--sur-vert);font-size:12.5px;font-weight:800;
  display:flex;align-items:center;justify-content:center;}
.rx-g-rappel-n svg{width:14px;height:14px;}
.rx-g-rappel-t{flex:1;min-width:0;font-size:13px;line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;}
.rx-g-rappel-t b{color:var(--green-btn);margin-right:6px;}
.rx-g-rappel > svg{flex:none;width:18px;height:18px;color:var(--green-btn);}
/* Avant de commencer. */
.rx-g-veille{margin:0 0 14px;padding:10px 12px;border-radius:10px;background:var(--page-bg);border:1px solid var(--border);font-size:13.5px;line-height:1.5;}
.rx-g-veille b{display:block;font-size:12px;color:var(--green-btn);margin-bottom:2px;}
.rx-g-accueil > .rx-msg{margin:0 0 16px;}
/* Une étape. */
.rx-g-haut{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:9px;}
.rx-g-ou{font-size:13px;font-weight:800;color:var(--green-btn);}
.rx-g-barre{display:flex;gap:4px;margin-bottom:14px;}
.rx-g-seg{flex:1 1 0;height:6px;border-radius:3px;border:none;padding:0;background:var(--green-strip-border);cursor:pointer;}
.rx-g-seg.faite{background:var(--green-btn);}
.rx-g-seg.ici{background:var(--green-mid);}
.rx-g-seg.ici.faite{background:var(--green-btn);box-shadow:0 0 0 1.5px var(--card-bg),0 0 0 3px var(--green-btn);}
/* La SCÈNE : la hauteur de la plus grande carte du plat (`guideEgaliser`) ;
   la carte la remplit, sa minuterie se pose en bas. */
.rx-g-scene{position:relative;overflow:hidden;border-radius:14px;display:flex;flex-direction:column;}
.rx-g-carte{flex:1 0 auto;display:flex;flex-direction:column;padding:16px;border-radius:14px;background:var(--page-bg);border:1px solid var(--border);}
.rx-g-mesure{position:absolute;left:0;right:0;top:0;visibility:hidden;pointer-events:none;}
.rx-g-num{display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:50%;background:var(--green-btn);color:var(--sur-vert);
  font-size:16px;font-weight:800;margin-bottom:10px;}
.rx-g-num svg{width:18px;height:18px;}
.rx-g-carte.faite .rx-g-num{background:var(--green-light);color:var(--green-btn);}
.rx-g-texte{margin:0;font-size:17px;line-height:1.55;color:var(--text);}
.rx-g-carte.faite .rx-g-texte{color:var(--text-muted);}
.rx-g-min-zone{margin-top:auto;padding-top:14px;}
.rx-g-min{display:flex;align-items:center;gap:9px;width:100%;height:44px;padding:0 14px;border-radius:12px;border:1.5px dashed var(--green-border);
  background:var(--card-bg);color:var(--green-btn);font:inherit;font-size:14px;font-weight:700;cursor:pointer;}
.rx-g-min[hidden],.rx-minuteries[hidden]{display:none;}
.rx-g-min svg{width:20px;height:20px;flex:none;}
.rx-g-min span{flex:1;text-align:left;}
.rx-g-min b{font-variant-numeric:tabular-nums;}
@media (hover:hover){.rx-g-min:hover{border-style:solid;background:var(--green-strip);}}
.rx-g-mins{padding:2px 10px;border-radius:12px;background:var(--card-bg);border:1px solid var(--green-border);}
.rx-g-mins .rx-min{border-top:none;}
.rx-g-mins .rx-min.sonne{margin:0 -10px;padding:7px 10px;}
.rx-g-nav{display:flex;align-items:center;gap:10px;margin-top:14px;}
.rx-g-nav .rx-g-grand{flex:1 1 auto;width:auto;min-width:0;}
.rx-g-prec,.rx-g-suiv{flex:none;width:48px;height:48px;border-radius:50%;border:1px solid var(--border);background:var(--card-bg);color:var(--text);
  cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;}
.rx-g-prec svg,.rx-g-suiv svg{width:20px;height:20px;}
.rx-g-prec:disabled,.rx-g-suiv:disabled{opacity:.3;cursor:default;}
@media (hover:hover){.rx-g-prec:not(:disabled):hover,.rx-g-suiv:not(:disabled):hover{border-color:var(--green-border);color:var(--green-btn);}}
/* LA FIN (10ᵉ et 11ᵉ passes) : l'assiette vue de dessus, « Bon appétit »
   DEDANS ; la fourchette et le couteau se trémoussent ; puis « À table ».
   150 px de large au plus (15ᵉ passe). */
.rx-g-fin{display:flex;flex-direction:column;align-items:center;text-align:center;padding:10px 8px 6px;}
.rx-pret{display:block;width:min(150px,46%);height:auto;margin:4px auto 22px;overflow:visible;color:var(--green-btn);}
.rx-pret path{fill:currentColor;}
.rx-pret-fond{fill:var(--green-strip);}
.rx-pret-t{font-family:inherit;font-size:13.5px;font-weight:800;fill:var(--green-btn);letter-spacing:-.01em;}
.rx-pret-a{transform-box:fill-box;transform-origin:center;animation:rx-pret-pose .55s cubic-bezier(.2,.9,.25,1.35) both;}
@keyframes rx-pret-pose{from{transform:scale(.55);opacity:0;}to{transform:none;opacity:1;}}
.rx-pret .rx-pret-f,.rx-pret .rx-pret-c{transform-box:fill-box;transform-origin:50% 85%;}
.rx-pret .rx-pret-f{animation:rx-tremousse-f 1.9s ease-in-out .5s infinite;}
.rx-pret .rx-pret-c{animation:rx-tremousse-c 1.9s ease-in-out .72s infinite;}
/* Les couverts qui se trémoussent : ceux de la fin, et ceux de « Maryse crée la photo » (la page d'un plat). */
@keyframes rx-tremousse-f{0%,58%,100%{transform:none;}8%{transform:translateY(-6px) rotate(-15deg);}18%{transform:translateY(-2px) rotate(12deg);}
  28%{transform:translateY(-5px) rotate(-9deg);}38%{transform:rotate(6deg);}48%{transform:rotate(-2deg);}}
@keyframes rx-tremousse-c{0%,58%,100%{transform:none;}8%{transform:translateY(-6px) rotate(15deg);}18%{transform:translateY(-2px) rotate(-12deg);}
  28%{transform:translateY(-5px) rotate(9deg);}38%{transform:rotate(-6deg);}48%{transform:rotate(2deg);}}
@media (prefers-reduced-motion:reduce){.rx-pret-a,.rx-pret .rx-pret-f,.rx-pret .rx-pret-c,.rx-tape i{animation:none;}}
/* Au clavier : un anneau, seulement au clavier. */
:where(html.nav-clavier) .rx-chat button:focus-visible,:where(html.nav-clavier) .rx-propose:focus-visible{outline:2px solid var(--green-btn);outline-offset:2px;}
:where(html.nav-clavier) .rx-g-fiche-ph:focus-visible{outline:none;}
:where(html.nav-clavier) .rx-g-fiche-ph:focus-visible .rx-g-hero-ph{box-shadow:0 0 0 2px var(--green-btn);}
