/* ════════════════════════════════════════════════════════════════════
   cookIT — « Le passe »
   DA « Le cageot pop » : les affiches de marché des années soixante.
   Un papier blanc chaud, des filets d'encre, des ombres portées dures,
   et des gommettes de prix collées de travers. Rien ne flotte : ce qui
   se voit est dessiné, cadré, posé.
   ════════════════════════════════════════════════════════════════════ */

/* ── Les polices, servies d'ici ──────────────────────────────────────
   Elles venaient de Google Fonts : deux requêtes vers un tiers à chaque
   premier chargement, et un texte qui saute quand le réseau du sous-sol
   du magasin met trois secondes à répondre. Le README promettait « aucune
   donnée qui sort de Cloudflare » ; c'est vrai maintenant.

   Archivo est variable : une plage de graisses tient en un fichier, et
   les valeurs intermédiaires de cette feuille (550, 560, 650) sont
   rendues telles quelles au lieu d'être approchées. Archivo Black est
   une famille à part, d'une seule graisse — c'est ainsi qu'elle est
   distribuée.

   Trois fichiers, parce que chacun sert :
     · latin        toute l'interface, et 310 des 311 fiches ;
     · vietnamien   le « ở » de « Phở bò », seul caractère du carnet hors
                    du latin. Archivo Black ne couvre pas ce jeu — le
                    titre de cette fiche-là gardera son repli système.
   Ce qu'on tape soi-même peut sortir de ces jeux : le système prend
   alors le relais, et c'est le comportement voulu.

   Archivo, sous SIL Open Font License — voir public/fonts/OFL.txt. */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url("/fonts/archivo-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url("/fonts/archivo-vietnamien.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: "Archivo Black";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/archivo-black-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* ── les matières ──────────────────────────────────────────────────
     Quatre valeurs font le décor. Le papier est le fond, le blanc est ce
     qu'on pose dessus, l'encre écrit et cadre, le gris papier dit « pas
     maintenant ». */
  --papier:  #FFF7E6;
  --blanc:   #FFFFFF;
  --encre:   #1E1A16;
  --gris:    #EFE6D2;

  /* ── les cinq couleurs de l'étal ───────────────────────────────────
     Une par famille d'étiquette : c'est ce que la base sait déjà dire
     (`tags.kind`) et que l'écran ignorait. Elles s'emploient en aplat,
     bordées d'encre. */
  --tomate:    #E4482C;   /* type : entrée, plat, dessert — et l'accent */
  --betterave: #B23A6A;   /* protéine : bœuf, poulet, poisson, végétarien */
  --mais:      #F2B233;   /* régime, dates, compteurs */
  --botte:     #3C7A3E;   /* saison — et tout ce qui est fait, servi, coché */
  --bleu:      #2456C8;   /* origine : Italien, Asiatique, Maghreb… */

  /* Les mêmes, assez foncées pour porter du texte à 15 px sur le papier
     comme sur le blanc. Une pastille de 11 px en capitales n'est pas du
     « gros texte » au sens de WCAG : la tomate de la marque y passe à
     3,99 : 1, ces deux-là à 5,1 et 5,4. L'aplat garde la couleur vive,
     le texte prend celle-ci. */
  --tomate-lu: #C2361B;
  --mais-lu:   #8A6410;
  --botte-lu:  #3C7A3E;

  /* Ce qu on écrit SUR un aplat de l étal. Les cinq couleurs ne s inversent
     pas la nuit — une pastille « Bœuf » est betterave à minuit comme à midi —,
     donc leur texte ne suit pas le thème non plus. */
  --sur-clair:  #1E1A16;   /* sur le maïs */
  --sur-sombre: #FFFFFF;   /* sur la tomate, la betterave, le bleu, la botte */

  /* ── les rôles ─────────────────────────────────────────────────────
     Ce que le reste de la feuille appelle. Aucune règle ne nomme une
     couleur en dur : c'est ce qui rend l'affiche de nuit possible. */
  --ground:     var(--papier);
  --surface:    var(--blanc);
  --surface-2:  var(--gris);
  --paper:      var(--blanc);

  --ink:        var(--encre);
  --ink-2:      #4A4139;   /* métadonnées, résumés  — 9,3 : 1 sur le papier */
  --ink-3:      #6B6157;   /* surtitres, mentions   — 5,7 : 1 sur le papier */
  --accent:     var(--tomate-lu);

  /* Le filet cadre un objet ; le liseré sépare deux lignes d'une même
     liste. Confondre les deux, c'est cerner de noir chaque ingrédient. */
  --filet:      var(--encre);
  --edge:       var(--filet);
  --edge-soft:  #CDBFA6;

  /* L'ombre est un décalage plein, jamais un flou : F7 posera les trois
     profondeurs. Ici, elle change seulement de couleur — celle de l'encre,
     celle du maïs la nuit. */
  --ombre:      var(--encre);
  /* Une ombre est un décalage plein, jamais un flou. Trois profondeurs, et
     pas une de plus : un bouton d'icône, un objet posé, une feuille. */
  --shadow-sm:  2px 2px 0 var(--ombre);
  --shadow-md:  3px 3px 0 var(--ombre);
  --shadow-lg:  6px 6px 0 var(--ombre);
  /* Ce que devient une ombre quand on appuie dessus. */
  --shadow-bas: 1px 1px 0 var(--ombre);

  /* Le filet cadre un objet ; le liseré sépare deux lignes d'une liste. */
  --filet-w:  2px;
  --filet-w2: 2.5px;

  --station-w: 236px;
  --pass-w: 348px;
  /* La mesure du plan de travail. Elle grandit par paliers plus bas :
     sur un 4K, un texte qui reste collé à gauche laisse un demi-écran
     vide, et un texte qui prend toute la vitre ne se lit plus. */
  --view-max: 1080px;
  --gutter: 28px;
  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-pill: 999px;

  /* Une seule famille, en deux graisses : Archivo Black pour ce qui
     crie — titres, boutons, pastilles, dates —, Archivo pour ce qui se
     lit. Les chiffres s'alignent en tabulaire ; le mono s'en va avec le
     kraft. */
  --display: "Archivo Black", "Arial Black", Arial, sans-serif;
  --body: "Archivo", ui-sans-serif, system-ui, sans-serif;
  --mono: var(--body);
}

/* ── L'affiche de nuit ────────────────────────────────────────────────
   Le papier et l'encre échangent leurs rôles, l'ombre passe au maïs, et
   les cinq couleurs de l'étal ne bougent pas : une pastille « Bœuf » est
   betterave à midi comme à minuit. Seules la tomate et le maïs prennent,
   pour le texte, la version claire plutôt que la version foncée.        */
[data-theme="dark"] {
  --papier: #1E1A16;
  --blanc:  #2A2521;
  --encre:  #FFF7E6;
  --gris:   #3A332C;

  --ink-2: #D9CDB8;
  --ink-3: #B8AB95;

  --tomate-lu: #F0644B;
  --mais-lu:   var(--mais);
  --botte-lu:  #7FA76A;

  --edge-soft: #4A4139;
  /* La nuit, l'ombre passe au maïs : de l'encre sur de l'encre ne se voit
     pas, et c'est l'ombre qui fait tenir le pop. */
  --ombre: var(--mais);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; }
body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}
button, input, textarea { font: inherit; color: inherit; }
:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px;
  /* Un second anneau clair pour rester visible sur les surfaces sombres,
     où l'ancien contour --ink se confondait avec le fond. */
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 30%, transparent);
}
::selection { background: var(--mais); color: var(--sur-clair); }

/* ─────────────────────────── vocabulaire type ─────────────────────── */
.display {
  font-family: var(--display);
  font-weight: 400;   /* Archivo Black n'a qu'une graisse : c'est la sienne. */
  font-size: clamp(2.15rem, 4.4vw, 2.9rem);
  line-height: .95;
  letter-spacing: -.025em;
  margin: 0;
  text-transform: none;
}
.eyebrow {
  font-weight: 700;
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 6px;
}
.lede { color: var(--ink-2); margin: 6px 0 0; max-width: 46ch; }
.muted { color: var(--ink-3); font-weight: 400; }
.mono { font-variant-numeric: tabular-nums; }
.spacer { flex: 1; }
.kbd {
  font-family: var(--display); font-size: .75rem; padding: 3px 6px;
  border: var(--filet-w) solid var(--filet); border-radius: 5px; background: var(--surface-2); color: var(--ink-2);
}

/* ── Les icônes ───────────────────────────────────────────────────
   Le trait suit le texte : une icône dans un bouton tomate est tomate,
   dans un rail sombre elle est claire. `--ic` permet d'en grossir une
   sans toucher au reste de la ligne. */
.ic {
  width: var(--ic, 1.15em); height: var(--ic, 1.15em); flex: none;
  fill: none; stroke: currentColor; stroke-width: 2.25;
  stroke-linecap: round; stroke-linejoin: round;
  vertical-align: -.22em;
}
.ic--plein { fill: currentColor; stroke: currentColor; stroke-width: 1.2; }
.icon-btn .ic { --ic: 1.25em; }
.btn .ic { --ic: 1.1em; }

/* ── La gommette ───────────────────────────────────────────────────
   Le prix collé sur un cageot : un rond bordé d'encre, posé de travers,
   avec son ombre. P6 la fera tomber sur un geste réussi ; elle est
   définie ici parce que c'est une surface, comme un bouton ou un champ. */
.gommette {
  /* La tomate lue, et non celle d'aplat : à cette taille, le blanc sur
     l'aplat tombe à 3,99 : 1. Le jeton « lu » le remonte à 5,47 le jour,
     et la nuit c'est l'encre qui s'y pose (voir plus bas). */
  --g: var(--tomate-lu);
  position: absolute; z-index: 2;
  display: grid; place-content: center; text-align: center;
  width: 66px; height: 66px; border-radius: 50%;
  background: var(--g); color: var(--sur-sombre);
  border: var(--filet-w2) solid var(--filet); box-shadow: var(--shadow-md);
  font-family: var(--display); font-size: .78rem; line-height: 1.05;
  letter-spacing: .02em; text-transform: uppercase;
  transform: rotate(var(--r, -9deg));
  pointer-events: none;
}
.gommette--mais { --g: var(--mais); color: var(--sur-clair); }
/* La nuit, la tomate lue s'éclaircit pour rester lisible comme texte :
   sous une gommette, c'est donc l'encre qu'il faut poser dessus. */
[data-theme="dark"] .gommette:not(.gommette--mais):not(.gommette--botte):not(.gommette--bleu) {
  color: var(--sur-clair);
}
.gommette--botte { --g: var(--botte); }
.gommette--bleu { --g: var(--bleu); }
.gommette--petite { width: 52px; height: 52px; font-size: .75rem; }
.repas > .gommette { top: -18px; right: -12px; }

/* Posée au milieu du plan de travail, le temps d'un geste. Elle arrive à
   une fois et demie sa taille et se pose avec un léger dépassement : c'est
   le mouvement d'un tampon qu'on abat, pas d'une fenêtre qui s'ouvre. */
.gommette--posee {
  position: fixed; left: 50%; top: 42%; z-index: 95;
  width: 116px; height: 116px; font-size: 1.05rem;
  margin: -58px 0 0 -58px;
  box-shadow: var(--shadow-lg);
  animation: coller .28s cubic-bezier(.2, 1.5, .4, 1) both,
             decoller .2s ease-in 1.05s both;
}
@keyframes coller {
  from { transform: rotate(var(--r)) scale(1.5); opacity: 0; }
  60%  { opacity: 1; }
  to   { transform: rotate(var(--r)) scale(1); opacity: 1; }
}
@keyframes decoller {
  to { transform: rotate(var(--r)) scale(.94); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .gommette--posee { animation: none; }
}

/* Le fantôme d'une vignette en vol. Posé sur le document, hors de tout
   flux : il ne pousse rien et n'existe que quatre cents millisecondes. */
.vol {
  position: fixed; z-index: 96; pointer-events: none;
  background-size: cover; background-position: center; background-color: var(--gris);
  border: var(--filet-w) solid var(--filet); border-radius: 12px;
  box-shadow: var(--shadow-md);
}

/* ───────────────────────────── boutons ────────────────────────────── */
.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--ink);
  --btn-edge: var(--edge);
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-edge);
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 16px; border-radius: var(--r-pill);
  font-family: var(--display); font-size: .86rem; letter-spacing: .005em; cursor: pointer;
  transition: transform .12s cubic-bezier(.2,.8,.3,1), background .15s, border-color .15s, box-shadow .15s;
  white-space: nowrap;
}
.btn { box-shadow: var(--shadow-md); }
/* Appuyé, l'objet descend sur son ombre : deux pixels de déplacement, une
   ombre qui tombe à un. C'est tout le mouvement du pop. */
