/*
 * Feuille de style du site.
 *
 * Priorité : se relire depuis un téléphone posé sur un plan de travail, à bout de
 * bras, avec les mains occupées. D'où un corps de texte large, des lignes courtes,
 * et des cibles tactiles d'au moins 44 points — la zone cliquable primant sur la
 * taille visuelle de l'icône (R48).
 *
 * Aucune surface ne se présente vide sans contenu de repli (R47) : les classes
 * `*__vide` portent les messages d'orientation, et elles sont stylées comme du
 * contenu, pas comme une erreur.
 *
 * **Les couleurs sont assumées.** Une palette de cuisine des années soixante-dix —
 * terre cuite, moutarde, avocat, turquoise, framboise — et non un seul accent posé
 * sur du gris. Elle a un travail à faire, pas seulement une humeur à donner :
 * aucune recette n'a de photo, donc c'est la couleur de catégorie qui distingue
 * une carte d'une autre dans une grille. Chaque catégorie porte la sienne par
 * `data-categorie`, et une catégorie inconnue retombe sur l'accent du site plutôt
 * que de perdre sa couleur.
 *
 * **Deux largeurs de page**, selon ce qu'on y fait. Lire une recette veut une
 * colonne courte ; parcourir une grille de cartes veut de la place. La classe de
 * corps posée par le rendu tranche, ce qui évite qu'un écran large affiche un
 * ruban de contenu au milieu de deux déserts.
 */

/*
 * **Deux familles de couleurs, et c'est la clé de la gaieté.**
 *
 * Une première version assombrissait chaque teinte pour qu'elle tienne le
 * contraste en tant que texte. Résultat : une palette de terre cuite éteinte,
 * juste sur le papier et triste à l'écran. Or les règles de contraste portent sur
 * le **texte**, pas sur un bandeau ni sur une pastille.
 *
 * D'où la séparation : les teintes vives (`--mandarine`, `--soleil`…) habillent
 * les **surfaces** — bandeaux, pastilles, boutons — et le texte posé dessus est
 * l'encre sombre du site, qui passe sur chacune d'elles. Les variantes `-encre`
 * existent pour le seul cas où la couleur doit être du texte sur le fond clair :
 * un lien, un intitulé.
 */
/*
 * La police du site : OpenDyslexic, lettres à base lestée.
 *
 * Choix de l'auteur, qui la lit plus facilement — le poids en bas de chaque
 * lettre l'empêche de basculer à l'œil, un « b » ne se lit pas « d ». Elle est
 * servie par le site plutôt qu'espérée sur l'appareil du lecteur : personne ne
 * l'a installée sur son téléphone.
 *
 * Elle est **sous-ensemblée au français** : 103 Ko par graisse à l'origine, 10 Ko
 * après. Le site est fait pour s'ouvrir depuis une cuisine, sur un réseau qui
 * n'est pas toujours bon, et deux cent onze kilo-octets de police auraient pesé
 * plus lourd que tout le reste réuni. Provenance, licence et commande de
 * sous-ensemble : `fonts/PROVENANCE.txt`, servi avec elle.
 *
 * `font-display: swap` : le texte s'affiche tout de suite dans la police système,
 * puis bascule. Attendre la police, c'est une page blanche sur un réseau lent.
 *
 * Les adresses sont **relatives à la feuille** : elle est servie sous
 * `/assets/style.<empreinte>.css`, donc `./fonts/…` désigne `/assets/fonts/…`
 * quel que soit le chemin de base, sans que R50 ait à intervenir.
 */
@font-face {
  font-family: "OpenDyslexic";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/opendyslexic-regular.woff2") format("woff2");
}

@font-face {
  font-family: "OpenDyslexic";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./fonts/opendyslexic-bold.woff2") format("woff2");
}

:root {
  --fond: #fff8ec;
  --fond-carte: #ffffff;
  --fond-doux: #ffeed3;
  --encre: #241a12;
  --encre-douce: #6d5847;
  --trait: #f0dcbe;

  /* Les vives, pour les surfaces. */
  --accent: #ff6b2c;
  --accent-doux: #ffe3d0;
  --soleil: #ffc233;
  --avocat: #8cc63f;
  --turquoise: #14c3bd;
  --framboise: #ff4d79;
  --aubergine: #9b7ae0;

  /* Les mêmes, en version texte : contraste tenu sur le fond clair. */
  --accent-encre: #c0410f;
  --avocat-encre: #4d7305;
  --turquoise-encre: #0a6f6b;
  --alerte: #b3271a;

  --police: "OpenDyslexic", system-ui, -apple-system, "Segoe UI", sans-serif;

  --cible: 44px;
  --rayon: 1.15rem;
  --rayon-pastille: 999px;
  --ombre-carte: 0 1px 2px rgb(64 32 8 / 6%), 0 10px 24px -14px rgb(64 32 8 / 35%);

  /*
   * Système de design B (chantier 1, U1) : le rythme, l'échelle et les
   * rayons du prototype Lightning retenu. Aucune couleur n'est ajoutée ici —
   * seulement de la mesure, construite sur la palette ci-dessus.
   */

  /* Le rythme d'espacement, 4 à 48 : un multiple reconnaissable à l'œil. */
  --pas-1: 4px;
  --pas-2: 8px;
  --pas-3: 12px;
  --pas-4: 16px;
  --pas-5: 24px;
  --pas-6: 32px;
  --pas-7: 48px;

  /* L'échelle typographique du système B : des pas nets, resserrés parce
     qu'OpenDyslexic est déjà large. Voir plus bas `.texte-b-*` pour
     l'interlignage qui va avec chaque pas (R9). */
  --texte-xs: 0.75rem;
  --texte-s: 0.84rem;
  --texte-m: 0.94rem;
  --texte-l: 1.1rem;
  --texte-xl: 1.38rem;
  --texte-2xl: 1.78rem;
  --texte-3xl: 2.3rem;

  /* Trois rayons, trois rôles : un contrôle, une tuile d'objet, un panneau. */
  --rayon-controle: 6px;
  --rayon-tuile: 9px;
  --rayon-panneau: 14px;
  /* L'ombre d'un panneau suit celle d'une carte : une référence, pas une
     redite. */
  --ombre-panneau: var(--ombre-carte);
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--fond);
  color: var(--encre);
  font-family: var(--police);
  /* Un cran sous la taille précédente : les lettres d'OpenDyslexic sont larges et
     hautes, et la même valeur donnait des lignes trop longues sur téléphone. */
  font-size: 1rem;
  line-height: 1.55;
}

img {
  max-width: 100%;
  height: auto;
}

a {
  color: var(--accent-encre);
}

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.saut-au-contenu {
  position: absolute;
  left: -9999px;
}

.saut-au-contenu:focus {
  left: 1rem;
  top: 1rem;
  z-index: 10;
  background: var(--fond-carte);
  padding: 0.75rem 1rem;
  border: 2px solid var(--accent);
  border-radius: 0.5rem;
}

/* Visible pour un lecteur d'écran, absent de l'écran : c'est ce support qui
   porte l'annonce du nombre de suggestions. `clip-path` plutôt que
   `display: none`, qui la retirerait aussi de l'arbre d'accessibilité. */