.btn:active { transform: translate(2px, 2px); box-shadow: var(--shadow-bas); }
.btn--quiet, .btn:disabled { box-shadow: none; }
.btn--quiet:active { transform: none; }
.btn--primary { --btn-bg: var(--encre); --btn-fg: var(--papier); --btn-edge: var(--filet); }
.btn--primary:hover { --btn-bg: color-mix(in srgb, var(--encre) 88%, var(--papier)); border-color: color-mix(in srgb, var(--encre) 88%, var(--papier)); }
[data-theme="dark"] .btn--primary { --btn-bg: var(--accent); --btn-fg: var(--papier); --btn-edge: var(--accent); }
.btn--quiet { --btn-bg: transparent; --btn-edge: transparent; color: var(--ink-2); }
.btn--quiet:hover { --btn-bg: var(--surface-2); border-color: transparent; }
.btn--danger { --btn-bg: transparent; --btn-edge: transparent; color: var(--accent); }
.btn--danger:hover { --btn-bg: color-mix(in srgb, var(--accent) 10%, transparent); border-color: transparent; }
.btn--small { padding: 6px 11px; font-size: .84rem; }
.btn--block { width: 100%; justify-content: center; }
.btn:disabled { opacity: .38; cursor: not-allowed; }
.btn:disabled:hover { border-color: var(--btn-edge); }
.btn--hero {
  --btn-bg: var(--encre); --btn-fg: var(--papier); --btn-edge: var(--encre);
  padding: 13px 20px; font-family: var(--display);
  font-weight: 600; letter-spacing: .01em;
}
.icon-btn {
  background: none; border: 0; color: var(--ink-3); cursor: pointer;
  /* Un `<button>` porte un padding par défaut — 1px 6px — que rien ne
     remettait à zéro : dans un bouton de trente pixels, il n'en restait
     dix-huit de large pour une icône qui en fait vingt et un. Elle
     débordait, et se décalait de trois pixels et demi vers la droite. */
  padding: 0;
  width: 30px; height: 30px; border-radius: var(--r-sm); display: grid; place-items: center;
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.field {
  width: 100%; background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-md); padding: 10px 12px;
}
.field { box-shadow: var(--shadow-md); }
.field::placeholder { color: var(--ink-3); }
.field:focus { outline: none; border-color: var(--tomate-lu); box-shadow: 3px 3px 0 var(--tomate-lu); }
textarea.field { resize: vertical; line-height: 1.6; }

/* ═══════════════════════════ CONNEXION ════════════════════════════ */
.login { display: grid; grid-template-columns: 1.05fr .95fr; min-height: 100dvh; }
.login-art {
  background: var(--gris);
  display: grid; align-content: center; justify-items: center; gap: 26px;
  position: relative; overflow: hidden; padding: 40px 20px;
  border-right: var(--filet-w2) solid var(--filet);
}

/* ── L'étiquette de cageot ─────────────────────────────────────────
   Pas d'illustration : une étiquette imprimée, comme celles qu'on agrafe
   sur les caisses de fruits. Cadre à double filet, nom au pochoir, et une
   gommette de prix collée de travers par-dessus.

   Le grain du carton et le kraft s'en vont avec la DA. Ce qui la tient
   maintenant, c'est le filet et l'ombre tomate ; la nuit les retourne
   avec le reste, sans une règle de plus. */
.etal {
  position: relative; width: min(340px, 78vw); aspect-ratio: 5 / 7;
  background: var(--surface);
  border: var(--filet-w2) solid var(--filet); border-radius: 4px;
  box-shadow: inset 0 0 0 3px var(--surface), inset 0 0 0 5px var(--filet),
              10px 10px 0 var(--tomate);
  display: grid; grid-template-rows: auto 1fr auto;
  padding: clamp(18px, 4vw, 26px); text-align: center;
  transform: rotate(-1.5deg);
}
.etal__top, .etal__bot {
  font-family: var(--display); font-size: .75rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-2);
}
.etal__top { border-bottom: var(--filet-w) solid var(--filet); padding-bottom: 10px; }
.etal__bot {
  border-top: var(--filet-w) solid var(--filet); padding-top: 10px;
  display: flex; justify-content: space-between; gap: 10px;
}
.etal__mid { display: grid; align-content: center; gap: 12px; }
.etal__name {
  font-family: var(--display); font-size: clamp(2.6rem, 8vw, 3.6rem);
  line-height: .86; letter-spacing: -.04em; color: var(--ink);
}
.etal__name i { font-style: normal; color: var(--tomate-lu); }
.etal__rule { height: 4px; background: var(--filet); margin: 0 auto; width: 46px; }
.etal__sub { font-weight: 600; font-size: 1.02rem; color: var(--ink-2); }
.etal__gommette { top: -22px; right: -18px; width: 84px; height: 84px; font-size: .85rem; }

/* La planche : les cinq couleurs de l'étal, alignées comme les gommettes
   de prix sur le bord d'un cageot. */
.planche { display: flex; gap: 9px; }
.planche i {
  width: 26px; height: 26px; border-radius: 50%; display: block;
  border: var(--filet-w) solid var(--filet); box-shadow: var(--shadow-sm);
}

.login-form {
  display: flex; flex-direction: column; gap: 14px; justify-content: center;
  padding: clamp(28px, 6vw, 80px); max-width: 560px;
}
.login-form h1 {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(2.6rem, 6vw, 4rem); line-height: .9; letter-spacing: -.02em; margin: 0;
}
.login-form .lede { margin-bottom: 12px; }
.login-form label { display: flex; flex-direction: column; gap: 6px; font-size: .82rem; color: var(--ink-2); font-weight: 550; }
.form-error { color: var(--accent); font-size: .88rem; margin: 0; }
.login-or {
  display: flex; align-items: center; gap: 12px; margin: 2px 0;
  color: var(--ink-3); font-size: .78rem;
}
.login-or::before, .login-or::after { content: ""; height: 1px; flex: 1; background: var(--edge); }

.sheet__body { display: flex; flex-direction: column; gap: 14px; padding: 22px; }
/* ── Les rappels ───────────────────────────────────────────────────
   Ce que l'app envoie, à qui, et de quoi le couper. La liste des
   appareils reprend celle des passkeys : c'est la même chose regardée —
   un appareil, un nom, un geste pour le retirer. */
.rappels-quoi { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.rappels-quoi li {
  padding: 11px 14px; border-radius: var(--r-md);
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  font-size: .86rem; line-height: 1.45;
}
.rappels-quoi b { font-family: var(--display); font-weight: 400; }
.rappels-titre {
  font-family: var(--display); font-size: .78rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); margin: 22px 0 8px;
}
.rappels-etat { font-size: .88rem; color: var(--ink-2); margin: 0 0 12px; }
#rappels-bascule { width: 100%; justify-content: center; }
/* L'appareil qu'on tient, parmi les autres. */
.passkey-list .est-ici { border-color: var(--tomate-lu); }
.passkey-list .est-ici strong::after {
  content: " · cet appareil"; font-weight: 400; color: var(--tomate-lu); font-size: .82rem;
}

.passkey-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.passkey-list li {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  border: var(--filet-w) solid var(--filet); border-radius: var(--r-md); background: var(--surface-2);
}
.passkey-list strong { font-weight: 600; font-size: .92rem; }
.passkey-list small { display: block; color: var(--ink-3); font-size: .78rem; }
.passkey-list .spacer { flex: 1; }

/* ═════════════════════════ COQUILLE APP ═══════════════════════════ */
.shell {
  display: grid;
  grid-template-columns: var(--station-w) minmax(0, 1fr);
  height: 100dvh;
}

/* ── poste de travail ─────────────────────────────────────────────── */
/* Le rail était sombre parce que le kraft avait besoin d'un cadre. Sur le
   papier, c'est le filet qui cadre : le rail passe au blanc, et la nuit le
   retourne avec le reste. */
.station {
  background: var(--surface); color: var(--ink);
  display: flex; flex-direction: column; padding: 20px 14px 16px;
  border-right: 2.5px solid var(--filet);
}
.wordmark {
  font-family: var(--display); font-weight: 700;
  font-size: 1.45rem; letter-spacing: -.02em; padding: 0 10px 22px;
}
.wordmark em { font-style: normal; color: var(--tomate-lu); }
.station__nav { display: flex; flex-direction: column; gap: 2px; }
.nav-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: 9px 10px; border-radius: var(--r-md);
  color: var(--ink);
  font-family: var(--display); font-size: .84rem;
  border: 2px solid transparent;
  transition: background .15s, color .15s, border-color .15s;
}
.nav-item:hover { border-color: var(--filet); }
.nav-item.is-active {
  background: var(--tomate-lu); color: var(--sur-sombre);
  border-color: var(--filet); box-shadow: 3px 3px 0 var(--ombre);
}

/* Les raccourcis, sous la navigation : ce que le clavier écoute vraiment. */
.raccourcis {
  list-style: none; margin: 14px 0 0; padding: 0 4px;
  display: flex; flex-direction: column; gap: 5px;
  font-size: .78rem; font-weight: 600; color: var(--ink-3);
}
.raccourcis li { display: flex; align-items: center; gap: 7px; }
.raccourcis .kbd { min-width: 2.1em; text-align: center; }
.badge {
  margin-left: auto; font-family: var(--display); font-size: .75rem;
  font-variant-numeric: tabular-nums;
  background: var(--mais); color: var(--sur-clair); border-radius: 999px; padding: 2px 7px;
}
.badge:empty { display: none; }
.station__foot { margin-top: auto; display: flex; flex-direction: column; gap: 4px; padding-top: 16px; }
.ghost-item {
  background: none; border: 0; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: 8px;
  color: var(--ink-2); font-size: .82rem; font-weight: 600;
  padding: 6px 10px; border-radius: var(--r-sm);
}
.ghost-item:hover { color: var(--ink); background: var(--surface-2); }
.who {
  display: flex; align-items: center; justify-content: space-between;
  border-top: 2px solid var(--edge-soft); margin-top: 6px; padding-top: 8px;
}
.who span { padding-left: 10px; font-size: .82rem; color: var(--ink-2); font-weight: 600; }

/* ── plan de travail ──────────────────────────────────────────────── */
.canvas { overflow-y: auto; scroll-behavior: smooth; }
.canvas__head {
  position: sticky; top: 0; z-index: 12;
  padding: 12px 0; background: color-mix(in srgb, var(--ground) 86%, transparent);
  border-bottom: var(--filet-w) solid var(--filet);
}
/* Le fond du bandeau file jusqu'aux bords, son contenu s'aligne sur la
   colonne de travail : sans ça, sur un grand écran, « Noter un plat »
   part à trois travées du titre qu'il concerne. */
.canvas__head-inner {
  display: flex; align-items: center; gap: 10px;
  max-width: var(--view-max); margin-inline: auto; padding-inline: var(--gutter);
}
.omni {
  /* `min-width: 0` : sans lui, la loupe et le libellé imposent une largeur
     minimale, et c'est « Noter un plat » qui sort de l'écran. */
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; cursor: pointer;
  background: var(--surface); border: var(--filet-w) solid var(--filet); border-radius: var(--r-md);
  padding: 8px 10px 8px 14px; color: var(--ink-3); text-align: left;
  transition: border-color .15s, box-shadow .15s;
}
.omni { box-shadow: var(--shadow-md); }
.omni:hover { border-color: var(--tomate-lu); }
.omni__label { flex: 1; min-width: 0; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sync {
  font-size: .78rem; font-weight: 600;
  color: var(--ink-3); display: flex; align-items: center; gap: 6px;
}
.sync::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--botte);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--botte) 22%, transparent);
}
.sync.is-busy::before { background: var(--mais); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mais) 25%, transparent); }
.view {
  padding: 26px var(--gutter) 64px; max-width: var(--view-max); margin-inline: auto;
  animation: rise .3s cubic-bezier(.2,.8,.3,1);
}
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }

.carnet__head { display: flex; align-items: flex-end; gap: 20px; flex-wrap: wrap; margin-bottom: 18px; }
.carnet__head > div:first-child { flex: 1; min-width: 220px; }
/* `wrap` : entre 861 et 1024 px, la recherche, le tri et les deux boutons
   ne tiennent pas sur une ligne et poussaient le bord droit hors de la
   vitre. Ils passent à la ligne plutôt que de déborder. */
.carnet__tools { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.carnet__tools .field { width: 220px; }

/* ── L'ordre du carnet ────────────────────────────────────────────
   Un menu, mais un vrai : cinq réponses à cinq questions, et il n'y a
   pas de place pour cinq pastilles à côté de la recherche. */
.tri {
  display: inline-flex; align-items: center; gap: 8px;
  height: 44px; padding: 0 14px 0 16px;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-pill); box-shadow: var(--shadow-md);
}
.tri__mot { font-family: var(--display); font-size: .86rem; }
.tri__choix {
  border: 0; background: none; cursor: pointer;
  font-family: var(--body); font-weight: 600; font-size: 16px; color: var(--ink-2);
}
.tri__choix:focus-visible { outline: 2px solid var(--tomate-lu); outline-offset: 3px; }
@media (hover: hover) { .tri__choix { font-size: .86rem; } }

/* ── « Une idée ? » ───────────────────────────────────────────────
   Feuilleter et tirer au sort sont deux façons de demander la même
   chose. Un rond, en maïs, posé de travers là où le pouce arrive. */
.idee {
  position: fixed; right: 18px; bottom: calc(96px + env(safe-area-inset-bottom));
  z-index: 40; width: 72px; height: 72px; border-radius: 50%;
  display: grid; place-content: center; text-align: center; cursor: pointer;
  background: var(--mais); color: var(--sur-clair);
  border: var(--filet-w2) solid var(--filet); box-shadow: var(--shadow-lg);
  font-family: var(--display); font-size: .8rem; line-height: 1.05;
  transform: rotate(-8deg);
}
.idee:active { transform: rotate(-8deg) translate(2px, 2px); box-shadow: var(--shadow-md); }
/* Sur un écran large, le rail et l'en-tête laissent la place : le bouton
   se range au coin de la fenêtre, hors du chemin des cartes. */
@media (min-width: 861px) { .idee { bottom: 26px; right: 26px; } }
/* Il n'a de sens que sur le carnet. */
#view-ideas[hidden] ~ .idee, .view[hidden] .idee { display: none; }

/* ── filtres ──────────────────────────────────────────────────────── */
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 7px 6px; margin-bottom: 20px; }
.chip {
  font-family: var(--display); font-size: .76rem; letter-spacing: .03em; text-transform: uppercase;
  background: transparent; color: var(--ink-2);
  border: var(--filet-w) solid var(--filet); border-radius: 999px; padding: 4px 11px; cursor: pointer;
  transition: background .14s, color .14s, border-color .14s;
}
.chip:hover { border-color: var(--ink-3); }
.chip.is-on { background: var(--encre); border-color: var(--encre); color: var(--papier); }
[data-theme="dark"] .chip.is-on { background: var(--mais); border-color: var(--mais); color: var(--sur-clair); }

/* ── Les pastilles ─────────────────────────────────────────────────
   Les étiquettes étaient des bouts de scotch : kraft sur kraft, séparées
   par 1,4 degré d'inclinaison. Ce sont des gommettes de prix, une couleur
   par famille — ce que la base sait dire depuis 0001 et que l'écran
   ignorait. Pleines sur une fiche ; blanches tant qu'un filtre dort.

   Elles penchent, de deux degrés, une fois sur deux : c'est la seule
   chose qu'on ait gardée du scotch. */
.pastille {
  --p: var(--ink-3);
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 11px; border-radius: var(--r-pill);
  border: var(--filet-w) solid var(--filet);
  background: var(--p); color: var(--sur-sombre);
  font-family: var(--display); font-size: .75rem; letter-spacing: .03em;
  text-transform: uppercase; line-height: 1.25; white-space: nowrap;
  /* Une étiquette longue — « méditerranéen » — ne se coupe pas et sortait
     de la carte sur un téléphone de 375 px. Elle se tronque plutôt que de
     pousser le bord droit hors de la vitre. */
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  transform: rotate(-2deg);
}
.pastille:nth-child(even) { transform: rotate(2deg); }

/* La tomate d'aplat ne porte aucun texte lisible : ni le blanc (3,99 : 1)
   ni l'encre (4,33) n'atteignent le seuil AA à cette taille. On prend donc
   la tomate « lue », qui porte le blanc à 5,47 — et l'encre la nuit, où ce
   jeton s'éclaircit pour rester lisible comme texte. */
.pastille[data-famille="type"]     { --p: var(--tomate-lu); }
.pastille[data-famille="proteine"] { --p: var(--betterave); }
.pastille[data-famille="regime"]   { --p: var(--mais); color: var(--sur-clair); }
.pastille[data-famille="origine"]  { --p: var(--bleu); }
.pastille[data-famille="saison"]   { --p: var(--botte); }
.pastille[data-famille="autre"]    { --p: var(--surface); color: var(--ink); }
/* Sans famille — « pas encore », « au choix », « le jour même » : un
   choix neutre, qui dit qu'on n'a rien décidé. */
.pastille[data-famille=""]         { --p: var(--surface); color: var(--ink); }
/* La nuit, la tomate lue s'éclaircit : c'est l'encre qui s'y lit, comme
   sur le maïs. Même règle pour l'onglet actif du rail, qui prend le même
   aplat et tombait à 3,17 : 1 avec du blanc. */
[data-theme="dark"] .pastille[data-famille="type"],
[data-theme="dark"] .pastille--pick[data-famille="type"].is-on,
[data-theme="dark"] .nav-item.is-active { color: var(--sur-clair); }

/* Une pastille qui se choisit : blanche tant qu'elle dort, pleine dès
   qu'elle sert. Le contour garde la couleur de sa famille, pour qu'on
   sache ce qu'on s'apprête à poser. */
/* Midi / soir, dans le tiroir qui pose un plat. */
.pick-creneau { display: flex; gap: 8px; margin: 10px 0 2px; }

.pastille--pick {
  cursor: pointer;
  background: var(--surface); color: var(--ink);
  box-shadow: inset 0 0 0 0 transparent;
}
.pastille--pick:hover { box-shadow: var(--shadow-sm); }
.pastille--pick.is-on { background: var(--p); color: var(--sur-sombre); box-shadow: var(--shadow-sm); }
.pastille--pick[data-famille="regime"].is-on { color: var(--sur-clair); }
.pastille--pick[data-famille="autre"].is-on,
.pastille--pick[data-famille=""].is-on { background: var(--encre); color: var(--papier); }
.pastille--pick .ic { --ic: .85em; margin-left: 1px; }
.pastille--pick em {
  font-family: var(--body); font-style: normal; font-weight: 600;
  font-size: .78em; opacity: .6; letter-spacing: 0;
}

/* Les familles, dans le tiroir et dans le formulaire. */
.familles { display: flex; flex-direction: column; gap: 16px; }
.famille__mot {
  font-family: var(--display); font-size: .75rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); margin: 0 0 7px;
}
.famille__pastilles { display: flex; flex-wrap: wrap; gap: 7px 6px; }

/* ── carte plat ───────────────────────────────────────────────────── */
.grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); }
.card {
  /* Pas de content-visibility ici : combiné à loading="lazy", il empêche
     l'image d'être considérée comme proche de l'écran, et la couverture
     ne se charge jamais. La vignette allégée fait le travail. */
  position: relative; display: flex; flex-direction: column;
  background: var(--surface); border: var(--filet-w2) solid var(--filet); border-radius: var(--r-lg);
  overflow: hidden; box-shadow: var(--shadow-md); cursor: pointer;
  transition: transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s;
}
@media (hover: hover) {
  .card:hover { transform: translate(-1px, -1px); box-shadow: var(--shadow-lg); }
}
/* Le bleu affiche porte « au passe » : la gommette du plan l'annonce, le
   bouton de la carte le dit, et le filet de la carte le rappelle. */
.card.is-sent { box-shadow: var(--shadow-md), inset 0 0 0 2px var(--bleu); }
.card.is-dragging { opacity: .45; }
.card__cover {
  aspect-ratio: 16 / 10; background: var(--surface-2) center/cover no-repeat;
  border-bottom: var(--filet-w) solid var(--filet); position: relative;
  width: 100%; height: auto; object-fit: cover; display: block;
}
.card__cover--blank {
  display: grid; place-items: center; background: var(--tint);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
}
.card__cover--blank::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(-45deg, rgba(255,255,255,.05) 0 1px, transparent 1px 9px);
}
.card__initial {
  position: relative; z-index: 1;
  font-family: var(--display); font-weight: 400;
  font-size: 3.4rem; line-height: 1; color: rgba(255,255,255,.4); letter-spacing: -.05em;
  text-transform: uppercase;
}
.card__body { padding: 12px 13px 13px; display: flex; flex-direction: column; gap: 9px; flex: 1; }
/* Le titre d'une carte est le bouton qui l'ouvre : il se tabule, il
   s'annonce, et il garde exactement l'allure d'un titre. */
.card__open {
  font: inherit; color: inherit; letter-spacing: inherit;
  background: none; border: 0; padding: 0; margin: 0;
  text-align: left; cursor: pointer; width: 100%;
}

/* Le lien d'évitement : hors de l'écran jusqu'à ce qu'on le vise. */
.saut {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--ink); font-family: var(--display);
  padding: 12px 18px; border: 2px solid var(--filet); border-radius: 0 0 var(--r-md) 0;
  text-decoration: none;
}
.saut:focus { left: 0; }
.canvas:focus { outline: none; }

.card__title {
  font-family: var(--display); font-weight: 400;
  font-size: .96rem; line-height: 1.08; letter-spacing: -.015em; margin: 0;
}
.card__pastilles { display: flex; flex-wrap: wrap; gap: 5px; }
.card__meta {
  font-size: .78rem; font-weight: 600; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  display: flex; gap: 10px; flex-wrap: wrap;
}
.card__actions { display: flex; gap: 6px; margin-top: auto; }
.card__actions .btn { flex: 1; padding: 7px 10px; font-size: .85rem; }
.card__send.is-sent { background: var(--bleu); border-color: var(--filet); color: var(--sur-sombre); }
.card__edit { position: absolute; top: 8px; right: 8px; background: var(--surface); border: var(--filet-w) solid var(--filet); }

/* ── bandeaux horizontaux ─────────────────────────────────────────── */
.strip { margin-top: 34px; }
.strip__title {
  font-family: var(--display); font-weight: 400;
  font-size: 1.15rem; letter-spacing: -.02em; margin: 0 0 12px;
  display: flex; align-items: baseline; gap: 10px;
}
.strip__note { font-size: .78rem; font-weight: 600; color: var(--ink-3); }
.row-scroll {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(196px, 1fr);
  gap: 12px; overflow-x: auto; padding-bottom: 6px; scroll-snap-type: x proximity;
}
.row-scroll > * { scroll-snap-align: start; }
/* Les dernières fiches rangées comme un menu : entrée, plat, dessert. */
.courses-strips { display: flex; flex-direction: column; gap: 20px; }
.courses-strip__title {
  font-family: var(--display); font-size: .76rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-2); margin: 0 0 9px; display: flex; align-items: baseline; gap: 8px;
}
.courses-strip__title .muted { letter-spacing: 0; }

/* ══════════════════════ Le prochain repas ═════════════════════════
   Ce qu'on mange ensuite, en grand, avant la grille des sept jours. La
   photo tient la moitié gauche sur un écran large et passe au-dessus sur
   un téléphone ; la gommette déborde du coin, comme un prix collé sur un
   cageot. Les plats qui accompagnent — l'entrée, le dessert — se rangent
   sous le principal : ils font partie du même repas, pas d'un autre. */
.repas {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
  /* La photo doit être grande, pas immense : bornée, elle laisse la
     première ligne de la semaine visible sous elle. */
  grid-template-rows: minmax(210px, 300px);
  background: var(--surface); border: var(--filet-w2) solid var(--filet);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  margin: 6px 0 20px;
}
.repas__photo {
  display: block; width: 100%; height: 100%; object-fit: cover;
  border-right: var(--filet-w2) solid var(--filet);
  border-radius: calc(var(--r-lg) - 3px) 0 0 calc(var(--r-lg) - 3px);
}
.repas__photo--vide { background: var(--tint); }
.repas__corps { padding: 20px 22px; display: flex; flex-direction: column; gap: 10px; justify-content: center; }
/* La ligne « mardi 8 · midi · pour 4 » est un bouton : c'est le chemin le
   plus court vers les couverts, et l'œil s'y pose avant tout le reste. */
.repas__quand {
  font: inherit; font-size: .82rem; font-weight: 600; color: var(--ink-3); margin: 0;
  font-variant-numeric: tabular-nums;
  display: flex; align-items: center; gap: 4px;
  background: none; border: 0; padding: 2px 0; cursor: pointer; text-align: left;
}
.repas__quand .ic { --ic: .95em; }
.repas__quand:hover { color: var(--ink); }
.repas__nom { font-family: var(--display); font-weight: 400; font-size: clamp(1.5rem, 3.2vw, 2.1rem); line-height: .98; letter-spacing: -.025em; margin: 0; }
.repas__pastilles { display: flex; flex-wrap: wrap; gap: 7px 6px; }
.repas__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }

/* Ce qui vient avec : une entrée, un dessert. Une ligne par plat, cliquable,
   avec son rôle en italique — c'est ce qui distingue « la tarte du repas »
   de « la tarte, posée dimanche ». */