.visuellement-cache {
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* ------------------------------------------------------------------ chrome */

/*
 * Le bandeau de couleurs sous l'en-tête et au-dessus du pied.
 *
 * C'est la signature du site, et elle ne coûte ni image ni requête : cinq bandes
 * de dégradé, **festonnées** par un masque de disques. Le feston est ce qui
 * distingue une signature d'un filet de gabarit — un bord de nappe, une croûte de
 * tarte — et là où le masque n'est pas reconnu, le bandeau reste un rectangle de
 * couleur : la dégradation est un dessin plus sobre, pas une page cassée.
 *
 * Les cinq couleurs sont celles des catégories : ce qu'on voit en haut de page est
 * le vocabulaire chromatique des cartes.
 */
.entete::after,
.pied::before,
.accueil__galerie::before {
  background: linear-gradient(
    90deg,
    var(--accent) 0 20%,
    var(--soleil) 20% 40%,
    var(--avocat) 40% 60%,
    var(--turquoise) 60% 80%,
    var(--framboise) 80% 100%
  );
  content: "";
  display: block;
  height: 10px;
  -webkit-mask: radial-gradient(circle at 8px 0, transparent 7px, #000 7.5px) 0 0 / 16px 10px
    repeat-x;
  mask: radial-gradient(circle at 8px 0, transparent 7px, #000 7.5px) 0 0 / 16px 10px repeat-x;
}

/* Sous l'en-tête, les festons pendent ; au-dessus du pied, ils remontent. */
.entete::after {
  -webkit-mask: radial-gradient(circle at 8px 10px, transparent 7px, #000 7.5px) 0 0 / 16px 10px
    repeat-x;
  mask: radial-gradient(circle at 8px 10px, transparent 7px, #000 7.5px) 0 0 / 16px 10px repeat-x;
}

/* Le bandeau reste **dans le flux** de l'en-tête, sous son contenu : posé en
   absolu, il recouvrait les premiers pixels de la page — visible dès qu'une page
   commence sans marge haute.
   L'en-tête porte le fond de la page et non celui des cartes : les creux du feston
   laissent voir ce qui est derrière, et sur une surface blanche ils auraient dessiné
   une seconde rupture entre le blanc et la crème. */
.entete {
  background: var(--fond);
}

.entete__interieur {
  align-items: center;
  display: flex;
  flex-wrap: nowrap;
  gap: 0.5rem 1.25rem;
  margin: 0 auto;
  max-width: 74rem;
  padding: 0.6rem 1.25rem;
}

.entete__marque {
  align-items: center;
  color: var(--encre);
  display: inline-flex;
  flex: 0 0 auto;
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  min-height: var(--cible);
  text-decoration: none;
}

/* Le nom porte déjà son point (« Gigot. ») : une décoration qui en ajoutait un
   second ferait « Gigot.. ». Une seule marque, celle du texte. */

/*
 * La recherche vit dans l'en-tête, sur toutes les pages.
 *
 * C'est un formulaire `GET` vers la page de recherche, et non un champ câblé en
 * JavaScript : sur les six autres pages du site, le module de recherche n'est pas
 * chargé, et un champ qui ne répondrait pas à la touche Entrée serait un piège. La
 * page de recherche lit `?q=` à l'ouverture, ce qui rend au passage une recherche
 * partageable par son adresse.
 */
.entete__recherche {
  display: flex;
  flex: 1 1 0%;
  gap: 0.4rem;
  max-width: 26rem;
  min-width: 12rem;
}

/* Au bureau, le lien « Rechercher » fait doublon avec le champ qui le
   précède : masqué ici, il redevient le seul chemin une fois replié dans le
   groupe secondaire sous 900 px (U2 — R3, KTD2, voir plus bas). */
.entete__nav a[href="/recherche/"] {
  display: none;
}

/* --------------------------------------------- groupe secondaire (U2 — R3, KTD2)
 *
 * Sous 900 px, l'en-tête de la façade ne garde en permanence que la marque,
 * le champ de recherche et les deux boutons d'authentification (R3) : la
 * navigation (« Favoris », et le lien « Rechercher », déjà un
 * doublon du champ) et le sélecteur de langue rejoignent un groupe
 * secondaire, replié derrière le bouton « Menu ». Trop d'éléments pour une
 * seconde rangée sans rien couper ou minimiser (R3) — c'est ce retrait, pas
 * seulement le retour à la ligne, qui fait tenir l'en-tête sur deux rangées.
 *
 * Deux conteneurs structurent les deux rangées mobiles (`.entete__rangee-*`) ;
 * au bureau, `display: contents` les efface tous deux ainsi que le groupe
 * secondaire lui-même — leurs enfants redeviennent des items ordinaires de
 * `.entete__interieur`, dans l'ordre déjà posé par cette règle (`order`,
 * ci-dessous), identique à l'en-tête d'avant cette unité.
 */
.entete__rangee-haute,
.entete__rangee-basse,
.entete__secondaire {
  display: contents;
}

.entete__marque {
  order: 1;
}

.entete__recherche {
  order: 2;
}

.entete__nav {
  order: 3;
}

.entete__connexion-groupe {
  order: 4;
}

/*
 * `.bouton.entete__menu`, et non `.entete__menu` seul (constat de revue,
 * 2026-10) : `.bouton { display: inline-flex }` vient plus bas dans cette
 * feuille, et à spécificité égale (une classe) c'est la règle la plus basse
 * qui gagne — `.entete__menu` perdait donc toujours, et le bouton restait
 * visible au bureau. Deux classes dans le sélecteur (une spécificité plus
 * haute) gagnent quel que soit l'ordre des règles, ici comme dans la
 * dérogation mobile ci-dessous.
 */
.bouton.entete__menu {
  display: none;
}

.entete__menu {
  order: 5;
}

.entete__langues {
  order: 6;
}

.entete__secondaire-voile {
  background: rgb(0 0 0 / 45%);
  display: none;
  inset: 0;
  position: fixed;
  z-index: 45;
}

.entete__secondaire-voile--ouvert {
  display: block;
}

@media (max-width: 56.25rem) {
  .entete__interieur {
    align-items: stretch;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: var(--pas-3);
  }

  /* Marque et bouton « Menu » seuls sur la première rangée (constat de
     revue sur le débordement horizontal du téléphone, 2026-10) : plus rien
     d'autre n'y dispute la largeur, à 360 px comme à 900. */
  .entete__rangee-haute {
    align-items: center;
    display: flex;
    gap: 0.5rem 1.25rem;
    justify-content: space-between;
  }

  /* La recherche, seule, pleine largeur sur la seconde rangée. */
  .entete__rangee-basse {
    align-items: center;
    display: flex;
    gap: 0.5rem;
  }

  .entete__rangee-basse .entete__recherche {
    max-width: none;
    min-width: 0;
    width: 100%;
  }

  .entete__connexion-groupe {
    margin-left: 0;
  }

  /* Même spécificité que la règle qui le cache (`.bouton.entete__menu`
     ci-dessus) : sinon la dérogation mobile perdrait à son tour face à
     `.bouton`. */
  .bouton.entete__menu {
    display: inline-flex;
  }

  /* Le lien « Rechercher » fait doublon avec le champ qui le précède sur
     grand écran (d'où son masquage à la racine, `.entete__nav a[href]`
     ci-dessus) ; replié dans le tiroir, il redevient le seul chemin. */
  .entete__nav a[href="/recherche/"] {
    display: inline-flex;
  }

  .entete__secondaire {
    background: var(--fond-carte);
    box-shadow: var(--ombre-panneau), 0 0 0 1px var(--trait);
    display: flex;
    flex-direction: column;
    gap: var(--pas-3);
    inset: 0 0 0 auto;
    padding: var(--pas-5) var(--pas-4);
    position: fixed;
    transition: translate 220ms ease-out;
    translate: 105% 0;
    width: min(86vw, 320px);
    z-index: 50;
  }

  .entete__secondaire--ouvert {
    translate: 0 0;
  }
}

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

.entete__champ {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  flex: 1 1 auto;
  font: inherit;
  /* 16 px au moins : en dessous, iOS zoome à la mise au point et le lecteur perd
     la page des yeux. */
  font-size: max(1rem, 16px);
  min-height: var(--cible);
  min-width: 0;
  padding: 0 0.9rem;
}

.entete__loupe {
  background: var(--accent);
  border: 0;
  border-radius: var(--rayon-pastille);
  /* De l'encre sur la couleur, jamais du blanc : c'est ce qui rend l'orange
     utilisable en surface tout en restant lisible. */
  color: var(--encre);
  cursor: pointer;
  font-size: 1.2rem;
  min-height: var(--cible);
  min-width: var(--cible);
  transition: transform 120ms ease;
}

.entete__loupe:active {
  transform: scale(0.94);
}

/* Sur la page de recherche, le champ de l'en-tête disparaît : la page a le sien,
   plus complet, en haut. Deux champs de recherche à l'écran ne disent pas lequel
   fait quoi. */
body.page-recherche .entete__recherche {
  display: none;
}

.entete__nav {
  display: flex;
  flex: 0 0 auto;
  gap: 1rem;
}

.entete__nav a {
  align-items: center;
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  font-size: 0.95rem;
  min-height: var(--cible);
  padding: 0 0.7rem;
  text-decoration: none;
}

.entete__nav a:hover,
.entete__nav a:focus-visible {
  background: var(--accent-doux);
}

.contenu {
  margin: 0 auto;
  /* Lire une recette : une colonne courte, mesurée sur la longueur de ligne. */
  max-width: 44rem;
  padding: 1.5rem 1.25rem 3rem;
}

/* Parcourir : la place pour une grille. */
body.page-accueil .contenu,
body.page-recherche .contenu,
body.page-favoris .contenu,
body.page-introuvable .contenu {
  max-width: 74rem;
}

.pied {
  border-top: 1px solid var(--trait);
  color: var(--encre-douce);
  font-size: 0.9rem;
  margin-top: 2rem;
  padding: 0;
  text-align: center;
}

.pied p {
  padding: 1.25rem;
}

/* ------------------------------------------ chrome de la coquille connectée */

/*
 * Chantier d'interface S1 et S2 : l'en-tête, le menu, les liens du compte et le
 * pied de `app.gigot.page` (`render/build.ts::appShellPage`), et les gestes sous
 * une fiche (`js/app-shell.ts::documentHtml`). Le chrome emprunte les classes de
 * la façade (`entete`, `entete__nav`, `contenu`, `pied`) ; cette section n'ajoute
 * que ce que la coquille a en propre. Les styles des écrans eux-mêmes vivent
 * ailleurs (chantier S4).
 */

/* Six entrées ne tiennent pas sur une ligne de téléphone : elles passent à la
   ligne plutôt que de déborder. */
.page-coquille .entete__nav {
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  margin-left: 0;
}

.page-coquille .entete__nav a[aria-current="page"] {
  background: var(--accent-doux);
  font-weight: 700;
}

.coquille__compte {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  margin-left: auto;
}

.coquille__compte a {
  align-items: center;
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  font: inherit;
  font-size: 0.95rem;
  min-height: var(--cible);
  padding: 0 0.7rem;
  text-decoration: none;
}

.coquille__compte a:hover,
.coquille__compte a:focus-visible {
  background: var(--accent-doux);
}

/* La zone ne dit qu'un échec de déconnexion (la réussite change de page) : elle
   en prend la couleur, et un filet qui le dit sans elle. */
.coquille__message-compte {
  border-left: 4px solid var(--alerte);
  color: var(--alerte);
  flex-basis: 100%;
  font-size: 0.9rem;
  margin: 0;
  padding-left: 0.6rem;
}

.coquille__message-compte:empty {
  display: none;
}

/* Le texte d'attente, avant que le module remplisse la zone : discret, et
   assez haut pour que la page ne saute pas quand l'écran arrive. */
.coquille__chargement {
  color: var(--encre-douce);
  margin: 1.5rem 0;
  min-height: 6rem;
}

/* La liste des recettes lisibles (`js/app-shell.ts::listingHtml`) : le total,
   puis les facettes en pastilles au-dessus des cartes. */
.coquille__total {
  color: var(--encre-douce);
  font-weight: 700;
  margin: 0 0 0.75rem;
}

.coquille__facettes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
}

.coquille__facette {
  align-items: center;
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  font-size: 0.9rem;
  gap: 0.4rem;
  max-width: 100%;
  overflow-wrap: anywhere;
  padding: 0.2rem 0.75rem;
}

/* Le nombre d'une facette porte la même classe que les liens du compte de
   l'en-tête : dans une facette, il redevient un simple nombre en ligne. */
.coquille__facette .coquille__compte {
  color: var(--encre-douce);
  display: inline;
  flex: none;
  font-weight: 700;
  margin-left: 0;
  /* Un libellé long passe à la ligne, jamais le nombre : « 12 » ne se coupe pas. */
  overflow-wrap: normal;
  white-space: nowrap;
}

/* La zone remplie reçoit le focus du lien d'évitement sans l'afficher : ce
   n'est pas une commande. */
.page-coquille main:focus {
  outline: none;
}

.page-coquille .recette__photo {
  border-radius: 0.75rem;
  display: block;
  margin: 0.5rem 0 1rem;
}

/* La fiche lue dans la coquille : ingrédients puis étapes, sans les coches ni
   l'échelle de la façade. Des listes simples, espacées pour la lecture. */
.recette__ingredients,
.recette__etapes {
  margin: 1rem 0;
  overflow-wrap: break-word;
  padding-left: 1.4rem;
}

.recette__ingredients li {
  padding: 0.2rem 0;
}

.recette__etapes li {
  padding: 0.35rem 0 0.35rem 0.25rem;
}

.recette__etapes li::marker {
  color: var(--accent-encre);
  font-weight: 700;
}

.recette__actions {
  border-top: 1px solid var(--trait);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 2rem 0 0;
  padding: 1rem 0 0;
}

.recette__action {
  align-items: center;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  min-height: var(--cible);
  padding: 0 0.9rem;
  text-decoration: none;
}

.recette__action:hover,
.recette__action:focus-visible {
  background: var(--accent-doux);
}

/* Le groupe « Se connecter » / « Créer un compte » de l'en-tête (R5, U8) :
   toujours visibles ensemble, jamais l'un sans l'autre. */
.entete__connexion-groupe {
  align-items: center;
  display: flex;
  flex: 0 0 auto;
  gap: 0.5rem;
  margin-left: auto;
  white-space: nowrap;
}

/* Le lien de la façade vers la connexion du site connecté (`layout.html`). */
.entete__connexion {
  align-items: center;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  font-size: 0.95rem;
  min-height: var(--cible);
  padding: 0 0.9rem;
  text-decoration: none;
}

.entete__connexion:hover,
.entete__connexion:focus-visible {
  background: var(--accent-doux);
}

/* ------------------------------------ fin du chrome de la coquille connectée */

/* --------------------------------------------------------------- typographie */

h1,
h2,
h3,
.carte__titre,
.entete__marque {
  font-family: var(--police);
  font-weight: 700;
}

h1 {
  font-size: 1.65rem;
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0 0 0.5rem;
  text-wrap: balance;
}

h2 {
  font-size: 1.25rem;
  margin: 0;
}

h3 {
  font-size: 1.05rem;
  margin: 1.5rem 0 0.5rem;
}

.accueil__intro,
.recette__resume {
  color: var(--encre-douce);
  margin-top: 0;
}

/* --------------------------------------------------------------- catalogue */

.accueil__titre {
  /* Le trait de couleur sous le titre d'accueil : la même main que le bandeau. */
  padding-bottom: 0.4rem;
}

.accueil__section {
  margin-top: 2.5rem;
}

/*
 * Un titre de section porte un tiret de couleur, pas un filet gris sur toute la
 * largeur. Le filet coupait la page en tranches ; le tiret désigne le titre.
 */
.accueil__section h2,
.recette__entete-section h2,
.entite__section h2,
.entite__definition h2,
.favoris__titre,
.recherche__titre {
  position: relative;
}

.accueil__section h2::after,
.recette__entete-section h2::after,
.entite__section h2::after,
.entite__definition h2::after,
.accueil__titre::after,
.favoris__titre::after,
.recherche__titre::after {
  background: var(--accent);
  border-radius: 999px;
  content: "";
  display: block;
  height: 5px;
  margin-top: 0.45rem;
  width: 3rem;
}

/* Le titre de l'accueil porte les trois premières couleurs, comme une enseigne. */
.accueil__titre::after {
  background: linear-gradient(
    90deg,
    var(--accent) 0 34%,
    var(--soleil) 34% 67%,
    var(--framboise) 67% 100%
  );
  width: 5.5rem;
}

.accueil__section--avant h2::after {
  background: var(--soleil);
}

/* -------------------------------------------------------- hero (U2, R2, R3) */

/*
 * Le hero de l'accueil (U2 du plan façade) : la mascotte en tête de page,
 * un seul mouvement discret à l'arrivée (KTD2), coupé sous
 * `prefers-reduced-motion: reduce` par la règle globale ci-dessous — qui
 * désarme déjà toute transition de la page, et à qui `accueil-hero-arrivee`
 * s'ajoute pour couper aussi cette animation-ci.
 */
.accueil__hero {
  align-items: center;
  display: grid;
  gap: var(--pas-4);
  justify-items: center;
  padding: var(--pas-5) 0 var(--pas-6);
  text-align: center;
}

.accueil__hero-image {
  animation: accueil-hero-arrivee 0.5s ease-out;
  margin: 0;
  width: 168px;
}

.accueil__hero-image img {
  height: auto;
  width: 100%;
}

@keyframes accueil-hero-arrivee {
  0% {
    opacity: 0;
    transform: translateY(12px);
  }

  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .accueil__hero-image {
    animation: none;
  }
}

@media (min-width: 48rem) {
  .accueil__hero {
    grid-auto-flow: column;
    justify-content: center;
    text-align: left;
  }
}

.catalogue {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
}

/*
 * La carte.
 *
 * Le lien couvre la carte entière : viser un titre de deux mots sur un téléphone
 * tenu d'une main ne marche pas (R48). Le bouton de retrait des favoris est le
 * seul élément qui vit à côté du lien, jamais dedans.
 */
.carte {
  --couleur: var(--accent);
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  overflow: hidden;
  position: relative;
  transition: border-color 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}

/* La carte se soulève au survol, et le pointeur ne se contente pas de changer de
   forme. Sur un écran tactile, rien de tout cela ne se voit — c'est pourquoi le
   dessin ne repose pas dessus. */
.carte:hover {
  box-shadow: 0 2px 4px rgb(64 32 8 / 8%), 0 18px 32px -18px color-mix(in srgb, var(--couleur) 60%, transparent);
  transform: translateY(-2px);
}

/*
 * Une couleur par nature de plat, sur les identifiants **du référentiel**.
 *
 * Seuls ceux qui peuvent atteindre une carte figurent ici : le moteur n'y met
 * qu'un tag de catégorie « type-de-plat », plus le repli sur le type de la recette
 * — « plat » ou « base » — quand la recette n'en porte aucun. Écrire une règle
 * pour « vegetarien » serait une couleur qu'aucune carte ne peut prendre.
 *
 * Une nature ajoutée au référentiel sans règle ici prend l'accent du site : elle
 * se voit, elle ne casse rien, et la corriger est une ligne.
 */
.carte[data-categorie="dessert"] {
  --couleur: var(--framboise);
}

.carte[data-categorie="plat-principal"],
.carte[data-categorie="plat"] {
  --couleur: var(--avocat);
}

.carte[data-categorie="base"] {
  --couleur: var(--soleil);
}

/* La barre haute de couleur : c'est elle qui rend une grille sans photo lisible
   d'un coup d'œil. */
.carte::before {
  background: var(--couleur);
  content: "";
  display: block;
  height: 9px;
}

.carte:hover,
.carte:focus-within {
  border-color: var(--couleur);
}

.carte__lien {
  color: inherit;
  display: block;
  text-decoration: none;
}

.carte__corps {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  /* La hauteur minimale vaut une cible tactile pleine, même sur une carte au
     titre d'un seul mot. */
  min-height: var(--cible);
  padding: 0.85rem 1rem 1rem;
}

.carte__vignette {
  aspect-ratio: 4 / 3;
  display: block;
  object-fit: cover;
  width: 100%;
}

/*
 * La pastille porte la **couleur pleine**, avec l'encre du site par-dessus.
 *
 * La version précédente teintait le fond à 20 % et fonçait le texte : lisible,
 * et fade — la couleur ne se voyait plus qu'en trace. Une teinte vive derrière de
 * l'encre sombre passe le contraste sur les cinq couleurs, et se voit de loin.
 */
.carte__categorie {
  align-self: start;
  background: var(--couleur);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 0.15rem 0.65rem;
  text-transform: uppercase;
}

.carte__titre {
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.3;
}

.carte__lien:hover .carte__titre,
.carte__lien:focus-visible .carte__titre {
  text-decoration: underline;
}

.carte__meta {
  color: var(--encre-douce);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.9rem;
  gap: 0.5rem;
}

.carte__note {
  font-style: italic;
}

/* Les mises en avant : le liseré coloré au lieu d'une bordure de plus. */
.catalogue--avant .carte {
  box-shadow: 0 0 0 1px var(--couleur);
}

.catalogue__vide,
.ingredients__vide,
.etapes__vide {
  color: var(--encre-douce);
  font-style: italic;
  list-style: none;
  padding: 0.5rem 0;
}

/* ----------------------------------------------------------------- recette */

.recette__image {
  margin: 0 0 1.5rem;
}

.recette__image img {
  border-radius: var(--rayon);
  display: block;
  width: 100%;
}

.recette__titre {
  margin-bottom: 0.35rem;
}

.recette__section {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  margin-top: 1.5rem;
  padding: 1rem 1.15rem 1.15rem;
}

.recette__entete-section {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  justify-content: space-between;
}

.recette__sous-titre {
  color: var(--encre-douce);
  font-size: 0.95rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.criteres {
  display: flex;
  flex-wrap: wrap;
  font-size: 0.95rem;
  gap: 0.5rem;
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
}

/* Les critères sont des pastilles : trois lignes de texte gris se lisaient comme
   une note de bas de page, alors qu'un allergène est l'information qu'on cherche
   en premier. */
.critere {
  background: var(--fond-doux);
  border-radius: var(--rayon-pastille);
  padding: 0.25rem 0.8rem;
}

.critere__intitule {
  font-weight: 600;
}

.critere--allergene {
  background: color-mix(in srgb, var(--alerte) 16%, var(--fond-carte));
  color: color-mix(in srgb, var(--alerte) 78%, var(--encre));
}

.critere--regime {
  background: color-mix(in srgb, var(--avocat) 32%, var(--fond-carte));
  color: var(--encre);
}

/* « Indéterminé » se distingue visuellement d'« absent » : un lecteur allergique
   lit une exemption comme une affirmation, et l'incertitude doit se voir. */
.critere--indetermine {
  background: transparent;
  border: 1px dashed var(--trait);
  color: var(--encre-douce);
  font-style: italic;
}

.ingredients,
.sous-recettes {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
}

.ingredient,
.sous-recette {
  border-bottom: 1px solid var(--trait);
}

.ingredient:last-child,
.sous-recette:last-child {
  border-bottom: 0;
}

.sous-recette {
  padding: 0.55rem 0;
}

/*
 * La ligne d'ingrédient est une **rangée à trois pièces** : la case, le sujet
 * (le nom et ses préparations), puis l'étiquette qui porte l'accompagnement.
 *
 * La case n'est pas dans l'étiquette, contrairement à une étape : le nom est un
 * lien, et un label relaie à sa case tout clic reçu par son contenu — le nom
 * posé dedans aurait navigué *et* coché. Voir `ligneIngredient`, qui porte la
 * décision. C'est donc la rangée, et non l'étiquette, qui tient la hauteur
 * tactile de R48.
 */
.ingredient {
  align-items: baseline;
  display: flex;
  gap: 0.6rem;
  min-height: var(--cible);
  padding: 0.5rem 0.25rem;
}

/*
 * La case reprend l'alignement qu'elle avait dans l'étiquette : sur la première
 * ligne de texte plutôt que sur la ligne de base, où une case de 1,4 rem
 * dépasserait le haut des lettres. Le reste de la rangée, lui, partage bien une
 * ligne de base — le nom et sa quantité n'ont pas le même corps, et deux textes
 * alignés par le haut se liraient en escalier.
 */
.ingredient > .coche__case {
  align-self: flex-start;
}

/*
 * L'étiquette prend tout ce que le sujet laisse, et s'étire sur la hauteur de la
 * rangée : la zone qui coche reste toute la ligne moins les mots qui mènent
 * ailleurs. `width` et le remplissage de `.coche` sont neutralisés ici — ils
 * sont taillés pour une étiquette qui enveloppe sa ligne, ce qu'elle n'est plus
 * ici.
 */
.ingredient > .coche {
  align-self: stretch;
  flex: 1 1 auto;
  min-height: 0;
  padding: 0;
  width: auto;
}

/*
 * R12 : l'ingrédient est plus visible que sa quantité. La graisse et l'encre
 * d'accent passent du nombre au nom — aucune couleur n'est inventée, elles
 * changent de porteur : le nom prend l'encre d'accent que **tout lien du site**
 * porte déjà, la quantité rejoint l'encre douce des notes.
 */
.ingredient__nom {
  font-size: 1.05rem;
  font-weight: 700;
}

/* Sans lien, le libellé garde son poids mais perd la couleur qui promettrait une
   page : l'ingrédient est là, sa page n'existe pas encore. */
span.ingredient__nom {
  color: var(--encre);
}

.ingredient__preparations {
  font-size: 0.9rem;
}

/*
 * La quantité accompagne : elle se lit après le nom, et ne tient plus de colonne.
 * C'est cette colonne qui obligeait une ligne « à volonté » à porter une
 * indentation de remplacement pour ne pas repartir seule à la marge ; venant
 * après le nom, son absence ne laisse plus de trou à combler. Les chiffres
 * restent tabulaires : une colonne de quantités se compare à l'œil.
 */
.ingredient__quantite {
  color: var(--encre-douce);
  font-variant-numeric: tabular-nums;
}

.ingredient__note,
.ingredient__provenance {
  color: var(--encre-douce);
  font-size: 0.9rem;
}

/* Une ligne cochée s'efface **entièrement**, sujet compris : la règle générale du
   cochage ne biffe que le contenu de l'étiquette, et le nom n'y est plus. */
.ingredient[data-cochee] > .ingredient__sujet,
/* Le lien du nom porte sa propre couleur, que l'héritage ne défait pas : sans
   cette seconde ligne, une ligne cochée gardait un nom en pleine encre
   d'accent. */
.ingredient[data-cochee] > .ingredient__sujet a {
  color: var(--encre-douce);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

.etapes {
  margin: 0.75rem 0 0;
  padding-left: 1.6rem;
}

/* Le numéro d'étape est un repère, pas une ponctuation : on le retrouve des yeux
   après avoir regardé la casserole. */
.etape::marker {
  color: var(--accent-encre);
  font-size: 1.1em;
  font-weight: 700;
}

.etape {
  margin-bottom: 0.6rem;
  padding-left: 0.25rem;
}

/* L'intitulé de groupe des étapes (R7) : un titre, frère de sa liste.
   La marge haute sépare deux moments du déroulé ; le premier intitulé n'a rien
   au-dessus de lui à séparer, et la liste qui suit immédiatement un intitulé ne
   redouble pas l'écart. */
.etapes__groupe {
  color: var(--turquoise-encre);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  margin: 1.5rem 0 0.3rem;
  text-transform: uppercase;
}

.etapes__groupe:first-of-type {
  margin-top: 0.75rem;
}

.etapes__groupe + .etapes {
  margin-top: 0;
}

.variantes {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
}

.variante {
  border-bottom: 1px solid var(--trait);
  padding: 0.55rem 0;
}

.variante:last-child {
  border-bottom: 0;
}

.variante__libelle {
  font-weight: 600;
}

.variante__allergenes {
  color: var(--encre-douce);
  font-size: 0.9rem;
}

/* ------------------------------- redirection, retrait et introuvable */

.redirection__note {
  color: var(--encre-douce);
  font-size: 0.95rem;
}

.introuvable__action,
.retiree__action,
.favoris__vide .bouton {
  align-items: center;
  background: var(--accent);
  border: 0;
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  display: inline-flex;
  font-weight: 600;
  min-height: var(--cible);
  padding: 0 1.35rem;
  text-decoration: none;
}

/* ---------------------------------------------------------------- boutons */

/* `a.bouton` et `button.bouton` partagent le même dessin (R8) : centrés dans
   leur capsule, sans soulignement, avec un interligne assez large pour que les
   accents hauts d'OpenDyslexic (« É », decisions.md) restent dans la capsule
   plutôt que d'en sortir par le haut. */
.bouton {
  align-items: center;
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-weight: 500;
  justify-content: center;
  line-height: 1.32;
  min-height: var(--cible);
  padding: 0 1.15rem;
  text-decoration: none;
  transition: transform 120ms ease, border-color 120ms ease;
}

.bouton:hover,
.bouton:focus-visible {
  border-color: var(--accent);
}

/* L'appui se sent : sur un plan de travail, les mains occupées, un bouton qui ne
   répond pas visuellement se fait appuyer deux fois. */
.bouton:active {
  transform: scale(0.97);
}

/* Le mode cuisine est l'action principale d'une fiche : elle se voit. */
.bouton--cuisine {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--encre);
  font-weight: 700;
}

.bouton--favori[aria-pressed="true"] {
  background: var(--soleil);
  border-color: var(--soleil);
  color: var(--encre);
  font-weight: 600;
}

.recette__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.25rem 0;
}

/* L'attente et l'échec de l'export PDF (U1). La zone prend toute la largeur
   restante et passe donc sous les boutons : un message qui s'insérerait entre
   eux les ferait sauter de place au premier clic. */
.recette__pdf-etat {
  color: var(--encre-douce);
  flex-basis: 100%;
  font-size: 0.9rem;
  margin: 0;
}

/* ---------------------------------------------------------------- échelle */

/*
 * Le sélecteur d'échelle. Boutons et champ mesurent une cible tactile pleine :
 * on s'en sert avec les mains occupées, parfois d'une seule main (R48).
 */
.echelle {
  align-items: stretch;
  display: flex;
  gap: 0.25rem;
}

.echelle__bouton {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  font-size: 1.25rem;
  line-height: 1;
  min-height: var(--cible);
  min-width: var(--cible);
}

.echelle__bouton:hover {
  border-color: var(--accent);
}

.echelle__label {
  align-items: center;
  display: flex;
  gap: 0.35rem;
}

.echelle__champ {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: 0.6rem;
  color: var(--encre);
  font: inherit;
  font-variant-numeric: tabular-nums;
  min-height: var(--cible);
  padding: 0 0.5rem;
  text-align: center;
  width: 4.5rem;
}

.echelle__intitule {
  color: var(--encre-douce);
  font-size: 0.9rem;
}

/* --------------------------------------------------------------- recherche */

/*
 * Sur écran large, les filtres tiennent une colonne et les résultats l'autre :
 * empilés, ils repoussaient les cartes sous la ligne de flottaison, et il fallait
 * remonter pour changer un critère.
 */
@media (min-width: 62rem) {
  .recherche {
    align-items: start;
    column-gap: 2.5rem;
    display: grid;
    grid-template-columns: 18rem 1fr;
  }

  .recherche__titre {
    grid-column: 1 / -1;
  }

  .recherche__form {
    position: sticky;
    top: 1.5rem;
  }
}

.recherche__form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin-bottom: 1.5rem;
}

.recherche__label {
  color: var(--encre-douce);
  font-size: 0.95rem;
}

.recherche__champ {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  font: inherit;
  /* 16 px au moins : en dessous, iOS zoome à la mise au point et le lecteur
     perd la page des yeux. */
  font-size: max(1rem, 16px);
  min-height: var(--cible);
  padding: 0 0.9rem;
  width: 100%;
}

/* Le champ texte de la page est le point d'entrée : il est plus grand que les
   autres champs, et il est le premier élément après le titre. */
.recherche__champ--principal {
  font-size: max(1.1rem, 16px);
  min-height: 3.25rem;
}

.recherche__bloc {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: 0;
  padding: 0.85rem;
}

.recherche__bloc legend,
.recherche__depliant > summary {
  color: var(--encre-douce);
  font-size: 0.95rem;
  padding: 0 0.3rem;
}

/*
 * Le bloc d'ingrédients est replié par défaut.
 *
 * C'est un filtre secondaire — on cherche d'abord par titre — et déplié il occupe
 * un écran de téléphone à lui seul, ce qui repoussait les résultats hors de vue.
 * `details` plutôt qu'un bouton : il se replie sans JavaScript.
 */
.recherche__depliant {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  padding: 0.5rem 0.85rem;
}

.recherche__depliant > summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  min-height: var(--cible);
}

.recherche__depliant[open] {
  padding-bottom: 0.85rem;
}

.recherche__depliant[open] > summary {
  border-bottom: 1px solid var(--trait);
  margin-bottom: 0.6rem;
}

.recherche__aide {
  color: var(--encre-douce);
  font-size: 0.9rem;
  margin: 0.5rem 0 0;
}

.facettes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Les catégories sont des pastilles cochables : une colonne de cases à cocher
   prenait la hauteur de la page, et on ne voyait plus les résultats. */
.facette {
  align-items: center;
  background: var(--fond-doux);
  border-radius: var(--rayon-pastille);
  cursor: pointer;
  display: flex;
  gap: 0.4rem;
  min-height: var(--cible);
  padding: 0 0.85rem;
}

.facette:has(input:checked) {
  background: var(--accent-doux);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.facette__compte {
  color: var(--encre-douce);
  font-size: 0.85rem;
}

/* La liste de suggestions se superpose au contenu : sur un écran de téléphone,
   pousser le reste de la page à chaque frappe déplacerait ce que le lecteur
   vise. */
.autocomplete {
  position: relative;
}

.autocomplete__liste {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: 0.6rem;
  box-shadow: 0 6px 20px rgb(0 0 0 / 12%);
  list-style: none;
  margin: 0.25rem 0 0;
  max-height: 60vh;
  overflow-y: auto;
  padding: 0;
  position: absolute;
  width: 100%;
  z-index: 2;
}

.suggestion {
  cursor: pointer;
  min-height: var(--cible);
  padding: 0.6rem;
}

.suggestion--active {
  background: var(--accent-doux);
}

.suggestion--vide {
  color: var(--encre-douce);
  cursor: default;
}

.pastilles {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.pastille {
  align-items: center;
  background: color-mix(in srgb, var(--avocat) 34%, var(--fond-carte));
  border-radius: var(--rayon-pastille);
  display: flex;
  font-size: 0.95rem;
  gap: 0.2rem;
  padding-left: 0.8rem;
}

.pastille__retirer {
  background: none;
  border: 0;
  color: var(--encre);
  cursor: pointer;
  font-size: 1.2rem;
  line-height: 1;
  min-height: var(--cible);
  min-width: var(--cible);
}

/* Les trois états transitoires se distinguent : le chargement est neutre,
   l'erreur est marquée, l'absence de résultat est du contenu. */
.recherche__etat {
  color: var(--encre-douce);
  margin: 1rem 0;
}

.recherche__etat--erreur {
  background: color-mix(in srgb, var(--alerte) 8%, var(--fond-carte));
  border-left: 4px solid var(--alerte);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  color: var(--encre);
  padding: 0.75rem 1rem;
}

.recherche__compte {
  font-size: 1.15rem;
  margin: 0 0 0.5rem;
}

.recherche__compte::after {
  background: var(--soleil);
  border-radius: 2px;
  content: "";
  display: block;
  height: 3px;
  margin-top: 0.35rem;
  width: 2.5rem;
}

.recherche__inconnues h3 {
  color: var(--encre-douce);
  font-size: 1rem;
  margin: 1.5rem 0 0.5rem;
}

/* ------------------------------------------------- cochage et zones tactiles */

/* L'étiquette couvre toute la ligne : viser une case de 16 pixels sur un plan de
   travail, les mains occupées, ne marche pas. La zone cliquable prime sur la
   taille visuelle de la case (R48). */
.coche {
  align-items: flex-start;
  cursor: pointer;
  display: flex;
  gap: 0.6rem;
  min-height: var(--cible);
  /* Le remplissage étend la zone bien au-delà de la case elle-même. */
  padding: 0.5rem 0.25rem;
  width: 100%;
}

.coche__case {
  accent-color: var(--accent);
  flex: 0 0 auto;
  height: 1.4rem;
  /* Aligné sur la première ligne de texte, pas centré sur un paragraphe de
     quatre lignes. */
  margin-top: 0.15rem;
  width: 1.4rem;
}

.coche__texte {
  flex: 1 1 auto;
}

/* Une ligne cochée s'efface sans disparaître : on doit pouvoir revenir. */
[data-cochee] > .coche > .coche__texte {
  color: var(--encre-douce);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* --------------------------------------------------------------- mode cuisine */

/*
 * Le plein écran est obtenu par cette feuille, pas par l'API Fullscreen : Safari
 * sur iPhone ne l'offre pas pour un élément arbitraire, et c'est la plateforme
 * visée. `100dvh` suit la barre d'outils escamotable — `100vh` laisserait la barre
 * de navigation recouvrir le pied d'écran — et les marges de sécurité gèrent les
 * encoches, le document portant déjà `viewport-fit=cover`.
 *
 * **Ce que le mode donne à l'étape, il le prend ailleurs.** L'écran est petit :
 * une liste d'ingrédients et une barre à trois rangées ne laissaient à l'étape
 * courante qu'une fenêtre étroite, alors qu'elle est la seule chose qu'on lit à
 * bout de bras. Les ingrédients passent donc en panneau escamotable — ouvert par
 * un bouton de la barre, superposé pour ne rien décaler — et la barre tient sur
 * une rangée, la position et la sortie remontant en tête d'écran.
 */
.cuisine-barre,
.cuisine-entete {
  display: none;
}

body.mode-cuisine {
  overflow: hidden;
}

body.mode-cuisine .entete,
body.mode-cuisine .pied,
body.mode-cuisine .recette__image,
body.mode-cuisine .recette__resume,
body.mode-cuisine .criteres,
body.mode-cuisine .recette__actions,
body.mode-cuisine .recette__section--lecteur,
body.mode-cuisine .variantes,
body.mode-cuisine .recette__titre,
/* La fiche à en-tête de l'app (système B, U6) : même rôle que le titre, le
   résumé et les critères de la façade ci-dessus — de la page qui entoure
   l'étape, pas l'étape elle-même. */
body.mode-cuisine .fiche-entete,
body.mode-cuisine .fiche-recette__entete,
body.mode-cuisine .recette__section--etapes > .recette__entete-section {
  display: none;
}

body.mode-cuisine .contenu {
  margin: 0;
  max-width: none;
  padding: 0;
}

body.mode-cuisine .recette {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  position: relative;
}

/*
 * L'app (système B, U6) encadre la fiche d'un `.fiche`, puis d'un `.panneau`
 * et son `.panneau__corps` (R6) — une boîte que la façade n'a pas. En mode
 * cuisine, ces trois boîtes s'effacent sans retirer leurs enfants du fil
 * (`display: contents`) : `.recette__section--etapes`, plus bas, redevient
 * ainsi l'enfant flexible direct de `.recette`, exactement la structure que
 * cette feuille — écrite pour la façade — attend déjà.
 */
body.mode-cuisine .recette .fiche,
body.mode-cuisine .recette .panneau,
body.mode-cuisine .recette .panneau__corps {
  display: contents;
}

body.mode-cuisine .recette__section {
  background: none;
  border: 0;
  border-radius: 0;
  margin: 0;
  padding: 0;
}

/* L'étape occupe tout ce qui reste, et son texte est centré dans cette place :
   c'est la seule ligne qu'on lit de loin. */
body.mode-cuisine .recette__section--etapes {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  /* `safe` : un centrage ordinaire rogne le haut d'un texte plus grand que sa
     boîte, et le début de l'étape est justement ce qu'on relit. Là où le mot-clé
     n'est pas reconnu, la déclaration tombe et le texte s'aligne en haut — ce qui
     est le repli qu'on veut. */
  justify-content: safe center;
  overflow-y: auto;
  padding: 1rem calc(1rem + env(safe-area-inset-right)) 1rem calc(1rem + env(safe-area-inset-left));
}

body.mode-cuisine .etapes {
  margin: 0;
  padding: 0;
}

/* Seule l'étape courante est affichée, en grand : à bout de bras, sur un plan de
   travail, une liste de douze étapes ne se relit pas. */
body.mode-cuisine .etape {
  display: none;
}

body.mode-cuisine .etape[data-courante] {
  display: block;
  font-size: 1.4rem;
  line-height: 1.45;
  margin: 0 auto;
  max-width: 34rem;
  padding: 0;
}

/*
 * L'étape d'après, en petit sous la courante.
 *
 * L'écran restait à moitié vide, et une étape seule ne dit pas ce qui vient :
 * savoir qu'il faudra de l'eau bouillante permet de la lancer pendant qu'on coupe.
 * Elle est estompée et plus petite — la courante reste ce qu'on lit à bout de
 * bras — et son numéro la distingue sans qu'aucun intitulé n'ait à le dire.
 */
body.mode-cuisine .etape[data-suivante] {
  border-top: 1px dashed var(--trait);
  color: var(--encre-douce);
  display: block;
  font-size: 1.05rem;
  line-height: 1.4;
  margin: 1.25rem auto 0;
  max-width: 34rem;
  padding: 0.9rem 0 0;
}

/* Sa case reste utilisable — on coche parfois en avance — mais elle ne réclame
   pas la place d'une cible pleine tant qu'elle n'est pas l'étape en cours. */
body.mode-cuisine .etape[data-suivante] .coche {
  min-height: 0;
  padding: 0;
}

body.mode-cuisine .etape[data-suivante] .coche__case {
  height: 1.2rem;
  width: 1.2rem;
}

body.mode-cuisine .etape[data-courante] .coche {
  padding: 0.5rem 0;
}

body.mode-cuisine .etape[data-courante] .coche__case {
  height: 1.8rem;
  margin-top: 0.35rem;
  width: 1.8rem;
}

/* Les intitulés de groupe disparaissent en mode cuisine. Une seule étape est à
   l'écran : les laisser afficherait trois titres au-dessus d'elle, sans qu'aucun
   ne corresponde forcément à l'étape en cours. La position, elle, est déjà dite
   en tête d'écran — « Étape 3 sur 5 ». */
body.mode-cuisine .etapes__groupe {
  display: none;
}

/* Le panneau d'ingrédients : masqué, et superposé quand on le demande. Superposé
   et non inséré, pour que l'ouvrir ne déplace pas l'étape qu'on est en train de
   lire. */
body.mode-cuisine .recette__section--ingredients {
  display: none;
}

body.mode-cuisine .recette[data-ingredients-ouverts] .recette__section--ingredients {
  background: var(--fond-carte);
  border-bottom: 2px solid var(--accent);
  display: block;
  left: 0;
  max-height: 70%;
  overflow-y: auto;
  padding: 0.75rem calc(0.75rem + env(safe-area-inset-right)) 0.75rem
    calc(0.75rem + env(safe-area-inset-left));
  position: absolute;
  right: 0;
  top: 0;
  z-index: 3;
}

body.mode-cuisine .cuisine-entete {
  align-items: center;
  /* La tête d'écran est écrite après les sections dans le document — le gabarit
     pose le cadre du mode en un bloc, à la fin de la fiche. C'est l'ordre de la
     boîte flexible qui la remonte, plutôt qu'un morceau de gabarit ailleurs. */
  order: -1;
  border-bottom: 1px solid var(--trait);
  display: flex;
  gap: 0.5rem;
  justify-content: space-between;
  padding: 0 calc(0.5rem + env(safe-area-inset-right)) 0 calc(0.5rem + env(safe-area-inset-left));
}

.cuisine-entete__position {
  color: var(--encre-douce);
  font-size: 0.95rem;
  margin: 0;
  padding-left: 0.5rem;
}

.cuisine-entete__quitter {
  background: none;
  border: 0;
  color: var(--encre-douce);
  cursor: pointer;
  font-size: 1.3rem;
  line-height: 1;
  min-height: var(--cible);
  min-width: var(--cible);
}

body.mode-cuisine .cuisine-barre {
  background: var(--fond-carte);
  border-top: 1px solid var(--trait);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: auto;
  /* La marge basse de sécurité évite que la barre passe sous l'indicateur
     d'accueil de l'iPhone. */
  padding: 0.5rem calc(0.5rem + env(safe-area-inset-right)) calc(0.5rem + env(safe-area-inset-bottom))
    calc(0.5rem + env(safe-area-inset-left));
}

.cuisine-barre__rangee {
  align-items: stretch;
  display: flex;
  gap: 0.4rem;
}

/* Deux grandes cibles en pied d'écran, atteignables au pouce d'une seule main.
   Le balayage serait plus élégant et moins fiable avec les mains grasses. */
.cuisine-barre__bouton {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  flex: 1 1 0;
  font: inherit;
  font-size: 1.05rem;
  min-height: 52px;
  padding: 0 0.5rem;
}

.cuisine-barre__bouton--suivant {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--fond-carte);
  flex: 2 1 0;
  font-weight: 600;
}

.cuisine-barre__ingredients {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  flex: 0 0 auto;
  font: inherit;
  font-size: 0.95rem;
  min-height: 52px;
  padding: 0 0.9rem;
}

.cuisine-barre__ingredients[aria-expanded="true"] {
  background: var(--accent-doux);
  border-color: var(--accent);
}

/*
 * Le cochage des ingrédients dans l'app (plan mode cuisine, U2 — R3, KTD4).
 *
 * La case n'existe dans le document de l'app qu'à partir du premier clic sur
 * « Cuisiner maintenant » : `app-checklist.ts::activateChecking` la construit
 * autour du texte déjà rendu, au même moment où `checklist.ts` est câblé —
 * jamais avant. Hors mode cuisine, la ligne reste un `<span
 * class="ingredient__texte">` nu, donc aucune règle de masquage n'est
 * nécessaire ici (une version antérieure en posait une, revenue sur ce choix
 * pour tenir le paquet principal sous son seuil de 64 Kio — voir l'en-tête
 * d'`app-checklist.ts`).
 *
 * La règle qui suit ne porte donc que la taille et la couleur de la case,
 * une fois posée : la règle générique des champs du compte (plus bas,
 * § « Écrans connectés ») ne doit pas s'y substituer — d'où trois classes
 * dans le sélecteur, plus spécifique qu'elle quel que soit l'ordre des deux
 * règles dans la feuille.
 */
.page-coquille .recette .coche__case {
  accent-color: var(--accent);
  flex: 0 0 auto;
  height: 1.4rem;
  margin-top: 0.15rem;
  width: 1.4rem;
}

.cuisine-barre__degradation {
  color: var(--alerte);
  font-size: 0.95rem;
  margin: 0;
}

/*
 * Les commandes vocales (VC-1). Le bouton et ses lignes d'état sont posés par
 * `voice-commands.ts`, jamais servis dans le balisage de la coquille. Le point
 * n'est pas animé : l'indicateur d'écoute tient à l'attribut et au libellé, pas
 * à un mouvement qu'il faudrait désactiver sous `prefers-reduced-motion`.
 */
.cuisine-barre__voix {
  align-self: flex-start;
  align-items: center;
  display: inline-flex;
  gap: 0.5rem;
  min-height: var(--cible);
}

.cuisine-barre__voix-point {
  background: var(--trait);
  border-radius: 50%;
  height: 0.7rem;
  width: 0.7rem;
}

.cuisine-barre__voix[data-ecoute] .cuisine-barre__voix-point {
  background: var(--alerte);
}

.cuisine-barre__voix-etat,
.cuisine-barre__voix-minuteur {
  font-size: 0.95rem;
  margin: 0;
}

.cuisine-barre__voix-minuteur {
  font-variant-numeric: tabular-nums;
}

/*
 * Le minuteur d'étape (plan mode cuisine, U1 — R1, R2). Masqué par défaut :
 * le balisage vit dans chaque `<li class="etape">` qui porte une durée
 * détectée, qu'on soit en mode cuisine ou non (c'est la même liste qui sert
 * les deux), donc sans cette règle il apparaîtrait aussi dans la liste
 * ordinaire des étapes, hors de la portée de ce chantier. Il ne reparaît que
 * pour l'étape courante du mode cuisine — jamais pour l'étape suivante en
 * aperçu, qui ne se clique pas.
 */
.minuteur {
  display: none;
}

body.mode-cuisine .etape[data-courante] .minuteur {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.9rem;
}

.minuteur__bouton {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  font: inherit;
  font-size: 0.95rem;
  min-height: var(--cible);
  padding: 0 0.9rem;
}

.minuteur__decompte {
  color: var(--encre-douce);
  font-size: 1.05rem;
  /* Des chiffres de largeur égale : un décompte qui ne saute pas de place à
     chaque seconde ne vaut pas la peine d'une animation, et n'en demande
     aucune à `prefers-reduced-motion`. */
  font-variant-numeric: tabular-nums;
  margin: 0;
}

/* La jauge du minuteur (CQ-5) : une barre qui se remplit, décorative (le décompte
   textuel au-dessus porte l'annonce). Aucune transition : l'avancement saute par
   pas de 250 ms, ce qui n'a rien à réduire pour `prefers-reduced-motion`. */
.minuteur__jauge {
  background: var(--trait);
  border-radius: var(--rayon-pastille);
  flex-basis: 100%;
  height: 0.5rem;
  overflow: hidden;
}

.minuteur__jauge::before {
  background: var(--accent);
  content: "";
  display: block;
  height: 100%;
  transform: scaleX(var(--avancement, 0));
  transform-origin: left;
}

/* Sur un écran étroit, les mots des boutons de navigation cèdent la place aux
   chevrons : le bouton reste aussi grand, seul son libellé raccourcit. */
@media (max-width: 26rem) {
  .cuisine-barre__bouton--precedent .cuisine-barre__mot {
    clip-path: inset(50%);
    position: absolute;
  }
}

/* ------------------------------------------------------------------ favoris */

.favoris__vide,
.favoris__note {
  color: var(--encre-douce);
}

.favoris__vide {
  background: var(--fond-carte);
  border: 1px dashed var(--trait);
  border-radius: var(--rayon);
  padding: 1rem 1.15rem;
}

.favori__absent {
  color: var(--encre-douce);
  font-size: 0.85rem;
}

/* Le bouton de retrait est posé sur la carte, au-dessus du lien qui la couvre. */
.favori__retirer {
  background: color-mix(in srgb, var(--fond-carte) 80%, transparent);
  border: 0;
  border-radius: var(--rayon-pastille);
  color: var(--encre-douce);
  cursor: pointer;
  font-size: 1.3rem;
  line-height: 1;
  min-height: var(--cible);
  min-width: var(--cible);
  position: absolute;
  right: 0.35rem;
  top: calc(6px + 0.35rem);
  z-index: 2;
}

.favori__retirer:hover,
.favori__retirer:focus-visible {
  color: var(--alerte);
}

/* ------------------------------------------------------------------ entité */

/*
 * La page d'un objet du corpus (U13) : ingrédient, préparation, pays, ouvrage,
 * adresse. Elle réemploie le bloc de `.recette__section` — la même carte, le
 * même titre à tiret — plutôt qu'un dessin propre : un lecteur qui arrive d'une
 * fiche doit reconnaître la maison, et la palette est une décision fermée.
 */

/* Le genre au-dessus du nom : « Ingrédient », « Pays ». Sans lui, « Maroc » en
   gros n'apprend pas au lecteur sur quelle sorte de page il vient d'arriver. */
.entite__genre {
  color: var(--encre-douce);
  font-size: 0.9rem;
  letter-spacing: 0.06em;
  margin: 0;
  text-transform: uppercase;
}

.entite__titre {
  margin-bottom: 0.35rem;
  margin-top: 0.15rem;
}

.entite__definition,
.entite__section {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  margin-top: 1.5rem;
  padding: 1rem 1.15rem 1.15rem;
}

/* La définition est ce que le lecteur a cliqué pour lire : elle porte un fond
   accentué, contrairement aux listes qui la suivent. */
.entite__definition {
  background: var(--fond-doux);
}

.entite__terme {
  color: var(--encre-douce);
  font-style: italic;
  margin: 0.6rem 0 0;
}

.entite__texte,
.astuce__texte {
  margin: 0.6rem 0 0;
}

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

.astuce + .astuce {
  border-top: 1px solid var(--trait);
  margin-top: 0.9rem;
  padding-top: 0.9rem;
}

.astuce__titre {
  font-size: 1rem;
  margin: 0;
}

/* Les voisines sont des pastilles cliquables, pas une liste à puces : ce sont
   des sorties vers d'autres pages, et elles se visent au doigt — d'où la
   hauteur minimale de cible. */
.entite__voisines {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 0.9rem 0 0;
  padding: 0;
}

.entite__voisine a {
  align-items: center;
  background: var(--fond-doux);
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  min-height: var(--cible);
  padding: 0.35rem 0.9rem;
  text-decoration: none;
}

.entite__voisine a:hover,
.entite__voisine a:focus-visible {
  background: var(--accent-doux);
  color: var(--accent-encre);
}

/* -------------------------------------------------- état du lecteur (U15) */

/*
 * La note, la moyenne et les commentaires d'une fiche (R21).
 *
 * Le bloc réemploie `.recette__section` — même carte, même titre — plutôt
 * qu'un dessin propre : la palette est une décision fermée, et un lecteur qui
 * descend d'une fiche jusqu'à son avis ne doit pas croire avoir changé de
 * site.
 */

/*
 * L'attente et l'indisponibilité portent la même encre douce : ce sont deux
 * façons de dire « pas de donnée », et les distinguer par la couleur
 * suggérerait qu'une des deux est une erreur du lecteur.
 */
.etat-lecteur__attente,
.etat-lecteur__indisponible {
  color: var(--encre-douce);
  margin: 0.6rem 0 0;
}

.etat-lecteur__moyenne {
  font-weight: 700;
  margin: 0.6rem 0 0;
}

/* Cinq crans, chacun une cible tactile pleine : on note d'une main (R48). */
.etat-lecteur__note {
  display: flex;
  gap: 0.35rem;
  margin: 0.6rem 0 1.1rem;
}

.note__cran {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  min-height: var(--cible);
  min-width: var(--cible);
}

.note__cran:hover {
  border-color: var(--accent);
}

/*
 * Le cran choisi se voit à sa surface pleine, pas seulement à sa bordure : une
 * bordure d'un pixel de plus n'est pas lisible sur un téléphone posé sur un
 * plan de travail.
 */
.note__cran[aria-pressed="true"] {
  background: var(--soleil);
  border-color: var(--soleil);
  font-weight: 700;
}

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

.commentaires__vide {
  color: var(--encre-douce);
}

.commentaire {
  border-top: 1px solid var(--trait);
  padding: 0.85rem 0;
}

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

/* L'auteur et la date sur une rangée, en plus petit : le texte est le sujet. */
.commentaire__entete {
  color: var(--encre-douce);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.85rem;
  gap: 0.5rem;
  margin: 0;
}

.commentaire__auteur {
  font-weight: 700;
}

.commentaire__texte {
  margin: 0.3rem 0 0;
  /* Un commentaire est du texte saisi : ses retours à la ligne sont voulus. */
  white-space: pre-wrap;
}

.etat-lecteur__label {
  display: block;
  font-weight: 700;
  margin-bottom: 0.3rem;
}

.etat-lecteur__champ {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  color: var(--encre);
  font: inherit;
  min-height: var(--cible);
  padding: 0.55rem 0.7rem;
  width: 100%;
}

.etat-lecteur__champ:focus-visible {
  border-color: var(--accent);
}

.etat-lecteur__echec {
  color: var(--alerte);
}

/* ------------------------------------------------------------- large écran */

@media (min-width: 48rem) {
  .contenu {
    padding: 2rem 1.5rem 4rem;
  }

  h1 {
    font-size: 2.1rem;
  }
}

/* Un lecteur qui a demandé moins de mouvement n'en reçoit aucun. */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}

/* ======================================== Écrans connectés (plan identité) */

/*
 * Les écrans du site connecté (hôte app) et les pages de droit de la façade :
 * connexion et inscription (U3), éditeur (U7), partage (U6), propositions
 * (U8), réglages (U10), suspension (U13), avis et signalements (U15), CGU et
 * contact.
 *
 * Ces écrans sont construits par le DOM (`assets/js/*-screen.ts`,
 * `auth-forms.ts`, `owner-editor.ts`) : ils posent peu de classes et laissent
 * nus leurs `input`, `select` et `button`. D'où deux étages :
 *
 * 1. des règles d'éléments bornées à `.page-coquille` (le corps de la
 *    coquille connectée) : aucun champ de la façade n'en hérite ;
 * 2. des classes communes — `.ecran`, `.formulaire`, `.champ`, `.message` —
 *    que portent aussi, par liste de sélecteurs, les classes que les écrans
 *    posent déjà. Un écran futur prend la classe commune ; un écran existant
 *    n'a pas à changer.
 *
 * **Aucune couleur nouvelle.** Tout passe par les jetons de `:root`. L'action
 * principale est une surface `--accent-doux` bordée d'`--accent`.
 *
 * La garde : `test/client/screen-styles.test.ts` vérifie que chaque classe
 * posée par ces écrans a une règle dans cette feuille.
 */

/* `hidden` doit gagner : plusieurs règles ci-dessous posent un `display`, et
   un `display` d'auteur l'emporte sur celui du navigateur. Le signalement de
   compte masque son label et son bouton après l'envoi ; le signalement public
   masque son formulaire et ses zones de réponse. */
.page-coquille [hidden],
.signalement [hidden],
.signalement[hidden] {
  display: none !important;
}

/* Tant que la coquille ne pose pas `.contenu` sur son `<main>`, la colonne et
   ses marges viennent d'ici : sans elles, sur téléphone, le texte colle au
   bord. Dès que `.contenu` est posé, cette règle ne s'applique plus. */
.page-coquille main:not(.contenu) {
  margin: 0 auto;
  max-width: 44rem;
  padding: 1.5rem 1.25rem 3rem;
}

/* ------------------------------------------------ contrôles de formulaire */

/*
 * Les contrôles nus des écrans. `font: inherit` puis une taille d'au moins
 * 16 px : en dessous, iOS zoome la page au focus et le lecteur perd sa mise
 * en page. La hauteur est celle d'une cible tactile (R48), la largeur suit la
 * colonne. La graisse est remise à 400 : un champ enveloppé dans son label
 * hériterait sinon du gras de l'intitulé.
 */
.page-coquille input,
.page-coquille select,
.page-coquille textarea,
.page-coquille button,
.champ,
.signalement__champ {
  /* Les parties natives (flèche d'un `select`, case, liste déroulante)
     restent claires, comme la page : Gigot. n'a pas de mode sombre. */
  color-scheme: light;
  font: inherit;
  font-size: max(1rem, 16px);
  font-weight: 400;
  max-width: 100%;
  min-height: var(--cible);
}

/* Le dessin de `.etat-lecteur__champ`, étendu aux champs des écrans. */
.page-coquille input:not([type="checkbox"], [type="radio"], [type="file"]),
.page-coquille select,
.page-coquille textarea,
.champ,
.signalement__champ {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  color: var(--encre);
  padding: 0.55rem 0.7rem;
  width: 100%;
}

.page-coquille textarea,
textarea.champ,
textarea.signalement__champ {
  line-height: 1.5;
  resize: vertical;
}

/* Le focus se voit deux fois : le contour global de `:focus-visible`, et la
   bordure qui passe à l'accent — la seconde reste quand un navigateur ne
   montre pas le contour d'un champ cliqué. */
.page-coquille input:focus,
.page-coquille select:focus,
.page-coquille textarea:focus,
.champ:focus,
.signalement__champ:focus {
  border-color: var(--accent);
}

/* Les cases : la taille du doigt par leur label, la couleur du site. */
.page-coquille input[type="checkbox"],
.page-coquille input[type="radio"] {
  accent-color: var(--accent);
  flex: none;
  height: 1.35rem;
  margin: 0.15rem 0 0;
  min-height: 0;
  width: 1.35rem;
}

.page-coquille input[type="file"] {
  color: var(--encre);
  padding: 0.5rem 0;
  width: 100%;
}

/* Le bouton du sélecteur de fichier : laissé au navigateur, il restait sous
   la taille d'une cible. */
.page-coquille input[type="file"]::file-selector-button {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  font: inherit;
  margin-right: 0.75rem;
  min-height: var(--cible);
  padding: 0 1.15rem;
}

/* Les boutons nus prennent le dessin de `.bouton`. */
.page-coquille button {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  padding: 0.45rem 1.15rem;
  transition: transform 120ms ease, border-color 120ms ease;
}

.page-coquille button:hover,
.page-coquille button:focus-visible {
  border-color: var(--accent);
}

.page-coquille button:active {
  transform: scale(0.97);
}

/* L'action principale d'un formulaire : l'envoi, et « Accepter ». */
.page-coquille button[type="submit"],
.page-coquille button[data-action="accepter"] {
  background: var(--accent-doux);
  border-color: var(--accent);
  font-weight: 700;
}

/*
 * Le geste qui détruit — supprimer le compte, supprimer un groupe pour de
 * bon : un aplat `--alerte` sous le texte `--fond`, le seul bouton plein des
 * écrans. Le couple tient à 6,2:1. Le préfixe
 * `.page-coquille` l'emporte sur le dessin des boutons nus.
 */
.bouton--danger,
.page-coquille .bouton--danger,
.page-coquille .groupes__confirmation button[data-role="confirmer"] {
  background: var(--alerte);
  border-color: var(--alerte);
  color: var(--fond);
  font-weight: 700;
}

/* Au survol et au focus, un anneau au lieu d'une bordure accent : l'orange
   sur le rouge ne se verrait pas. */
.bouton--danger:hover,
.bouton--danger:focus-visible,
.page-coquille .bouton--danger:hover,
.page-coquille .bouton--danger:focus-visible,
.page-coquille .groupes__confirmation button[data-role="confirmer"]:hover,
.page-coquille .groupes__confirmation button[data-role="confirmer"]:focus-visible {
  border-color: var(--alerte);
  box-shadow: 0 0 0 2px var(--fond), 0 0 0 4px var(--alerte);
}

/* Un bouton désactivé le dit à l'œil, pas seulement au clic. */
.page-coquille button:disabled,
.bouton:disabled {
  cursor: not-allowed;
  opacity: 0.55;
  transform: none;
}

/* ------------------------------------------------------ écrans, formulaires */

/* Un écran : une colonne de blocs, dont un texte long ou une adresse sans
   espace ne fait jamais déborder la page. */
.ecran,
.authentification,
.editeur,
.partage,
.partage__corps,
.groupes,
.groupes__corps,
.proposition,
.propositions,
.reglages,
.avis,
.suspension,
.signalement,
.page-texte {
  min-width: 0;
  overflow-wrap: break-word;
}

.ecran h2,
.partage h2,
.groupes h2,
.reglages h2,
.propositions h2,
.avis h2,
.suspension h2,
.page-texte h2 {
  margin: 1.75rem 0 0.6rem;
}

/* Un formulaire : une pile, l'intitulé au-dessus de son champ. Les champs se
   séparent par la marge de l'intitulé qui suit, pas par un écart uniforme :
   l'intitulé reste collé à son champ. */
.formulaire,
.authentification,
.editeur,
.proposition form,
.signalement__formulaire {
  align-items: stretch;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.formulaire > label,
.authentification > label,
.editeur > label,
.proposition form > label,
.signalement > label,
.champ__label {
  display: block;
  font-weight: 700;
  margin-top: 0.85rem;
}

/* Le dessin de `.etat-lecteur__label`, pour le signalement public. */
.signalement__label {
  display: block;
  font-weight: 700;
  margin: 0 0 0.3rem;
}

.signalement__formulaire > p {
  margin: 0 0 0.85rem;
}

/* Un label qui enveloppe son champ (propositions, signalement de compte) :
   l'intitulé en tête, le champ dessous. */
.proposition form > label,
.signalement > label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.signalement > label {
  margin: 1rem 0;
}

/* Le bouton d'envoi garde sa taille : étiré sur la colonne, il se lirait comme
   un bandeau. */
.formulaire > button,
.authentification > button,
.editeur > button,
.proposition form > button {
  align-self: flex-start;
  margin-top: 1.1rem;
}

.signalement__facultatif {
  color: var(--encre-douce);
  font-weight: 400;
}

.signalement__note {
  color: var(--encre-douce);
}

.signalement__titre {
  margin-bottom: 0.75rem;
}

/* ------------------------------------------------------------------ messages */

/*
 * Les zones de réponse. Elles existent vides dès le rendu (une région live
 * doit précéder son texte pour être annoncée) : vides, elles ne dessinent
 * rien — sans `display: none`, qui les retirerait de l'arbre d'accessibilité.
 */
.message,
.authentification__message,
.editeur__statut,
.editeur__message,
.partage__message,
.groupes__message,
.suspension__message,
.proposition__message,
.propositions__message,
.reglages__message,
.signalement__succes,
.signalement__echec,
.suspension :where([role="status"]),
.signalement :where([role="status"]) {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  margin: 0.75rem 0;
  overflow-wrap: anywhere;
  padding: 0.75rem 1rem;
}

.message:empty,
.authentification__message:empty,
.editeur__statut:empty,
.editeur__message:empty,
.partage__message:empty,
.groupes__message:empty,
.suspension__message:empty,
.proposition__message:empty,
.propositions__message:empty,
.reglages__message:empty,
.signalement__succes:empty,
.signalement__echec:empty,
.suspension :where([role="status"]):empty,
.signalement :where([role="status"]):empty {
  background: none;
  border: 0;
  box-shadow: none;
  margin: 0;
  padding: 0;
}

/* L'échec : la couleur d'alerte, et un filet qui le dit sans la couleur. */
.message--erreur,
.authentification__message,
.editeur__message,
.reglages__message[role="alert"],
.signalement__echec {
  border-left: 4px solid var(--alerte);
  color: var(--alerte);
}

/* Le succès porte une carte, comme un message du site. */
.message--succes,
.editeur__statut,
.signalement__succes {
  border-left: 4px solid var(--avocat);
  box-shadow: var(--ombre-carte);
}

.editeur__message p {
  margin: 0;
}

.editeur__message p + p {
  margin-top: 0.35rem;
}

/* Le repli d'un écran vide ou indisponible : du contenu, pas une erreur (R47),
   au dessin de `.favoris__vide`. */
.coquille__vide {
  background: var(--fond-carte);
  border: 1px dashed var(--trait);
  border-radius: var(--rayon);
  color: var(--encre-douce);
  padding: 1rem 1.15rem;
}

/* -------------------------------------------------------- authentification */

.authentification {
  max-width: 28rem;
}

.authentification__lien {
  margin: 0.75rem 0 0;
}

.authentification__lien a {
  align-items: center;
  display: inline-flex;
  min-height: var(--cible);
}

/* La règle du mot de passe, sous son champ : une aide, pas une alerte. */
.authentification__regle,
.authentification__info {
  color: var(--encre-douce);
  margin: 0.2rem 0 0;
}

.authentification__info:empty {
  margin: 0;
}

/* « Recevoir un nouveau code » : une action secondaire, sous l'envoi. Son
   compte à rebours change de largeur chaque seconde ; il ne tire pas la
   colonne. */
.authentification > .authentification__renvoi {
  align-self: flex-start;
  margin-top: 0.6rem;
}

.authentification__attente {
  color: var(--encre-douce);
  margin: 0.3rem 0 0;
}

.authentification__attente:empty {
  margin: 0;
}

/* ---------------------------------------------------------------- éditeur */

/*
 * Le formulaire de l'éditeur (S3) : `owner-editor-form.ts` et
 * `ingredient-lines-editor.ts`. Chaque champ vit dans un `editeur__groupe`
 * (l'intitulé, le champ, son erreur) ; les ingrédients et les étapes sont des
 * listes de rangées (`editeur__rangee`), chacune un `fieldset` qu'on monte,
 * descend et retire.
 */

/* Les liens de la fiche et du partage, en tête : à hauteur de doigt. */
.editeur__liens {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 0.5rem;
  margin: 0 0 0.25rem;
}

.editeur__liens a {
  align-items: center;
  display: inline-flex;
  min-height: var(--cible);
}

/* Le référentiel qui n'a pas pu se charger : un avertissement, pas une
   erreur de saisie — le filet est celui de l'accent, le texte reste l'encre. */
.editeur__avertissement {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-left: 4px solid var(--accent);
  border-radius: var(--rayon);
  margin: 0.5rem 0;
  padding: 0.75rem 1rem;
}

/* Un champ : l'intitulé au-dessus, le champ, puis son erreur. Au premier
   niveau du formulaire, la marge de tête sépare les champs (le `gap` de
   `.editeur` les collerait). */
.editeur__groupe {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.editeur > .editeur__groupe,
.editeur__photo > .editeur__groupe {
  margin-top: 0.85rem;
}

/* Un nombre de personnes n'a pas besoin de toute la colonne. */
.editeur > .editeur__groupe:has(> input[type="number"]) {
  max-width: 12rem;
}

.editeur__label {
  font-weight: 700;
}

.editeur__facultatif {
  color: var(--encre-douce);
  font-weight: 400;
}

/* Une case et son intitulé sur une ligne : l'intitulé, relié par `for`, porte
   la cible tactile. */
.editeur__groupe:has(> .editeur__case) {
  align-items: center;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0 0.6rem;
}

.editeur__groupe:has(> .editeur__case) > .editeur__label {
  align-items: center;
  cursor: pointer;
  display: flex;
  flex: 1 1 0;
  font-weight: 400;
  min-height: var(--cible);
  min-width: 0;
}

.page-coquille .editeur__case {
  margin: 0;
}

.editeur__groupe:has(> .editeur__case) > .editeur__erreur {
  flex-basis: 100%;
}

/* L'erreur d'un champ : la couleur d'alerte, et un filet qui la dit sans la
   couleur ; le champ fautif prend la même bordure. */
.editeur__erreur {
  border-left: 3px solid var(--alerte);
  color: var(--alerte);
  margin: 0;
  overflow-wrap: anywhere;
  padding-left: 0.5rem;
}

.page-coquille .editeur [aria-invalid="true"] {
  border-color: var(--alerte);
  box-shadow: 0 0 0 1px var(--alerte);
}

/* L'annonce du nombre de suggestions : lue par un lecteur d'écran, absente
   de l'écran — la technique de `.visuellement-cache`. */
.editeur__annonce {
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* Les ingrédients, les étapes, la photo : des sections du formulaire, sans
   cadre — les rangées portent le leur. `min-width: 0` défait le
   `min-content` qu'un `fieldset` impose, qui ferait déborder un téléphone. */
.editeur__ingredients,
.editeur__etapes,
.editeur__photo {
  border: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin: 1.5rem 0 0;
  min-width: 0;
  padding: 0;
}

.editeur__ingredients > legend,
.editeur__etapes > legend,
.editeur__photo > legend {
  font-size: 1.2rem;
  font-weight: 700;
  margin-bottom: 0.4rem;
  padding: 0;
}

/* « Ajouter un ingrédient », « Ajouter une étape », « Ajouter la photo ». */
.editeur__ingredients > button,
.editeur__etapes > button,
.editeur__photo > button {
  align-self: flex-start;
  margin-top: 0.5rem;
}

.editeur__rangees {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.editeur__rangee {
  min-width: 0;
}

/* Une rangée : une carte. L'ingrédient tient toute la ligne (un libellé du
   référentiel peut être long) ; sur un écran large, la quantité et l'unité,
   les deux cases, puis les préparations et la précision vont par paires,
   assez larges pour que l'erreur d'un champ ne se lise pas mot par mot ;
   sous 30 rem, tout s'empile (voir plus bas). */
.editeur__bloc {
  align-items: flex-start;
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 0.75rem;
  margin: 0;
  min-width: 0;
  padding: 0.6rem 1rem 1rem;
}

.editeur__bloc > legend {
  font-weight: 700;
  padding: 0 0.35rem;
}

.editeur__bloc > .editeur__groupe {
  flex: 1 1 100%;
}

.editeur__ingredients .editeur__bloc > .editeur__groupe:has([data-champ="quantity"]),
.editeur__ingredients .editeur__bloc > .editeur__groupe:has([data-champ="unit"]) {
  flex: 1 1 12rem;
}

.editeur__ingredients .editeur__bloc > .editeur__groupe:has(> .editeur__case),
.editeur__ingredients .editeur__bloc > .editeur__groupe:has([data-champ="preparations"]),
.editeur__ingredients .editeur__bloc > .editeur__groupe:has([data-champ="note"]) {
  flex: 1 1 14rem;
}

/* Un champ désactivé (« à volonté » coché) le dit à l'œil. */
.page-coquille .editeur__bloc :is(input, select):disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* Monter, descendre, retirer : sous les champs, à hauteur et largeur de
   doigt ; « Retirer » part à droite, loin des deux autres. */
.editeur__actions-rangee {
  display: flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.editeur__actions-rangee > button {
  min-width: var(--cible);
}

.editeur__actions-rangee > button[data-retirer] {
  margin-left: auto;
}

/* Au bord de la liste, « Monter » ou « Descendre » reste focalisable mais
   sans effet (`aria-disabled`) : il le dit à l'œil. */
.page-coquille .editeur__actions-rangee > button[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.55;
  transform: none;
}

.page-coquille .editeur__actions-rangee > button[aria-disabled="true"]:hover {
  border-color: var(--trait);
}

@media (max-width: 30rem) {
  .editeur__bloc {
    padding: 0.5rem 0.75rem 0.75rem;
  }

  .editeur__ingredients .editeur__bloc > .editeur__groupe,
  .editeur__ingredients .editeur__bloc > .editeur__groupe:is(:has([data-champ]), :has(> .editeur__case)) {
    flex: 1 1 100%;
  }

  /* Les trois boutons se partagent la ligne : aucun ne dépasse, chacun
     garde sa cible. */
  .editeur__actions-rangee > button {
    flex: 1 1 auto;
    padding-left: 0.6rem;
    padding-right: 0.6rem;
  }

  .editeur__actions-rangee > button[data-retirer] {
    margin-left: 0;
  }
}

/* La liste d'autocomplétion d'un ingrédient : sous le champ, par-dessus les
   rangées qui suivent — pousser la page à chaque frappe déplacerait ce que le
   doigt vise. Le dessin de `.autocomplete__liste` ; l'option active porte en
   plus un filet, qui la montre sans la couleur. */
.editeur__groupe:has(> .suggestions) {
  position: relative;
}

.suggestions {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: 0.6rem;
  box-shadow: var(--ombre-carte);
  color: var(--encre);
  left: 0;
  list-style: none;
  margin: 0.25rem 0 0;
  max-height: min(60vh, 18rem);
  overflow-y: auto;
  padding: 0;
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 5;
}

.suggestions:empty {
  display: none;
}

.suggestions > .suggestion {
  overflow-wrap: anywhere;
}

.suggestions > .suggestion:hover {
  background: var(--fond-doux);
}

.suggestions > .suggestion--active,
.suggestions > .suggestion--active:hover {
  background: var(--accent-doux);
  box-shadow: inset 4px 0 0 var(--accent);
  font-weight: 700;
}

/* Publier, puis la photo : hors du formulaire, à la suite, séparés de
   l'envoi par un filet. */
.editeur__publication {
  border-top: 1px solid var(--trait);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.75rem;
  padding-top: 1.25rem;
}

.editeur__photo {
  border-top: 1px solid var(--trait);
  padding-top: 1rem;
}

.editeur__photo > legend {
  float: left;
  width: 100%;
}

/* ------------------------------------------- listes d'adresses et de lignes */

/*
 * Les listes des écrans : accords et partages nominatifs, membres d'un groupe,
 * file des propositions, avis, recettes suspendues. Une adresse électronique
 * est un seul mot pour le navigateur : `overflow-wrap: anywhere` la coupe
 * plutôt que de pousser la page au-delà de l'écran.
 */
.partage__liste,
.groupes__liste,
.groupes__membres,
.propositions ul,
.avis ul,
.suspension ul,
.reglages__liens ul {
  list-style: none;
  margin: 0;
  overflow-wrap: anywhere;
  padding: 0;
}

.partage__ligne,
.groupes__membre,
.avis li,
.suspension li {
  border-top: 1px solid var(--trait);
  padding: 0.6rem 0;
}

.partage__ligne:first-child,
.groupes__membre:first-child,
.avis li:first-child,
.suspension li:first-child {
  border-top: 0;
}

/* Une ligne d'accord, de partage ou de membre : le texte, et « Retirer » au
   bout. Le bouton passe dessous quand la ligne n'a plus la place. */
.partage__ligne,
.groupes__membre {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  justify-content: space-between;
}

.partage__ligne > button,
.groupes__membre > button {
  flex: none;
}

.groupes__membre > span {
  flex: 1 1 12rem;
  min-width: 0;
}

/* Le texte d'appoint d'un écran : un état vide, une légende, une aide sous un
   champ. Il se lit, mais après le reste. */
.partage__vide,
.groupes__vide,
.partage__legende,
.groupes__compte,
.reglages__aide,
.avis__recours,
.suspension__etat {
  color: var(--encre-douce);
}

.partage__legende,
.groupes__compte,
.reglages__aide {
  font-size: 0.9rem;
}

.reglages__aide {
  margin: 0.3rem 0 0;
}

/* Une aide vide (aucune raison de bloquer le bouton) ne laisse pas de trou. */
.reglages__aide:empty {
  margin: 0;
}

/* ------------------------------------------------------- partage, groupes */

/* L'audience dit ce que les accords ouvrent vraiment : elle se lit d'abord. */
.partage__audience {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-left: 4px solid var(--accent);
  border-radius: var(--rayon);
  margin: 0.75rem 0;
  padding: 0.75rem 1rem;
}

.partage__titre,
.groupes__titre {
  overflow-wrap: anywhere;
}

.partage__lien a {
  align-items: center;
  display: inline-flex;
  min-height: var(--cible);
}

/* Accorder, partager, créer un groupe, ajouter une adresse : une rangée sur
   un écran large, l'intitulé au-dessus de chaque champ, le bouton aligné sur
   le bas des champs ; une pile sur un téléphone (requête sous 26 rem). */
.partage__formulaire,
.groupes__formulaire {
  align-items: flex-end;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 0.5rem 0;
}

.partage__champ,
.groupes__champ {
  display: flex;
  flex: 1 1 13rem;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.partage__label,
.groupes__label {
  font-weight: 700;
}

.partage__formulaire > button,
.groupes__formulaire > button {
  flex: none;
}

@media (max-width: 26rem) {
  .partage__formulaire,
  .groupes__formulaire {
    align-items: stretch;
    flex-direction: column;
  }

  .partage__champ,
  .groupes__champ {
    flex: none;
    width: 100%;
  }

  .partage__formulaire > button,
  .groupes__formulaire > button {
    align-self: flex-start;
  }
}

/* Un groupe : une carte, son nom, ses membres, l'ajout, puis la suppression. */
.groupes__groupe {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  margin: 0 0 1rem;
  padding: 0.9rem 1rem 1rem;
}

/* Le nom est un `h2` : le préfixe l'emporte sur la marge des titres d'écran. */
.groupes .groupes__nom {
  margin: 0;
  overflow-wrap: anywhere;
}

.groupes__compte {
  margin: 0.15rem 0 0.5rem;
}

.groupes__intro {
  margin: 0.5rem 0 0;
}

.groupes__vide {
  margin: 0.5rem 0;
}

.groupes__groupe > button[data-role="supprimer-groupe"],
.groupes__groupe > button[data-role="quitter-groupe"] {
  margin-top: 0.75rem;
}

/* Deux sections, les groupes qu'on a créés puis ceux dont on fait partie ; et
   dans un groupe, le petit titre des invitations en attente. */
.groupes .groupes__section {
  margin: 1.5rem 0 0.75rem;
}

.groupes .groupes__sous-titre {
  font-size: 1rem;
  margin: 0.75rem 0 0.25rem;
}

/* La place de la confirmation, vide tant qu'on n'a pas demandé la
   suppression : une ligne de lecture, pas toute la carte. */
.groupes__suppression {
  max-width: 36rem;
}

/* Renommer : le bouton sous le compte, puis le formulaire qu'il ouvre, à la
   même largeur de lecture que la confirmation. Vide, la place ne se voit pas. */
.groupes__groupe > button[data-role="renommer-groupe"] {
  margin-bottom: 0.25rem;
}

.groupes__renommage {
  max-width: 36rem;
}

.groupes__renommage:empty {
  display: none;
}

/* La confirmation d'une suppression : ce qui sera perdu, puis les deux
   gestes. Le filet d'alerte le dit sans la couleur seule. */
.groupes__confirmation {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-left: 4px solid var(--alerte);
  border-radius: var(--rayon);
  margin: 0.75rem 0 0;
  padding: 0.75rem 1rem;
}

.groupes__confirmation p {
  margin: 0 0 0.75rem;
}

.groupes__confirmation button {
  margin: 0.25rem 0.5rem 0 0;
}

/* ------------------------------------------------------------ propositions */

.propositions li {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  margin: 0 0 0.85rem;
  padding: 0.85rem 1rem;
}

.propositions li button {
  margin: 0.5rem 0.5rem 0 0;
}

.proposition__recette {
  font-weight: 700;
  margin: 0;
}

.proposition__champs {
  color: var(--encre-douce);
  font-size: 0.9rem;
  margin: 0.25rem 0 0;
}

.proposition__a-revoir {
  border-left: 4px solid var(--alerte);
  color: var(--alerte);
  margin: 0.5rem 0 0;
  padding-left: 0.6rem;
}

/* « Voir la proposition » : ce que le propriétaire lit avant de trancher. Le
   résumé est une cible à hauteur de doigt ; le contenu, du texte suivi. */
.proposition__detail {
  margin: 0.6rem 0 0;
}

.proposition__detail > summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-weight: 700;
  min-height: var(--cible);
}

.proposition__document {
  margin: 0.25rem 0 0;
}

.proposition__document dt {
  color: var(--encre-douce);
  font-size: 0.9rem;
  font-weight: 700;
  margin-top: 0.6rem;
}

.proposition__document dd {
  margin: 0.15rem 0 0;
}

.proposition__document dd > p,
.proposition__document dd > ol,
.proposition__document dd > ul {
  margin: 0;
}

.proposition__document dd > ol,
.proposition__document dd > ul {
  padding-left: 1.4rem;
}

/* Un champ sans dessin propre, ou le document entier : en JSON, qui revient à
   la ligne au lieu de pousser la page. */
.proposition__brut {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  font-size: 0.85rem;
  margin: 0.25rem 0 0;
  max-height: 24rem;
  overflow: auto;
  padding: 0.6rem 0.8rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Le refus en deux temps : la question, puis la confirmation ou l'annulation. */
.proposition__confirmation {
  border-left: 4px solid var(--alerte);
  margin: 0.6rem 0 0;
  padding-left: 0.6rem;
}

.proposition__confirmation > p {
  margin: 0;
}

/* Les étapes du formulaire : une rangée par étape, son texte et son groupe. */
.proposition__etapes {
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  margin: 0.85rem 0 0;
  min-width: 0;
  padding: 0.5rem 1rem 1rem;
}

.proposition__etapes > legend {
  font-weight: 700;
  padding: 0 0.35rem;
}

.proposition__etapes > ol {
  list-style: none;
  margin: 0;
  padding: 0;
}

.proposition__etape {
  border-top: 1px solid var(--trait);
  padding: 0.5rem 0;
}

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

.proposition__etape > label {
  display: flex;
  flex-direction: column;
  font-weight: 700;
  gap: 0.3rem;
  margin-top: 0.5rem;
}

.proposition__etapes > button {
  margin-top: 0.75rem;
}

/* ----------------------------------------------- réglages et suspension */

/* Une case et son libellé : toute la ligne est cliquable, à hauteur de doigt. */
.reglages label {
  align-items: flex-start;
  cursor: pointer;
  display: flex;
  font-weight: 700;
  gap: 0.65rem;
  min-height: var(--cible);
  padding: 0.55rem 0;
}

/* Un bloc de réglages : séparé du précédent par un filet, pas par une carte —
   seule la zone de suppression en porte une. */
.reglages__bloc {
  border-top: 1px solid var(--trait);
  margin-top: 1.25rem;
  padding-top: 0.25rem;
}

/* Un groupe de choix (Préférences) : un `fieldset` sans cadre de navigateur,
   sa légende dans le rôle d'un titre de bloc. */
.reglages__choix {
  border-width: 1px 0 0;
  margin-inline: 0;
  min-width: 0;
  padding-inline: 0;
}

.reglages__choix legend {
  font-size: var(--texte-l);
  font-weight: 700;
  padding: 0 0.5rem 0 0;
}

/* La mention « Bientôt » d'une ligne de réglage : une pastille, le même mot
   que l'état d'écran de `screen-states.ts`. */
.reglages__bientot {
  background: var(--accent-doux);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  font-size: var(--texte-xs);
  font-weight: 700;
  padding: 0.1rem 0.6rem;
}

/* Le nom affiché, en lecture seule. */
.reglages__nom {
  font-size: var(--texte-l);
  font-weight: 700;
  margin: 0;
}

/* Les liens du compte : des pastilles à hauteur de doigt, qui passent à la
   ligne. */
.reglages__liens {
  margin: 1rem 0 0;
}

.reglages__liens ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.reglages__liens a {
  align-items: center;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  display: inline-flex;
  min-height: var(--cible);
  padding: 0 0.9rem;
  text-decoration: none;
}

.reglages__liens a:hover,
.reglages__liens a:focus-visible {
  background: var(--accent-doux);
}

/* La suppression du compte : une zone à part, bordée d'alerte, qu'on ne
   traverse pas par mégarde. Le filet épais le dit sans la couleur seule. */
.reglages__danger {
  background: var(--fond-carte);
  border: 1px solid var(--alerte);
  border-left-width: 4px;
  border-radius: var(--rayon);
  margin-top: 1.75rem;
  padding: 0 1rem 1rem;
}

.reglages__danger h2 {
  color: var(--alerte);
}

.reglages__danger ul {
  margin: 0.5rem 0;
  padding-left: 1.25rem;
}

/* Ici l'intitulé est relié à son champ par `for` : il se pose au-dessus, sans
   le dessin de ligne des cases. */
.reglages__danger > label {
  display: block;
  margin-top: 0.85rem;
  min-height: 0;
  padding: 0 0 0.3rem;
}

.reglages__danger > .bouton--danger {
  margin-top: 1.1rem;
}

/* Le choix des recettes qui restent partagées : la case, le titre, sa mention.
   Le titre est un lien hors d'un `<label>` (voir `suspension-screen.ts`) : la
   ligne garde la hauteur d'une cible. */
.suspension__choix {
  margin-top: 1.25rem;
}

/* Une grille à deux colonnes : la case à gauche, le titre et sa mention dans
   la colonne du texte. En ligne, un titre long passait sous la case seule. */
.suspension__recettes li {
  align-items: start;
  column-gap: 0.65rem;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  min-height: var(--cible);
  padding: 0.55rem 0;
}

.suspension__recettes li > :not(input) {
  grid-column: 2;
}

.suspension__etat {
  font-size: 0.9rem;
}

.suspension__etat:empty {
  display: none;
}

.suspension [data-role="compte-choix"] {
  color: var(--encre-douce);
  font-weight: 700;
}

.suspension__choix > .bouton {
  margin-top: 0.25rem;
}

/* --------------------------------------------------------------------- avis */

/* Le lien vers l'éditeur, au bout d'une décision : à hauteur de doigt, et
   jamais coupé en deux lignes. */
.avis__editeur {
  align-items: center;
  display: inline-flex;
  min-height: var(--cible);
  white-space: nowrap;
}

.avis__recours {
  font-size: 0.95rem;
  margin: 1rem 0 0;
}

/* La phrase d'un avis : un titre long, écrit par un compte, se coupe plutôt
   que de pousser la ligne hors de l'écran. */
.avis__texte {
  overflow-wrap: anywhere;
}

/* Le titre de la recette, en lien vers sa fiche : il se lit comme le nom
   qu'il est, pas comme un bouton. */
.avis__recette {
  font-weight: 700;
}
/* ------------------------------------------------------------ pages de droit */

/* Les CGU et le contact : du texte suivi, lu d'un bout à l'autre. */
.page-texte__avertissement {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-left: 4px solid var(--soleil);
  border-radius: var(--rayon);
  padding: 0.75rem 1rem;
}

/* ========================================== Système de design B (chantier 1) */

/*
 * La fondation de la direction B, choisie sur le prototype Lightning
 * (`.context/compound-engineering/ce-prototype/2026-10-03-direction-visuelle/`,
 * écrans `0B1` à `0B3`, et `decisions.md`). Ces classes ne sont posées par
 * aucun écran pour l'instant : les unités suivantes du chantier les
 * appliquent à l'app, à la console puis à la façade. Elles ne construisent
 * que sur les jetons de `:root` (ci-dessus) — aucune couleur nouvelle, la
 * seule police reste OpenDyslexic (R16).
 *
 * Trois familles de boutons (R8) étendent `.bouton` (plus haut) plutôt que
 * de le redéfinir : `.bouton--secondaire` en reprend le dessin nu,
 * `.bouton--principal` est son unique aplat par écran, `.bouton--destructif`
 * est le second aplat, réservé au geste qui détruit.
 */

.bouton--secondaire {
  background: var(--fond-doux);
  border-color: var(--trait);
  color: var(--encre);
  font-weight: 600;
}

.bouton--principal {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--encre);
  font-weight: 700;
}

.bouton--destructif {
  background: var(--framboise);
  border-color: var(--framboise);
  color: var(--encre);
  font-weight: 700;
}

.bouton--principal:hover,
.bouton--principal:focus-visible,
.bouton--destructif:hover,
.bouton--destructif:focus-visible {
  border-color: var(--encre);
}

/* --------------------------------------------------------- échelle typographique */

/*
 * Des pas nets (R9). L'interlignage distingue deux étages : les grands
 * titres (xl à 3xl, ~1,42) et les titres plus modestes d'un panneau ou d'une
 * fiche (l et m, ~1,32) — mesurés sur le prototype, parce que les accents
 * d'OpenDyslexic sont hauts et touchent la ligne du dessus si l'interlignage
 * est celui, plus serré, du corps de texte. `xs` et `s` restent du texte
 * courant, pas des titres : ils gardent l'interlignage du corps.
 */
.texte-b-xs {
  font-size: var(--texte-xs);
  line-height: 1.5;
}

.texte-b-s {
  font-size: var(--texte-s);
  line-height: 1.5;
}

.texte-b-m {
  font-size: var(--texte-m);
  font-weight: 700;
  line-height: 1.32;
}

.texte-b-l {
  font-size: var(--texte-l);
  font-weight: 700;
  line-height: 1.32;
}

.texte-b-xl {
  font-size: var(--texte-xl);
  font-weight: 700;
  line-height: 1.42;
}

.texte-b-2xl {
  font-size: var(--texte-2xl);
  font-weight: 700;
  line-height: 1.42;
}

.texte-b-3xl {
  font-size: var(--texte-3xl);
  font-weight: 700;
  line-height: 1.42;
}

/* ------------------------------------------------------------- tuiles d'objet */

/*
 * Une couleur par type d'objet (R7, KTD5) : l'attribut `data-type-objet`
 * associe chaque type à un jeton déjà dans la palette, sur le modèle de
 * `.carte[data-categorie]` plus haut. Un type qui manque retombe sur
 * l'accent du site (la couleur par défaut de `.tuile`), comme une catégorie
 * inconnue d'une carte.
 */
.tuile {
  align-items: center;
  background: var(--accent);
  border-radius: var(--rayon-tuile);
  color: var(--encre);
  display: inline-grid;
  flex: none;
  height: 36px;
  justify-content: center;
  width: 36px;
}

.tuile--grande {
  border-radius: 12px;
  height: 52px;
  width: 52px;
}

.tuile--moyenne {
  border-radius: 7px;
  height: 28px;
  width: 28px;
}

.tuile[data-type-objet="recette"] {
  background: var(--accent);
}

.tuile[data-type-objet="proposition"] {
  background: var(--aubergine);
}

.tuile[data-type-objet="avis"] {
  background: var(--avocat);
}

.tuile[data-type-objet="groupe"] {
  background: var(--turquoise);
}

.tuile[data-type-objet="partage-suspendu"] {
  background: var(--framboise);
}

.tuile[data-type-objet="administration"] {
  background: var(--encre);
  color: var(--fond);
}

/* ---------------------------------------------------------------- fiche à en-tête */

/*
 * L'écran d'objet entier (R6) : la fiche à en-tête, puis le reste du
 * contenu (panneaux, actions), chacun espacé du suivant.
 */
.fiche {
  display: grid;
  gap: var(--pas-4);
  grid-template-columns: minmax(0, 1fr);
}

/*
 * Chaque écran d'objet s'ouvre sur cette fiche (R6) : la tuile, le titre,
 * les actions à droite — une seule principale — puis une rangée de repères
 * chiffrés qui occupe toute la largeur.
 */
.fiche-entete {
  align-items: center;
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  display: grid;
  gap: var(--pas-3) var(--pas-4);
  grid-template-columns: auto minmax(0, 1fr) auto;
  padding: var(--pas-5);
}

/* Sans tuile (R7 ; constat de revue, 2026-10) : la fiche d'authentification
   n'en porte aucune (`auth-forms.ts::ficheEcran`) — une seule colonne, pas
   les trois prévues pour tuile + titre + actions. */
.fiche-entete--sans-tuile {
  grid-template-columns: minmax(0, 1fr);
}

.fiche-entete__titre {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
}

.fiche-entete__meta {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  margin: 0 0 var(--pas-1);
}

.fiche-entete__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.fiche-entete__reperes {
  background: var(--fond);
  border-top: 1px solid var(--trait);
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 0 calc(var(--pas-5) * -1) calc(var(--pas-5) * -1);
}

.fiche-entete__repere {
  border-left: 1px solid var(--trait);
  padding: var(--pas-3) var(--pas-5);
}

.fiche-entete__repere:first-child {
  border-left: 0;
}

.fiche-entete__repere dt {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
}

.fiche-entete__repere dd {
  font-size: var(--texte-l);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  line-height: 1.32;
  margin: 0;
}

@media (max-width: 47.5rem) {
  .fiche-entete {
    grid-template-columns: auto minmax(0, 1fr);
    padding: var(--pas-4);
  }

  .fiche-entete__actions {
    grid-column: 1 / -1;
  }

  .fiche-entete__actions .bouton {
    flex: 1 1 auto;
  }

  .fiche-entete__reperes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: 0 calc(var(--pas-4) * -1) calc(var(--pas-4) * -1);
  }

  .fiche-entete__repere:nth-child(3) {
    border-left: 0;
  }

  .fiche-entete__repere:nth-child(n + 3) {
    border-left: 0;
    border-top: 1px solid var(--trait);
  }
}

/* ---------------------------------------------------------------------- panneaux */

.panneau {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  min-width: 0;
}

.panneau__entete {
  align-items: center;
  border-bottom: 1px solid var(--trait);
  display: flex;
  gap: var(--pas-3);
  min-height: 60px;
  padding: var(--pas-3) var(--pas-4);
}

.panneau__entete-titre {
  font-size: var(--texte-m);
  font-weight: 700;
  line-height: 1.32;
}

.panneau__entete-suite {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
  margin-left: auto;
}

.panneau__corps {
  padding: var(--pas-4);
}

/* ---------------------------------------------------------------- listes, grilles */

/*
 * Une vue liste et une vue grille (R11) : la liste `.liste-b` pour comparer
 * des colonnes, la grille `.grille-b` pour parcourir. Le même jeu de données
 * passe de l'une à l'autre ; c'est l'écran qui affiche l'une, masque
 * l'autre (voir `.carte` plus haut, reprise ici pour la grille).
 */
.liste-b {
  border-collapse: collapse;
  font-size: var(--texte-s);
  width: 100%;
}

.liste-b th {
  border-bottom: 1px solid var(--trait);
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  font-weight: 400;
  padding: var(--pas-2) var(--pas-3);
  text-align: left;
  white-space: nowrap;
}

.liste-b td {
  border-bottom: 1px solid var(--trait);
  padding: var(--pas-3);
  vertical-align: middle;
}

.liste-b tr:last-child td {
  border-bottom: 0;
}

.liste-b .liste-b__numerique {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.grille-b {
  display: grid;
  gap: var(--pas-4);
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
}

/* -------------------------------------------------------------- pastilles de filtre */

.pastille-filtre {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  font-size: var(--texte-xs);
  min-height: 36px;
  padding: 0 var(--pas-3);
}

.pastille-filtre:hover {
  border-color: var(--encre-douce);
}

.pastille-filtre[aria-pressed="true"] {
  background: var(--encre);
  border-color: var(--encre);
  color: var(--fond);
  font-weight: 700;
}

/* ---------------------------------------------------------- chevrons d'avancement */

/*
 * L'avancement d'une recette (R10) : Brouillon → Relue → Prête à partager.
 * Chaque étape est un chevron, découpé par `clip-path` ; la forme est
 * purement décorative (`aria-hidden` sur l'icône le cas échéant), l'état se
 * lit dans le texte et dans `aria-current="step"` sur l'étape courante.
 */
.chemin {
  display: flex;
  gap: 3px;
  list-style: none;
  margin: 0;
  min-width: 0;
  padding: 0;
}

.chemin__etape {
  align-items: center;
  background: var(--fond-doux);
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 50%, calc(100% - 13px) 100%, 0 100%, 13px 50%);
  color: var(--encre-douce);
  display: flex;
  flex: 1;
  font-size: var(--texte-xs);
  font-weight: 700;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
  padding: 0 var(--pas-4);
  height: 38px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chemin__etape:first-child {
  border-radius: 19px 0 0 19px;
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 50%, calc(100% - 13px) 100%, 0 100%);
}

.chemin__etape:last-child {
  border-radius: 0 19px 19px 0;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 13px 50%);
}

.chemin__etape--faite {
  background: var(--avocat);
  color: var(--encre);
}

.chemin__etape--courante {
  background: var(--encre);
  color: var(--fond);
}

/* -------------------------------------------------- rail latéral et barre mobile */

/*
 * Le rail de l'espace connecté et de la console (R2) : groupé par section,
 * un compteur par entrée, repliable sur un appareil large, tiroir superposé
 * sous 900 px. « Administration » vit dans `.rail__administration`, en bas,
 * et n'est rendue par la coquille que pour l'administrateur (U4, U7).
 */
.rail {
  background: var(--fond-carte);
  border-right: 1px solid var(--trait);
  display: flex;
  flex-direction: column;
  gap: var(--pas-4);
  /* Collant sur la hauteur de la zone de travail, sous l'en-tête global
     (60px, `.entete-globale`) : le rail occupe toujours toute la hauteur
     visible, et son pied (« Administration », le repli) reste en bas plutôt
     que de flotter sous un contenu court. */
  align-self: stretch;
  height: calc(100vh - 60px);
  overflow-y: auto;
  padding: var(--pas-4) var(--pas-3) var(--pas-5);
  position: sticky;
  top: 60px;
  width: 272px;
}

.rail--replie {
  width: 76px;
}

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

.rail__groupe-titre {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  font-weight: 400;
  padding: 0 var(--pas-3) var(--pas-1);
}

.rail--replie .rail__groupe-titre {
  display: none;
}

.rail__nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.rail__lien {
  align-items: center;
  border-radius: var(--rayon-controle);
  color: var(--encre);
  display: flex;
  gap: var(--pas-3);
  min-height: var(--cible);
  padding: 0 var(--pas-2);
  position: relative;
  text-decoration: none;
}

.rail__lien:hover {
  background: var(--fond);
}

.rail__lien[aria-current="page"] {
  background: var(--accent-doux);
  font-weight: 700;
}

/* Le filet de couleur qui marque l'entrée active : posé en dehors du rail,
   il ne mord pas sur le texte d'un libellé long. */
.rail__lien[aria-current="page"]::before {
  background: var(--accent-encre);
  border-radius: 0 4px 4px 0;
  bottom: 8px;
  content: "";
  left: -12px;
  position: absolute;
  top: 8px;
  width: 4px;
}

.rail__libelle {
  flex: 1;
  line-height: 1.35;
  min-width: 0;
  padding: 6px 0;
}

.rail--replie .rail__libelle {
  display: none;
}

.rail__compteur {
  background: var(--fond);
  border-radius: var(--rayon-pastille);
  display: inline-grid;
  font-size: var(--texte-xs);
  font-weight: 700;
  height: 20px;
  min-width: 22px;
  place-items: center;
  padding: 0 6px;
}

.rail__lien[aria-current="page"] .rail__compteur {
  background: var(--fond-carte);
}

.rail--replie .rail__compteur {
  display: none;
}

/* « Administration » : isolée en bas par un filet, pas par une carte. */
.rail__administration {
  border-top: 1px solid var(--trait);
  margin-top: auto;
  padding-top: var(--pas-3);
}

.rail__replier {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-controle);
  color: var(--encre-douce);
  cursor: pointer;
  display: flex;
  font-size: var(--texte-xs);
  gap: var(--pas-3);
  min-height: var(--cible);
  padding: 0 var(--pas-2);
  width: 100%;
}

.rail__replier:hover {
  background: var(--fond);
  color: var(--encre);
}

.rail--replie .rail__lien,
.rail--replie .rail__replier {
  justify-content: center;
}

/* En-tête global (R4) : la marque, la recherche, les notifications et le
   compte. Sticky en tête de la zone de travail. */
.entete-globale {
  align-items: center;
  background: var(--fond-carte);
  border-bottom: 1px solid var(--trait);
  display: grid;
  gap: var(--pas-4);
  grid-template-columns: auto minmax(0, 1fr) auto;
  /* `min-height`, pas `height` (constat de revue, 2026-10) : une hauteur figée
     laissait un contenu trop à l'étroit (Connexion/Inscription à 360–390 px)
     déborder **hors** de la boîte plutôt que l'agrandir — recouvrant alors ce
     qui suit, un chevauchement, pas un débordement visible. Une hauteur
     minimale laisse la grille grandir si une rangée a besoin de plus de
     place. */
  min-height: 60px;
  padding: 0 var(--pas-4);
  position: sticky;
  top: 0;
  z-index: 30;
}

.entete-globale__recherche {
  justify-self: center;
  max-width: 35rem;
  position: relative;
  width: 100%;
}

.entete-globale__recherche input {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-controle);
  font-size: var(--texte-s);
  height: 42px;
  padding: 0 4rem 0 2.625rem;
  width: 100%;
}

.entete-globale__recherche-icone {
  color: var(--encre-douce);
  left: var(--pas-3);
  position: absolute;
  top: 50%;
  translate: 0 -50%;
}

/* Le bouton de recherche mobile (U1 — R2, KTD1) : révélé sous le même point de
   rupture qui masque déjà le champ (`.entete-globale__recherche`), le seul
   déclencheur qui reste visible au doigt sous 900 px. Masqué au bureau, où le
   champ fait déjà ce travail — pas de second déclencheur redondant (R2). */
.entete-globale__recherche-mobile {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-controle);
  cursor: pointer;
  display: none;
  justify-self: center;
  min-height: var(--cible);
  min-width: var(--cible);
  position: relative;
}

.entete-globale__recherche-mobile .entete-globale__recherche-icone {
  left: 50%;
  translate: -50% -50%;
}

/* Le raccourci « / », rappelé dans le champ lui-même : une étiquette, pas
   un bouton — un clic dessus ne fait rien de plus qu'un clic sur le champ. */
.entete-globale__recherche-raccourci {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: 4px;
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  padding: 0 6px;
  position: absolute;
  right: var(--pas-2);
  top: 50%;
  translate: 0 -50%;
}

.entete-globale__actions {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
}

/*
 * La barre globale de l'app (U3 — R1, R2, R44) : le lanceur, la marque et le
 * nom de l'app ouverte à gauche ; la recherche au milieu ; « Nouvelle
 * recette », Alertes et le compte à droite. Les boutons nus ont le dessin des
 * boutons de formulaire (`.page-coquille button`) : chaque règle ci-dessous
 * porte `.entete-globale` en préfixe pour l'emporter dessus.
 */
.entete-globale__identite {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
  min-width: 0;
}

/* `.entete__marque` porte `order: 1` (façade, plus haut) : le nom de l'app
   passe donc après elle, et le lanceur, d'ordre 0, avant. */
.entete-globale__app {
  border-left: 1px solid var(--trait);
  order: 2;
  color: var(--encre-douce);
  font-size: var(--texte-m);
  font-weight: 700;
  padding-left: var(--pas-3);
  white-space: nowrap;
}

.lanceur,
.menu-compte {
  position: relative;
}

.entete-globale .lanceur__bouton,
.entete-globale .menu-compte__bouton {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-controle);
  color: var(--encre);
  display: inline-flex;
  height: var(--cible);
  justify-content: center;
  padding: 0;
  width: var(--cible);
}

.entete-globale .lanceur__bouton:hover,
.entete-globale .lanceur__bouton:focus-visible,
.entete-globale .lanceur__bouton[aria-expanded="true"] {
  background: var(--fond-doux);
}

/* La pastille du compte : les initiales, ou une personne si le compte n'a pas de nom. */
.menu-compte__pastille {
  align-items: center;
  background: var(--accent-doux);
  border: 1px solid var(--accent);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  display: inline-flex;
  font-size: var(--texte-s);
  font-weight: 700;
  height: 36px;
  justify-content: center;
  line-height: 1;
  width: 36px;
}

.menu-compte__icone {
  display: inline-flex;
}

/* Les deux panneaux flottent sous leur bouton ; le lanceur s'ouvre vers la
   droite (il est à gauche), le menu du compte vers la gauche. */
.lanceur__panneau,
.menu-compte__panneau {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  padding: var(--pas-2);
  position: absolute;
  top: calc(100% + var(--pas-2));
  z-index: 60;
}

.lanceur__panneau {
  left: 0;
  width: min(21rem, calc(100vw - 2 * var(--pas-4)));
}

.menu-compte__panneau {
  max-width: calc(100vw - 2 * var(--pas-4));
  right: 0;
  width: max-content;
}

.lanceur__tuiles {
  display: grid;
  gap: var(--pas-2);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.lanceur__tuile {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-tuile);
  color: var(--encre);
  display: flex;
  flex-direction: column;
  gap: 2px;
  justify-content: center;
  min-height: 5.5rem;
  padding: var(--pas-3);
  text-decoration: none;
}

.lanceur__tuile:hover,
.lanceur__tuile:focus-visible {
  background: var(--accent-doux);
  border-color: var(--accent);
}

.lanceur__nom {
  font-size: var(--texte-m);
}

.lanceur__description {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  line-height: 1.35;
}

/* Qui est connecté : le nom, sinon l'adresse, en tête du menu. */
.menu-compte__qui {
  border-bottom: 1px solid var(--trait);
  color: var(--encre-douce);
  font-size: var(--texte-s);
  margin: 0 0 var(--pas-2);
  max-width: 18rem;
  overflow-wrap: anywhere;
  padding: var(--pas-2) var(--pas-3);
}

.menu-compte__liste {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

.menu-compte__liste li:last-child {
  border-top: 1px solid var(--trait);
  margin-top: var(--pas-1);
  padding-top: var(--pas-1);
}

.menu-compte__lien,
.page-coquille .menu-compte__lien--bouton {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-controle);
  color: var(--encre);
  cursor: pointer;
  display: flex;
  font: inherit;
  font-size: var(--texte-m);
  min-height: var(--cible);
  padding: 0 var(--pas-3);
  text-align: left;
  text-decoration: none;
  width: 100%;
}

.menu-compte__lien:hover,
.menu-compte__lien:focus-visible,
.page-coquille .menu-compte__lien--bouton:hover,
.page-coquille .menu-compte__lien--bouton:focus-visible {
  background: var(--accent-doux);
}

.menu-compte__lien[aria-current="page"] {
  font-weight: 700;
}

.page-coquille .menu-compte__lien--bouton:disabled {
  cursor: progress;
  opacity: 0.6;
}

/* « + Nouvelle recette » (R44) et Alertes (R4) : le libellé se cache sur
   téléphone sans quitter l'arbre d'accessibilité (`@media` plus bas). */
.entete-globale__nouvelle {
  gap: var(--pas-2);
  white-space: nowrap;
}

.entete-globale__alertes {
  align-items: center;
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  display: inline-flex;
  gap: var(--pas-2);
  min-height: var(--cible);
  min-width: var(--cible);
  justify-content: center;
  padding: 0 var(--pas-3);
  text-decoration: none;
  white-space: nowrap;
}

.entete-globale__alertes:hover,
.entete-globale__alertes:focus-visible {
  background: var(--fond-doux);
}

.entete-globale__compteur {
  background: var(--alerte);
  border-radius: var(--rayon-pastille);
  color: var(--fond-carte);
  display: inline-grid;
  font-size: var(--texte-xs);
  font-weight: 700;
  height: 20px;
  min-width: 22px;
  padding: 0 6px;
  place-items: center;
}

/* `display` l'emporterait sur `hidden` : un compteur à zéro restait affiché,
   et lu (« Alertes0 », test en navigateur du 25/10). */
.entete-globale__compteur[hidden] {
  display: none;
}

/* Les onglets de l'app ouverte (R3) : une barre horizontale sous l'en-tête sur
   un appareil large ; fixée en bas du téléphone sous 900 px (plus bas). */
.barre-app {
  background: var(--fond-carte);
  border-bottom: 1px solid var(--trait);
  padding: 0 var(--pas-4);
}

.barre-app__onglets {
  display: flex;
  gap: var(--pas-1);
}

.barre-app__onglet {
  align-items: center;
  border-bottom: 3px solid transparent;
  color: var(--encre-douce);
  display: inline-flex;
  font-size: var(--texte-m);
  min-height: var(--cible);
  padding: 0 var(--pas-3);
  text-decoration: none;
}

/* L'icône d'un onglet ne sert que sur téléphone (plus bas). */
.barre-app__icone {
  display: none;
}

.barre-app__onglet:hover,
.barre-app__onglet:focus-visible {
  background: var(--fond);
  color: var(--encre);
}

.barre-app__onglet[aria-current="page"] {
  border-bottom-color: var(--accent);
  color: var(--encre);
  font-weight: 700;
}

/* Un onglet que le compte a caché (U20, R42) : `display` de la règle de base
   l'emporterait sur `hidden`. */
.barre-app__onglet[hidden] {
  display: none;
}

/* La barre d'utilitaires de l'Atelier (U22, R43) : le minuteur et les Courses,
   fixés en bas de la page. Les contrôles passent à la ligne plutôt que de
   défiler de côté, de 320 à 375 px. Sur téléphone, elle se pose au-dessus de
   la barre d'onglets (65 px : 64 de hauteur et un trait), sans la masquer. */
.barre-utilitaires {
  align-items: flex-end;
  bottom: var(--pas-2);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--pas-2);
  max-width: calc(100vw - 2 * var(--pas-3));
  position: fixed;
  right: var(--pas-3);
  z-index: 39;
}

/* Repliée : un seul bouton de 44 px. Dépliée : le minuteur et Courses, sur une carte qui passe à la ligne. */
.barre-utilitaires__bascule {
  background: var(--fond-carte);
  max-width: 100%;
  min-height: var(--cible);
}

.barre-utilitaires__panneau {
  align-items: center;
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-controle);
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2) var(--pas-3);
  justify-content: space-between;
  max-width: 100%;
  padding: var(--pas-2) var(--pas-3);
}