.repas__suite { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 2px; }
.repas__avec {
  font-family: var(--display); font-size: .75rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3); margin: 0;
}
.repas__plat {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-pill); padding: 4px 12px 4px 4px; cursor: pointer;
  font-size: .84rem; font-weight: 600; color: var(--ink);
}
.repas__plat:hover { box-shadow: var(--shadow-sm); }
.repas__plat img, .repas__vide {
  width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: none;
  border: var(--filet-w) solid var(--filet); display: block;
}
.repas__plat em { font-style: italic; font-weight: 500; color: var(--ink-3); font-size: .78rem; }

/* ── Le repas qu'on a laissé passer ───────────────────────────────
   À quinze heures, le déjeuner de jeudi est encore posé : soit on l'a
   mangé sans le dire, soit il a glissé au soir, soit on verra plus tard.
   L'app ne tranche pas à notre place, elle demande. */
.oublie {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px;
  background: color-mix(in srgb, var(--mais) 18%, var(--surface));
  border: var(--filet-w) dashed var(--filet); border-radius: var(--r-lg);
  padding: 16px 18px 16px 22px; margin: 6px 0 14px;
}
.oublie .gommette { position: static; margin: -4px 0; }
.oublie__dit { flex: 1; min-width: 12rem; }
.oublie__quoi { font-family: var(--display); font-size: 1rem; letter-spacing: -.015em; margin: 0; }
.oublie__plats { font-size: .84rem; font-weight: 600; color: var(--ink-2); margin: 3px 0 0; }
.oublie__actions { display: flex; flex-wrap: wrap; gap: 8px; }

@media (max-width: 720px) {
  .repas { grid-template-columns: 1fr; grid-template-rows: auto auto; }
  .repas__photo {
    aspect-ratio: 4 / 3; height: auto;
    border-right: 0; border-bottom: var(--filet-w2) solid var(--filet);
    border-radius: calc(var(--r-lg) - 3px) calc(var(--r-lg) - 3px) 0 0;
  }
  .repas__corps { padding: 16px; }
  .repas .gommette { width: 58px; height: 58px; font-size: .72rem; top: -14px; right: -8px; }
  .repas__actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* ── La semaine ───────────────────────────────────────────────────
   Sept jours empilés. Un jour vide reste une ligne : c'est ce qui
   distingue « rien de prévu » de « ce jour n'existe pas ».         */
.week { display: flex; flex-direction: column; gap: 8px; }
.wday {
  background: var(--surface); border: var(--filet-w2) solid var(--filet);
  border-radius: var(--r-lg); padding: 10px 12px 12px;
}
.wday.is-empty { background: transparent; border-style: dashed; padding-bottom: 10px; }
.wday.is-today { border-color: var(--accent); border-width: 2px; padding: 9px 11px 11px; }
.wday--loose { border-style: dashed; border-color: var(--edge); background: var(--surface-2); }
.wday__head { display: flex; align-items: center; gap: 9px; }
.wday__date {
  font-family: var(--display); font-weight: 400; font-size: 1.2rem;
  line-height: 1; color: var(--ink); min-width: 1.4em; text-align: center;
}
.wday.is-today .wday__date { color: var(--accent); }
.wday__name {
  font-family: var(--display); font-size: .78rem; letter-spacing: .01em;
  color: var(--ink-2);
}
.wday.is-today .wday__name { color: var(--accent); }
.wday__add {
  background: none; border: 0; cursor: pointer; padding: 6px 8px; margin: -6px -8px -6px 0;
  border-radius: var(--r-pill); color: var(--ink-3); font-size: .84rem; font-weight: 500;
}
.wday__add:hover { background: var(--surface-2); color: var(--ink); }
.wday.is-empty .wday__add { color: var(--ink-2); }

/* ── Un plat posé sur un jour ─────────────────────────────────────
   Une ligne, et rien de plus : la photo, le nom, un résumé qui se
   déplie, et un rond qui ouvre la feuille. Tout ce qui agit — les quatre
   réglages, cuisiner, servir — vit dans la feuille « Ce service ». Sur la
   ligne, la cible du doigt est la photo ou le nom ; deux ronds muets
   juste à côté se touchaient sans le vouloir, et « Servi » retire le
   plat. */
.wdish {
  display: grid; grid-template-columns: 52px minmax(0, 1fr) auto;
  gap: 11px; align-items: center;
  margin-top: 9px; padding: 8px;
  background: var(--paper); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-md); box-shadow: var(--shadow-sm);
}
.wdish__thumb {
  width: 52px; height: 52px; border-radius: 10px; object-fit: cover; display: block;
  border: var(--filet-w) solid var(--filet); box-sizing: border-box;
}
.wdish__texte { min-width: 0; display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.wdish__nom {
  font: inherit; font-family: var(--display); font-size: 1rem; line-height: 1.05;
  letter-spacing: -.015em; color: var(--ink);
  background: none; border: 0; padding: 0; cursor: pointer; text-align: left;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Le résumé est un bouton : c'est lui qui ouvre la feuille. Il ne se
   souligne pas, il se déplie — d'où le chevron. */
.wdish__reglages {
  font: inherit; font-size: .78rem; color: var(--ink-2);
  display: flex; align-items: center; gap: 4px; max-width: 100%;
  background: none; border: 0; padding: 2px 0; cursor: pointer; text-align: left;
}
.wdish__reglages .ic { --ic: .95em; color: var(--ink-3); }
.wdish__resume { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wdish__resume b { color: var(--tomate-lu); font-weight: 700; }
.wdish__resume i { font-style: italic; color: var(--ink-3); }
.wdish__gestes { display: flex; gap: 6px; }

/* Un geste rond, bordé : cuisiner, servir, et les deux flèches du
   compteur de couverts. */
.rond {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  display: grid; place-items: center; cursor: pointer;
  background: var(--surface); color: var(--ink);
  border: var(--filet-w) solid var(--filet); box-shadow: var(--shadow-sm);
  font-family: var(--display); font-size: 1.1rem;
}
.rond:active { transform: translate(2px, 2px); box-shadow: none; }
.rond--feu { color: var(--tomate-lu); }
.rond--servi { color: var(--botte-lu); }
/* Le rond de la ligne n'agit plus : il ouvre. Gris, donc, et non coloré —
   une couleur promettrait un geste, et c'est précisément ce qu'on retire. */
.rond--detail { color: var(--ink-3); }
.rond--detail:hover { color: var(--ink); }

/* ── La feuille d'un service ──────────────────────────────────────── */
.service { display: flex; flex-direction: column; gap: 18px; }
.service__plat {
  font-family: var(--display); font-size: 1.15rem; letter-spacing: -.02em; margin: 0;
}
.service__bloc { display: flex; flex-direction: column; }
/* Cuisiner et servir : côte à côte, écrits en toutes lettres. Ils étaient
   deux ronds muets sur la ligne de la semaine, à côté de la photo et du
   nom — on les touchait en visant autre chose. */
.service__gestes { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.service__gestes .btn { flex: 1 1 auto; justify-content: center; }
/* Ce que « Servi » fait vraiment, écrit sous le bouton : il retire le plat
   de la semaine, et c'est ce que personne ne devinait. */
.service__dit { font-size: .76rem; color: var(--ink-3); margin: 0; }
.compteur { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.compteur__n {
  font-family: var(--display); font-size: 1.5rem; min-width: 2ch; text-align: center;
  font-variant-numeric: tabular-nums;
}
.compteur__dit { font-size: .82rem; font-weight: 600; color: var(--ink-3); }

.blank {
  border: var(--filet-w) dashed var(--filet); border-radius: var(--r-lg);
  padding: 40px 24px; text-align: center; color: var(--ink-2);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
/* Le dessin est plus large que haut : un cageot, un panier, une casserole
   se reconnaissent à leur silhouette, pas à leur détail. */
.blank__dessin {
  width: 96px; height: 68px; color: var(--ink-3);
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.blank__geste { margin-top: 8px; }
.blank strong { font-family: var(--display); font-weight: 400; font-size: 1.25rem; letter-spacing: -.02em; color: var(--ink); }
.blank p { max-width: 42ch; margin: 0; }

/* ── Le tiroir : choisir un plat pour un jour ─────────────────────── */
.pick-list { display: flex; flex-direction: column; gap: 4px; max-height: 54vh; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.pick {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  background: var(--surface); border: var(--filet-w) solid var(--filet); border-radius: var(--r-md);
  padding: 8px 10px; cursor: pointer;
}
.pick:hover { border-color: var(--ink-3); }
.pick[data-already] { opacity: .5; }
.pick__thumb { width: 40px; height: 40px; border-radius: 9px; flex: none; background-size: cover; background-position: center; }
.pick__text { min-width: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
.pick__name {
  font-family: var(--display); font-weight: 400; font-size: .95rem; line-height: 1.1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pick__meta { font-size: .76rem; font-weight: 600; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.pick__state {
  font-family: var(--display); font-size: .75rem;
  color: var(--accent); flex: none;
}
.pick[data-already] .pick__state { color: var(--ink-3); }

/* ═══════════════════════════ LES COURSES ══════════════════════════ */
/* ── La progression ───────────────────────────────────────────────
   Une barre qu'on remplit, et le compte collé au bout en gommette. Elle
   verdit au dernier article : c'est le seul moment des courses où il se
   passe quelque chose. */
.avancee { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.barre {
  --p: 0;
  flex: 1; height: 22px; border-radius: var(--r-pill); overflow: hidden;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  box-shadow: var(--shadow-md);
}
.barre i {
  display: block; height: 100%; width: calc(var(--p) * 1%);
  background: var(--tomate); border-right: var(--filet-w) solid var(--filet);
  transition: width .25s cubic-bezier(.2,.8,.3,1), background .25s;
}
.barre.est-pleine i { background: var(--botte); border-right: 0; }
.avancee__n { position: static; flex: none; }

.extra-form { display: flex; gap: 8px; margin-bottom: 22px; }

/* ── Les rayons ───────────────────────────────────────────────────
   Une étiquette posée sur l'étal, pas un titre souligné : le nom du
   rayon dans un cadre, le compte en gommette, et le trait qui file
   jusqu'au bout de la ligne. */
.aisle { margin-bottom: 26px; }
.aisle__head {
  display: flex; align-items: center; gap: 9px; margin: 0 0 10px;
  font-family: var(--display); font-size: .76rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink);
}
.aisle__head b {
  font-weight: 400; letter-spacing: .1em;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-sm); padding: 5px 11px; box-shadow: var(--shadow-sm);
}
.aisle__head::after {
  content: ""; flex: 1; height: var(--filet-w); background: var(--filet); opacity: .2;
}
.aisle__count {
  order: 3; margin-left: 0; flex: none;
  display: grid; place-items: center; min-width: 26px; height: 26px; padding: 0 7px;
  border-radius: var(--r-pill); background: var(--mais); color: var(--sur-clair);
  border: 1.5px solid var(--filet); font-size: .72rem; font-variant-numeric: tabular-nums;
}

/* ── Une ligne de la liste ────────────────────────────────────────
   La case du système ne prend ni le filet ni la couleur du reste : on la
   cache et on en dessine une. Elle reste dans le document — c'est elle
   qui porte l'état, le clavier et le lecteur d'écran. */
/* `position: relative` ancre la vraie case — invisible, en absolu — à sa
   propre ligne. Sans cela, elle se cale sur le premier ancêtre positionné :
   dans la liste des courses, la page entière défile et personne ne le voit,
   mais dans un plein écran qui défile en interne, la case reste plantée où
   elle a été posée et devient inatteignable dès qu'on descend. */
.line {
  position: relative;
  display: flex; align-items: center; gap: 12px; padding: 10px 4px;
  border-bottom: 1.5px dashed var(--edge-soft); cursor: pointer;
}
.line:hover { background: color-mix(in srgb, var(--surface) 70%, transparent); }
.line__check { position: relative; display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; cursor: pointer; }
.line__ranger { flex: none; }
@media (max-width: 480px) {
  #shopping .line:has(.line__ranger) { flex-wrap: wrap; }
  #shopping .line__check { flex-basis: 100%; }
  #shopping .line__ranger { margin-left: 36px; }
}
.line input[type="checkbox"] {
  position: absolute; opacity: 0; width: 24px; height: 24px; margin: 0; cursor: pointer;
}
.case {
  width: 24px; height: 24px; flex: none; display: grid; place-items: center;
  border: var(--filet-w) solid var(--filet); border-radius: var(--r-sm);
  background: var(--surface); color: transparent; box-sizing: border-box;
  transition: background .12s, color .12s;
}
.case .ic { --ic: 1em; stroke-width: 3; }
.line input:checked ~ .case { background: var(--botte); color: var(--sur-sombre); }
.line input:focus-visible ~ .case { outline: 2px solid var(--tomate-lu); outline-offset: 2px; }
.line__qty {
  font-family: var(--display); font-size: .78rem; color: var(--accent);
  font-variant-numeric: tabular-nums;
  min-width: 62px; text-align: right; flex: none; align-self: baseline;
}
.line__item { flex: 1; }
.line__from { display: block; font-size: .8rem; color: var(--ink-3); }
.line.is-done .line__item, .line.is-done .line__qty { opacity: .42; text-decoration: line-through; }
.line__approx { color: var(--ink-3); font-size: .8rem; }

/* ═══════════════════════════ LA SÉANCE ════════════════════════════ */
/* ── Ce qu'on fait maintenant ─────────────────────────────────────
   Les repas du jour, chacun avec ses plats en pastilles. Décocher, c'est
   dire « pas celui-là tout de suite » — et la mise en place, le four et
   les quantités suivent. */
.choix {
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-md); box-shadow: var(--shadow-md);
  padding: 14px 16px; margin-bottom: 22px;
  display: flex; flex-direction: column; gap: 12px;
}
.choix__tete { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.choix__tete .famille__mot { margin: 0; }
.choix__n {
  font-family: var(--display); font-size: .75rem; color: var(--ink-2);
  font-variant-numeric: tabular-nums; margin-right: auto;
}
.choix__repas { display: flex; flex-direction: column; gap: 6px; }
.choix__quand { font-size: .8rem; font-weight: 600; color: var(--ink-2); margin: 0; }
.seance__rien { color: var(--ink-2); font-weight: 500; }

/* Cuisiner d'avance : le jour, ce qu'on sort, le four, puis les fiches.
   Les lignes cochables reprennent .line des courses — c'est le même geste,
   il n'y a pas de raison qu'il ait l'air différent. */

.sdays { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px; }
.sday {
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 7px 13px; border: var(--filet-w) solid var(--filet); border-radius: 999px;
  background: transparent; color: var(--ink-2); cursor: pointer;
  font-family: var(--display); font-size: .8rem; letter-spacing: .01em;
}
.sday:hover { border-color: var(--ink-3); }
.sday.is-on { background: var(--accent); border-color: var(--accent); color: var(--paper); }
.sday__n { font-size: .78rem; opacity: .65; }

.seance__h {
  display: flex; align-items: baseline; gap: 10px; margin: 0 0 8px;
  font-family: var(--display); font-size: .76rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-2);
  border-bottom: 1px solid var(--edge-soft); padding-bottom: 7px;
}
.seance__n { margin-left: auto; text-transform: none; letter-spacing: .04em; }

.mep { margin-bottom: 30px; }
.mep__aisle { margin-bottom: 16px; }
.mep__head {
  margin: 0 0 2px; font-family: var(--display); font-size: .75rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); font-weight: 400;
}

/* Le four énonce un fait ; il ne range pas la séance à la place du cuisinier. */
.four {
  margin-bottom: 30px; padding: 14px 16px;
  border: var(--filet-w) solid var(--filet); border-left: 3px solid var(--accent);
  border-radius: 3px; background: var(--surface);
}
.four__lede { margin: 0 0 8px; font-size: .78rem; color: var(--ink-3); }
.four__temp { margin: 0 0 4px; font-size: .86rem; color: var(--ink-2); }
.four__temp b {
  display: inline-block; min-width: 68px;
  color: var(--accent); font-weight: 400;
}

.sdish { margin-bottom: 30px; }
.sdish__name {
  padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--display); font-size: 1rem; color: var(--ink); letter-spacing: -.01em;
}
.sdish__name:hover { color: var(--accent); }
.sdish__meta {
  margin: 0 0 6px; font-size: .78rem; color: var(--ink-3);
}
.sdish__cook { margin-top: 10px; }

/* ══════════════════════ PALETTE DE COMMANDES ══════════════════════ */
.palette {
  position: fixed; inset: 0; z-index: 60; background: rgba(30,26,22,.5);
  display: grid; place-items: start center; padding-top: 12vh;
  animation: fade .16s ease;
}
@keyframes fade { from { opacity: 0; } }
.palette__box {
  width: min(560px, 92vw); background: var(--surface); border: var(--filet-w2) solid var(--filet);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg); overflow: hidden;
  animation: pop .2s cubic-bezier(.2,.8,.3,1);
}
@keyframes pop { from { opacity: 0; transform: translateY(-8px) scale(.98); } }
.palette__input { width: 100%; border: 0; border-bottom: 1px solid var(--edge-soft); padding: 15px 18px; background: none; font-size: 1rem; }
.palette__input:focus { outline: none; }
.palette__results { list-style: none; margin: 0; padding: 6px; max-height: 52vh; overflow-y: auto; }
.palette__results li {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border-radius: var(--r-sm); cursor: pointer; font-size: .92rem;
}
.palette__results li[aria-selected="true"] { background: var(--surface-2); }
.palette__results .role {
  font-size: .76rem; font-weight: 600;
  color: var(--ink-3); margin-left: auto;
}

/* ═══════════════════════════ FICHE PLAT ═══════════════════════════ */
.sheet {
  border: var(--filet-w2) solid var(--filet); border-radius: 18px; padding: 0; width: min(620px, 94vw);
  background: var(--surface); color: var(--ink); box-shadow: var(--shadow-lg);
}
.sheet::backdrop { background: rgba(30,26,22,.62); }
#dish-form { display: flex; flex-direction: column; gap: 15px; padding: 22px; max-height: 88dvh; overflow-y: auto; }
#dish-form > label { display: flex; flex-direction: column; gap: 6px; font-size: .82rem; color: var(--ink-2); font-weight: 550; }
.sheet__head { display: grid; grid-template-columns: 1fr auto; align-items: start; }
.sheet__head h2 { grid-column: 1; font-family: var(--display); font-weight: 700; font-size: 1.5rem; letter-spacing: -.015em; margin: 0; }
.sheet__head .eyebrow { grid-column: 1; }
.sheet__head .icon-btn { grid-column: 2; grid-row: 1 / span 2; }
.sheet__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.label-row { display: flex; align-items: baseline; gap: 8px; }
.label-row .muted { font-size: .8rem; font-weight: 400; }
.sheet__pair label { display: flex; flex-direction: column; gap: 6px; font-size: .82rem; color: var(--ink-2); font-weight: 550; }
.sheet__foot {
  display: flex; gap: 8px; align-items: center; position: sticky; bottom: -22px;
  background: var(--surface); padding: 12px 0 2px; border-top: 1px solid var(--edge-soft); margin-top: 4px;
}
.import { background: var(--surface-2); border: var(--filet-w) solid var(--filet); border-radius: var(--r-md); padding: 12px; }
.import > label { display: block; font-size: .82rem; color: var(--ink-2); font-weight: 550; margin-bottom: 7px; }
.import__row { display: flex; gap: 8px; align-items: flex-start; }
/* Le champ accepte un lien d'une ligne comme une recette entière collée :
   il grandit sous le texte plutôt que de le cacher. */
.import__row textarea { flex: 1; resize: vertical; min-height: 2.6rem; }
.import__row .btn { flex: none; }
.import__status { margin: 8px 0 0; font-size: .82rem; color: var(--ink-2); }
.import__status.is-error { color: var(--accent); }
/* ── la fiche en lecture ──────────────────────────────────────────── */
.sheet--wide { width: min(880px, 94vw); }
.detail { max-height: 90dvh; overflow-y: auto; overflow-anchor:none; }
#detail-modal { overflow:hidden; }
#detail-modal .detail { box-sizing:border-box; max-height:calc(90dvh - 4px); }
.detail__reading { display:flex; align-items:center; flex-wrap:wrap; gap:8px 12px; position:sticky; top:0; z-index:2; background:var(--surface); padding:8px 0; border-bottom:1px solid var(--edge-soft); }
.detail__reading > span { font-size:.85rem; color:var(--ink-2); }
.detail__reading [aria-pressed="true"] { background:var(--surface-2); border-color:var(--ink-2); }
@media (max-width:520px) { .detail__reading > span { flex-basis:100%; } }
.detail[data-reading-mode="quick"] .detail__cols { grid-template-columns:1fr; }
.detail[data-reading-mode="quick"] > .detail__cover,
.detail[data-reading-mode="quick"] > .detail__bar,
.detail[data-reading-mode="quick"] > .detail__pastilles,
.detail[data-reading-mode="quick"] > .detail__variants,
.detail[data-reading-mode="quick"] > .detail__collections { display:none; }
.detail__cover {
  display: block; width: 100%; aspect-ratio: 21 / 9; object-fit: cover;
  border-radius: var(--r-md); background: var(--surface-2); border: var(--filet-w) solid var(--filet);
}
.detail__bar { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.detail__meta { margin: 0; color: var(--ink-2); font-size: .88rem; }
.detail__pastilles { display: flex; flex-wrap: wrap; gap: 7px 6px; }
.detail__cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: 26px; align-items: start; }
.detail__title {
  font-family: var(--display); font-weight: 600; font-size: 1rem;
  margin: 0 0 10px; display: flex; align-items: baseline; gap: 8px;
}
.detail__title .muted { font-size: .8rem; }
.detail__ingredients { list-style: none; margin: 0; padding: 0; font-size: .9rem; }
.detail__ingredients li { padding: 6px 0; border-bottom: 1px dashed var(--edge-soft); }
.detail__steps { margin: 0; padding: 0 0 0 1.1em; font-size: .92rem; }
.detail__steps li { padding: 5px 0 9px; line-height: 1.55; }
.detail__steps li::marker { color: var(--accent); font-size: .82rem; }
.detail__notes p { margin: 0; color: var(--ink-2); font-size: .9rem; }
[data-private-tip] { overflow-wrap: anywhere; }
[data-private-tip] p { white-space: pre-wrap; }
[data-tip-details] { margin:8px 0; }
[data-tip-details] summary { cursor:pointer; }

/* ── favoris et étoiles ───────────────────────────────────────────── */
.card__fav {
  position: absolute; top: 8px; left: 8px; font-size: 1.05rem; line-height: 1;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
}
.card__fav.is-on, .btn.is-on { color: var(--accent); }
.btn.is-on { border-color: var(--accent); }
.stars { color: var(--mais-lu); white-space: nowrap; display: inline-flex; gap: 1px; align-items: center; }
.stars__off { display: inline-flex; gap: 1px; align-items: center; }
.stars__off { color: var(--ink-3); opacity: .38; }
.stars--pick { display: inline-flex; align-items: center; gap: 2px; }
.stars__btn {
  background: none; border: 0; cursor: pointer; padding: 2px;
  font-size: 1.2rem; line-height: 1; color: var(--ink-3); opacity: .4;
  transition: opacity .12s, color .12s, transform .12s;
}
.stars__btn.is-on { color: var(--mais-lu); opacity: 1; }
.stars--pick:hover .stars__btn:not(.is-on) { opacity: .75; }
.stars__btn:hover { transform: scale(1.16); opacity: 1; color: var(--mais-lu); }
.stars__note { margin-left: 8px; font-size: .78rem; }

@media (max-width: 720px) {
  .detail__cols { grid-template-columns: 1fr; gap: 18px; }
  .detail__cover { aspect-ratio: 16 / 10; }
}

.photo-field { border: var(--filet-w) solid var(--filet); border-radius: var(--r-md); padding: 12px; }
.photo-field > .label-row { display: flex; gap: 8px; align-items: baseline; font-size: .82rem; color: var(--ink-2); font-weight: 550; margin-bottom: 9px; }
.photo-field__row { display: flex; gap: 12px; align-items: center; }
.photo-field__thumb {
  width: 112px; flex: none; aspect-ratio: 16 / 10;
  background: var(--surface-2) center/cover no-repeat;
  border: var(--filet-w) solid var(--filet); border-radius: var(--r-sm);
}
.photo-field__thumb--blank {
  background:
    repeating-linear-gradient(-45deg, rgba(30,26,22,.10) 0 1px, transparent 1px 9px),
    var(--surface-2);
}
.photo-field__actions { display: flex; flex-wrap: wrap; gap: 8px; }
.tags-field { border: var(--filet-w) solid var(--filet); border-radius: var(--r-md); padding: 12px; margin: 0; }
.tags-field legend { font-size: .78rem; color: var(--ink-2); padding: 0 6px; font-weight: 600; }
.pastille-picker { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; }
.new-tag { display: flex; gap: 6px; }

/* ═════════════════════════════ DIVERS ═════════════════════════════ */
/* ── Le compte, quand le rail n'est pas là ────────────────────────── */
.icon-btn--account { display: none; flex: none; }
.account-list { display: flex; flex-direction: column; gap: 8px; }
.account-item {
  display: flex; flex-direction: column; gap: 3px; text-align: left; width: 100%;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-md); padding: 12px 14px; cursor: pointer;
}
.account-item:hover { border-color: var(--ink-3); }
.account-item span { font-weight: 600; }
.account-item small { color: var(--ink-3); font-size: .82rem; }
.account-item--danger span { color: var(--accent); }

/* Le magasin qu'on ajoute : une puce qui ne ressemble pas aux autres. */
.chip--ajout { border-style: dashed; color: var(--ink-3); }

/* Les outils des courses : trois boutons sur un écran large, un menu sur
   un téléphone — où ils prenaient deux rangs avant le premier article. */
.outils { display: flex; gap: 8px; align-items: center; }
.outils__menu { display: none; }
@media (max-width: 860px) {
  .outils__large { display: none; }
  .outils__menu { display: inline-flex; }
  .outils { margin-left: auto; }
}

/* ── Le trajet du magasin ─────────────────────────────────────────
   Le rang à gauche, en chiffres : c'est l'ordre qu'on lit, pas les noms. */
.trajet { display: flex; flex-direction: column; }
.trajet__ligne {
  display: grid; grid-template-columns: 2.2rem minmax(0, 1fr) auto auto;
  align-items: center; gap: 8px;
  padding: 7px 0; border-top: 1px solid var(--edge-soft);
}
.trajet__rang { font-size: .8rem; color: var(--ink-3); }
.trajet__nom { font-weight: 550; }
.trajet .icon-btn[disabled] { opacity: .3; cursor: default; }

/* ── L'équilibre de la semaine ────────────────────────────────────
   Un fait, pas un conseil : le compte à gauche, la remarque en italique. */
.balance {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  margin: 14px 2px 0; font-size: .8rem;
  color: var(--ink-3); letter-spacing: .04em;
}
.balance em { font-family: var(--body); font-size: .8rem; color: var(--tomate-lu); font-style: italic; }

/* ── Le placard ───────────────────────────────────────────────────
   Une ligne sans case à cocher : ce n'est pas à acheter, c'est déjà là. */
.placard { display: flex; flex-direction: column; }
.line--placard {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px;
}
.line--placard.is-manquant .line__item { color: var(--tomate-lu); }
.placard__lede { margin: 2px 0 8px; font-size: .82rem; }
.aisle--placard { opacity: .85; }

/* ── Le journal de cuisson ────────────────────────────────────────
   Une ligne par service, groupée par mois. Le jour tient dans une
   colonne fixe pour que les dates s'alignent en lisant vite. */
.journal { display: flex; flex-direction: column; gap: 18px; }
.journal__head {
  font-family: var(--display); font-weight: 600; font-size: 1rem;
  text-transform: capitalize; margin: 0 0 6px;
}
.journal__ligne {
  display: grid; grid-template-columns: 2.2rem minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px;
  padding: 7px 0; border-top: 1px solid var(--edge-soft);
}
.journal__jour {
  font-size: .78rem; color: var(--ink-3); text-align: right;
}
.journal__plat {
  background: none; border: 0; padding: 0; cursor: pointer; text-align: left;
  font: inherit; color: inherit; font-weight: 550;
}
.journal__plat:hover { text-decoration: underline; }

/* ── Le témoin réseau ─────────────────────────────────────────────── */
.sync.is-offline {
  color: var(--tomate-lu); border-color: var(--tomate-lu);
  background: color-mix(in srgb, var(--tomate) 10%, transparent);
}
.sync.is-pending {
  color: var(--mais-lu); border-color: var(--mais-lu);
  background: color-mix(in srgb, var(--mais) 14%, transparent);
}
.sync.is-rejected { color: var(--tomate); }
.sync.is-rejected::before { background: var(--tomate); }
/* Une ligne de courses dont l'écriture attend encore le réseau. */
.line.is-waiting .line__item::after {
  content: " · pas encore envoyé";
  font-size: .7em; color: var(--mais-lu);
}

/* ═══════════════════════════ LE GESTE ═════════════════════════════ */

/* ── Choisir : le feuilletage ─────────────────────────────────────── */
.deck {
  position: fixed; inset: 0; z-index: 80; background: var(--ground);
  display: flex; flex-direction: column;
  padding: calc(10px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom));
}
.deck__head { display: flex; align-items: center; gap: 10px; flex: none; }
.deck__count { font-size: .8rem; font-weight: 600; color: var(--ink-3); margin: 0; }
.deck__stage { flex: 1; position: relative; display: grid; place-items: center; margin: 14px 0; min-height: 0; }

.deck__under {
  position: absolute; width: min(340px, 100%); height: min(100%, 520px);
  border-radius: 22px; background: var(--surface-2); border: var(--filet-w) solid var(--filet);
}
.deck__under--1 { transform: rotate(-2.4deg) translateY(6px) scale(.97); }
.deck__under--2 { transform: rotate(1.8deg) translateY(11px) scale(.94); }

.deck__card {
  position: relative; width: min(340px, 100%); height: min(100%, 520px);
  border-radius: 22px; overflow: hidden; background: var(--paper);
  border: var(--filet-w) solid var(--filet); box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; touch-action: pan-y;
  transition: transform .3s cubic-bezier(.2,.8,.3,1), opacity .3s;
  cursor: grab; user-select: none;
}
.deck__card:active { cursor: grabbing; }
/* Le bord dit ce que le geste va faire avant qu'on lâche. */
.deck__card.is-put { border-color: var(--botte); box-shadow: 0 0 0 3px color-mix(in srgb, var(--botte) 40%, transparent), var(--shadow-lg); }
.deck__card.is-skip { border-color: var(--ink-3); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink-3) 34%, transparent), var(--shadow-lg); }

.deck__img { flex: 1; min-height: 0; background-size: cover; background-position: center; position: relative; }
.deck__tag {
  position: absolute; top: 12px; left: 12px;
  font-family: var(--display); font-size: .75rem;
  background: rgba(30,26,22,.66); color: var(--papier); padding: 4px 10px; border-radius: var(--r-pill);
}
.deck__heart { position: absolute; top: 12px; right: 12px; color: var(--tomate); font-size: 1.2rem; }
.deck__pad { flex: none; padding: 14px 16px 16px; }
.deck__name {
  font-family: var(--display); font-weight: 900; font-size: 1.5rem; line-height: 1.04;
  letter-spacing: -.02em; margin: 0;
}
.deck__meta { font-size: .8rem; color: var(--ink-3); margin-top: 7px; }
.deck__stars { margin-top: 6px; }
.deck__ing { font-size: .84rem; color: var(--ink-2); margin-top: 9px; line-height: 1.45; }

.deck__end { text-align: center; color: var(--ink-3); display: grid; gap: 12px; justify-items: center; }
.deck__end strong { font-family: var(--display); font-weight: 700; font-size: 1.4rem; color: var(--ink); }

.deck__acts { flex: none; display: flex; align-items: center; justify-content: center; gap: 18px; }
.round {
  width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center;
  font-size: 1.2rem; background: var(--surface); border: var(--filet-w) solid var(--filet);
  cursor: pointer; color: var(--ink-2); box-shadow: var(--shadow-sm);
  transition: transform .12s cubic-bezier(.2,.8,.3,1);
}
.round:active { transform: translateY(1px) scale(.96); }
.round--big { width: 66px; height: 66px; font-size: 1.6rem; background: var(--tomate); border-color: var(--tomate); color: var(--papier); }
.deck__hint {
  flex: none; text-align: center; margin-top: 12px;
  font-size: .78rem; font-weight: 600; color: var(--ink-3);
}
@media (hover: hover) { .deck__hint { visibility: hidden; } }

/* ── Cuisiner : le plein écran ────────────────────────────────────── */
/* ── Cuisiner ─────────────────────────────────────────────────────
   Le dernier écran sombre. Le fond d'encre venait du kraft — il fallait
   bien que quelque chose cadre. Sur papier, ce sont les filets qui
   cadrent, et un plan de travail éclairé se lit mieux qu'un écran noir
   quand on a de la farine sur les doigts.

   Une règle guide tout l'écran : ce qui doit se lire à un mètre est
   grand, et rien d'autre ne l'est. */
.cook {
  position: fixed; inset: 0; z-index: 85;
  background: var(--ground); color: var(--ink);
  display: flex; flex-direction: column;
  padding-top: env(safe-area-inset-top);
}
.cook__head {
  display: flex; align-items: center; gap: 10px; padding: 12px 16px; flex: none;
  border-bottom: var(--filet-w) solid var(--filet);
}
.cook__name {
  font-family: var(--display); font-size: 1.05rem; letter-spacing: -.015em; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Le minuteur est un badge : c'est le seul chiffre de l'écran qui bouge
   tout seul, et on le cherche du regard. */
.cook__timer {
  font-family: var(--display); font-size: 1.05rem; font-variant-numeric: tabular-nums;
  margin: 0; padding: 5px 11px; flex: none;
  background: var(--mais); color: var(--sur-clair);
  border: var(--filet-w) solid var(--filet); border-radius: var(--r-sm);
  box-shadow: var(--shadow-sm);
}
.cook__avancee { display: flex; align-items: center; gap: 12px; padding: 14px 16px 0; flex: none; }
.cook__etape { position: static; flex: none; }

.cook__body { flex: 1; overflow-y: auto; padding: 20px 16px 8px; }
.cook__k {
  font-family: var(--display); font-size: .78rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); margin: 0;
}
.cook__scaled { color: var(--tomate-lu); letter-spacing: .02em; }
/* L'étape, à un mètre du plan de travail. */
.cook__x {
  font-size: clamp(1.3rem, 5.2vw, 1.55rem); font-weight: 500;
  line-height: 1.32; margin: 10px 0 0; max-width: 34ch;
}

/* Ce qu'on sort pour cette étape : une carte cochable, comme la liste des
   courses — c'est le même geste, il n'y a pas de raison qu'il diffère. */
.cook__ing {
  margin-top: 24px; padding: 14px 16px;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-md); box-shadow: var(--shadow-md);
}
.cook__ing .cook__k { margin-bottom: 6px; }
.cook__ing .ing {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer; padding: 9px 0;
  color: var(--ink); font-size: 1rem;
  border-bottom: 1.5px dashed var(--edge-soft);
}
.cook__ing .ing:last-child { border-bottom: 0; }
.cook__ing .ing i {
  width: 24px; height: 24px; border-radius: var(--r-sm);
  border: var(--filet-w) solid var(--filet);
  flex: none; display: grid; place-items: center; font-style: normal;
  color: transparent; box-sizing: border-box; transition: background .12s, color .12s;
}
.cook__ing .ing--on { color: var(--ink-3); text-decoration: line-through; }
.cook__ing .ing i .ic { --ic: 1em; stroke-width: 3; }
.cook__ing .ing--on i { background: var(--botte); border-color: var(--filet); color: var(--sur-sombre); }

/* La note d'après-coup : au pointillé, parce qu'elle n'est pas de la
   recette — c'est ce qu'on s'est dit la dernière fois. */
.cook__notes {
  margin-top: 20px; padding: 12px 14px; border-radius: var(--r-md);
  border: var(--filet-w) dashed var(--filet);
  font-size: .92rem; color: var(--ink-2);
}

.cook__foot {
  flex: none; display: flex; gap: 8px; align-items: center;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  border-top: var(--filet-w) solid var(--filet); background: var(--surface);
}
.cook__foot .btn--primary { flex: 1; justify-content: center; }
.cook__fin { color: var(--botte-lu); }
.cook__foot .btn:disabled { opacity: .35; box-shadow: none; }

/* ── Au magasin ────────────────────────────────────────────────────
   Presque tout vient du mode cuisine : c'est le même plein écran, le même
   pas-à-pas, le même geste de cocher. Ne restent ici que les trois choses
   qui diffèrent. */

/* Le rayon se lit de loin, comme l'étape — mais en plus gros : on le
   cherche du regard en poussant le caddie, pas en se penchant. */
.magasin .cook__x { font-family: var(--display); font-size: clamp(1.6rem, 7vw, 2.1rem); }

/* Le compteur du rayon prend la place du chronomètre. Vidé, il passe au
   vert : c'est le signal qu'on peut avancer. */
#magasin-reste { min-width: 2.4ch; text-align: center; }
#magasin-reste.est-fini { background: var(--botte); color: var(--sur-sombre); }

/* Les lignes sont celles de la liste, mais posées à bout de bras. Elles
   ne sont pas dans une carte comme les ingrédients du mode cuisine : une
   carte imbriquée empêchait le corps de connaître sa propre hauteur, et
   les derniers articles d'un long rayon restaient hors d'atteinte. */
.magasin__liste {
  margin-top: 22px; padding: 6px 16px;
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  border-radius: var(--r-md); box-shadow: var(--shadow-md);
}
.magasin__liste .line { padding: 13px 0; font-size: 1.05rem; }
.magasin__liste .line:last-child { border-bottom: 0; }
/* De quoi atteindre la dernière case sans que le pied la recouvre. */
.magasin .cook__body { padding-bottom: 28px; }

/* Le rayon est vidé : « Rayon suivant » devient l'endroit où aller. Le
   fond et le texte sont posés ici, pas empruntés à `btn--primary` — deux
   variantes de bouton permutées à la volée laissaient le fond derrière. */
.magasin .cook__foot .btn.est-appel {
  flex: 1; justify-content: center;
  background: var(--encre); color: var(--papier); border-color: var(--encre);
  box-shadow: var(--shadow-md);
}

/* Le bouton qui part en courses : l'action de l'écran, pas un réglage,
   donc hors du menu d'outils où les trois autres se rangent. */
.courses__partir { gap: 8px; }
.courses__partir .ic { width: 19px; height: 19px; }

/* Le bouton qui lance la durée lue dans l'étape. Discret : c'est une
   proposition, pas l'action principale de l'écran. */
.cook__minuteur { margin-top: 14px; gap: 8px; }
.cook__minuteur .ic { width: 18px; height: 18px; color: var(--tomate-lu); }

/* La vignette laisse défiler et zoomer la page. Un départ latéral
   volontaire reste la prise du déplacement ; glisser.js distingue les gestes. */
[data-glisser] { touch-action: pan-y pinch-zoom; cursor: grab; }
[data-glisser]:active { cursor: grabbing; }
.est-souleve { opacity: .3; }

/* La copie qui suit le doigt. Hors de tout flux, et transparente au
   pointeur : sans quoi elle se désignerait elle-même comme cible. */
.glisse__fantome {
  position: fixed; z-index: 97; pointer-events: none;
  border-radius: var(--r-sm); object-fit: cover;
  box-shadow: var(--shadow-lg); transform-origin: center;
  outline: var(--filet-w2) solid var(--encre);
}
/* Pendant le geste, plus rien d'autre ne se sélectionne ni ne se clique. */
body.glisse-en-cours { user-select: none; }
/* La barre d'onglets couvre le bas de l'écran, donc les dernières fentes :
   elle sort du jeu le temps du geste, et le dit en s'effaçant. Le doigt la
   traverse pour atteindre ce qu'il y a dessous. */
body.glisse-en-cours .tabbar { pointer-events: none; opacity: .35; }
/* De quoi faire défiler jusqu'à la dernière fente sans qu'elle reste
   coincée derrière la barre. */
body.glisse-en-cours #view-week { padding-bottom: 96px; }

/* Les fentes : fermées au repos, ouvertes quand un plat est en l'air.
   Côte à côte et courtes — deux blocs pleine largeur par jour ajoutaient
   près de six cents pixels et repoussaient tout hors de la vitre, si bien
   qu'aucune fente n'était plus atteignable sur un téléphone. */
/* Sous l'en-tête du jour, et non après ses plats : on dépose « dans »
   un jour, et la première fente doit être à portée sans défiler. */
.fentes { display: none; gap: 6px; margin: 6px 0 2px; }
body.glisse-en-cours .fentes { display: grid; grid-template-columns: 1fr 1fr; }
.fente {
  padding: 7px 8px;
  border: var(--filet-w2) dashed var(--edge-soft); border-radius: var(--r-md);
  font-family: var(--display); font-size: .74rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); text-align: center;
}
.fente.est-visee {
  border-style: solid; border-color: var(--tomate-lu);
  background: var(--surface); color: var(--tomate-lu);
}