.barre-utilitaires__panneau[hidden] {
  display: none;
}

.barre-utilitaires__minuteur {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  min-width: 0;
}

.barre-utilitaires__libelle {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
}

.barre-utilitaires input[type="number"] {
  border: 1px solid var(--trait);
  border-radius: var(--rayon-controle);
  font: inherit;
  min-height: var(--cible);
  padding: 0 var(--pas-2);
  width: 4.5rem;
}

.barre-utilitaires__decompte {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.barre-utilitaires .bouton:disabled {
  cursor: default;
}

/* La page garde de quoi défiler sous le bouton. */
.page-coquille:has(.barre-utilitaires) {
  padding-bottom: 4.5rem;
}

@media (max-width: 56.25rem) {
  .barre-utilitaires {
    bottom: calc(65px + env(safe-area-inset-bottom) + var(--pas-2));
  }

  .page-coquille:has(.barre-utilitaires) {
    padding-bottom: calc(68px + 4.5rem + env(safe-area-inset-bottom));
  }
}

/* Sur téléphone, le rail devient une barre de cinq onglets (R3) : les
   entrées les plus utilisées, puis « Plus » qui ouvre le reste du rail
   dans un tiroir superposé. */
.barre-mobile {
  background: var(--fond-carte);
  border-top: 1px solid var(--trait);
  bottom: 0;
  display: none;
  height: 68px;
  left: 0;
  padding-bottom: env(safe-area-inset-bottom);
  position: fixed;
  right: 0;
  z-index: 40;
}

.barre-mobile__action {
  align-items: center;
  background: none;
  border: 0;
  color: var(--encre-douce);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  font-size: 0.68rem;
  gap: 2px;
  justify-content: center;
  letter-spacing: 0;
  min-width: 0;
  padding: 0 4px;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
}

/* « Plus » est un bouton : sans cette règle, il hériterait de la forme des
   boutons de formulaire (pastille, marge, corps plus grand) et casserait la
   rangée des cinq onglets égaux. */
.barre-mobile button.barre-mobile__action {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  font-family: inherit;
  font-size: 0.68rem;
  letter-spacing: inherit;
  margin: 0;
  min-height: 0;
  padding: 0;
}

.barre-mobile__action[aria-current="page"] {
  color: var(--encre);
  font-weight: 700;
}

/* Le voile qui referme le tiroir d'un clic en dehors de lui : un aplat
   neutre, comme
   l'ombre de `.autocomplete__liste` plus haut. */
.rail__voile {
  background: rgb(0 0 0 / 45%);
  bottom: 0;
  display: none;
  left: 0;
  position: fixed;
  right: 0;
  top: 0;
  z-index: 45;
}

.rail__voile--ouvert {
  display: block;
}

@media (max-width: 56.25rem) {
  .rail {
    bottom: 0;
    box-shadow: var(--ombre-panneau), 0 0 0 1px var(--trait);
    left: 0;
    padding-top: var(--pas-5);
    position: fixed;
    top: 0;
    transition: translate 220ms ease-out;
    translate: -105% 0;
    width: min(86vw, 320px);
    z-index: 50;
  }

  .rail.rail--ouvert {
    translate: 0 0;
  }

  .rail--replie .rail__libelle,
  .rail--replie .rail__compteur,
  .rail--replie .rail__groupe-titre {
    display: revert;
  }

  .rail__replier {
    display: none;
  }

  .entete-globale__recherche {
    display: none;
  }

  .entete-globale__recherche-mobile {
    display: inline-flex;
  }

  /*
   * À 360–390 px, la marque, le bouton de recherche et les actions d'un
   * COMPTE (Notifications, Se déconnecter) ne tiennent plus sur une seule
   * rangée de trois colonnes sans rien couper (constat de revue, 2026-10) :
   * les actions passent alors sur une rangée à elles, pleine largeur —
   * `margin-left: auto` de `.coquille__compte` les aligne à droite, sans
   * chevaucher ce qui précède.
   *
   * **Seulement si la recherche mobile est présente** (`:has()`, retour de
   * revue après capture) : pour un visiteur, cette recherche se cache
   * (`style.css` plus haut, `[data-session]`), et ses deux seules actions
   * (Connexion, Inscription) tiennent sans peine à côté de la marque — les
   * y laisser évite exactement ce que la capture `after/a-connexion-390.png`
   * montrait : la pastille de recherche isolée, « Connexion » seul sur une
   * deuxième ligne, « Inscription » hors champ. Avant que le script pose
   * `hidden` (bref instant, ou JavaScript absent), la recherche est **non**
   * cachée par défaut : ce repli garde alors le même comportement qu'avant
   * cette revue (prudent, pas cassé) — voir `shell-session.ts`, « aucune
   * fuite neuve ».
   */
  .entete-globale:has(.entete-globale__recherche-mobile:not([hidden])) .entete-globale__actions {
    grid-column: 1 / -1;
    padding-top: var(--pas-2);
  }

  /*
   * Le cas inverse — visiteur, recherche cachée (retour de revue, 2026-10) :
   * deux colonnes au lieu de trois (la troisième, « auto », ne portait de
   * toute façon jamais rien ici) et un intervalle resserré, pour que
   * Connexion et Inscription gagnent chacun les pixels que la colonne et le
   * second intervalle leur cédaient sans les utiliser.
   */
  .entete-globale:not(:has(.entete-globale__recherche-mobile:not([hidden]))) {
    column-gap: var(--pas-2);
    grid-template-columns: auto 1fr;
  }

  /*
   * Les deux pastilles de compte resserrent leur rembourrage et leur taille
   * à ce point de rupture (même constat) — un compte y gagne la même
   * économie pour « Se déconnecter », sans rien perdre : cette action est
   * déjà seule sur sa rangée. Mesuré nécessaire à 360 px : Connexion et
   * Inscription, dans la police et le rembourrage d'avant cette revue,
   * mesuraient ensemble 269 px construits, bien plus que les 226 px dont la
   * marque et l'intervalle resserré leur laissaient à cette largeur — d'où
   * un chevauchement sur deux rangées plutôt qu'une, exactement ce que la
   * capture `after/a-connexion-390.png` montrait. 0,85 rem reste une taille
   * de lecture (`--texte-s`), pas un corps réduit à l'illisible.
   */
  .coquille__compte a {
    font-size: var(--texte-s);
    letter-spacing: 0;
    padding: 0 0.3rem;
    white-space: nowrap;
  }

  .coquille__compte {
    gap: 0.25rem 0.3rem;
  }

  /* Chaque onglet prend la largeur de son libellé (« Propositions » est
     trois fois plus long que « Avis ») et l'espace restant se répartit
     entre eux : cinq colonnes égales rognaient le plus long. */
  .barre-mobile {
    display: grid;
    grid-template-columns: repeat(5, auto);
    justify-content: space-evenly;
  }
}

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

/* Le corps de la coquille connectée (U4) : rail et contenu côte à côte, sous
   l'en-tête global. `.page-coquille .contenu` reprend toute la largeur qui
   reste — pas la colonne de lecture étroite de la façade. */
.coquille__corps {
  align-items: stretch;
  display: flex;
}

.page-coquille .contenu {
  flex: 1;
  max-width: none;
  min-width: 0;
}

@media (max-width: 56.25rem) {
  .coquille__corps {
    display: block;
  }
}

/* Le panneau de recherche globale (U5 — R4) : une boîte flottante centrée,
   sur le même patron que la palette du prototype D — plein écran sur
   téléphone (R3, KTD11). */
.recherche-globale[hidden] {
  display: none;
}

.recherche-globale__voile {
  background: rgb(0 0 0 / 45%);
  inset: 0;
  position: fixed;
  z-index: 60;
}

.recherche-globale__boite {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  display: flex;
  flex-direction: column;
  left: 50%;
  max-height: min(34rem, calc(100dvh - 120px));
  overflow: hidden;
  position: fixed;
  top: 12vh;
  translate: -50% 0;
  width: min(36rem, calc(100vw - 32px));
  z-index: 61;
}

.recherche-globale__champ {
  align-items: center;
  border-bottom: 1px solid var(--trait);
  display: flex;
  gap: var(--pas-3);
  min-height: 58px;
  padding: 0 var(--pas-4);
}

.recherche-globale__champ input {
  background: none;
  border: 0;
  flex: 1;
  font-size: var(--texte-l);
  min-height: var(--cible);
  outline: 0;
}

.recherche-globale__liste {
  overflow-y: auto;
  padding: var(--pas-2) 0;
}

.recherche-globale__liste[hidden] {
  display: none;
}

.recherche-globale__item {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: var(--pas-3);
  justify-content: space-between;
  min-height: var(--cible);
  padding: 0 var(--pas-4);
}

.recherche-globale__item[aria-selected="true"] {
  background: var(--fond-doux);
}

.recherche-globale__libelle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.recherche-globale__indice {
  color: var(--encre-douce);
  flex: none;
  font-size: var(--texte-xs);
}

@media (max-width: 56.25rem) {
  .recherche-globale__boite {
    border-radius: 0;
    height: 100%;
    inset: 0;
    max-height: none;
    translate: none;
    width: 100%;
  }
}

/* ------------------------------------------------------------- squelettes */

/*
 * L'état de chargement (R12, KTD7) : un squelette de contenu dans le
 * panneau, jamais la mascotte — elle n'apparaît que pour un écran entier
 * vide ou en erreur (U2). Le balayage est le seul mouvement du squelette,
 * et il s'arrête si le système en demande moins.
 */
/* Le conteneur posé par `renderLoadingSkeleton` (`screen-states.ts`) : les
   pas du squelette l'un sous l'autre, dans le padding du panneau. */
.squelette-ecran {
  display: grid;
  gap: var(--pas-2);
  padding: var(--pas-4);
}

.squelette {
  animation: squelette-balayage 1.4s ease infinite;
  background: linear-gradient(90deg, var(--fond-doux) 25%, var(--trait) 37%, var(--fond-doux) 63%);
  background-size: 400% 100%;
  border-radius: var(--rayon-controle);
}

.squelette--titre {
  height: 1.4rem;
  margin: 0 0 var(--pas-3);
  width: 60%;
}

.squelette--ligne {
  height: 0.9rem;
  margin: 0 0 var(--pas-2);
}

.squelette--ligne:last-child {
  margin-bottom: 0;
  width: 75%;
}

.squelette--tuile {
  border-radius: var(--rayon-tuile);
  height: 36px;
  width: 36px;
}

@keyframes squelette-balayage {
  0% {
    background-position: 100% 50%;
  }

  100% {
    background-position: 0 50%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .squelette {
    animation: none;
    background-position: 50% 50%;
  }
}

/* --------------------------------------------------------- états d'écran (U2) */

/*
 * L'état vide et l'état d'erreur partagent un seul habillage (R12, KTD7) :
 * une pose de la mascotte, un titre, une phrase, une action facultative.
 * Seule l'image change d'un état à l'autre — posée par `screen-states.ts`.
 *
 * Le fond crème des brouillons (une image carrée, non détourée) se fond dans
 * `--fond-doux` par `mix-blend-mode: multiply` : la mascotte s'y découpe sans
 * qu'un détourage ait été refait à la main, comme dans le prototype B
 * (`0B3-lightning-espace.html`, bloc `.vide`). Un dessin illo détouré n'aurait
 * pas besoin de ce traitement, mais il ne le gêne pas non plus : il resterait
 * correct sur un fond déjà transparent.
 */
.etat-ecran {
  display: grid;
  gap: var(--pas-4);
  justify-items: center;
  padding: var(--pas-6) var(--pas-5);
  text-align: center;
}

.etat-ecran__image {
  aspect-ratio: 4 / 3;
  background: var(--fond-doux);
  border-radius: 50% 50% var(--rayon-panneau) var(--rayon-panneau) / 60% 60% var(--rayon-panneau) var(--rayon-panneau);
  overflow: hidden;
  width: 168px;
}

.etat-ecran__image img {
  height: 100%;
  mix-blend-mode: multiply;
  object-fit: cover;
  width: 100%;
}

.etat-ecran__titre {
  font-size: var(--texte-l);
  font-weight: 700;
  line-height: 1.32;
}

.etat-ecran__texte {
  color: var(--encre-douce);
  font-size: var(--texte-s);
  max-width: 34ch;
}

.etat-ecran__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  justify-content: center;
}

/* ------------------------------------- l'accueil et la félicitation (R6, R8, `onboarding.ts`) */

/*
 * Le bandeau d'accueil au premier login, et la félicitation de la première
 * recette : même composant, deux usages. Le premier flotte en tête de page
 * (`document.body.prepend`, `onboarding.ts::mountWelcomeBanner`) ; le second
 * s'insère en tête de la fiche de la recette qui vient d'être créée.
 */
.bandeau-accueil {
  align-items: center;
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  display: flex;
  gap: var(--pas-3);
  margin: var(--pas-3);
  padding: var(--pas-3) var(--pas-4);
}

.bandeau-accueil__image {
  background: var(--fond-doux);
  border-radius: var(--rayon-panneau);
  flex-shrink: 0;
  height: 56px;
  mix-blend-mode: multiply;
  object-fit: cover;
  width: 56px;
}

.bandeau-accueil__texte {
  flex: 1;
  min-width: 0;
}

.bandeau-accueil__titre {
  font-size: var(--texte-m);
  font-weight: 700;
  line-height: 1.32;
}

.bandeau-accueil__fermer {
  flex-shrink: 0;
}

/* --------------------------------------------- vignette d'ingrédient (U2, R14) */

/*
 * L'emplacement réservé d'une future illustration d'ingrédient (R14) : un
 * aplat et un repère discret, jamais un faux dessin. `--reservee` est la
 * seule variante pour l'instant — un écran qui pose une vraie vignette (une
 * photo) garde `.vignette-ingredient` nu et ajoute son `<img>`.
 */
.vignette-ingredient {
  background: var(--fond-doux);
  border-radius: var(--rayon-controle);
  display: inline-grid;
  flex: none;
  height: 36px;
  place-items: center;
  width: 36px;
}

.vignette-ingredient--reservee {
  border: 1px dashed var(--trait);
}

.vignette-ingredient--reservee::after {
  background: var(--trait);
  border-radius: var(--rayon-controle);
  content: "";
  display: block;
  height: 14px;
  width: 14px;
}

/* ------------------------------------------- l'app en option B (revue du 23/10) */

/*
 * Ronan a choisi l'option B le 23/10 : l'app garde son tableau de bord (rail,
 * fiche à en-tête, repères chiffrés, tableau) et prend la signature du site —
 * le bandeau en festons sous l'en-tête, le pied et sa bande, la couleur des
 * catégories. Chaque règle de ce bloc corrige une des onze fautes relevées ce
 * jour-là ; le numéro est rappelé en tête. Tout est borné à `.page-coquille`
 * quand la console partage la classe (`.entete-globale`, `.barre-mobile`).
 */

/* Faute 3 : le bandeau en festons sous l'en-tête de l'app, celui de la façade
   (`.entete::after` plus haut), sur toute la largeur de la grille. Il remplace
   le filet du bas : deux ruptures l'une sous l'autre se liraient en escalier. */
.page-coquille .entete-globale {
  border-bottom: 0;
  row-gap: 0;
}

.page-coquille .entete-globale::after {
  align-self: end;
  background: linear-gradient(
    90deg,
    var(--accent) 0 20%,
    var(--soleil) 20% 40%,
    var(--avocat) 40% 60%,
    var(--turquoise) 60% 80%,
    var(--framboise) 80% 100%
  );
  content: "";
  grid-column: 1 / -1;
  height: 10px;
  margin: 0 calc(var(--pas-4) * -1);
  -webkit-mask: radial-gradient(circle at 8px 10px, transparent 7px, #000 7.5px) 0 0 / 16px 10px
    repeat-x;
  mask: radial-gradient(circle at 8px 10px, transparent 7px, #000 7.5px) 0 0 / 16px 10px repeat-x;
}

/* Faute 11 : chaque lien du pied est une cible de 44 px, plus une ligne de
   texte de 17. Vaut pour la façade comme pour l'app. */
.pied__liens a {
  align-items: center;
  display: inline-flex;
  min-height: var(--cible);
  padding: 0 0.25rem;
}

/* Faute 6 : plus de carré de couleur vide devant les titres. La tuile reste
   dans le balisage des écrans qui la posent encore ; elle ne se dessine plus,
   et la grille perd la colonne qu'elle tenait. */
.fiche-entete > .tuile {
  display: none;
}

.fiche-entete {
  grid-template-columns: minmax(0, 1fr) auto;
}

.fiche-entete--sans-tuile {
  grid-template-columns: minmax(0, 1fr);
}

/* Faute 2 : le résumé sous le titre de la fiche (« Pour 4 personnes ·
   Préparation 10 min · Cuisson 25 min »). */
.fiche-entete__resume {
  color: var(--encre-douce);
  margin: var(--pas-1) 0 0;
}

/* Faute 7 : sur la fiche, les gestes forment leur propre rangée dans la fiche
   d'en-tête, « Je cuisine » en premier. */
.fiche-entete__actions--rangee {
  grid-column: 1 / -1;
}

/* « Je cuisine » est un `<button>` : sans ce préfixe, le dessin des boutons
   nus (`.page-coquille button`) l'emportait et l'action principale avait
   l'air d'une action secondaire. */
.page-coquille button.bouton--principal {
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 700;
}

/* Faute 3 : les repères chiffrés prennent la barre de couleur des cartes, dans
   l'ordre du bandeau. */
.page-coquille .fiche-entete__repere {
  box-shadow: inset 0 4px 0 var(--accent);
}

.page-coquille .fiche-entete__repere:nth-child(2) {
  box-shadow: inset 0 4px 0 var(--soleil);
}

.page-coquille .fiche-entete__repere:nth-child(3) {
  box-shadow: inset 0 4px 0 var(--avocat);
}

.page-coquille .fiche-entete__repere:nth-child(4) {
  box-shadow: inset 0 4px 0 var(--framboise);
}

/* Le retour à la liste, au-dessus de la fiche (parcours de la démo). */
.fiche__retour {
  align-items: center;
  display: inline-flex;
  justify-self: start;
  min-height: var(--cible);
}

/* Le réglage des portions, dans le repère « Pour ». */
.portions {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
}

.page-coquille .portions__pas {
  border-radius: 50%;
  font-weight: 700;
  min-height: var(--cible);
  min-width: var(--cible);
  padding: 0;
}

/* Faute 1 et 2 : la ligne d'ingrédient de l'app — la quantité en gras, le nom,
   les précisions dessous. Une ligne par rangée de 44 px, séparées d'un filet. */
.recette__intertitre {
  font-size: var(--texte-l);
  font-weight: 700;
  margin: 0 0 var(--pas-2);
}

.page-coquille .recette__ingredients {
  list-style: none;
  padding-left: 0;
}

.page-coquille .recette__ingredients .ingredient {
  border-bottom: 1px solid var(--trait);
  display: block;
}

.page-coquille .recette__ingredients .ingredient:last-child {
  border-bottom: 0;
}

.page-coquille .recette .ingredient__quantite {
  color: var(--encre);
  font-weight: 700;
}

/* Le texte de la ligne, que le mode cuisine glisse dans l'étiquette de la case. */
.ingredient__texte {
  overflow-wrap: break-word;
}

.ingredient__precision {
  color: var(--encre-douce);
  display: block;
  font-size: 0.84rem;
}

/* En mode cuisine, la ligne redevient une rangée : la case, puis le texte. */
.page-coquille .recette__ingredients .ingredient:has(.coche) {
  display: flex;
}

/* Faute 2 : « Le mot du chef », la note de la recette, sur le fond doux. */
.recette__mot {
  background: var(--fond-doux);
}

.recette__mot p {
  margin: 0;
}

/* En mode cuisine, l'écran n'est plus qu'à l'étape (retour du fil de test,
   23/10) : le chrome de l'app s'efface — en-tête et son bandeau, rail, barre
   du bas — et la croix de sortie n'est plus recouverte par l'en-tête collant.
   `.recette .panneau.recette__mot` et `.panneau.fiche-entete` l'emportent sur le
   `display: contents` que le mode
   pose sur chaque `.recette .panneau`. */
body.mode-cuisine .recette .panneau.recette__mot,
body.mode-cuisine .recette .panneau.fiche-entete,
body.mode-cuisine .recette .panneau.fiche-recette__entete,
body.mode-cuisine .fiche__retour,
body.mode-cuisine .recette__intertitre,
body.mode-cuisine .entete-globale,
body.mode-cuisine .rail,
body.mode-cuisine .rail__voile,
body.mode-cuisine .barre-mobile,
body.mode-cuisine .barre-app {
  display: none;
}

body.mode-cuisine.page-coquille {
  padding-bottom: 0;
}

/* Un mot long (« croustillant ») passe à la ligne au lieu de sortir d'une
   carte étroite de la grille. */
.page-coquille .grille-b .carte__titre {
  hyphens: auto;
  overflow-wrap: anywhere;
}

/* Faute 9 : deux gestes, deux formes. Les filtres restent des pilules ; le
   choix Liste/Grille devient une bascule rectangulaire (`--rayon-controle`). */
/* La grille de l'app est une liste : sans ce remis à zéro, le retrait par
   défaut d'un `<ul>` décalait les cartes vers la droite. */
.page-coquille .grille-b {
  list-style: none;
  margin: 0;
  padding: 0;
}

.liste-outils {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-3);
  justify-content: space-between;
  margin: 0 0 var(--pas-3);
}

.liste-outils__filtres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.page-coquille .pastille-filtre {
  background: var(--fond-carte);
  font-size: var(--texte-xs);
  min-height: var(--cible);
  padding: 0 var(--pas-3);
}

.page-coquille .pastille-filtre[aria-pressed="true"] {
  background: var(--encre);
  border-color: var(--encre);
  color: var(--fond);
  font-weight: 700;
}

.bascule {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-controle);
  display: inline-flex;
  overflow: hidden;
}

.page-coquille .bascule__option {
  background: none;
  border: 0;
  border-radius: 0;
  font-size: var(--texte-xs);
  min-height: var(--cible);
  padding: 0 var(--pas-3);
}

.page-coquille .bascule__option + .bascule__option {
  border-left: 1px solid var(--trait);
}

.page-coquille .bascule__option[aria-pressed="true"] {
  background: var(--fond-doux);
  box-shadow: inset 0 -3px 0 var(--accent);
  font-weight: 700;
}

/* Faute 3 : la catégorie d'une ligne du tableau, dans la couleur de la barre
   des cartes (`.carte[data-categorie]` plus haut). */
.pastille-categorie {
  --couleur: var(--accent);
  background: var(--couleur);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 0.15rem 0.65rem;
  text-transform: uppercase;
  white-space: nowrap;
}

.pastille-categorie[data-categorie="dessert"] {
  --couleur: var(--framboise);
}

.pastille-categorie[data-categorie="plat-principal"],
.pastille-categorie[data-categorie="plat"] {
  --couleur: var(--avocat);
}

.pastille-categorie[data-categorie="base"] {
  --couleur: var(--soleil);
}

@media (max-width: 56.25rem) {
  /* Faute 4 : l'en-tête tient sur une ligne à 375 px. Le lanceur, la marque,
     la recherche, « + », Alertes et le compte tiennent chacun dans une cible
     de 44 px : les libellés de « Nouvelle recette » et d'Alertes se cachent
     (ils restent le nom accessible du bouton). */
  .entete-globale__nouvelle .entete-globale__libelle,
  .entete-globale__alertes .entete-globale__libelle {
    clip-path: inset(50%);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
  }

  .entete-globale__nouvelle {
    padding: 0;
    width: var(--cible);
  }

  .entete-globale__alertes {
    padding: 0;
    position: relative;
  }

  .entete-globale__compteur {
    font-size: 0.68rem;
    height: 18px;
    min-width: 18px;
    padding: 0 4px;
    position: absolute;
    right: -2px;
    top: 2px;
  }

  /* Le nom de l'app ouverte passe sous la marque : les deux ne tiennent pas
     côte à côte à 375 px. */
  .entete-globale__identite {
    column-gap: var(--pas-2);
    display: grid;
    grid-template-areas: "lanceur marque" "lanceur app";
    grid-template-columns: auto auto;
    justify-content: start;
  }

  .entete-globale__identite > .lanceur {
    grid-area: lanceur;
  }

  .entete-globale__identite > .entete__marque {
    grid-area: marque;
    min-height: 0;
  }

  .entete-globale__app {
    border-left: 0;
    font-size: var(--texte-xs);
    grid-area: app;
    line-height: 1.1;
    padding-left: 0;
  }

  /* Une visite sans compte n'a ni lanceur ni nom d'app : la marque retrouve sa place. */
  .entete-globale__identite:not(:has(.lanceur:not([hidden]))) {
    display: flex;
  }

  .entete-globale__actions {
    gap: var(--pas-1);
  }

  /* Les onglets de l'app ouverte (R3) : fixés en bas du téléphone. */
  .barre-app {
    border-bottom: 0;
    border-top: 1px solid var(--trait);
    bottom: 0;
    left: 0;
    padding: 0 0 env(safe-area-inset-bottom);
    position: fixed;
    right: 0;
    z-index: 40;
  }

  /* Les quatre onglets, toujours visibles d'un coup (décision de Ronan,
     25/10, comme la barre de navigation d'Android) : une icône par onglet, et
     le nom de l'onglet ouvert seul, sous son icône, en 12 px au moins et en
     orange foncé de la palette. Les
     autres noms restent lus par un lecteur d'écran, cachés à la façon de
     `.visuellement-cache`. Les quatre se partagent la largeur à parts égales ;
     un nom tient sur une ligne et n'est jamais coupé : l'onglet ouvert
     s'élargit plutôt. */
  .barre-app__onglets {
    display: flex;
    gap: 0;
    padding: 0 var(--pas-1);
  }

  .barre-app__onglet {
    border-bottom: 0;
    border-top: 3px solid transparent;
    flex: 1 1 0;
    flex-direction: column;
    font-size: 0.75rem;
    gap: 2px;
    hyphens: manual;
    justify-content: center;
    line-height: 1.25;
    min-height: 64px;
    min-width: min-content;
    overflow-wrap: normal;
    padding: 0 var(--pas-1);
    position: relative;
    text-align: center;
  }

  .barre-app__icone {
    display: block;
    fill: none;
    flex: none;
    height: 24px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
    width: 24px;
  }

  .barre-app__libelle {
    white-space: nowrap;
  }

  .barre-app__onglet:not([aria-current="page"]) .barre-app__libelle {
    clip-path: inset(50%);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
  }

  .barre-app__onglet[aria-current="page"] {
    color: var(--accent-encre);
  }

  .barre-app__onglet[aria-current="page"] .barre-app__icone {
    stroke-width: 2.2;
  }

  .barre-app__onglet[aria-current="page"] {
    border-top-color: var(--accent);
  }

  .page-coquille .entete-globale:has(.entete-globale__recherche-mobile:not([hidden])) .entete-globale__actions {
    grid-column: auto;
    padding-top: 0;
  }

  .page-coquille .entete-globale {
    column-gap: var(--pas-2);
  }

  /* La marque est un lien : une cible de 44 px de haut, comme les autres. */
  .page-coquille .entete__marque {
    align-items: center;
    display: inline-flex;
    font-size: 1.1rem;
    min-height: var(--cible);
  }

  /* Faute 7, sur téléphone : « Je cuisine » prend toute la rangée, les autres
     gestes se partagent les suivantes. */
  .fiche-entete__actions--rangee .bouton--principal {
    flex-basis: 100%;
  }

  /* Sur téléphone, le tableau garde le titre et la durée : trois colonnes ne
     tiennent pas dans 375 px. La couleur de catégorie reste sur les cartes
     de la grille. */
  .page-coquille .liste-b th:nth-child(2),
  .page-coquille .liste-b td:nth-child(2) {
    display: none;
  }

  .page-coquille .liste-b .liste-b__numerique {
    white-space: nowrap;
  }

  /* La grille sur téléphone (retour de Ronan, 23/10) : deux cartes compactes
     par rangée, au lieu d'une seule carte décalée par le retrait de liste. */
  .page-coquille .grille-b {
    gap: var(--pas-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .page-coquille .grille-b .carte__corps {
    padding: var(--pas-2) var(--pas-3) var(--pas-3);
  }

  .page-coquille .grille-b .carte__titre {
    font-size: 0.9rem;
  }

  /* Faute 5 : des libellés d'au moins 0,84 rem dans la barre du bas. */
  .page-coquille .barre-mobile__action,
  .page-coquille .barre-mobile button.barre-mobile__action {
    font-size: 0.84rem;
  }

  /* Le pied ne passe plus sous la barre fixe du bas. */
  .page-coquille {
    padding-bottom: calc(68px + env(safe-area-inset-bottom));
  }

  /* Faute 6, suite : sur téléphone aussi, la fiche perd la colonne de la tuile. */
  .fiche-entete {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --------------------------------------------------- fin du système de design B */

/* ------------------------------------------------------------ Accueil à widgets */

/*
 * L'Accueil (U19 — R11 à R16) : une grille de widgets sous la fiche d'en-tête.
 * Au téléphone, deux colonnes : une vue « petite » prend une cellule, « moyenne »
 * comme « liste » toute la largeur, et une petite seule sur sa rangée la prend
 * entière (`widget--seule`, posé par `home-screen.ts`). Dès 56,25 rem — là où les
 * onglets quittent le bas de l'écran — quatre colonnes : petite 1, moyenne et
 * liste 2. Chaque colonne est `minmax(0, 1fr)` : rien ne force une largeur, donc
 * rien ne défile de côté entre 320 et 1280 px. Aucune couleur neuve.
 *
 * Les boutons de cette section portent `.page-coquille` : sans lui, la règle
 * des boutons nus (`.page-coquille button`, plus haut) l'emporte sur leur
 * rembourrage, leur fond et leur bordure.
 */
.accueil {
  align-content: start;
}

/* Sur grand écran, la fiche ne court pas sur toute la largeur de la coquille
   (`.page-coquille .contenu` n'a pas de largeur maximale). */
.page-coquille .accueil {
  margin-inline: auto;
  max-width: 64rem;
  width: 100%;
}

.accueil__galerie-hote:empty {
  display: none;
}

/* La ligne de l'en-tête : le titre et l'icône de réglages, ou, en édition,
   « + Ajouter » et « Terminé ». Elle garde la hauteur d'une cible tactile dans
   les deux états, pour que l'en-tête ne saute pas en entrant ni en sortant. */
.accueil__ligne {
  min-height: var(--cible);
}

.accueil__titre-ligne,
.accueil__edition {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
  min-height: var(--cible);
}

/* En édition, le titre reste ; « + Ajouter » et « Terminé » le suivent sur sa
   ligne (« Terminé » à droite), ou passent dessous quand elle ne suffit pas. */
.accueil__titre-ligne {
  flex-wrap: wrap;
}

.accueil__edition {
  flex: 1 1 15rem;
  justify-content: space-between;
}

/* Hors édition, le seul geste visible : une icône grise, cible de 44 px. */
.page-coquille .accueil__icone {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-tuile);
  box-sizing: border-box;
  color: var(--encre-douce);
  cursor: pointer;
  display: inline-flex;
  flex: none;
  height: var(--cible);
  justify-content: center;
  min-height: var(--cible);
  min-width: var(--cible);
  padding: 0;
  width: var(--cible);
}

.accueil__icone svg {
  height: 22px;
  width: 22px;
}

.page-coquille .accueil__icone:hover {
  background: var(--fond-doux);
  color: var(--encre);
}

.page-coquille .accueil__pastille {
  align-items: center;
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  display: inline-flex;
  font-size: var(--texte-s);
  min-height: var(--cible);
  padding: 0 var(--pas-4);
}

.page-coquille .accueil__pastille:hover {
  border-color: var(--accent);
}

.page-coquille .accueil__pastille--forte {
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 700;
}

.page-coquille .accueil__lien {
  background: none;
  border: 0;
  color: var(--accent-encre);
  cursor: pointer;
  font-weight: 700;
  min-height: var(--cible);
  min-width: var(--cible);
  padding: 0 var(--pas-2);
}

.accueil__message {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-left: 4px solid var(--alerte);
  border-radius: var(--rayon);
  color: var(--alerte);
  margin: 0;
  overflow-wrap: anywhere;
  padding: 0.75rem 1rem;
}

.accueil__message:empty {
  background: none;
  border: 0;
  padding: 0;
}

.accueil__widgets {
  display: grid;
  gap: var(--pas-3);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 40rem) {
  .accueil__widgets {
    gap: var(--pas-4);
  }
}

.accueil__astuce {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  margin: 0;
  text-align: center;
}

.widget--moyenne,
.widget--liste,
.widget--seule {
  grid-column: 1 / -1;
}

/* Le widget choisi montre ses tailles : une petite prend alors toute la rangée
   (deux colonnes au téléphone, deux sur grand écran) pour que la pastille tienne. */
.widget--petite.widget--choisi {
  grid-column: 1 / -1;
}

/* Grand écran : quatre colonnes ; `widget--seule` (deux colonnes) ne joue plus.
   L'ordre à l'écran reste celui de la disposition : pas de `dense`. */
@media (min-width: 56.25rem) {
  .accueil__widgets {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .widget--petite,
  .widget--seule {
    grid-column: span 1;
  }

  .widget--moyenne,
  .widget--liste,
  .widget--petite.widget--choisi {
    grid-column: span 2;
  }
}

.widget {
  align-content: start;
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  display: grid;
  gap: var(--pas-3);
  /* Une colonne bornée : sans elle, un contenu qui ne se replie pas (la pastille
     des tailles) élargirait la carte au-delà de sa cellule. */
  grid-template-columns: minmax(0, 1fr);
  min-width: 0;
  padding: var(--pas-4);
  position: relative;
}

/* Une carte qui a une cible évidente (son lien unique, la page des favoris). */
.widget--ouvrante {
  cursor: pointer;
}

/* Un appui long sur un lien ne doit pas ouvrir l'aperçu du navigateur : il entre en édition. */
.accueil__widgets .widget {
  -webkit-touch-callout: none;
}

/* L'état vide d'une petite vue : une ligne discrète, pas un paragraphe. */
.widget__vide-court {
  color: var(--encre-douce);
  font-size: var(--texte-s);
}

.widget__entete {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2) var(--pas-3);
}

.widget__titre {
  font-size: var(--texte-l);
  font-weight: 700;
  line-height: 1.32;
  margin: 0;
  overflow-wrap: anywhere;
}

.widget__corps {
  display: grid;
  gap: var(--pas-3);
  justify-items: start;
  min-width: 0;
}

.widget__corps > * {
  max-width: 100%;
  min-width: 0;
}

.widget__corps p {
  margin: 0;
  overflow-wrap: anywhere;
}

.widget__tirage,
.widget__cartes {
  width: 100%;
}

.widget__cartes {
  grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.widget__plat {
  font-size: var(--texte-l);
  font-weight: 700;
  overflow-wrap: anywhere;
}

.widget--moyenne .widget__plat {
  font-size: var(--texte-xl);
}

.widget__plat-meta {
  color: var(--encre-douce);
  font-size: var(--texte-s);
}

/* Une petite vue tient dans une demi-largeur de téléphone : un peu moins de marge. */
.widget--petite {
  padding: var(--pas-3);
}

.widget__nombre {
  font-size: var(--texte-l);
  font-weight: 700;
}

/* Le nombre de « Mes favoris » mène à la liste : une cible entière, au doigt. */
a.widget__nombre {
  align-items: center;
  display: inline-flex;
  min-height: var(--cible);
}

.widget__liens {
  display: grid;
  gap: var(--pas-1);
  list-style: none;
  margin: 0;
  padding: 0;
}

.widget__liens a {
  display: inline-block;
  min-height: var(--cible);
  overflow-wrap: anywhere;
  padding-block: var(--pas-2);
}

/* La mention « Bientôt » d'un widget : le même dessin que celle des réglages. */
.widget__bientot {
  background: var(--accent-doux);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  font-size: var(--texte-xs);
  font-weight: 700;
  padding: 0.1rem 0.6rem;
}

/* L'édition : un contour pointillé orange, sans ombre, sans tremblement. Le
   widget se focalise ; choisi, son contour devient plein et ses tailles
   paraissent. Les 2 px de bordure remplacent 1 px : le rembourrage perd 1 px
   pour que rien ne bouge. */
.widget--edition {
  border: 2px dashed var(--accent);
  box-shadow: none;
  cursor: grab;
  padding: calc(var(--pas-4) - 1px);
  user-select: none;
  -webkit-user-select: none;
}

.widget--petite.widget--edition {
  padding: calc(var(--pas-3) - 1px);
}

.widget--edition:hover {
  background: var(--fond);
}

.widget--choisi {
  background: var(--fond-carte);
  border-style: solid;
}

.widget--saisi {
  cursor: grabbing;
  opacity: 0.5;
}

/* En édition, l'en-tête porte la poignée avant le titre et « Retirer » après.
   Poignée et « Retirer » sont compacts à l'œil et gardent leur cible de 44 px
   par un pseudo-élément : le titre prend la place qui reste. Dans une petite
   carte, ils tiennent une rangée à eux et le titre a toute la largeur dessous. */
.widget--edition .widget__entete {
  gap: var(--pas-1) var(--pas-2);
}

.widget--edition .widget__titre {
  flex: 1 1 10rem;
  min-width: 0;
}

/* La mention « Bientôt » passe sous la rangée du titre : elle ne lui prend pas de largeur. */
.widget--edition .widget__bientot {
  order: 4;
}


.widget--petite.widget--edition .widget__titre {
  flex-basis: 100%;
  order: 3;
}

.widget__poignee {
  align-items: center;
  color: var(--encre-douce);
  display: inline-flex;
  flex: none;
  font-size: var(--texte-m);
  height: 28px;
  justify-content: center;
  position: relative;
  /* Au doigt, la poignée glisse : le navigateur ne doit pas y voir un défilement. */
  touch-action: none;
  width: 18px;
}

.widget__poignee::after {
  content: "";
  inset: -8px -13px;
  position: absolute;
}

.page-coquille .widget__retirer {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-tuile);
  color: var(--encre-douce);
  cursor: pointer;
  display: inline-flex;
  flex: none;
  font-size: var(--texte-xs);
  justify-content: center;
  margin-inline-start: auto;
  min-height: 28px;
  min-width: 0;
  padding: 0 var(--pas-1);
  position: relative;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.page-coquille .widget__retirer::after {
  content: "";
  inset: -8px -8px;
  position: absolute;
}

.page-coquille .widget__retirer:hover {
  background: var(--fond-doux);
  color: var(--accent-encre);
}

.page-coquille .widget__retirer[aria-disabled="true"] {
  cursor: default;
  opacity: 0.45;
  text-decoration: none;
}

.page-coquille .widget__retirer[aria-disabled="true"]:hover {
  background: none;
  color: var(--encre-douce);
}

/* Les tailles du widget choisi : une pastille segmentée. */
.widget__tailles {
  background: var(--fond-doux);
  border-radius: var(--rayon-pastille);
  display: flex;
  flex-wrap: nowrap;
  gap: 2px;
  justify-self: start;
  max-width: 100%;
  padding: 3px;
}

.page-coquille .widget__taille {
  background: none;
  border: 0;
  border-radius: var(--rayon-pastille);
  cursor: pointer;
  font-size: var(--texte-s);
  min-height: var(--cible);
  min-width: 0;
  padding: 0 var(--pas-2);
}

.page-coquille .widget__taille[aria-pressed="true"] {
  background: var(--fond-carte);
  box-shadow: var(--ombre-panneau);
  font-weight: 700;
}

/* Le message de retrait, hors de la grille : en bas de l'écran, au-dessus des
   onglets du téléphone (65 px : 64 de hauteur et un trait, voir `.barre-app`) ;
   en bas à droite quand les onglets sont dans l'en-tête. */
.accueil__toast {
  align-items: center;
  background: var(--encre);
  border-radius: var(--rayon-tuile);
  bottom: calc(65px + env(safe-area-inset-bottom) + var(--pas-3));
  color: var(--fond);
  display: flex;
  font-size: var(--texte-s);
  gap: var(--pas-2);
  justify-content: space-between;
  left: var(--pas-4);
  padding: var(--pas-1) var(--pas-2) var(--pas-1) var(--pas-4);
  position: fixed;
  right: var(--pas-4);
  z-index: 50;
}

.accueil__toast[hidden] {
  display: none;
}

.accueil__toast-texte {
  overflow-wrap: anywhere;
}

.page-coquille .accueil__toast-annuler {
  background: none;
  border: 0;
  color: var(--soleil);
  cursor: pointer;
  font-weight: 700;
  min-height: var(--cible);
  min-width: var(--cible);
  padding: 0 var(--pas-3);
}

.page-coquille .accueil__toast-annuler[hidden] {
  display: none;
}

@media (min-width: 56.25rem) {
  .accueil__toast {
    bottom: var(--pas-5);
    left: auto;
    right: var(--pas-5);
    width: 21rem;
  }
}

/* La galerie : un dialogue modal. Une feuille en bas de l'écran au téléphone,
   au centre d'un grand écran, sous la bande de festons de la charte (la règle de
   l'en-tête, plus haut), devant un voile. Posée au-dessus des onglets du
   téléphone (z-index 40) et du reste de la coquille. */
.accueil__calque {
  inset: 0;
  position: fixed;
  z-index: 80;
}

.accueil__voile {
  background: rgb(0 0 0 / 45%);
  inset: 0;
  position: absolute;
}

.accueil__galerie {
  background: var(--fond-carte);
  border-radius: var(--rayon-panneau) var(--rayon-panneau) 0 0;
  bottom: 0;
  box-shadow: var(--ombre-panneau);
  display: flex;
  flex-direction: column;
  left: 0;
  max-height: 84dvh;
  overflow: hidden;
  position: absolute;
  right: 0;
}

.accueil__galerie::before {
  flex: none;
}

.accueil__galerie-corps {
  display: grid;
  gap: var(--pas-3);
  overflow-y: auto;
  padding: var(--pas-4) var(--pas-4) calc(var(--pas-4) + env(safe-area-inset-bottom));
}

@media (min-width: 48rem) {
  .accueil__galerie {
    border-radius: var(--rayon-panneau);
    bottom: auto;
    left: 50%;
    max-height: 85dvh;
    right: auto;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(40rem, calc(100% - 2 * var(--pas-4)));
  }
}

@media (prefers-reduced-motion: no-preference) {
  .accueil__voile {
    animation: accueil-voile 0.2s ease;
  }

  .accueil__galerie {
    animation: accueil-feuille 0.22s ease;
  }

  @media (min-width: 48rem) {
    .accueil__galerie {
      animation: accueil-voile 0.2s ease;
    }
  }
}

@keyframes accueil-voile {
  from {
    opacity: 0;
  }
}

@keyframes accueil-feuille {
  from {
    transform: translateY(100%);
  }
}

.accueil__galerie-tete {
  align-items: center;
  display: flex;
  gap: var(--pas-3);
  justify-content: space-between;
}

.accueil__galerie-titre {
  font-size: var(--texte-l);
  margin: 0;
}

.accueil__galerie-titre:focus {
  outline: none;
}

.galerie {
  display: grid;
  gap: var(--pas-3);
  grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.galerie__element {
  align-content: start;
  background: var(--fond-doux);
  border-radius: var(--rayon-tuile);
  display: grid;
  gap: var(--pas-2);
  justify-items: start;
  min-width: 0;
  padding: var(--pas-3);
}

.galerie__entete {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.galerie__titre {
  font-size: var(--texte-m);
  margin: 0;
}

.galerie__description {
  font-size: var(--texte-s);
  margin: 0;
}

.page-coquille .galerie__ajouter {
  background: var(--accent-doux);
  border: 1px solid var(--accent);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  cursor: pointer;
  font-size: var(--texte-s);
  font-weight: 700;
  min-height: var(--cible);
  padding: 0 var(--pas-4);
  white-space: nowrap;
}

.page-coquille .galerie__ajouter:hover {
  background: var(--accent);
}

/* Un widget « Bientôt » : le bouton reste actif, mais calme. */
.page-coquille .galerie__ajouter--calme {
  background: none;
  border: 1px dashed var(--trait);
  color: var(--encre-douce);
  font-weight: 400;
}

.page-coquille .galerie__ajouter--calme:hover {
  background: none;
  border-color: var(--accent);
  color: var(--encre);
}

.galerie__deja {
  align-items: center;
  color: var(--turquoise-encre);
  display: inline-flex;
  font-size: var(--texte-s);
  min-height: var(--cible);
}

.accueil__galerie-pied {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  justify-content: center;
}

.page-coquille .accueil__discret {
  background: none;
  border: 0;
  color: var(--encre-douce);
  cursor: pointer;
  font-size: var(--texte-s);
  min-height: var(--cible);
  padding: 0 var(--pas-3);
  text-decoration: underline;
}

/* La question de la remise, dans le pied de la galerie. */
.accueil__question {
  flex-basis: 100%;
  font-size: var(--texte-s);
  margin: 0;
  text-align: center;
}

/* ======================================================================
   PR 2 de l'interface : la fiche refaite (U11, U23), le Livre (U10) et
   Chercher (U13). Aucune couleur nouvelle : les variables du site.
   ====================================================================== */

/* ---------------------------------------------------------- la fiche recette */

/*
 * Un seul cadre : la photo, le titre, les critères, la barre d'actions et les
 * tuiles forment l'en-tête ; dessous, ingrédients et étapes se partagent le même
 * panneau (R24, R25).
 */
.fiche-recette {
  gap: var(--pas-4);
}

.fiche-recette__entete {
  overflow: visible;
}

.fiche-recette__entete .recette__image {
  margin: 0;
}

.fiche-recette__entete .recette__image img {
  border-radius: var(--rayon-panneau) var(--rayon-panneau) 0 0;
  max-height: 22rem;
  object-fit: cover;
}

.fiche-recette__tete {
  display: grid;
  gap: var(--pas-3);
  padding: var(--pas-5) var(--pas-5) var(--pas-4);
}

.fiche-recette__tete .fiche-entete__titre,
.fiche-recette__tete .criteres {
  margin: 0;
}

.fiche-recette__parent {
  color: var(--encre-douce);
  margin: 0;
}

/* Les allergènes de l'app : un seul bloc, en texte droit (l'italique et le
   pointillé se lisaient mal en OpenDyslexic). Ce qui reste à vérifier suit, sur
   sa propre ligne ; une longue liste se replie. */
.fiche-recette__tete .critere--allergenes {
  border-radius: var(--rayon-panneau);
  flex-basis: 100%;
  padding: var(--pas-2) var(--pas-3);
}

.critere--allergenes .critere__suite {
  display: block;
  margin-top: var(--pas-1);
}

.critere--allergenes details p {
  margin: var(--pas-1) 0 0;
}

.critere--allergenes summary {
  cursor: pointer;
}

.critere__voir {
  text-decoration: underline;
}

/* Les saisons « Bientôt » : visibles, sans case. */
.fiche-recette__tete .critere--discret {
  background: none;
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  padding: 0.25rem 0;
}

/* Sur téléphone, l'en-tête doit laisser voir des ingrédients sans défiler :
   photo plus basse, marges et boutons resserrés. */
@media (max-width: 36rem) {
  .fiche-recette__entete .recette__image img {
    max-height: 7.5rem;
  }

  /* Catégorie et saisons partagent une ligne, sous le bloc des allergènes. */
  .fiche-recette__tete .criteres .critere:not(.critere--allergenes) {
    padding: 0.1rem 0.6rem;
  }

  /* Le corps de la fiche commence plus haut : moins de marge au-dessus des ingrédients. */
  .fiche-recette {
    gap: var(--pas-2);
  }

  .fiche-recette .recette__colonnes {
    padding-top: var(--pas-3);
  }

  .fiche-recette .recette__intertitre {
    margin-bottom: var(--pas-1);
  }

  .fiche-recette__tete {
    gap: var(--pas-2);
    padding: var(--pas-3) var(--pas-4);
  }

  .fiche-recette__tete .criteres {
    font-size: var(--texte-s);
    gap: var(--pas-1);
  }


  /* Barre d'outils façon iOS : chaque geste en icône seule, cible de 44 px,
     et la barre ne dépasse jamais la largeur de l'écran. */
  .page-coquille .fiche-recette__tete .fiche-actions .bouton {
    justify-content: center;
    min-height: var(--cible);
    min-width: var(--cible);
    padding: 0 var(--pas-2);
  }

  .fiche-recette__tete .fiche-actions {
    max-width: 100%;
    min-width: 0;
  }

  .fiche-actions__mot {
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
  }

  .fiche-actions__icone {
    font-size: var(--texte-l);
  }

  /* L'encadré des allergènes reste dans la largeur, même avec un long nom. */
  .fiche-recette__tete .critere--allergenes {
    box-sizing: border-box;
    max-width: 100%;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* Une seule ligne de gestes : Partager et Commentaires passent dans « … ». */
  .fiche-recette__tete .fiche-actions {
    flex-wrap: nowrap;
    gap: var(--pas-1);
  }

  .page-coquille .fiche-recette__tete .fiche-actions__large {
    display: none;
  }

  .fiche-recette__tete .fiche-entete__titre {
    font-size: var(--texte-xl);
    line-height: 1.2;
  }
}


.fiche-recette__entete .fiche-entete__reperes {
  border-radius: 0 0 var(--rayon-panneau) var(--rayon-panneau);
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  margin: 0;
}

/* Le nombre de personnes se règle dans sa tuile : − nombre unité +. */
.portions__unite {
  font-size: var(--texte-s);
}

/* Sur téléphone, « − 4 personnes + » ne tient pas dans une demi-largeur (deux
   boutons de 44 px, en OpenDyslexic) : la tuile prend toute la rangée au lieu de
   déborder sur sa voisine. */
@media (max-width: 36rem) {
  .fiche-recette__entete .fiche-entete__repere--portions {
    grid-column: 1 / -1;
  }

  /* Les tuiles qui suivent repartent en deux colonnes : filet en haut, et à
     gauche seulement pour celle de droite (la parité se décale d'un cran). */
  .fiche-recette__entete .fiche-entete__repere--portions ~ .fiche-entete__repere {
    border-left: 1px solid var(--trait);
    border-top: 1px solid var(--trait);
  }

  .fiche-recette__entete .fiche-entete__repere--portions ~ .fiche-entete__repere:nth-child(even) {
    border-left: 0;
  }

  .fiche-recette__entete .fiche-entete__repere {
    padding: var(--pas-1) var(--pas-3);
  }

  /* Les tuiles serrées : libellé et valeur sur une ligne, valeur moins haute. */
  .fiche-recette__entete .fiche-entete__repere:not(.fiche-entete__repere--portions) {
    align-items: baseline;
    column-gap: var(--pas-1);
    display: flex;
    flex-wrap: wrap;
  }

  /* Une valeur ne se coupe jamais (« 1 h 45 ») : faute de place, c'est le
     libellé qui passe au-dessus. */
  .fiche-recette__entete .fiche-entete__repere dd {
    font-size: var(--texte-m);
    margin: 0;
    white-space: nowrap;
  }
}

/* Ingrédients à gauche, collants au défilement ; étapes à droite. Sur téléphone,
   les ingrédients puis les étapes, en une colonne. */
.recette__colonnes {
  display: grid;
  gap: var(--pas-5);
  grid-template-columns: minmax(0, 1fr);
}

.recette__colonnes .recette__section {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 56.25rem) {
  .recette__colonnes {
    align-items: start;
    grid-template-columns: minmax(16rem, 22rem) minmax(0, 1fr);
  }

  .recette__colonnes > .recette__section--ingredients {
    max-height: calc(100dvh - 60px - 2 * var(--pas-4));
    overflow-y: auto;
    position: sticky;
    top: calc(60px + var(--pas-4));
  }
}

/* « Je cuisine » en tête des étapes : le titre à gauche, le bouton à droite. */
.recette__section--etapes > .recette__entete-section {
  align-items: center;
  margin-bottom: var(--pas-2);
}

/* Un groupe par recette associée (R54) : son titre est un lien vers sa fiche. */
.ingredients-groupe + .ingredients-groupe {
  margin-top: var(--pas-4);
}

.ingredients-groupe__titre {
  font-size: var(--texte-m);
  font-weight: 700;
  margin: 0;
}

.ingredients-groupe__mention {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  margin: var(--pas-1) 0 0;
}

.ingredients-groupe--propre {
  border-top: 1px solid var(--trait);
  padding-top: var(--pas-3);
}

.ingredients-groupe--ferme .ingredient__texte {
  color: var(--encre-douce);
}

/* ------------------------------------------------ la barre d'actions et ses menus */

.fiche-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  position: relative;
}

.fiche-actions__message {
  color: var(--encre-douce);
  margin: 0;
}

.fiche-menu {
  position: relative;
}

.page-coquille .fiche-menu__bouton[aria-expanded="true"] {
  background: var(--fond-doux);
  border-color: var(--accent);
}

.fiche-menu__panneau {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  left: 0;
  list-style: none;
  margin: 0;
  max-width: calc(100vw - 2 * var(--pas-4));
  min-width: 15rem;
  padding: var(--pas-2);
  position: absolute;
  top: calc(100% + var(--pas-2));
  z-index: 40;
}

.fiche-menu:last-child .fiche-menu__panneau {
  left: auto;
  right: 0;
}

.fiche-menu__element,
.page-coquille .fiche-menu__element {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--rayon-controle);
  color: var(--encre);
  display: flex;
  gap: var(--pas-3);
  justify-content: space-between;
  min-height: var(--cible);
  padding: 0 var(--pas-3);
  text-align: left;
  text-decoration: none;
  width: 100%;
}

.fiche-menu__element:hover,
.fiche-menu__element:focus-visible,
.page-coquille .fiche-menu__element:hover,
.page-coquille .fiche-menu__element:focus-visible {
  background: var(--accent-doux);
}

.fiche-menu__element--bientot,
.fiche-menu__element[aria-disabled="true"] {
  color: var(--encre-douce);
  cursor: default;
}

.fiche-menu__element[aria-disabled="true"]:hover {
  background: none;
}

.fiche-menu__bientot {
  font-size: var(--texte-xs);
}

.fiche-menu__raison {
  color: var(--encre-douce);
  display: block;
  font-size: var(--texte-xs);
  padding: 0 var(--pas-3) var(--pas-2);
}

@media (max-width: 47.5rem) {
  /* Sur téléphone, un menu prend toute la largeur de la barre : il ne sort pas de l'écran. */
  .fiche-menu {
    position: static;
  }

  .fiche-menu__panneau,
  .fiche-menu:last-child .fiche-menu__panneau {
    left: 0;
    right: 0;
  }
}

/* Le panneau « Ajouter à un livre », sous la barre. */
.fiche-panneau {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  display: grid;
  gap: var(--pas-3);
  padding: var(--pas-4);
}

.fiche-panneau__entete {
  align-items: center;
  display: flex;
  gap: var(--pas-3);
  justify-content: space-between;
}

.fiche-panneau__titre {
  font-size: var(--texte-l);
  margin: 0;
}

.fiche-panneau__etat {
  color: var(--encre-douce);
  margin: 0;
}

.fiche-panneau__etat:empty {
  display: none;
}

.fiche-panneau__livres {
  display: grid;
  gap: var(--pas-1);
  list-style: none;
  margin: 0;
  padding: 0;
}

.fiche-panneau__livre {
  min-width: 0;
}

.fiche-panneau__etiquette {
  align-items: center;
  display: flex;
  gap: var(--pas-3);
  min-height: var(--cible);
}

.fiche-panneau__case {
  flex: none;
}

.fiche-panneau__fermer {
  flex: none;
}

.fiche-panneau__nouveau {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.fiche-panneau__nom {
  flex: 1 1 12rem;
  width: auto;
}

/* ------------------------------------------------------------------ le Livre */

.livre {
  gap: var(--pas-4);
}

.livre__corps {
  display: grid;
  gap: var(--pas-5);
}

.livre__livres,
.livre__recettes {
  min-width: 0;
}

.livre__pilules {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.livre__titre-section {
  font-size: var(--texte-l);
  margin: 0;
}

.livre__livres-tete {
  align-items: center;
  display: flex;
  gap: var(--pas-3);
  justify-content: space-between;
  margin-bottom: var(--pas-3);
}

.livre__recettes .livre__titre-section {
  margin-bottom: var(--pas-3);
}

/* Les livres en couvertures, comme les playlists d'une bibliothèque : une tranche
   de couleur, le nom, le nombre de recettes. */
.livre__couvertures {
  display: grid;
  gap: var(--pas-3);
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  list-style: none;
  margin: 0;
  padding: 0;
}

.livre__couverture {
  background: var(--fond);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-tuile);
  border-top: 8px solid var(--accent);
  color: var(--encre);
  display: flex;
  flex-direction: column;
  gap: 2px;
  justify-content: flex-end;
  min-height: 8rem;
  padding: var(--pas-3);
  text-decoration: none;
}

.livre__couverture[data-teinte="1"] {
  border-top-color: var(--turquoise);
}

.livre__couverture[data-teinte="2"] {
  border-top-color: var(--framboise);
}

.livre__couverture[data-teinte="3"] {
  border-top-color: var(--avocat);
}

.livre__couverture[data-teinte="4"] {
  border-top-color: var(--aubergine);
}

.livre__couverture[data-teinte="5"] {
  border-top-color: var(--soleil);
}

.livre__couverture:hover,
.livre__couverture:focus-visible {
  border-color: var(--encre-douce);
}

.livre__couverture-nom {
  font-weight: 700;
  overflow-wrap: anywhere;
}

.livre__couverture-compte {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
}

.livre__nouveau,
.livre__renommer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  margin-bottom: var(--pas-3);
}

.livre__nom {
  flex: 1 1 14rem;
  width: auto;
}

.livre__vide {
  background: var(--fond);
  border: 1px dashed var(--trait);
  border-radius: var(--rayon-tuile);
  display: grid;
  gap: var(--pas-2);
  justify-items: start;
  padding: var(--pas-4);
}

.livre__vide p {
  margin: 0;
}

.livre__vide-titre {
  font-weight: 700;
}

.livre__aucune {
  background: var(--fond);
  border-radius: var(--rayon-tuile);
  padding: var(--pas-4);
}

.livre__aucune-titre {
  font-size: var(--texte-m);
  margin: 0 0 var(--pas-1);
}

.livre__aucune p {
  color: var(--encre-douce);
  margin: 0;
}

.livre__confirmation {
  display: grid;
  gap: var(--pas-2);
}

.livre__entete {
  grid-template-columns: minmax(0, 1fr) auto;
}

.page-coquille .livre__retirer {
  font-size: var(--texte-xs);
  min-height: var(--cible);
}

.liste-b__auteur {
  color: var(--encre-douce);
  display: block;
  font-size: var(--texte-xs);
}

.liste-b__action {
  text-align: right;
}

/* ------------------------------------------------------------------- Chercher */

.chercher {
  gap: var(--pas-4);
}

.chercher__corps {
  display: grid;
  gap: var(--pas-4);
}

.chercher__formulaire {
  margin: 0;
}

.chercher__champ {
  width: 100%;
}

.chercher__case {
  align-items: center;
  display: flex;
  gap: var(--pas-2);
  min-height: var(--cible);
}

.chercher__filtres {
  display: grid;
  gap: var(--pas-3);
}

.chercher__famille {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2) var(--pas-3);
}

.chercher__famille-nom {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  min-width: 5rem;
}

.chercher__pilules {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.chercher__actifs {
  margin-top: var(--pas-1);
}

.chercher__groupe {
  min-width: 0;
}

.chercher__bientot {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
}

.page-coquille .pastille-filtre:disabled {
  border-style: dashed;
  color: var(--encre-douce);
  cursor: default;
}

.chercher__resultats {
  display: grid;
  gap: var(--pas-5);
}

.chercher__groupe-titre {
  font-size: var(--texte-l);
  margin: 0 0 var(--pas-3);
}

.chercher__compte {
  color: var(--encre-douce);
  font-size: var(--texte-s);
  font-weight: 400;
}

.chercher__arrivee {
  color: var(--encre-douce);
  margin: 0;
}

.chercher__recentes,
.chercher__liste {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  list-style: none;
  margin: 0 0 var(--pas-3);
  padding: 0;
}

.chercher__aucun {
  background: var(--fond);
  border-radius: var(--rayon-tuile);
  padding: var(--pas-4);
}

.chercher__aucun p {
  color: var(--encre-douce);
  margin: 0;
}

/* Sous les résultats de la recherche globale : tous les résultats, sur Chercher. */
.recherche-globale__tout {
  align-items: center;
  border-top: 1px solid var(--trait);
  display: flex;
  justify-content: center;
  min-height: var(--cible);
  padding: 0 var(--pas-4);
}

/* -------------------------------------------------------------------- console */

/*
 * La coquille de la console (`packages/atelier/src/server/console.ts`), depuis
 * U3 (KTD2) : jusque-là une feuille en ligne, propre à l'hôte de console, dont
 * cette unité a fait une page qui charge cette feuille-ci par un `<link>`,
 * comme les deux autres hôtes. Ses règles rejoignent donc la feuille commune
 * plutôt que de vivre une seconde fois, mais restent un bloc à part : elles ne
 * dessinent aucun composant du système B (pas de tuile, pas de fiche à
 * en-tête), seulement la structure nue — `<details>`, `<fieldset>`, des listes
 * — que `console.ts` écrit en chaîne. `.console` porte le `<body>` de cette
 * coquille et n'apparaît sur aucun autre gabarit : ce bloc ne peut donc rien
 * changer aux deux autres hôtes.
 *
 * Aucune couleur n'est posée ici : `body.console` hérite le fond, l'encre et
 * la police (OpenDyslexic, via `--police`) de la règle `body` globale, plus
 * haut dans cette feuille — c'est précisément ce que `font-src 'self'` protège
 * désormais sur cet hôte. Les tailles de cible reprennent `--cible`, déjà déclaré
 * pour les mêmes 44 px que l'ancienne feuille en ligne écrivait en dur.
 *
 * Depuis U7, la coquille porte aussi le rail, l'en-tête global et la barre
 * mobile du système B (`.rail`, `.entete-globale`, `.barre-mobile`,
 * `.coquille__corps` — déclarés plus haut, communs aux trois hôtes) : la
 * largeur fixe que ce bloc imposait au `<body>` a donc disparu, exactement
 * comme `.page-coquille` ne la pose pas davantage sur celui de l'app.
 */
.console {
  color-scheme: light;
}

body.console {
  margin: 0;
  line-height: 1.5;
}

.console h1 {
  font-size: 1.25rem;
}

.console h2 {
  font-size: 1.125rem;
  margin-block: 1.5rem 0.5rem;
}

.console main:not(.contenu) {
  display: block;
  margin: 0 auto;
  max-width: 48rem;
  padding: 1rem;
}

.console details {
  border: 1px solid currentColor;
  border-radius: 0.25rem;
  margin-block: 0.5rem;
}

.console summary {
  min-height: var(--cible);
  padding: 0.5rem;
  cursor: pointer;
  overflow-wrap: anywhere;
}

.console fieldset {
  border: 0;
  margin: 0;
  padding: 0.5rem;
}

.console legend {
  padding: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.console label {
  display: block;
  margin-block: 0.5rem;
  overflow-wrap: anywhere;
}

.console input,
.console select,
.console textarea,
.console button {
  font: inherit;
  width: 100%;
  min-height: var(--cible);
  padding: 0.5rem;
}

.console button {
  cursor: pointer;
}

.console ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.console li {
  overflow-wrap: anywhere;
}

.console li button {
  width: auto;
}

.console nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.console nav a {
  display: inline-block;
  min-height: var(--cible);
  padding-block: 0.5rem;
}

.console section {
  scroll-margin-top: 1rem;
}

.console .evitement {
  position: absolute;
  left: -100vw;
}

.console .evitement:focus {
  position: static;
}

/* ---------------------------------------------------------------- fin console */

/* La liste des recettes de l'Atelier sur téléphone (retour de Ronan, 25/10) :
   le tableau de quatre colonnes faisait 446 px à 375 px, élargissait la page et
   repoussait la barre d'onglets hors de l'écran. Sous 600 px, chaque ligne
   devient une carte empilée ; la colonne que le tableau masque (le deuxième
   rang) est redite par `data-label`. Rien ne dépasse la largeur de la page. */
@media (max-width: 37.5rem) {
  .page-coquille .liste-b--atelier,
  .page-coquille .liste-b--atelier tbody,
  .page-coquille .liste-b--atelier tr,
  .page-coquille .liste-b--atelier td {
    box-sizing: border-box;
    display: block;
    max-width: 100%;
    width: 100%;
  }

  .page-coquille .liste-b--atelier thead {
    block-size: 1px;
    clip-path: inset(50%);
    inline-size: 1px;
    overflow: hidden;
    position: absolute;
  }

  .page-coquille .liste-b--atelier tr {
    border-bottom: 1px solid var(--trait);
    padding: var(--pas-2) 0;
  }

  .page-coquille .liste-b--atelier td,
  .page-coquille .liste-b--atelier td:nth-child(2) {
    align-items: baseline;
    border: 0;
    display: flex;
    gap: var(--pas-3);
    justify-content: space-between;
    min-width: 0;
    overflow-wrap: anywhere;
    padding: var(--pas-1) var(--pas-3);
    text-align: left;
  }

  .page-coquille .liste-b--atelier td:first-child {
    display: block;
    font-weight: 700;
    min-height: var(--cible);
    padding-block: var(--pas-2);
  }

  .page-coquille .liste-b--atelier td:first-child a {
    display: inline-block;
    min-height: var(--cible);
    padding-block: var(--pas-2);
  }

  .page-coquille .liste-b--atelier td[data-label]::before {
    color: var(--encre-douce);
    content: attr(data-label);
    flex: none;
    font-size: var(--texte-xs);
  }

  .page-coquille .liste-b--atelier .liste-b__numerique {
    white-space: normal;
  }

  /* Les pastilles de vues passent à la ligne au lieu d'élargir la page. */
  .page-coquille [data-role="atelier-recettes"] .liste-outils__filtres {
    flex-wrap: wrap;
    min-width: 0;
  }
}

/* La ligne « Bientôt ici » de la page de travail : discrète, sur une à deux lignes. */
.fiche-entete__bientot {
  margin: 0;
}

/* À 320 px, la loupe de l'en-tête recouvrait la fin de « Gigot. » de 15 px
   (relevé de Ronan, 25/10) : intervalles et marges resserrés, de quoi rendre
   plus de 15 px à la marque sans changer ni police ni couleur. */
@media (max-width: 22.5rem) {
  .entete-globale {
    column-gap: var(--pas-2);
    padding-inline: var(--pas-2);
  }

  .entete-globale__app {
    padding-left: var(--pas-2);
  }
}

/* Le bouton flottant du minuteur ne recouvre plus le bas du contenu : les pages
   Atelier gardent, sous leur dernier élément, la hauteur du bouton et son
   décalage (la règle sur `.page-coquille` seule laissait « Modifiée » dessous). */
body:has(.barre-utilitaires) {
  padding-bottom: calc(var(--cible) + var(--pas-4));
}

@media (max-width: 56.25rem) {
  body:has(.barre-utilitaires) {
    padding-bottom: calc(var(--cible) + var(--pas-4) + 65px + env(safe-area-inset-bottom));
  }
}

/* Mesuré dans Chromium (25/10) : à 320 et 360 px, la grille de l'en-tête
   (marque | recherche | actions) plaçait la loupe de 44 px dans la colonne
   étroite du milieu, sur la fin de « Gigot. », et la page défilait de 8 px.
   Sous 24 rem, deux colonnes : la marque (qui peut rétrécir) et la loupe ;
   les actions gardent leur rangée pleine largeur. */
@media (max-width: 24rem) {
  /* Les libellés cachés à la lecture (`visuellement-cache`) dépassaient de 8 px
     à droite, sans qu'aucune boîte ne le montre : `clip` ne crée pas de défilement
     (mesuré dans Chromium, 25/10). Un panneau ouvert, lui, reste dans les 320 px. */
  .page-coquille .entete-globale {
    overflow-x: clip;
  }
}

@media (max-width: 22rem) {
  /* Rangée 1 : le lanceur, la marque et le nom de l'app, sur une ligne.
     Rangée 2 : la loupe, « + », Alertes et le compte, alignés à droite. */
  .page-coquille .entete-globale:has(.entete-globale__recherche-mobile:not([hidden])) {
    grid-template-columns: minmax(0, 1fr) auto;
    max-width: 100%;
    padding-block: var(--pas-1);
    row-gap: 0;
  }

  .page-coquille .entete-globale:has(.entete-globale__recherche-mobile:not([hidden])) .entete-globale__identite {
    flex-wrap: nowrap;
    grid-column: 1 / -1;
    grid-row: 1;
    min-width: 0;
  }

  /* Le lanceur, la marque et le nom de l'app sur une seule ligne. */
  .page-coquille .entete-globale__identite {
    grid-template-areas: "lanceur marque app";
    grid-template-columns: auto auto minmax(0, 1fr);
  }

  .entete-globale__recherche-mobile .entete-globale__recherche-icone {
    font-size: 1.4rem;
  }

  .page-coquille .entete-globale:has(.entete-globale__recherche-mobile:not([hidden])) .entete-globale__recherche-mobile {
    grid-column: 1;
    grid-row: 2;
    justify-self: end;
  }

  .page-coquille .entete-globale:has(.entete-globale__recherche-mobile:not([hidden])) .entete-globale__actions {
    grid-column: 2;
    grid-row: 2;
    justify-content: flex-end;
    padding-top: 0;
  }

}

/* La barre d'onglets du bas ne dépasse jamais la largeur de la page. */
.barre-app__onglets {
  box-sizing: border-box;
  max-width: 100%;
}

/* Le bouton du minuteur, replié : compact, collé à gauche (là où la liste
   n'a pas de colonne d'information en bout de ligne), cible de 44 px gardée. */
.barre-utilitaires {
  align-items: flex-start;
  left: var(--pas-3);
  right: auto;
}

.barre-utilitaires .barre-utilitaires__bascule {
  /* Fond opaque, bordure et ombre de la palette : le bouton se détache du texte dessous. */
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  box-shadow: var(--ombre-panneau);
  align-items: center;
  display: inline-flex;
  font-size: var(--texte-s);
  gap: var(--pas-1);
  padding-inline: var(--pas-2);
}

/* Avant que l'état de session soit posé (`shell-session.ts::applyShellSession`),
   les liens du visiteur restent cachés : un compte connecté ne les voit plus
   passer au chargement, et la recherche n'est plus écrasée. */
html:not([data-session-posee]) .coquille__compte {
  display: none;
}

/* Les copies « téléphone » de Partager et Commentaires dans « … » : absentes
   sur un grand écran, où la barre les montre déjà — ligne comprise. Placée en
   fin de feuille pour l'emporter sur `.page-coquille .fiche-menu__element`. */
@media (min-width: 36.0625rem) {
  .page-coquille .fiche-menu__panneau li:has(> .fiche-menu__element--telephone),
  .page-coquille .fiche-menu__element--telephone {
    display: none;
  }
}

/* ------------------------------------------- le panneau Partager (PR 3, U17) */

/* Un dialogue par-dessus la fiche ou le livre : un voile, puis une boîte
   centrée qui défile seule. Sur téléphone, 16 px de marge de chaque côté. */
.panneau-partage__voile {
  background: rgb(0 0 0 / 45%);
  inset: 0;
  position: fixed;
  z-index: 70;
}

.panneau-partage__boite {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  display: flex;
  flex-direction: column;
  left: 50%;
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  padding: var(--pas-4);
  position: fixed;
  top: 16px;
  translate: -50% 0;
  width: min(40rem, calc(100vw - 32px));
  z-index: 71;
}

.panneau-partage__entete {
  align-items: flex-start;
  display: flex;
  gap: var(--pas-3);
  justify-content: space-between;
}

.panneau-partage__intitule {
  min-width: 0;
}

.panneau-partage__titre {
  font-size: var(--texte-xl);
  margin: 0;
}

.panneau-partage__nom {
  color: var(--encre-douce);
  margin: var(--pas-1) 0 0;
  overflow-wrap: anywhere;
}

.panneau-partage__corps {
  display: grid;
  gap: var(--pas-4);
  min-width: 0;
  overflow-wrap: anywhere;
}

.panneau-partage__choix {
  border: 0;
  display: grid;
  gap: var(--pas-2);
  margin: 0;
  min-width: 0;
  padding: 0;
}

.panneau-partage__legende {
  font-weight: 700;
  margin-bottom: var(--pas-2);
  padding: 0;
}

.panneau-partage__option {
  align-items: center;
  column-gap: var(--pas-3);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  min-height: var(--cible);
}

.panneau-partage__option > select {
  grid-column: 2;
}

.panneau-partage__option--grisee {
  color: var(--encre-douce);
}

.panneau-partage__option input[type="radio"] {
  height: 1.25rem;
  margin: 0;
  width: 1.25rem;
}

.panneau-partage__etiquette {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
  min-height: var(--cible);
}

.panneau-partage__choix-nom {
  font-weight: 700;
}

.panneau-partage__compte {
  color: var(--encre-douce);
  font-size: var(--texte-s);
}

.panneau-partage__aide {
  color: var(--encre-douce);
  font-size: var(--texte-s);
  grid-column: 2;
  margin: 0;
}

.panneau-partage__bloc {
  display: grid;
  gap: var(--pas-2);
  min-width: 0;
}

.panneau-partage__sous-titre {
  font-size: var(--texte-l);
  margin: 0;
}

/* La confirmation d'un choix (Moi seul avec une liste, Tout le monde) et la
   demande en relecture : le filet de couleur le dit sans la couleur seule. */
.panneau-partage__confirmation,
.panneau-partage__relecture,
.panneau-partage__refus {
  background: var(--fond-doux);
  border: 1px solid var(--trait);
  border-left: 4px solid var(--accent);
  border-radius: var(--rayon);
  display: grid;
  gap: var(--pas-2);
  margin: 0;
  padding: var(--pas-3) var(--pas-4);
}

.panneau-partage__confirmation p,
.panneau-partage__relecture p {
  margin: 0;
}

.panneau-partage__confirmation-titre {
  font-weight: 700;
}

.panneau-partage__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.panneau-partage__pastille {
  background: var(--accent-doux);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  font-size: var(--texte-xs);
  font-weight: 700;
  padding: 0.1rem 0.6rem;
}

.panneau-partage__qui {
  flex: 1 1 10rem;
  min-width: 0;
}

.panneau-partage__repere {
  background: var(--fond-doux);
  border-radius: var(--rayon-pastille);
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  padding: 0.1rem 0.5rem;
}

.panneau-partage__suggestion {
  background: var(--fond-doux);
  border-radius: var(--rayon);
  display: grid;
  gap: var(--pas-2);
  padding: var(--pas-3) var(--pas-4);
}

.panneau-partage__suggestion p {
  margin: 0;
}

/* Une ligne de la liste a un niveau : le sélecteur ne déborde pas du téléphone. */
.panneau-partage__boite .partage__ligne select {
  max-width: 100%;
}

/* « En relecture » sur la barre de la fiche, à côté de Partager. */
.fiche-actions__relecture {
  background: var(--accent-doux);
  border-radius: var(--rayon-pastille);
  color: var(--encre);
  font-size: var(--texte-s);
  font-weight: 700;
  padding: 0.25rem 0.75rem;
}

/* Une fonction « Bientôt » : un bouton grisé et sa pastille, sur la même ligne. */
.geste-bientot {
  align-items: center;
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.geste-bientot > button[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.6;
}

/* L'éditeur n'a plus de champ d'audience : une phrase et un lien. */
.editeur__lecteurs {
  color: var(--encre-douce);
  margin: var(--pas-3) 0 0;
}

/* Livre : l'origine d'une couverture, et « Partager » sous les livres à moi. */
.livre__couverture-origine {
  color: var(--encre-douce);
  font-size: var(--texte-xs);
  font-weight: 700;
}

.livre__couvertures > li {
  display: grid;
  gap: var(--pas-1);
  grid-template-rows: 1fr auto;
}

.livre__partager {
  justify-self: start;
}

.livre__ranger {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.livre__ranger[hidden] {
  display: none;
}

.livre__ranger > select {
  flex: 1 1 14rem;
  min-width: 0;
}

/* La racine du dialogue ne dessine rien elle-même : son voile et sa boîte sont fixes. */
.panneau-partage {
  display: contents;
}

/*
 * Le champ du nom affiché (`name-form.ts`) : au profil et dans la demande de
 * la première connexion. Le champ et « Enregistrer » sur une ligne, qui passe
 * à la ligne sur un téléphone étroit.
 */
.formulaire-nom {
  display: grid;
  gap: var(--pas-2);
  margin-top: var(--pas-2);
}

.formulaire-nom__ligne {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pas-2);
}

.formulaire-nom__ligne .champ {
  flex: 1 1 12rem;
  min-width: 0;
}

/* La demande du nom, obligatoire à la première connexion : une boîte au-dessus de tout, sans sortie. */
.demande-nom {
  align-items: flex-start;
  background: rgb(0 0 0 / 0.45);
  display: flex;
  inset: 0;
  overflow: auto;
  justify-content: center;
  padding: var(--pas-4) var(--pas-3);
  position: fixed;
  z-index: 1000;
}

.demande-nom__boite {
  background: var(--fond-carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-panneau);
  box-shadow: var(--ombre-panneau);
  display: grid;
  gap: var(--pas-2);
  margin-top: 8vh;
  max-width: 26rem;
  padding: var(--pas-4);
  width: 100%;
}