/* ── Les minuteurs ─────────────────────────────────────────────────
   Au-dessus du mode cuisine (80) et sous le toast (90) : un minuteur qui
   tourne doit rester lisible pendant qu'on cuisine, sans masquer le
   message qui répond à ce qu'on vient de faire. */
/* Ancré des deux côtés, et non par `left: 50%` : un élément fixe qui ne
   fixe que sa gauche n'a que la moitié de l'écran pour s'étendre, et deux
   cuissons passaient à la ligne sur un téléphone alors qu'elles tenaient
   largement. `pointer-events` rendu aux pastilles seules, pour que la
   bande vide ne bloque pas ce qu'il y a dessous. */
.minuteurs {
  position: fixed; left: 0; right: 0; bottom: 78px; z-index: 86;
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  padding: 0 12px; pointer-events: none;
}
.minuteur { pointer-events: auto; }
.cook:not([hidden]) ~ .minuteurs { bottom: 96px; }
.minuteur {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 7px 12px; border-radius: 999px; font: inherit;
  background: var(--surface); color: var(--ink);
  border: var(--filet-w2) solid var(--encre); box-shadow: var(--shadow-md);
}
.minuteur__reste { font-family: var(--mono); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Court, pour que deux cuissons tiennent côte à côte sur un téléphone :
   à seize caractères, le riz et la sauce passaient à la ligne. */
.minuteur__quoi {
  max-width: 9ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .8rem; color: var(--ink-2);
}
/* Fini : l'aplat tomate, celui qu'on voit du bout de la cuisine. */
.minuteur--fini {
  background: var(--tomate); color: var(--sur-sombre); border-color: var(--encre);
}
.minuteur--fini .minuteur__quoi { color: var(--sur-sombre); opacity: .85; }
@media (prefers-reduced-motion: no-preference) {
  .minuteur--fini { animation: sonne 1s cubic-bezier(.2,.8,.3,1) infinite; }
}
@keyframes sonne {
  0%, 100% { transform: none; }
  8% { transform: translateY(-3px) rotate(-1.5deg); }
  16% { transform: translateY(0) rotate(1.5deg); }
  24% { transform: none; }
}

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  background: var(--encre); color: var(--papier); padding: 11px 20px; border-radius: 999px;
  font-size: .88rem; font-weight: 550; z-index: 90; box-shadow: var(--shadow-lg);
  animation: toast .28s cubic-bezier(.2,.8,.3,1);
}
[data-theme="dark"] .toast { background: var(--accent); color: var(--papier); }
/* « Annuler », dans le toast. C'est le seul endroit de l'app où l'on
   revient en arrière : il se voit, sans crier. */
.toast { display: inline-flex; align-items: center; gap: 14px; }
.toast__action {
  font: inherit; font-family: var(--display); cursor: pointer;
  background: none; border: 0; padding: 0;
  color: var(--mais); text-decoration: underline; text-underline-offset: 3px;
}
@keyframes toast { from { opacity: 0; transform: translate(-50%, 12px); } }
.tabbar { display: none; }

/* ══════════════════════════ L'INVITÉ ══════════════════════════════
   Deux personnes, deux écrans. Celui qui cuisine veut tout ; celui qui
   mange veut savoir ce qu'il y a au menu et pouvoir dire si ça lui dit.

   Presque tout se joue ici, à partir d'un attribut sur la racine : ce
   qui ne sert pas à qui mange s'efface, et rien n'est reconstruit. Le
   jour où l'invité voudra une chose de plus, c'est une ligne.

   Ce n'est pas une permission : l'invité repasse cuisinier par le menu,
   et le CSS ne protège rien. C'est un réglage de confort. */

/* Le rail, la barre d'onglets réduite, et tout ce qui écrit. */
[data-role="invite"] .station,
[data-role="invite"] #new-dish,
[data-role="invite"] #clear-pass,
[data-role="invite"] .wday__add,
[data-role="invite"] .wdish__gestes,
[data-role="invite"] .wdish__reglages,
[data-role="invite"] .card__edit,
[data-role="invite"] .card__actions,
[data-role="invite"] .idee,
[data-role="invite"] .tab[data-view="courses"],
[data-role="invite"] .tab[data-view="seance"],
[data-role="invite"] .repas__actions,
[data-role="invite"] #oublie { display: none; }

/* Sans rail, le plan de travail prend toute la vitre. */
[data-role="invite"] .shell { grid-template-columns: 1fr; }
/* Deux onglets suffisent : le menu, et le carnet pour y piocher. */
[data-role="invite"] .tabbar { grid-template-columns: repeat(2, 1fr); display: grid; }

/* Le titre de la semaine dit ce qu'elle est pour qui la lit. */
[data-role="invite"] .week { pointer-events: none; }
[data-role="invite"] .week .reactions { pointer-events: auto; }
[data-role="invite"] .wdish { grid-template-columns: 52px minmax(0, 1fr); }

/* ── Les réactions ────────────────────────────────────────────────
   Trois réponses, pas cinq : ça me tente, d'accord, pas ce soir. Une
   échelle plus fine demanderait de réfléchir, alors qu'on répond en
   passant, entre deux portes. */
.reactions { display: flex; gap: 6px; align-items: center; margin-top: 6px; }
.reaction {
  display: inline-grid; place-items: center; cursor: pointer;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--surface); color: var(--ink-2);
  border: var(--filet-w) solid var(--filet);
}
.reaction:hover { box-shadow: var(--shadow-sm); }
.reaction.is-on { background: var(--tomate-lu); color: var(--sur-sombre); box-shadow: var(--shadow-sm); }
.reaction.is-on[data-reaction="oui"] { background: var(--botte); }
.reaction.is-on[data-reaction="non"] { background: var(--ink-2); }
.reaction .ic { --ic: 1.05em; }
/* Ce que l'invité a répondu, vu par le cuisinier : une mention, pas un
   bouton — il ne répond pas à la place de l'autre. */
.reaction--dit { cursor: default; pointer-events: none; }
.reactions__dit { font-size: .78rem; font-weight: 600; color: var(--ink-2); }

/* ── Le choix du rôle, à la connexion ─────────────────────────────── */
/* Deux choix, deux boutons. Pas de radio puis « valider » : la question
   n'a qu'une réponse à donner, le clic la donne. */
.roles { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.role {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; align-items: center;
  width: 100%; padding: 14px; text-align: left; cursor: pointer;
  font: inherit; color: inherit; border-radius: var(--r-md);
  background: var(--surface); border: var(--filet-w) solid var(--filet);
  box-shadow: var(--shadow-sm);
  transition: transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s;
}
.role:hover, .role:focus-visible {
  box-shadow: var(--shadow-md); border-color: var(--tomate-lu); transform: translate(-1px, -1px);
}
.role .ic { grid-row: span 2; width: 28px; height: 28px; color: var(--tomate-lu); }
.role__nom { font-family: var(--display); font-size: 1rem; }
.role__dit { font-size: .82rem; color: var(--ink-2); }

/* ═══════════════════════════ RESPONSIVE ═══════════════════════════ */
@media (max-width: 860px) {
  :root { --gutter: 16px; }
  .login { grid-template-columns: 1fr; }
  .login-art { min-height: 40vh; border-right: 0; border-bottom: var(--filet-w2) solid var(--filet); }
  .etal { width: min(258px, 70vw); }
  .etal__gommette { right: -12px; top: -16px; width: 68px; height: 68px; font-size: .75rem; }
  .shell { grid-template-columns: 1fr; grid-template-rows: 1fr auto; height: 100dvh; }
  .station { display: none; }
  .icon-btn--account { display: grid; }
  .canvas { padding-top: env(safe-area-inset-top); }
  .canvas__head { padding: 10px 0; }
  .canvas__head-inner { gap: 8px; }
  .canvas__head .sync { display: inline-flex; font-size: .7rem; padding: 6px; }
  .canvas__head .btn--primary { padding: 9px 12px; }
  .view { padding: 18px var(--gutter) 40px; }
  .carnet__tools { width: 100%; }
  .carnet__tools .field { flex: 1; width: auto; }
  /* Quatre écrans depuis la séance : à trois colonnes le quatrième onglet
     tombait sur une deuxième ligne et mangeait le bas de la page. */
  /* Au-dessus de la barre d'onglets, qui n'existe qu'ici et mange le bas
     de l'écran. `env()` vaut zéro tant que l'app n'est pas installée. */
  .minuteurs { bottom: calc(92px + env(safe-area-inset-bottom)); }
  .tabbar {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px;
    background: var(--surface); border-top: 2.5px solid var(--filet);
    padding: 7px 7px calc(7px + env(safe-area-inset-bottom));
  }
  /* L'icône au-dessus du mot : côte à côte, « Semaine » et sa pastille
     poussaient « Séance » hors de l'écran sur un iPhone. */
  .tab {
    position: relative;
    background: none; border: 2px solid transparent; cursor: pointer;
    padding: 6px 2px; border-radius: var(--r-md);
    color: var(--ink-2); font-family: var(--display); font-size: .75rem;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-width: 0; white-space: nowrap;
  }
  .tab .ic { --ic: 1.25em; }
  .tab .badge { position: absolute; top: 2px; right: max(4px, 18%); margin: 0; }
  .tab.is-active {
    background: var(--tomate-lu); color: var(--sur-sombre);
    border-color: var(--filet); box-shadow: 2px 2px 0 var(--ombre);
  }
  .tab .badge { margin-left: 0; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
  .card__body { padding: 10px; }
}

/* ════════════════════════════════════════════════════════════════════
   TÉLÉPHONE — réglé sur un iPhone 15 Pro (393 × 852 pt)
   Trois choses comptent : le pouce (44 pt de cible), l'Île dynamique et
   la barre d'accueil (env(safe-area-inset-*)), et le fait qu'une modale
   plein écran vaut mieux qu'une boîte flottante sur un écran de 6,1".
   ════════════════════════════════════════════════════════════════════ */
@media (max-width: 480px) {
  :root { --gutter: 14px; }
  /* Les modales deviennent des feuilles qui montent du bas. */
  .sheet, .sheet--wide {
    width: 100vw; max-width: 100vw; max-height: 94dvh;
    margin: auto 0 0;                    /* la feuille se colle en bas de l'écran */
    border-radius: 20px 20px 0 0; border-bottom: 0;
  }
  #dish-form, .sheet__body {
    padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
    max-height: 94dvh; overscroll-behavior: contain;
  }
  .sheet__foot {
    bottom: calc(-18px - env(safe-area-inset-bottom));
    padding-bottom: calc(10px + env(safe-area-inset-bottom));
    flex-wrap: wrap;
  }
  .sheet__foot .btn { flex: 1 1 auto; text-align: center; }
  .sheet__foot .spacer { display: none; }

  /* Le pouce ne vise pas à 30 px. */
  .icon-btn { width: 40px; height: 40px; }
  .btn { padding: 11px 15px; }
  .btn--small { padding: 9px 13px; }
  .stars__btn { padding: 7px 5px; font-size: 1.5rem; }
  .chip, .pastille--pick { padding: 8px 12px; }

  /* En-tête : l'Île dynamique mange le haut de l'écran. */
  .canvas__head { padding: 8px 0; }
  .canvas__head-inner { gap: 6px; }
  .canvas__head .btn--primary { padding: 10px 12px; font-size: .88rem; }
  .omni__label { font-size: .84rem; }
  .omni .kbd { display: none; }
  .view { padding: 16px var(--gutter) 42px; }

  /* Le carnet : recherche pleine largeur, les deux boutons dessous. */
  .carnet__head { gap: 12px; margin-bottom: 14px; }
  .carnet__tools { flex-wrap: wrap; }
  .carnet__tools .field { flex: 1 1 100%; width: 100%; }
  .carnet__tools .btn { flex: 1 1 0; }
  .display { font-size: 1.85rem; }

  /* Deux cartes par ligne tiennent dans 393 pt, mais serrées. */
  .grid { grid-template-columns: repeat(auto-fill, minmax(146px, 1fr)); gap: 9px; }
  /* Le titre d'une carte est le bouton qui l'ouvre : il se tabule, il
   s'annonce, et il garde exactement l'allure d'un titre. */
.card__open {
  font: inherit; color: inherit; letter-spacing: inherit;
  background: none; border: 0; padding: 0; margin: 0;
  text-align: left; cursor: pointer; width: 100%;
}

/* Le lien d'évitement : hors de l'écran jusqu'à ce qu'on le vise. */
.saut {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--ink); font-family: var(--display);
  padding: 12px 18px; border: 2px solid var(--filet); border-radius: 0 0 var(--r-md) 0;
  text-decoration: none;
}
.saut:focus { left: 0; }
.canvas:focus { outline: none; }

.card__title { font-size: .95rem; }
  .card__meta { font-size: .8rem; }
  .row-scroll {
    grid-auto-columns: minmax(168px, 1fr);
    margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter);   /* les cartes filent jusqu'au bord */
    scroll-padding-left: var(--gutter);
  }
  .strip { margin-top: 26px; }
  .courses-strips { gap: 16px; }

  /* La semaine, au pouce. */
  .wday__head { gap: 8px; }
  /* La fiche en lecture, en une colonne, sans débordement horizontal. */
  .detail__cover { aspect-ratio: 3 / 2; }
  .detail__bar { gap: 10px; }
  .detail__steps { padding-left: 1.25em; }
  .detail__steps li { padding: 6px 0 10px; }

  .toast { bottom: calc(78px + env(safe-area-inset-bottom)); width: max-content; max-width: 88vw; }
}

/* ════════════════════════════════════════════════════════════════════
   GRAND ÉCRAN — le passe sur un 27", un ultra-large ou un 4K
   Le défaut de l'app était réglé sur un portable : une colonne de
   1 080 px collée à gauche, et sur une vitre de 3 840 px les deux tiers
   de l'écran restaient du fond nu. Trois paliers y répondent, et à
   chacun deux choses bougent — la colonne de travail gagne un cran, et
   ce qui était une pile devient des colonnes. Ce qui ne bouge pas : la
   mesure d'une ligne de texte, qui ne se lit pas mieux à 2 000 px.
   ════════════════════════════════════════════════════════════════════ */
@media (min-width: 1280px) {
  :root { --view-max: 1240px; --gutter: 36px; --station-w: 252px; }
  /* Le panneau de droite de l'accueil grandit avec la vitre, le formulaire
     non : centré, il ne se retrouve pas seul contre son bord gauche. */
  .login-form { margin-inline: auto; }

  /* Un champ de recherche de deux mètres de long ne cherche pas mieux.
     Il garde sa mesure, et les actions rejoignent le bord droit de la
     colonne de travail plutôt que de flotter au milieu. */
  .omni { flex: 0 1 620px; margin-right: auto; }
  /* Même raison pour la ligne « ajouter au chariot ». */
  .extra-form { max-width: 640px; }
}

@media (min-width: 1600px) {
  :root { --view-max: 1560px; --gutter: 44px; --station-w: 272px; }

  /* La semaine sur deux colonnes. Sept jours empilés sur 1 500 px, c'est
     sept bandes de 90 px de haut et une page qui descend pour rien. */
  .week {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 16px; align-content: start;
  }
  /* Les plats sans jour ne sont pas un huitième jour : ils gardent la
     ligne entière, sous la grille. */
  .wday--loose { grid-column: 1 / -1; }

  /* Les courses en colonnes de rayons. Un rayon ne se coupe jamais en
     deux : on ne lit pas la fin des surgelés en haut de la colonne d'à
     côté, la liste se tient au chariot. */
  #shopping { columns: 2; column-gap: calc(var(--gutter) + 12px); }
  .aisle { break-inside: avoid; }

  /* La séance : la mise en place et le four d'un côté, les fiches de
     l'autre. Ce qu'on sort une fois pour toute la séance reste sous les
     yeux pendant qu'on descend les étapes. */
  #seance {
    display: grid;
    /* La colonne des fiches s'arrête à une mesure lisible : une étape de
       recette écrite sur 1 600 px se relit du début à chaque ligne. */
    grid-template-columns: minmax(272px, 22rem) minmax(0, 84ch);
    gap: 0 calc(var(--gutter) + 16px); align-items: start;
  }

  /* Un rail d'idées ne s'étire pas : à cinq fiches sur 1 500 px, chaque
     carte ferait 300 px de large et ne ressemblerait plus à celles du
     carnet juste au-dessus. */
  .row-scroll { grid-auto-columns: minmax(200px, 268px); justify-content: start; }

  /* La fiche en lecture a enfin la place de ses deux colonnes. */
  .sheet { width: min(720px, 88vw); }
  .sheet--wide { width: min(1060px, 88vw); }
}

@media (min-width: 2000px) {
  :root { --view-max: 1820px; --gutter: 56px; --station-w: 300px; }

  #shopping { columns: 3; }
  /* Des cartes un peu plus grandes plutôt que douze par ligne. */
  .grid { grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: 18px; }
}

/* Un 4K à 100 % : 3 840 px de large, et le corps de 15 px fait trois
   millimètres de haut. Rien à redessiner, seulement l'échelle à remonter. */
@media (min-width: 2560px) {
  :root { --view-max: 2200px; --gutter: 64px; }
  body { font-size: 16.5px; }
}

/* Un 4K entier, ou un ultra-large. Un quatrième rayon de courses tient, et
   le carnet montre une rangée de plus. La colonne s'arrête là : au-delà,
   ce n'est plus de la place gagnée, c'est un cou qui tourne. */
@media (min-width: 3200px) {
  :root { --view-max: 2600px; }
  #shopping { columns: 4; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); }
}

/* Le clavier iOS zoome sur tout champ sous 16 px : on ne descend pas plus bas. */
@media (max-width: 480px) and (hover: none) {
  .field, input, textarea, select { font-size: 16px; }
  .card:hover { transform: none; box-shadow: var(--shadow-md); }
  .stars--pick:hover .stars__btn:not(.is-on) { opacity: .4; }
  .stars__btn:hover { transform: none; }
  .stars__btn:hover:not(.is-on) { color: var(--ink-3); opacity: .4; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .card:hover { transform: none; }
}

/* ════════════════════════════════════════════════════════════════════
   TACTILE — en dernier, pour que rien ne réécrive ces règles.

   Un flou d'arrière-plan oblige le navigateur à relire les pixels sous
   l'élément à chaque image du défilement. Sur une barre collante, et sur
   167 pastilles de carte, c'est la cause principale d'un défilement
   saccadé au doigt. Le survol, lui, reste collé après un appui : il
   déclenchait la transition des cartes pendant qu'on faisait défiler.
   ════════════════════════════════════════════════════════════════════ */
@media (hover: none) {
  .card { transition: none; }
  .card:active { transform: scale(.985); transition: transform .1s; }
}

/* ════════════════════════════════════════════════════════════════════
   INSTALLÉE — ce qui sépare une page web d'une app sur l'écran d'accueil.

   Rien ici ne change l'apparence : ce sont les petits signaux qui
   trahissent le navigateur. Le halo gris au doigt, le menu de copie sur
   un appui long, la sélection de texte qui s'accroche à un bouton, la
   page qui rebondit au bout du défilement.
   ════════════════════════════════════════════════════════════════════ */

/* Le rectangle gris d'iOS à chaque appui : aucune app native n'en a. */
html { -webkit-tap-highlight-color: transparent; }

/* Un appui long sur un bouton ou une photo ouvrait le menu « Copier /
   Partager ». Le texte des recettes, lui, doit rester sélectionnable :
   on veut pouvoir copier une liste d'ingrédients. */
button, .card, .wdish, .pick, .tab, .nav-item, .pastille,
.deck__card, img {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.detail, .detail *, .cook__x, .cook__ing, .line__item, .field, input, textarea {
  -webkit-touch-callout: default;
  -webkit-user-select: text;
  user-select: text;
}

/* Le rebond en bout de course fait apparaître le fond du navigateur
   derrière l'app. Contenu dans chaque zone qui défile. */
.canvas, .ticket-stack, .pick-list, .cook__body, .app__scroll {
  overscroll-behavior: contain;
}

/* Le défilement doux est fait pour un clic sur une ancre ; au doigt, il
   ajoute une inertie qui n'est pas celle du système. */
@media (hover: none) {
  .canvas { scroll-behavior: auto; }
}

/* ── Installée : les marges de sécurité ──────────────────────────────
   Le manifeste demande « standalone », donc plus de barre d'adresse en
   bas ni de barre d'onglets en haut. La barre du bas doit dégager
   l'indicateur d'accueil, et les feuilles modales aussi. */
@media (display-mode: standalone) {
  .canvas { padding-top: env(safe-area-inset-top); }
  .login { padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); }
  .deck, .cook { padding-top: calc(10px + env(safe-area-inset-top)); }

  /* Sans barre de navigateur, la barre d'onglets touche le bas de la
     vitre : l'indicateur d'accueil passerait par-dessus les libellés. */
  .tabbar { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
}

/* Explicit recipe siblings, using the existing fiche controls. */
.detail__variants { margin: 1rem 0; padding: 1rem 0; border-block: 1px solid var(--filet); }
.detail__variants .btn { margin: .25rem .25rem .25rem 0; white-space: normal; text-align: left; }

/* Organisation du carnet */
.carnet-sections { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--edge-soft); margin-bottom: 16px; }
.carnet-sections button { border: 0; border-bottom: 3px solid transparent; padding: 12px 10px; background: transparent; color: var(--ink); cursor: pointer; font: inherit; }
.carnet-sections button[aria-pressed="true"] { border-bottom-color: var(--accent); font-weight: 700; }
.carnet-sections span { color: var(--ink-3); font-size: .85em; }
.carnet-collections { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.carnet-collections select { width: auto; max-width: 100%; }
.collection-choice { display: flex; align-items: center; gap: 8px; padding: 8px 0; }
.detail__collections { margin: 16px 0; }
#collections-modal { max-height: 88dvh; overflow-y: auto; border-radius: 10px; padding: 24px; width: min(560px, calc(100% - 32px)); box-sizing: border-box; }
.collection-edit { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0 16px; }
.collection-edit .field { flex: 1; min-width: 120px; }
.collections-list { margin-top: 24px; }

.private-tip-form { margin-top: 16px; }
.private-tip-form fieldset { border: 0; padding: 0; display: grid; gap: 8px; min-width: 0; }
.private-tip-form .field { width: 100%; }

/* Le statut reste accessible au pouce sur téléphone et dans le magasin. */
button.sync { background: transparent; border: 0; cursor: pointer; font: inherit; color: inherit; align-items: center; gap: 6px; }
button.sync:focus-visible { outline: 2px solid var(--encre); outline-offset: 3px; }
.magasin .sync { font-size: .75rem; flex-shrink: 0; }
#service-conflict { padding: 12px; border: var(--filet-w) solid var(--tomate); }
#sync-details section { margin-block: 12px; }
