/* CSS commun de l'interface admin/web. */

/* Réserve la gouttière de scrollbar verticale UNIQUEMENT sur desktop (souris) :
   évite le léger décalage horizontal du contenu centré entre une page courte
   (sans scrollbar) et une page longue (avec scrollbar) — propre aux scrollbars
   CLASSIQUES qui prennent de la largeur.
   ⚠️ Surtout PAS sur tactile : sur iOS/Android les scrollbars sont en OVERLAY
   (largeur zéro). Y réserver une gouttière la fait apparaître/disparaître entre
   deux navigations → la barre fixed (nav) « part à droite très vite et revient »
   (flickering latéral remonté sur iPhone Safari + PWA). `pointer: fine` ne cible
   que les pointeurs précis (souris/trackpad) = desktop. */
@media (pointer: fine) {
  html {
    scrollbar-gutter: stable;
  }
}

/* ============================================================== */
/* Police de l'app : Hanken Grotesk (self-hostée, CSP font-src    */
/* 'self'). Police VARIABLE → un seul .woff2 par sous-ensemble    */
/* couvre tous les poids (100→900) : enfin un vrai « medium »     */
/* (500) pour les boutons, là où Arial n'avait que 400/700.      */
/* Licence OFL. font-display: swap = pas de texte invisible au    */
/* chargement (fallback Arial le temps du téléchargement).        */
/* latin = U+0000-00FF couvre le français ; latin-ext en bonus.   */
/* ============================================================== */
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/hanken-grotesk-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: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/hanken-grotesk-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root,
[data-theme="dark"] {
      --bg:#0f172a;
      --panel:#111827;
      --panel-2:#1f2937;
      --panel-sunken:#0b1220;
      --hover-bg:#2b3a52;
      --topbar-bg:#020617;
      --text:#e5e7eb;
      --muted:#94a3b8;
      --border:#334155;
      --primary:#e8714f;
      --primary-hover:#d15a3e;
      --secondary:#475569;
      --secondary-hover:#334155;
      --danger:#dc2626;
      --danger-hover:#b91c1c;
      --success:#3f9a63;
      --success-hover:#347f53;
      --warning-text:#fbbf24;
      --shadow-color:rgba(0,0,0,.35);
      /* Hauteur de la bottom-nav mobile = item min-height 62 + padding-bottom 8
         + border-top 1 = 71px. Constante de LAYOUT (pas une couleur) : posée ici
         pour être partagée. Les boutons flottants (verrou liste de courses,
         enregistrer recette) s'ancrent au-dessus via
         calc(var(--bottom-nav-h) + env(safe-area-inset-bottom) + gap) → si on
         retouche la hauteur de la nav, SEUL ce nombre change (plus de valeurs en
         dur éparpillées qui re-créent l'overlap). Cf CONSIGNES. */
      --bottom-nav-h: 71px;
    }

    * { box-sizing: border-box; }

    body {
      margin: 0;
      /* Hanken Grotesk (self-hostée) en 1er ; Arial = fallback le temps du
         téléchargement (font-display: swap) et si la police ne charge pas. */
      font-family: 'Hanken Grotesk', Arial, sans-serif;
      background: var(--bg);
      color: var(--text);
    }

    a { color: inherit; text-decoration: none; }

    /* ============================================================ */
    /* Accessibilité tactile globale                                  */
    /* touch-action: manipulation supprime le délai ~300ms iOS Safari */
    /* de "double-tap-to-zoom" sur les éléments cliquables, le tap   */
    /* devient instantané.                                           */
    /* -webkit-tap-highlight-color: transparent retire le carré gris */
    /* natif iOS/Android au tap (remplacé par notre propre :active). */
    /* ============================================================ */
    button, a, [role="button"], summary,
    label.theme-option, label.menu-filter-pill, label.menu-filter-pill-wide,
    label.menu-ingredient-label {
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
    }

    .container {
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
    }

    .topbar {
      background: var(--topbar-bg);
      border-bottom: 1px solid var(--border);
      position: sticky;
      top: 0;
      z-index: 20;
    }

    .topbar-inner {
      max-width: 1200px;
      margin: 0 auto;
      padding: 14px 20px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 16px;
    }

    .brand {
      display: flex;
      align-items: center;
    }

    .brand a,
    .nav a {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      min-height: 44px;
      padding: 10px 14px;
      background: var(--panel-2);
      border: 1px solid var(--border);
      border-radius: 10px;
    }

    .brand a {
      font-size: 22px;
      line-height: 1;
    }

    .nav {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      align-items: center;
    }

    .nav form {
      margin: 0;
    }

    .nav-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      min-height: 44px;
      padding: 10px 14px;
      background: var(--panel-2);
      border: 1px solid var(--border);
      border-radius: 10px;
      color: var(--text);
      cursor: pointer;
    }

    .topbar-user {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      min-height: 44px;
      padding: 10px 14px;
      background: var(--panel-sunken);
      border: 1px solid var(--border);
      border-radius: 10px;
      color: var(--muted);
      white-space: nowrap;
    }

    .auth-shell {
      width: 100%;
      max-width: 420px;
      margin: 7vh auto 0;
    }

    .auth-note {
      color: var(--muted);
      margin-top: 0;
      margin-bottom: 18px;
    }

    .auth-actions {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-top: 18px;
    }

    .nav-icon {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 18px;
      height: 18px;
      flex: 0 0 auto;
    }

    .nav-icon svg {
      width: 100%;
      height: 100%;
      display: block;
    }

    .nav-label {
      white-space: nowrap;
    }

    .page-title {
      margin: 8px 0 18px;
      font-size: 30px;
    }

    .page-subtitle {
      color: var(--muted);
      margin-top: -8px;
      margin-bottom: 24px;
    }

    .page-header {
      display: flex;
      align-items: center;
      justify-content: flex-start;
      gap: 12px;
      flex-wrap: wrap;
      margin: 8px 0 18px;
    }

    .page-header .page-title {
      margin: 0;
    }

    .page-header-action {
      flex-shrink: 0;
    }

    .panel {
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: 18px;
      margin-bottom: 18px;
    }

    .grid-2 {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 16px;
    }

    .grid-3 {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 16px;
    }

    .row {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 16px;
      margin-bottom: 14px;
    }

    label {
      display: block;
      margin-bottom: 6px;
      font-weight: bold;
    }

    .required {
    color: var(--danger);
    font-weight: bold;
    }

    input, textarea, select {
      width: 100%;
      padding: 11px 12px;
      border-radius: 10px;
      border: 1px solid var(--border);
      background: var(--panel-sunken);
      color: var(--text);
      font-size: 15px;
    }

    textarea {
      min-height: 120px;
      resize: vertical;
    }

    table {
      width: 100%;
      border-collapse: collapse;
    }

    th, td {
      padding: 12px 10px;
      border-bottom: 1px solid var(--border);
      text-align: left;
      vertical-align: top;
    }

    .actions {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      align-items: center;
    }

    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      padding: 10px 14px;
      border-radius: 10px;
      border: 1px solid transparent;
      cursor: pointer;
      font-size: 14px;
      /* font-weight 500 (medium) = libellé un peu plus épais que regular SANS
         être gras. Rendu réel grâce à Hanken Grotesk (police variable de l'app,
         vrai poids 500) — Arial, l'ancienne police, n'avait que 400/700. 600 a
         été testé puis jugé « trop gras ». */
      font-weight: 500;
      text-decoration: none;
      background: var(--primary);
      color: white;
    }

    /* État désactivé GLOBAL pour tous les .btn (variantes incluses :
       primary/secondary/danger/success/ghost). Avant : seuls 2 contextes
       ponctuels dimmaient leurs boutons → ailleurs (ex. /admin/users, vue
       super-admin : « Mettre à jour » / « Retirer » désactivés sur les
       lignes responsables) le bouton restait pleine couleur, indiscernable
       d'un bouton actif. Opacité + curseur not-allowed = lisible sur les
       3 thèmes. */
    .btn:disabled,
    .btn[disabled] {
      opacity: 0.5;
      cursor: not-allowed;
      box-shadow: none;
    }

    .ingredients-footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 12px;
    }

    .add-ingredient-icon {
      display: none;
    }

    .btn:hover { background: var(--primary-hover); }

    .btn.secondary {
      background: var(--panel-2);
      /* LIBELLÉ en --text (lisible) pour qu'on voie que c'est un bouton
         cliquable ; ICÔNE en --muted (calme, thème) via la règle .ico
         ci-dessous. Avant : tout en --muted → le libellé paraissait
         « désactivé », le bouton ne se lisait pas comme cliquable. */
      color: var(--text);
      border: 1px solid var(--border);
    }

    /* Icône des boutons agrandie (18px) + trait plus épais → présence
       « bouton » (cf .recipe-action-icon de /recipes). Avant : 1em ≈ 14px. */
    .btn .ico {
      width: 18px;
      height: 18px;
      stroke-width: 2.25;
    }

    .btn.secondary .ico {
      color: var(--muted);
    }

    .btn.secondary:hover {
      background: var(--hover-bg);
      border-color: var(--primary);
      color: var(--text);
    }

    .btn.secondary:hover .ico {
      color: var(--text);
    }

    .btn.danger {
      background: var(--danger);
    }

    .btn.danger:hover {
      background: var(--danger-hover);
    }

    .btn.success {
      background: var(--success);
    }

    .btn.success:hover {
      background: var(--success-hover);
    }

    .flash {
      background: #0b2545;
      border: 1px solid #1d4ed8;
      color: #dbeafe;
      padding: 12px 14px;
      border-radius: 10px;
      margin-bottom: 16px;
    }

    .muted {
      color: var(--muted);
    }

    .card-link {
      display: block;
      background: var(--panel);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: 18px;
      transition: transform .08s ease;
    }

    .card-link:hover {
      transform: translateY(-1px);
      border-color: var(--primary);
    }

    .stat {
      font-size: 32px;
      font-weight: bold;
      margin-top: 6px;
    }

    .ingredient-grid {
      display: grid;
      grid-template-columns: 2.2fr 1fr 1fr 1.3fr auto;
      gap: 10px;
      align-items: start;
    }

    .ingredient-row {
      margin-bottom: 10px;
    }

    .autocomplete {
    position: relative;
    }

    .autocomplete-menu {
      position: absolute;
      top: calc(100% + 4px);
      left: 0;
      right: 0;
      z-index: 50;
      display: none;
      background: var(--panel-sunken);
      border: 1px solid var(--border);
      border-radius: 10px;
      max-height: 220px;
      overflow-y: auto;
      box-shadow: 0 8px 24px var(--shadow-color, rgba(0, 0, 0, .25));
      -webkit-overflow-scrolling: touch;
      touch-action: pan-y;
      overscroll-behavior: contain;
    }

    .autocomplete-menu.show {
      display: block;
    }

    .autocomplete-item {
      display: block;
      width: 100%;
      padding: 10px 12px;
      border: 0;
      background: transparent;
      color: var(--text);
      text-align: left;
      cursor: pointer;
    }

    .autocomplete-item:hover {
      background: var(--panel-2);
    }

    .ingredient-head {
      color: var(--muted);
      font-size: 13px;
      margin-bottom: 8px;
    }

    .picker-actions {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .chip {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      background: var(--panel-2);
      border: 1px solid var(--border);
      border-radius: 999px;
      padding: 7px 10px;
      font-size: 13px;
      color: var(--text);
    }

    .chip button {
      background: transparent;
      color: var(--muted);
      border: 0;
      cursor: pointer;
      font-size: 14px;
      padding: 0;
    }

    .searchbar {
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 10px;
    }

    .search-actions {
      justify-content: flex-end;
    }

    .search-icon {
      display: none;
      width: 18px;
      height: 18px;
      flex: 0 0 auto;
    }

    .search-icon svg {
      width: 100%;
      height: 100%;
      display: block;
    }

    .search-label {
      white-space: nowrap;
    }

    .inline-form {
      display: inline;
    }

    @media (max-width: 900px) {
  .grid-2, .grid-3, .row {
    grid-template-columns: 1fr;
  }

    .topbar-inner {
      flex-direction: row;
      align-items: center;
      justify-content: space-between;
      padding: 10px 12px;
      gap: 8px;
    }

    .nav {
      flex: 1 1 auto;
      min-width: 0;
      justify-content: flex-end;
      flex-wrap: nowrap;
      gap: 6px;
    }

    .brand a,
    .nav a,
    .nav-btn {
      width: 44px;
      min-width: 44px;
      height: 44px;
      padding: 0;
    }

    .nav-icon {
      display: block;
    }

    .nav-label {
      display: none;
    }

    .topbar-user {
      display: none;
    }

  .ingredient-head {
    display: none;
  }

  .ingredient-grid {
    grid-template-columns: minmax(0, 1fr) 52px;
    column-gap: 10px;
    row-gap: 10px;
    align-items: start;
  }

  .ingredient-grid > input,
  .ingredient-grid > .autocomplete {
    grid-column: 1;
    min-width: 0;
  }

  .ingredient-grid > .remove-ingredient-btn {
    grid-column: 2;
    grid-row: 1 / span 4;
    align-self: start;
    width: 52px;
    min-width: 52px;
    padding: 10px 0;
  }

  #ingredients-container .ingredient-row + .ingredient-row {
    border-top: 1px solid var(--border);
    padding-top: 14px;
    margin-top: 14px;
  }

  .autocomplete-menu {
    position: static;
    margin-top: 6px;
    max-height: 180px;
  }

  .ingredients-footer {
    justify-content: flex-end;
  }

  .add-ingredient-btn {
    width: 52px;
    min-width: 52px;
    padding: 10px 0;
  }

  .add-ingredient-label {
    display: none;
  }

  .add-ingredient-icon {
    display: inline;
    font-size: 22px;
    line-height: 1;
  }

}

.table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.recipes-table {
  width: 100%;
}

.col-actions,
.cell-actions {
  width: 1%;
  white-space: nowrap;
}

.recipe-actions {
  justify-content: flex-start;
}

/* Liste des recettes (vue admin) — cards empilées : infos en haut, actions en bas. */
.recipes-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  /* Rythme vertical resserré (densité demandée) : moins d'air entre les cards. */
  gap: 8px;
}

.recipe-row {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
  transition: border-color 0.12s ease;
}

.recipe-row:hover {
  border-color: var(--primary);
}

/* Couche glissante (swipe-to-delete) : porte le fond + le padding de la
   card et translate horizontalement sous le doigt. Au repos translateX(0),
   elle recouvre entièrement le fond rouge `.recipe-row-swipe-hint`. */
.recipe-row-surface {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  /* Densité : moins d'air entre le bloc « head » (vignette + titre) et la
     rangée d'actions, et padding vertical resserré. */
  gap: 8px;
  padding: 10px 14px;
  border-radius: inherit;
  background: var(--panel-2);
  /* Geste vertical = scroll natif ; horizontal = capturé par le JS swipe. */
  touch-action: pan-y;
  transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Pendant un drag actif, le JS retire la transition (suivi 1:1 du doigt)
   via .is-dragging, et clippe le débord via .is-swiping sur la card. */
.recipe-row-surface.is-dragging {
  transition: none;
}

.recipe-row.is-swiping {
  overflow: hidden;
}

/* Menu « ⋮ » ouvert : on hisse toute la card au-dessus des suivantes. Sans
   ça le dropdown vit dans le stacking context de .recipe-row-surface
   (z-index:1) et passe DERRIÈRE la surface de la card suivante (z-index:1
   aussi, mais peinte après dans le DOM) → il apparaissait sous les vignettes
   des recettes du dessous. */
.recipe-row:has(.recipe-row-more[open]) {
  z-index: 20;
}

/* Fond rouge « Supprimer » révélé quand la surface glisse vers la gauche.
   Purement visuel : la suppression réelle passe par le form du menu « ⋮ »
   (cf recipes_row_actions.js). Icône alignée à droite, là où le doigt
   ouvre la zone. */
.recipe-row-swipe-hint {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 22px;
  border-radius: inherit;
  background: var(--danger);
  color: #ffffff;
}

.recipe-row-swipe-hint .ico {
  width: 24px;
  height: 24px;
  transition: transform 0.12s ease;
}

/* Passé le seuil de déclenchement, l'icône grossit (« relâche pour
   supprimer »). Classe posée par le JS. */
.recipe-row.is-armed .recipe-row-swipe-hint .ico {
  transform: scale(1.25);
}

@media (prefers-reduced-motion: reduce) {
  /* Le suivi du doigt reste (manipulation directe, motion essentielle),
     on raccourcit juste le retour élastique. */
  .recipe-row-surface {
    transition-duration: 0.08s;
  }
}

.recipe-row-head {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  gap: 12px;
  align-items: center;
}

.recipe-row-thumb-link {
  display: block;
  border-radius: 10px;
  overflow: hidden;
  transition: transform 0.12s ease;
}

.recipe-row-thumb-link:hover {
  transform: scale(1.03);
}

.recipe-row-thumb {
  width: 72px;
  height: 72px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  position: relative;
}

.recipe-row-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.recipe-row-thumb-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--panel-2) 0%, var(--panel-sunken) 100%);
}

.recipe-row-thumb-fallback {
  font-size: 28px;
  opacity: 0.4;
}

.recipe-row-info {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.recipe-row-name {
  font-weight: 600;
  font-size: 16px;
  color: var(--text);
  text-decoration: none;
  line-height: 1.25;
  word-break: break-word;
}

.recipe-row-name:hover {
  color: var(--primary);
}

/* Méta « Type · ⏱ Durée » — SOURCE UNIQUE du style (générée par le macro Jinja
   recipe_meta(), cf templates/_text.html). Utilisée sous le nom dans /recipes,
   les cartes /menu, les pickers… Les classes par page (.menu-card-meta etc.) ne
   portent QUE leur layout (marges), plus la taille/couleur. */
.recipe-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 14px;
  flex-wrap: wrap;
}

.recipe-row-toggle-form {
  margin: 0;
  display: inline-flex;
}

.recipe-row-toggle-btn {
  /* Bouton 40px visible + zone tap étendue à 44 via ::after. */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  background: color-mix(in srgb, var(--primary) 12%, var(--panel-sunken));
  color: var(--primary);
  border: 1px solid color-mix(in srgb, var(--primary) 40%, var(--border));
  border-radius: 50%;
  font-size: 22px;
  line-height: 1;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease, transform 0.12s ease;
}

.recipe-row-toggle-btn::after {
  content: "";
  position: absolute;
  inset: -2px;
}

.recipe-row-toggle-btn:hover,
.recipe-row-toggle-btn:focus-visible {
  background: var(--primary);
  color: #ffffff;
  outline: none;
  transform: scale(1.08);
}

.recipe-row-toggle-btn:active {
  transform: scale(0.96);
}

/* Variante "retirer" : couleur danger pour signaler que la recette est
   déjà dans la sélection et que ce clic l'enlève. */
.recipe-row-toggle-btn-remove {
  background: color-mix(in srgb, var(--danger) 14%, var(--panel-sunken));
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
}

.recipe-row-toggle-btn-remove:hover,
.recipe-row-toggle-btn-remove:focus-visible {
  background: var(--danger);
  color: #ffffff;
}

.recipe-row-empty {
  list-style: none;
  text-align: center;
  padding: 16px;
}

@media (max-width: 700px) {
  .recipes-table th,
  .recipes-table td {
    padding: 10px 8px;
    font-size: 14px;
  }

  .recipes-table .col-type {
    display: none;
  }

  /* (refonte des boutons de la liste des recettes : voir
     « Liste des recettes — refonte boutons » en fin de fichier.) */

.searchbar {
  grid-template-columns: 1fr;
  gap: 8px;
}

.searchbar .actions {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  flex-wrap: nowrap;
  gap: 6px;
}

.searchbar .btn {
  width: 44px;
  min-width: 44px;
  height: 44px;
  padding: 0;
}

.search-icon {
  display: block;
}

.search-label {
  display: none;
}


}

    .recipe-form {
  padding-bottom: 90px;
}

/* Import recette depuis URL externe (vague 8.1 Phase 1).             */
/* Panel discret au-dessus du form sur /recipes/new uniquement.       */

.recipe-import-panel {
  margin: 0 0 16px;
  padding: 14px 16px;
  background: color-mix(in srgb, var(--primary) 6%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--primary) 20%, var(--border));
  border-radius: 12px;
}

/* Tabs (vague 8.1 Phase 2) ----------------------------------------- */
.recipe-import-panel-tabs {
  display: flex;
  gap: 4px;
  margin: -4px -4px 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--primary) 15%, var(--border));
}

.recipe-import-panel-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease;
  margin-bottom: -1px; /* aligne avec border-bottom du conteneur */
}

.recipe-import-panel-tab:hover,
.recipe-import-panel-tab:focus-visible {
  color: var(--text);
  outline: none;
}

.recipe-import-panel-tab.is-active {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

.recipe-import-panel-pane {
  display: none;
}

.recipe-import-panel-pane.is-active {
  display: block;
}

.recipe-import-panel-hint {
  font-size: 13px;
  color: var(--muted);
  margin: 0 0 10px;
}

/* Textarea pane Texte ---------------------------------------------- */
.recipe-import-panel-textarea-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.recipe-import-panel-textarea {
  width: 100%;
  min-height: 180px;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  resize: vertical;
}

.recipe-import-panel-textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--field-focus-ring);
}

.recipe-import-panel-text-actions {
  display: flex;
  justify-content: flex-end;
}

.recipe-import-panel-text-actions .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Badge quota IA (Vague 9.1) — affiché en haut du panel d'import,
   uniquement si le tenant n'est pas en mode illimité. */
.recipe-import-panel-quota {
  margin: 0 0 12px 0;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--primary) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent);
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Pane Photo ------------------------------------------------------- */
.recipe-import-panel-image-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.recipe-import-panel-image-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  cursor: pointer;
}

.recipe-import-panel-image-feedback {
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-muted);
  font-style: italic;
  word-break: break-all;
}

.recipe-import-panel-image-submit-wrap {
  margin-top: 12px;
  display: flex;
  justify-content: flex-end;
}

.recipe-import-panel-image-submit-wrap .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
}

.recipe-import-panel-image-submit-wrap .btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}


.recipe-import-panel-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}

.recipe-import-panel-input {
  flex: 1;
  min-width: 0;
  /* Reprend le style des autres input du form (cf section input/textarea) */
}

.recipe-import-panel-row .btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
}

/* :not([hidden]) sinon `display: inline-block` override l'attribut
   HTML `hidden` et le spinner reste visible au load (bug visuel :
   il tournait dans le bouton dès l'affichage du form). Le JS bascule
   `spinner.hidden = true/false` selon l'état du fetch. */
.recipe-import-panel-spinner:not([hidden]) {
  width: 14px;
  height: 14px;
  border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: recipe-import-spin 0.7s linear infinite;
  display: inline-block;
}

@keyframes recipe-import-spin {
  to { transform: rotate(360deg); }
}

.recipe-import-panel-error {
  margin: 8px 0 0;
  font-size: 13px;
  color: var(--danger);
}

@media (max-width: 540px) {
  .recipe-import-panel-row {
    flex-direction: column;
  }
  .recipe-import-panel-row .btn {
    justify-content: center;
  }
}

.form-actions {
  margin-top: 8px;
}

/* Bob « flottant » partagé : léger va-et-vient vertical pour donner du relief
   aux FAB (enregistrer recette, verrou liste de courses). Ne descend jamais
   sous le repos (0 → -6 → 0) → ne peut pas re-toucher la nav. Marche nickel sur
   iOS Safari (anim CSS de transform). Désactivé en reduced-motion. */
@keyframes ribouns-float-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

.floating-savebar {
  position: fixed;
  /* Ancré à DROITE (cohérent avec le verrou des listes de courses), plus à
     gauche. */
  right: 16px;
  /* --kb-inset (posé par recipe_form.js via la VisualViewport API) = hauteur
     recouverte par le clavier virtuel mobile. max() : au repos on garde la
     position basse, clavier ouvert on remonte juste au-dessus du clavier. */
  bottom: max(16px, calc(var(--kb-inset, 0px) + 12px));
  transform: translateY(120%);
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
  z-index: 40;
}

.floating-savebar.show {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Le bob vit sur le BOUTON (enfant), pas sur le wrapper : le wrapper porte déjà
   le translateY de show/hide → on ne se marche pas dessus. Animé seulement
   quand le savebar est visible. */
.floating-savebar.show .floating-save-btn {
  animation: ribouns-float-bob 3.2s ease-in-out infinite;
}

.floating-save-btn {
  width: 52px;
  height: 52px;
  padding: 0;
  border-radius: 999px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, .35);
}

.floating-save-btn svg {
  width: 22px;
  height: 22px;
  display: block;
}

/* Accessibilité : pas de bob pour les utilisateurs « réduire les animations ». */
@media (prefers-reduced-motion: reduce) {
  .floating-savebar.show .floating-save-btn,
  body.page-shopping-list-detail .shopping-float-lock .btn {
    animation: none;
  }
}

@media (max-width: 700px) {
  .floating-savebar {
    right: 14px;
    /* Au repos : au-dessus de la bottom-nav (var(--bottom-nav-h)) avec un
       dégagement de 16px pour ne pas être collé. Clavier ouvert : --kb-inset
       prend le dessus et place le bouton au-dessus du clavier (la bottom-nav
       est de toute façon masquée par le clavier). */
    bottom: max(calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0) + 16px), calc(var(--kb-inset, 0px) + 12px));
  }

  .floating-save-btn {
    width: 48px;
    height: 48px;
  }
}


.admin-inline-form,
.admin-user-form,
.admin-table-form {
  display: grid;
  gap: 10px;
}

.admin-inline-form {
  grid-template-columns: minmax(220px, 1fr) auto;
  align-items: end;
}

.admin-table-form {
  grid-template-columns: minmax(150px, 1fr) auto;
  align-items: center;
}

/* Vague 9.1 — page /admin/quotas */
.admin-quotas-form {
  /* Forms invisibles utilisés via l'attribut HTML5 form="id" pour
     pouvoir répartir inputs/buttons sur plusieurs <td>. Aucun visuel
     à afficher pour le form lui-même. */
  display: contents;
}

.admin-quotas-table input.admin-quotas-input[type="number"] {
  width: 90px;
  min-height: 36px;
  padding: 4px 8px;
}

.admin-quotas-checkbox-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  min-height: 36px;
}

.admin-quotas-submit {
  min-height: 36px;
  padding: 4px 12px;
}

.admin-rules {
  display: grid;
  gap: 12px;
}

.account-password-form,
.account-default-tenant-form,
.account-info-list {
  display: grid;
  gap: 14px;
}

.account-info-list > div {
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-sunken);
}

.topbar-user strong {
  color: var(--text);
}

@media (max-width: 800px) {
  .admin-inline-form,
  .admin-table-form,
  .admin-grid {
    grid-template-columns: 1fr;
  }
}



/* Styles spécifiques : ingredients_admin.html */

body.page-ingredients-admin .ingredient-admin-stack {
    display: grid;
    gap: 14px;
  }

  body.page-ingredients-admin .ingredient-duplicate-card {
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 16px;
    background: var(--panel-sunken);
  }

  body.page-ingredients-admin .ingredient-duplicate-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
  }

  body.page-ingredients-admin .ingredient-duplicate-title {
    font-size: 22px;
    font-weight: bold;
    margin-top: 4px;
  }

  body.page-ingredients-admin .ingredient-badges {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }

  body.page-ingredients-admin .ingredient-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 10px;
    border-radius: 999px;
    background: var(--panel-2);
    border: 1px solid #475569;
    font-size: 13px;
  }

  body.page-ingredients-admin .ingredient-variant-list {
    display: grid;
    gap: 10px;
  }

  body.page-ingredients-admin .ingredient-group-merge-form {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
    margin-top: 12px;
    margin-bottom: 12px;
  }

  body.page-ingredients-admin .ingredient-group-merge-form input[type="text"] {
    flex: 1 1 280px;
  }

  body.page-ingredients-admin .ingredient-group-merge-form select {
    flex: 1 1 280px;
    min-width: 220px;
  }

  body.page-ingredients-admin .ingredient-meta-list {
    display: grid;
    gap: 6px;
    margin-top: 10px;
  }

  body.page-ingredients-admin .ingredient-meta-line {
    font-size: 13px;
    color: var(--muted);
  }

  body.page-ingredients-admin .ingredient-variant-row {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(220px, 1fr) auto;
    gap: 10px;
    align-items: center;
    padding: 12px;
    border-radius: 12px;
    background: var(--panel);
    border: 1px solid var(--border);
  }

  body.page-ingredients-admin .ingredient-variant-name {
    font-weight: bold;
    margin-bottom: 4px;
  }

  body.page-ingredients-admin .ingredient-variant-recipes {
    font-size: 13px;
    color: var(--muted);
  }

  body.page-ingredients-admin .ingredient-rename-inline {
    display: contents;
  }

  body.page-ingredients-admin .ingredient-rename-inline input {
    min-width: 0;
  }

  body.page-ingredients-admin .ingredient-suggestion-note {
    margin-top: 12px;
    color: var(--muted);
    font-size: 13px;
  }

  body.page-ingredients-admin .ingredient-table-form {
    display: grid;
    grid-template-columns: minmax(180px, 1fr) auto;
    gap: 8px;
    align-items: center;
  }

  body.page-ingredients-admin .ingredient-name-cell {
    min-width: 220px;
  }

  body.page-ingredients-admin .ingredient-name-cell strong {
    display: block;
    margin-bottom: 4px;
  }

  body.page-ingredients-admin .ingredient-table-recipes {
    color: var(--muted);
    font-size: 13px;
  }

  body.page-ingredients-admin .inline-recipe-link {
    text-decoration: underline;
    text-underline-offset: 2px;
  }

  body.page-ingredients-admin .panel-collapsible > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }

  body.page-ingredients-admin .panel-collapsible > summary::-webkit-details-marker {
    display: none;
  }

  body.page-ingredients-admin .panel-collapsible-title {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  body.page-ingredients-admin .panel-collapsible-title h2 {
    margin: 0;
  }

  body.page-ingredients-admin .panel-collapsible-chevron {
    font-size: 18px;
    color: var(--muted);
    transition: transform 0.18s ease;
  }

  body.page-ingredients-admin .panel-collapsible[open] .panel-collapsible-chevron {
    transform: rotate(90deg);
  }

  body.page-ingredients-admin .panel-collapsible-body {
    margin-top: 14px;
  }

  body.page-ingredients-admin .ingredient-duplicate-toolbar {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
  }

  body.page-ingredients-admin .ingredient-ignored-list {
    display: grid;
    gap: 10px;
  }

  body.page-ingredients-admin .ingredient-ignored-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px;
    border-radius: 12px;
    background: var(--panel);
    border: 1px solid var(--border);
  }

  body.page-ingredients-admin .ingredient-ignored-text {
    display: grid;
    gap: 4px;
  }

  body.page-ingredients-admin .ingredient-ignored-names {
    font-size: 13px;
    color: var(--muted);
  }

  body.page-ingredients-admin .ingredient-ignored-summary-note {
    margin-top: 6px;
  }

  body.page-ingredients-admin .panel-collapsible {
    transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
  }

  body.page-ingredients-admin .panel-collapsible > summary {
    padding: 6px 4px;
    border-radius: 12px;
    transition: background .18s ease, transform .18s ease;
  }

  body.page-ingredients-admin .panel-collapsible > summary:active {
    transform: scale(0.995);
  }

  body.page-ingredients-admin .panel-collapsible-title {
    min-width: 0;
  }

  body.page-ingredients-admin .panel-collapsible-title h2 {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 22px;
  }

  body.page-ingredients-admin .panel-collapsible-hint {
    font-size: 12px;
    color: var(--muted);
    letter-spacing: 0.01em;
  }

  body.page-ingredients-admin .panel-collapsible-chevron {
    width: 34px;
    height: 34px;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    background: color-mix(in srgb, var(--text) 6%, transparent);
    font-size: 18px;
  }

  body.page-ingredients-admin .panel-collapsible-name-main {
    border-color: color-mix(in srgb, var(--primary) 50%, var(--border));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 12%, transparent) inset;
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--primary) 12%, transparent), color-mix(in srgb, var(--primary) 4%, transparent)),
      var(--panel);
  }

  body.page-ingredients-admin .panel-collapsible-name-main > summary {
    background: color-mix(in srgb, var(--primary) 12%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-name-main > summary:hover {
    background: color-mix(in srgb, var(--primary) 18%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-name-main .panel-collapsible-title h2 {
    color: color-mix(in srgb, var(--primary) 28%, var(--text));
  }

  body.page-ingredients-admin .panel-collapsible-name-main .panel-collapsible-title h2::before {
    content: "●";
    font-size: 14px;
    color: var(--primary);
  }

  body.page-ingredients-admin .panel-collapsible-name-main:hover .panel-collapsible-chevron {
    background: color-mix(in srgb, var(--primary) 18%, transparent);
    border-color: color-mix(in srgb, var(--primary) 50%, var(--border));
    color: var(--primary);
  }

  body.page-ingredients-admin .panel-collapsible-name-muted {
    border-color: color-mix(in srgb, var(--primary) 25%, var(--border));
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--primary) 6%, transparent), color-mix(in srgb, var(--primary) 2%, transparent)),
      var(--panel);
  }

  body.page-ingredients-admin .panel-collapsible-name-muted > summary {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-name-muted > summary:hover {
    background: color-mix(in srgb, var(--primary) 10%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-name-muted .panel-collapsible-title h2 {
    color: color-mix(in srgb, var(--primary) 22%, var(--text));
  }

  body.page-ingredients-admin .panel-collapsible-name-muted .panel-collapsible-title h2::before {
    content: "●";
    font-size: 14px;
    color: var(--primary);
  }

  body.page-ingredients-admin .panel-collapsible-name-muted:hover .panel-collapsible-chevron {
    background: color-mix(in srgb, var(--primary) 11%, transparent);
    border-color: color-mix(in srgb, var(--primary) 35%, var(--border));
    color: var(--primary);
  }

  body.page-ingredients-admin .panel-collapsible-category-main {
    border-color: color-mix(in srgb, var(--success) 50%, var(--border));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--success) 12%, transparent) inset;
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--success) 12%, transparent), color-mix(in srgb, var(--success) 4%, transparent)),
      var(--panel);
  }

  body.page-ingredients-admin .panel-collapsible-category-main > summary {
    background: color-mix(in srgb, var(--success) 12%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-category-main > summary:hover {
    background: color-mix(in srgb, var(--success) 18%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-category-main .panel-collapsible-title h2 {
    color: color-mix(in srgb, var(--success) 30%, var(--text));
  }

  body.page-ingredients-admin .panel-collapsible-category-main .panel-collapsible-title h2::before {
    content: "●";
    font-size: 14px;
    color: var(--success);
  }

  body.page-ingredients-admin .panel-collapsible-category-main:hover .panel-collapsible-chevron {
    background: color-mix(in srgb, var(--success) 18%, transparent);
    border-color: color-mix(in srgb, var(--success) 50%, var(--border));
    color: var(--success);
  }

  body.page-ingredients-admin .panel-collapsible-category-muted {
    border-color: color-mix(in srgb, var(--success) 25%, var(--border));
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--success) 6%, transparent), color-mix(in srgb, var(--success) 2%, transparent)),
      var(--panel);
  }

  body.page-ingredients-admin .panel-collapsible-category-muted > summary {
    background: color-mix(in srgb, var(--success) 6%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-category-muted > summary:hover {
    background: color-mix(in srgb, var(--success) 10%, transparent);
  }

  body.page-ingredients-admin .panel-collapsible-category-muted .panel-collapsible-title h2 {
    color: color-mix(in srgb, var(--success) 22%, var(--text));
  }

  body.page-ingredients-admin .panel-collapsible-category-muted .panel-collapsible-title h2::before {
    content: "●";
    font-size: 14px;
    color: var(--success);
  }

  body.page-ingredients-admin .panel-collapsible-category-muted:hover .panel-collapsible-chevron {
    background: color-mix(in srgb, var(--success) 11%, transparent);
    border-color: color-mix(in srgb, var(--success) 35%, var(--border));
    color: var(--success);
  }

  @media (max-width: 900px) {
body.page-ingredients-admin .ingredient-variant-row,
body.page-ingredients-admin .ingredient-table-form {
      grid-template-columns: 1fr;
    }

    body.page-ingredients-admin .ingredient-ignored-row {
      flex-direction: column;
      align-items: stretch;
    }

    body.page-ingredients-admin .ingredient-rename-inline {
      display: grid;
      grid-template-columns: 1fr;
      gap: 10px;
    }

}


/* Styles spécifiques : shopping_list_detail.html */

/* Dans une liste, on cache la navbar globale (desktop .topbar ET la barre
   supérieure mobile) pour garder toute la place au téléphone — la page a son
   propre haut FLOTTANT (.shopping-float-top : bouton retour + progression, cf
   fin de fichier). La bottom-nav est aussi masquée (mode immersion). */
  body.page-shopping-list-detail .topbar,
  body.page-shopping-list-detail .mobile-topbar {
    display: none;
  }

  body.page-shopping-list-detail .container {
    max-width: 760px;
    /* Dégage le haut FLOTTANT (bouton retour + barre de progression fixes). */
    padding: calc(env(safe-area-inset-top, 0px) + 62px) 10px 14px;
  }

  body.page-shopping-list-detail .shopping-app {
    display: grid;
    gap: 10px;
  }

  /* Ex-.shopping-topbar (cadre sticky + titre + back) retiré : remplacé par le
     haut FLOTTANT (.shopping-float-top, cf fin de fichier). */

  body.page-shopping-list-detail .shopping-save-state {
    min-height: 0;
    color: var(--muted);
    font-size: 0;
    text-align: center;
  }

  body.page-shopping-list-detail .shopping-progress {
    height: 8px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--panel-sunken);
    border: 1px solid var(--border);
  }

  body.page-shopping-list-detail .shopping-progress > span {
    display: block;
    height: 100%;
    background: var(--success);
    transition: width .18s ease, background .18s ease;
  }

  body.page-shopping-list-detail .shopping-progress > span:not([class*="progress-w-"]) {
    width: 0;
  }

  body.page-shopping-list-detail .shopping-categories {
    display: grid;
    gap: 8px;
    padding-bottom: 72px;
  }

  body.page-shopping-list-detail .shopping-category,
body.page-shopping-list-detail .shopping-edit-category {
    display: grid;
    gap: 6px;
    padding: 10px 12px;
    /* Pas la marge de .panel (18px) qui s'ajoutait au gap de la grille →
       l'espace vertical entre magasins était trop grand. Le gap du conteneur
       (.shopping-categories, 8px) suffit. */
    margin: 0;
  }

  body.page-shopping-list-detail .shopping-category.is-empty,
body.page-shopping-list-detail .shopping-edit-category.is-empty {
    opacity: .72;
  }

  body.page-shopping-list-detail .shopping-category-title {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    align-items: center;
    margin-bottom: 2px;
  }

  body.page-shopping-list-detail .shopping-category-title h2 {
    margin: 0;
    font-size: 20px;
    line-height: 1.2;
  }

  /* Icône du magasin en --muted (le nom du magasin reste en --text). */
  body.page-shopping-list-detail .shopping-category-title h2 .ico {
    color: var(--muted);
  }

  body.page-shopping-list-detail .shopping-category-count {
    color: var(--muted);
    font-size: 12px;
  }

  body.page-shopping-list-detail .shopping-row {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr);
    gap: 9px;
    align-items: start;
    padding: 7px 0;
    border-bottom: 1px solid rgba(148, 163, 184, .12);
    /* Toute la ligne est cliquable en mode courses (cf JS). */
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    border-radius: 8px;
    transition: background 120ms ease, transform 90ms ease;
  }

  /* Retour visuel au tap/clic sur la ligne entière. */
  body.page-shopping-list-detail .shopping-row:active {
    background: var(--hover-bg);
    transform: scale(0.99);
  }

  body.page-shopping-list-detail .shopping-row:last-child {
    border-bottom: 0;
  }

  body.page-shopping-list-detail .shopping-check,
body.page-shopping-list-detail .shopping-edit-check {
    width: 24px;
    height: 24px;
    margin: 2px 0 0;
    accent-color: var(--success);
  }

  body.page-shopping-list-detail .shopping-line-main {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: wrap;
    min-width: 0;
  }

  body.page-shopping-list-detail .shopping-line-name {
    font-size: 17px;
    font-weight: 700;
    word-break: break-word;
  }

  body.page-shopping-list-detail .shopping-line-quantity {
    color: var(--muted);
    font-size: 15px;
  }

  body.page-shopping-list-detail .shopping-row.is-checked .shopping-line-name,
body.page-shopping-list-detail .shopping-row.is-checked .shopping-line-quantity,
body.page-shopping-list-detail .shopping-edit-row.is-checked .shopping-edit-text {
    text-decoration: line-through;
    opacity: .58;
  }

  body.page-shopping-list-detail .shopping-empty-category {
    color: var(--muted);
    font-size: 14px;
    padding: 5px 0 2px 34px;
  }

  body.page-shopping-list-detail .shopping-checked-section {
    display: grid;
    gap: 6px;
    padding: 13px 12px;
    opacity: .85;
  }

  body.page-shopping-list-detail .shopping-checked-section[hidden] {
    display: none;
  }

  body.page-shopping-list-detail .shopping-checked-summary {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    list-style: none;
    font-size: 16px;
    font-weight: 700;
    color: var(--muted);
  }

  body.page-shopping-list-detail .shopping-checked-summary::-webkit-details-marker {
    display: none;
  }

  body.page-shopping-list-detail .shopping-checked-count {
    font-size: 12px;
    font-weight: 600;
    color: var(--muted);
    background: color-mix(in srgb, var(--success) 18%, transparent);
    border-radius: 999px;
    padding: 2px 9px;
  }

  body.page-shopping-list-detail .shopping-checked-list {
    display: grid;
    gap: 0;
    margin-top: 4px;
  }

  body.page-shopping-list-detail .shopping-checked-list .shopping-row {
    border-bottom: 1px solid color-mix(in srgb, var(--border) 45%, transparent);
  }

  body.page-shopping-list-detail .shopping-checked-list .shopping-row:last-child {
    border-bottom: 0;
  }

  body.page-shopping-list-detail .shopping-editor-panel {
    padding: 0;
    overflow: visible;
  }

  body.page-shopping-list-detail .shopping-rich-editor {
    display: grid;
    gap: 8px;
    padding-bottom: 88px;
  }

  body.page-shopping-list-detail .shopping-edit-heading-input {
    width: 100%;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--text);
    font-size: 20px;
    font-weight: 800;
    line-height: 1.2;
    border-radius: 0;
  }

  body.page-shopping-list-detail .shopping-edit-category-title {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 34px;
    gap: 8px;
    align-items: center;
  }

  body.page-shopping-list-detail .shopping-remove-category {
    width: 32px;
    height: 30px;
    min-height: 30px;
    padding: 0;
    border-radius: 999px;
    font-size: 15px;
  }

  body.page-shopping-list-detail .shopping-edit-heading-input:focus {
    outline: none;
    box-shadow: none;
  }

  body.page-shopping-list-detail .shopping-edit-list {
    display: grid;
    gap: 2px;
  }

  body.page-shopping-list-detail .shopping-edit-row {
    display: grid;
    grid-template-columns: 32px minmax(0, 1fr) 34px;
    gap: 8px;
    align-items: start;
    padding: 6px 0;
    border-bottom: 1px solid rgba(148, 163, 184, .10);
  }

  body.page-shopping-list-detail .shopping-edit-row:last-child {
    border-bottom: 0;
  }

  body.page-shopping-list-detail .shopping-edit-text {
    min-height: 28px;
    padding: 1px 0;
    color: var(--text);
    font-size: 17px;
    font-weight: 600;
    line-height: 1.45;
    word-break: break-word;
    outline: none;
  }

  body.page-shopping-list-detail .shopping-edit-text:empty::before {
    content: "Nouvel ingrédient";
    color: var(--muted);
    opacity: .8;
  }

  body.page-shopping-list-detail .shopping-edit-text:focus {
    outline: none;
  }

  body.page-shopping-list-detail .shopping-remove-row,
body.page-shopping-list-detail .shopping-add-line {
    width: 32px;
    height: 30px;
    min-height: 30px;
    padding: 0;
    border-radius: 999px;
    font-size: 15px;
  }

  body.page-shopping-list-detail .shopping-add-line {
    width: auto;
    justify-self: start;
    padding: 0 10px;
    margin-left: 40px;
    color: var(--muted);
  }

  body.page-shopping-list-detail .shopping-edit-empty-note {
    color: var(--muted);
    font-size: 14px;
    margin-left: 40px;
  }

  body.page-shopping-list-detail .shopping-add-category-between {
    display: flex;
    justify-content: center;
    margin: -2px 0;
  }

  body.page-shopping-list-detail .shopping-add-category-btn {
    width: auto;
    min-height: 30px;
    padding: 0 12px;
    border-radius: 999px;
    color: var(--muted);
    font-size: 13px;
  }

  body.page-shopping-list-detail .shopping-float-lock {
    position: fixed;
    right: 14px;
    /* Ancré au-dessus de la bottom-nav via var(--bottom-nav-h) + 16px de gap
       (avant : 50px en dur → overlap depuis que la nav a été surélevée à ~71px).
       Sur desktop la bottom-nav est cachée → le bouton revient en bas via le
       @media (min-width: 701px) ci-dessous. */
    bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0) + 16px);
    z-index: 50;
  }

  @media (min-width: 701px) {
    body.page-shopping-list-detail .shopping-float-lock {
      bottom: 14px;
    }
  }

  body.page-shopping-list-detail .shopping-float-lock .btn {
    width: 52px;
    min-width: 52px;
    height: 52px;
    min-height: 52px;
    padding: 0;
    border-radius: 999px;
    font-size: 22px;
    box-shadow: 0 12px 28px rgba(0,0,0,.35);
    /* Bob flottant (cf @keyframes ribouns-float-bob). */
    animation: ribouns-float-bob 3.2s ease-in-out infinite;
  }

  @media (min-width: 761px) {
body.page-shopping-list-detail .container {
      padding-top: calc(env(safe-area-inset-top, 0px) + 62px);
    }
}


/* Styles spécifiques : shopping_lists.html */

/* FAB « Nouvelle liste » — même look que le bouton flottant Enregistrer de
   l'édition recette : rond corail plein, coin bas-droit, au-dessus de la nav
   sur mobile, léger bob flottant. Le titre « Listes de courses » a été retiré
   (cohérent avec /recipes, contexte évident via l'onglet Courses actif). */
body.page-shopping-lists .shopping-lists-add-fab {
  position: fixed;
  right: 16px;
  bottom: 16px;
  margin: 0;
  z-index: 40;
}

body.page-shopping-lists .shopping-lists-add-fab .btn {
  width: 52px;
  height: 52px;
  padding: 0;
  border-radius: 999px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
  animation: ribouns-float-bob 3.2s ease-in-out infinite;
}

/* .ico a un vertical-align global → display:block recentre le « + » dans le
   bouton flex. */
body.page-shopping-lists .shopping-lists-add-fab .btn .ico,
body.page-shopping-lists .shopping-lists-add-fab .btn svg {
  display: block;
  width: 24px;
  height: 24px;
}

@media (max-width: 700px) {
  body.page-shopping-lists .shopping-lists-add-fab {
    right: 14px;
    bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0) + 16px);
  }

  body.page-shopping-lists .shopping-lists-add-fab .btn {
    width: 48px;
    height: 48px;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.page-shopping-lists .shopping-lists-add-fab .btn {
    animation: none;
  }
}

body.page-shopping-lists .shopping-list-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
  }

  body.page-shopping-lists .shopping-list-card {
    display: grid;
    gap: 12px;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 16px;
    padding: 16px;
  }

  body.page-shopping-lists .shopping-list-card-head {
    display: flex;
    /* center : le head n'a plus que le titre (1 ligne) + le badge « Défaut ». */
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }

  body.page-shopping-lists .shopping-list-title {
    font-size: 22px;
    font-weight: 800;
    line-height: 1.15;
  }

  /* Rangée progression : compteur + barre (extensibles) à gauche, poubelle de
     suppression à droite (déplacée depuis le coin haut-droit du head). */
  body.page-shopping-lists .shopping-list-progress-row {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  body.page-shopping-lists .shopping-list-progress-info {
    flex: 1 1 auto;
    min-width: 0;
  }

  body.page-shopping-lists .shopping-list-delete-form {
    flex: 0 0 auto;
  }

  body.page-shopping-lists .shopping-list-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 10px;
    border-radius: 999px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    color: var(--muted);
    font-size: 13px;
  }

  body.page-shopping-lists .shopping-progress {
    height: 10px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--panel-sunken);
    border: 1px solid var(--border);
  }

  body.page-shopping-lists .shopping-progress > span {
    display: block;
    height: 100%;
    background: var(--success);
  }

  body.page-shopping-lists .shopping-list-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
  }

  body.page-shopping-lists .shopping-list-actions .menu-action,
body.page-shopping-lists .shopping-list-actions form {
    flex: 1 1 140px;
  }

  @media (max-width: 640px) {
body.page-shopping-lists .shopping-list-grid {
      grid-template-columns: 1fr;
    }

    body.page-shopping-lists .shopping-list-actions {
      display: flex;
      flex-wrap: nowrap;
      align-items: stretch;
      gap: 10px;
    }

    /* Ouvrir occupe l'espace, Supprimer reste compact (plus petit). */
    body.page-shopping-lists .shopping-list-actions > .menu-action {
      flex: 1 1 auto;
    }

    body.page-shopping-lists .shopping-list-actions > form {
      flex: 0 0 auto;
    }

}


/* Feedback tactile des boutons d'authentification/pairing. */

.auth-actions .btn {
    position: relative;
    overflow: hidden;
    transform: translateZ(0);
    transition: transform .07s ease, filter .07s ease, box-shadow .07s ease, opacity .12s ease;
    -webkit-tap-highlight-color: transparent;
  }

  .auth-actions .btn::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: rgba(255,255,255,.14);
    opacity: 0;
    pointer-events: none;
    transition: opacity .08s ease;
  }

  .auth-actions .btn:active,
  .auth-actions .btn.is-pressed {
    transform: translateY(2px) scale(.985);
    filter: brightness(1.14);
    box-shadow:
      inset 0 5px 12px rgba(0,0,0,.34),
      0 0 0 2px rgba(147,197,253,.26);
  }

  .auth-actions .btn:active::before,
  .auth-actions .btn.is-pressed::before {
    opacity: 1;
  }

/* Utilitaires partagés pour éviter les styles inline répétitifs. */
.u-m-0 { margin: 0; }
.u-mt-0 { margin-top: 0; }
.u-mt-6 { margin-top: 6px; }
.u-mt-8 { margin-top: 8px; }
.u-mt-10 { margin-top: 10px; }
.u-mt-14 { margin-top: 14px; }
.u-mb-8 { margin-bottom: 8px; }
.u-mb-10 { margin-bottom: 10px; }
.u-mb-14 { margin-bottom: 14px; }
.u-mb-22 { margin-bottom: 22px; }
.u-cursor-default { cursor: default; }
.u-opacity-soft { opacity: .78; }
.home-card-title { font-size: 24px; font-weight: bold; margin-top: 6px; }


.audit-table td {
  vertical-align: top;
}

.audit-user-agent {
  max-width: 360px;
  margin-top: 6px;
  overflow-wrap: anywhere;
  font-size: 12px;
}


/* CSP-friendly progress bar widths. */
.progress-w-0 { width: 0%; }
.progress-w-1 { width: 1%; }
.progress-w-2 { width: 2%; }
.progress-w-3 { width: 3%; }
.progress-w-4 { width: 4%; }
.progress-w-5 { width: 5%; }
.progress-w-6 { width: 6%; }
.progress-w-7 { width: 7%; }
.progress-w-8 { width: 8%; }
.progress-w-9 { width: 9%; }
.progress-w-10 { width: 10%; }
.progress-w-11 { width: 11%; }
.progress-w-12 { width: 12%; }
.progress-w-13 { width: 13%; }
.progress-w-14 { width: 14%; }
.progress-w-15 { width: 15%; }
.progress-w-16 { width: 16%; }
.progress-w-17 { width: 17%; }
.progress-w-18 { width: 18%; }
.progress-w-19 { width: 19%; }
.progress-w-20 { width: 20%; }
.progress-w-21 { width: 21%; }
.progress-w-22 { width: 22%; }
.progress-w-23 { width: 23%; }
.progress-w-24 { width: 24%; }
.progress-w-25 { width: 25%; }
.progress-w-26 { width: 26%; }
.progress-w-27 { width: 27%; }
.progress-w-28 { width: 28%; }
.progress-w-29 { width: 29%; }
.progress-w-30 { width: 30%; }
.progress-w-31 { width: 31%; }
.progress-w-32 { width: 32%; }
.progress-w-33 { width: 33%; }
.progress-w-34 { width: 34%; }
.progress-w-35 { width: 35%; }
.progress-w-36 { width: 36%; }
.progress-w-37 { width: 37%; }
.progress-w-38 { width: 38%; }
.progress-w-39 { width: 39%; }
.progress-w-40 { width: 40%; }
.progress-w-41 { width: 41%; }
.progress-w-42 { width: 42%; }
.progress-w-43 { width: 43%; }
.progress-w-44 { width: 44%; }
.progress-w-45 { width: 45%; }
.progress-w-46 { width: 46%; }
.progress-w-47 { width: 47%; }
.progress-w-48 { width: 48%; }
.progress-w-49 { width: 49%; }
.progress-w-50 { width: 50%; }
.progress-w-51 { width: 51%; }
.progress-w-52 { width: 52%; }
.progress-w-53 { width: 53%; }
.progress-w-54 { width: 54%; }
.progress-w-55 { width: 55%; }
.progress-w-56 { width: 56%; }
.progress-w-57 { width: 57%; }
.progress-w-58 { width: 58%; }
.progress-w-59 { width: 59%; }
.progress-w-60 { width: 60%; }
.progress-w-61 { width: 61%; }
.progress-w-62 { width: 62%; }
.progress-w-63 { width: 63%; }
.progress-w-64 { width: 64%; }
.progress-w-65 { width: 65%; }
.progress-w-66 { width: 66%; }
.progress-w-67 { width: 67%; }
.progress-w-68 { width: 68%; }
.progress-w-69 { width: 69%; }
.progress-w-70 { width: 70%; }
.progress-w-71 { width: 71%; }
.progress-w-72 { width: 72%; }
.progress-w-73 { width: 73%; }
.progress-w-74 { width: 74%; }
.progress-w-75 { width: 75%; }
.progress-w-76 { width: 76%; }
.progress-w-77 { width: 77%; }
.progress-w-78 { width: 78%; }
.progress-w-79 { width: 79%; }
.progress-w-80 { width: 80%; }
.progress-w-81 { width: 81%; }
.progress-w-82 { width: 82%; }
.progress-w-83 { width: 83%; }
.progress-w-84 { width: 84%; }
.progress-w-85 { width: 85%; }
.progress-w-86 { width: 86%; }
.progress-w-87 { width: 87%; }
.progress-w-88 { width: 88%; }
.progress-w-89 { width: 89%; }
.progress-w-90 { width: 90%; }
.progress-w-91 { width: 91%; }
.progress-w-92 { width: 92%; }
.progress-w-93 { width: 93%; }
.progress-w-94 { width: 94%; }
.progress-w-95 { width: 95%; }
.progress-w-96 { width: 96%; }
.progress-w-97 { width: 97%; }
.progress-w-98 { width: 98%; }
.progress-w-99 { width: 99%; }
.progress-w-100 { width: 100%; }
.progress-h-0 { background: hsl(0, 78%, 46%); }
.progress-h-1 { background: hsl(1, 78%, 46%); }
.progress-h-2 { background: hsl(2, 78%, 46%); }
.progress-h-3 { background: hsl(3, 78%, 46%); }
.progress-h-4 { background: hsl(4, 78%, 46%); }
.progress-h-5 { background: hsl(5, 78%, 46%); }
.progress-h-6 { background: hsl(6, 78%, 46%); }
.progress-h-7 { background: hsl(7, 78%, 46%); }
.progress-h-8 { background: hsl(8, 78%, 46%); }
.progress-h-9 { background: hsl(9, 78%, 46%); }
.progress-h-10 { background: hsl(10, 78%, 46%); }
.progress-h-11 { background: hsl(11, 78%, 46%); }
.progress-h-12 { background: hsl(12, 78%, 46%); }
.progress-h-13 { background: hsl(13, 78%, 46%); }
.progress-h-14 { background: hsl(14, 78%, 46%); }
.progress-h-15 { background: hsl(15, 78%, 46%); }
.progress-h-16 { background: hsl(16, 78%, 46%); }
.progress-h-17 { background: hsl(17, 78%, 46%); }
.progress-h-18 { background: hsl(18, 78%, 46%); }
.progress-h-19 { background: hsl(19, 78%, 46%); }
.progress-h-20 { background: hsl(20, 78%, 46%); }
.progress-h-21 { background: hsl(21, 78%, 46%); }
.progress-h-22 { background: hsl(22, 78%, 46%); }
.progress-h-23 { background: hsl(23, 78%, 46%); }
.progress-h-24 { background: hsl(24, 78%, 46%); }
.progress-h-25 { background: hsl(25, 78%, 46%); }
.progress-h-26 { background: hsl(26, 78%, 46%); }
.progress-h-27 { background: hsl(27, 78%, 46%); }
.progress-h-28 { background: hsl(28, 78%, 46%); }
.progress-h-29 { background: hsl(29, 78%, 46%); }
.progress-h-30 { background: hsl(30, 78%, 46%); }
.progress-h-31 { background: hsl(31, 78%, 46%); }
.progress-h-32 { background: hsl(32, 78%, 46%); }
.progress-h-33 { background: hsl(33, 78%, 46%); }
.progress-h-34 { background: hsl(34, 78%, 46%); }
.progress-h-35 { background: hsl(35, 78%, 46%); }
.progress-h-36 { background: hsl(36, 78%, 46%); }
.progress-h-37 { background: hsl(37, 78%, 46%); }
.progress-h-38 { background: hsl(38, 78%, 46%); }
.progress-h-39 { background: hsl(39, 78%, 46%); }
.progress-h-40 { background: hsl(40, 78%, 46%); }
.progress-h-41 { background: hsl(41, 78%, 46%); }
.progress-h-42 { background: hsl(42, 78%, 46%); }
.progress-h-43 { background: hsl(43, 78%, 46%); }
.progress-h-44 { background: hsl(44, 78%, 46%); }
.progress-h-45 { background: hsl(45, 78%, 46%); }
.progress-h-46 { background: hsl(46, 78%, 46%); }
.progress-h-47 { background: hsl(47, 78%, 46%); }
.progress-h-48 { background: hsl(48, 78%, 46%); }
.progress-h-49 { background: hsl(49, 78%, 46%); }
.progress-h-50 { background: hsl(50, 78%, 46%); }
.progress-h-51 { background: hsl(51, 78%, 46%); }
.progress-h-52 { background: hsl(52, 78%, 46%); }
.progress-h-53 { background: hsl(53, 78%, 46%); }
.progress-h-54 { background: hsl(54, 78%, 46%); }
.progress-h-55 { background: hsl(55, 78%, 46%); }
.progress-h-56 { background: hsl(56, 78%, 46%); }
.progress-h-57 { background: hsl(57, 78%, 46%); }
.progress-h-58 { background: hsl(58, 78%, 46%); }
.progress-h-59 { background: hsl(59, 78%, 46%); }
.progress-h-60 { background: hsl(60, 78%, 46%); }
.progress-h-61 { background: hsl(61, 78%, 46%); }
.progress-h-62 { background: hsl(62, 78%, 46%); }
.progress-h-63 { background: hsl(63, 78%, 46%); }
.progress-h-64 { background: hsl(64, 78%, 46%); }
.progress-h-65 { background: hsl(65, 78%, 46%); }
.progress-h-66 { background: hsl(66, 78%, 46%); }
.progress-h-67 { background: hsl(67, 78%, 46%); }
.progress-h-68 { background: hsl(68, 78%, 46%); }
.progress-h-69 { background: hsl(69, 78%, 46%); }
.progress-h-70 { background: hsl(70, 78%, 46%); }
.progress-h-71 { background: hsl(71, 78%, 46%); }
.progress-h-72 { background: hsl(72, 78%, 46%); }
.progress-h-73 { background: hsl(73, 78%, 46%); }
.progress-h-74 { background: hsl(74, 78%, 46%); }
.progress-h-75 { background: hsl(75, 78%, 46%); }
.progress-h-76 { background: hsl(76, 78%, 46%); }
.progress-h-77 { background: hsl(77, 78%, 46%); }
.progress-h-78 { background: hsl(78, 78%, 46%); }
.progress-h-79 { background: hsl(79, 78%, 46%); }
.progress-h-80 { background: hsl(80, 78%, 46%); }
.progress-h-81 { background: hsl(81, 78%, 46%); }
.progress-h-82 { background: hsl(82, 78%, 46%); }
.progress-h-83 { background: hsl(83, 78%, 46%); }
.progress-h-84 { background: hsl(84, 78%, 46%); }
.progress-h-85 { background: hsl(85, 78%, 46%); }
.progress-h-86 { background: hsl(86, 78%, 46%); }
.progress-h-87 { background: hsl(87, 78%, 46%); }
.progress-h-88 { background: hsl(88, 78%, 46%); }
.progress-h-89 { background: hsl(89, 78%, 46%); }
.progress-h-90 { background: hsl(90, 78%, 46%); }
.progress-h-91 { background: hsl(91, 78%, 46%); }
.progress-h-92 { background: hsl(92, 78%, 46%); }
.progress-h-93 { background: hsl(93, 78%, 46%); }
.progress-h-94 { background: hsl(94, 78%, 46%); }
.progress-h-95 { background: hsl(95, 78%, 46%); }
.progress-h-96 { background: hsl(96, 78%, 46%); }
.progress-h-97 { background: hsl(97, 78%, 46%); }
.progress-h-98 { background: hsl(98, 78%, 46%); }
.progress-h-99 { background: hsl(99, 78%, 46%); }
.progress-h-100 { background: hsl(100, 78%, 46%); }
.progress-h-101 { background: hsl(101, 78%, 46%); }
.progress-h-102 { background: hsl(102, 78%, 46%); }
.progress-h-103 { background: hsl(103, 78%, 46%); }
.progress-h-104 { background: hsl(104, 78%, 46%); }
.progress-h-105 { background: hsl(105, 78%, 46%); }
.progress-h-106 { background: hsl(106, 78%, 46%); }
.progress-h-107 { background: hsl(107, 78%, 46%); }
.progress-h-108 { background: hsl(108, 78%, 46%); }
.progress-h-109 { background: hsl(109, 78%, 46%); }
.progress-h-110 { background: hsl(110, 78%, 46%); }
.progress-h-111 { background: hsl(111, 78%, 46%); }
.progress-h-112 { background: hsl(112, 78%, 46%); }
.progress-h-113 { background: hsl(113, 78%, 46%); }
.progress-h-114 { background: hsl(114, 78%, 46%); }
.progress-h-115 { background: hsl(115, 78%, 46%); }
.progress-h-116 { background: hsl(116, 78%, 46%); }
.progress-h-117 { background: hsl(117, 78%, 46%); }
.progress-h-118 { background: hsl(118, 78%, 46%); }
.progress-h-119 { background: hsl(119, 78%, 46%); }
.progress-h-120 { background: hsl(120, 78%, 46%); }

.checkbox-line {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-weight: normal;
}

.checkbox-line input[type="checkbox"] {
  width: auto;
  margin-top: 3px;
}

/* Styles spécifiques : admin_users.html */
body.page-admin-users .checkbox-line,
.checkbox-line {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 10px;
  font-weight: normal;
  line-height: 1.35;
}

body.page-admin-users .checkbox-line input[type="checkbox"],
.checkbox-line input[type="checkbox"] {
  width: 18px;
  min-width: 18px;
  height: 18px;
  margin: 1px 0 0;
  padding: 0;
  flex: 0 0 auto;
}

.admin-tenant-create-form {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) auto;
  gap: 10px;
  align-items: end;
  margin: 14px 0;
}

.admin-tenant-list {
  display: grid;
  gap: 10px;
}

.admin-tenant-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-sunken);
}

.admin-tenant-row form {
  margin: 0;
}

.field-help {
  margin-top: 6px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.35;
}

@media (max-width: 800px) {
  .admin-tenant-create-form,
  .admin-tenant-row {
    grid-template-columns: 1fr;
  }

  .admin-tenant-create-form .btn,
  .admin-tenant-row .btn,
  .admin-tenant-row form {
    width: 100%;
  }
}

/* Invitations user → user (panneau admin). Mêmes briques visuelles que la
   liste des espaces. */
.admin-invite-form {
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
}

.admin-invite-list {
  display: grid;
  gap: 10px;
}

.admin-invite-list-title {
  margin: 4px 0 0;
  font-size: 1rem;
}

.admin-invite-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel-sunken);
}

.admin-invite-row form {
  margin: 0;
}

@media (max-width: 640px) {
  .admin-invite-row {
    grid-template-columns: 1fr;
  }

  .admin-invite-row .btn,
  .admin-invite-row form {
    width: 100%;
  }
}

/* Navigation compacte */
.nav-more {
  display: block;
  position: relative;
  margin-left: 2px;
}

.nav-more > summary {
  list-style: none;
}

.nav-more > summary::-webkit-details-marker {
  display: none;
}

.nav-more-summary {
  cursor: pointer;
}

.nav-more-summary .nav-icon {
  display: block;
}

.nav-more-summary .nav-label {
  display: inline-flex;
}

/* Le clic doit TOUJOURS atterrir sur le <summary> (qui toggle le <details>) :
   sinon, suivant la zone touchée (icône / label / padding), un enfant peut
   capter l'événement sans déclencher l'ouverture — d'où la sensation que la
   partie droite du bouton « n'est pas cliquable » sur desktop. */
.nav-more-summary .nav-icon,
.nav-more-summary .nav-label,
.nav-more-summary .nav-icon * {
  pointer-events: none;
}

.nav-more-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 80;
  display: grid;
  gap: 8px;
  min-width: 230px;
  padding: 10px;
  background: var(--topbar-bg);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 18px 42px rgba(0, 0, 0, .35);
}

.nav-more-menu a,
.nav-more-menu .nav-btn {
  width: 100%;
  justify-content: flex-start;
  min-height: 44px;
  padding: 10px 12px;
}

.nav-more-menu form {
  width: 100%;
}

.nav-more-menu .nav-icon,
.nav-more-menu .nav-label {
  display: inline-flex;
}

.nav-login-link .nav-icon {
  display: none;
}

@media (max-width: 900px) {
  .nav-item-home,
  .nav-item-secondary,
  .nav-logout-form {
    display: none !important;
  }


  .nav-more-summary {
    width: 44px;
    min-width: 44px;
    height: 44px;
    padding: 0;
  }

  .nav-more-summary .nav-label {
    display: none;
  }

  .nav-more[open] .nav-more-summary {
    background: var(--primary);
  }

  .nav-more-menu {
    max-width: calc(100vw - 24px);
  }

  .nav-more-menu .nav-icon {
    display: block;
  }

  .nav-more-menu .nav-label {
    display: inline-flex;
  }

  .nav-more-menu .nav-btn {
    color: var(--text);
  }

  .nav-login-link .nav-icon {
    display: block;
  }
}

.touch-pair-expiry {
  color: #fbbf24;
}

/* ============================================================== */
/* Bottom nav mobile — pattern app native (Instagram/Notion).      */
/* Cachée par défaut, activée sur ≤700px (en remplacement de la    */
/* topbar qui devient elle-même cachée à ce breakpoint).           */
/* ============================================================== */

.bottom-nav {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  background: var(--topbar-bg);
  border-top: 1px solid var(--border);
  /* Padding latéral : éloigne les libellés des bords (coins arrondis iPhone).
     Padding bas : un peu d'air sous les icônes (la nav paraissait trop petite
     depuis le retrait de viewport-fit=cover, qui apportait ~34px de safe-area).
     Les `env()` restent en fallback si cover est un jour réactivé. */
  padding: 0 calc(10px + env(safe-area-inset-right, 0)) calc(8px + env(safe-area-inset-bottom, 0)) calc(10px + env(safe-area-inset-left, 0));
  box-shadow: 0 -2px 12px var(--shadow-color, rgba(0, 0, 0, 0.20));
  /* Glisse hors champ en « mode écriture » (form recette) — cf fin de fichier. */
  transition: transform 0.22s ease, opacity 0.22s ease;
}

.bottom-nav-item {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 4px;
  padding: 9px 4px 8px;
  min-height: 62px;
  color: var(--muted);
  text-decoration: none;
  font-size: 11px;
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: color 0.12s ease, background 0.12s ease;
  position: relative;
  /* Pas de rectangle de sélection gris au tap (iOS/Android) — seule la
     coloration de l'onglet actif sert de feedback. */
  -webkit-tap-highlight-color: transparent;
}

/* :focus-visible toujours actif (accessibilité clavier). Le :hover est
   isolé dans @media (hover: hover) : sur iOS Safari, un :hover sur un
   lien provoque le « double-tap » (1er tap = applique le hover, 2e tap =
   suit le lien). En le réservant aux appareils à vraie souris, le tap
   tactile navigue du 1er coup (le :active ci-dessous gère le feedback). */
.bottom-nav-item:focus-visible {
  color: var(--text);
  outline: none;
  background: var(--hover-bg);
}

@media (hover: hover) {
  .bottom-nav-item:hover {
    color: var(--text);
    background: var(--hover-bg);
  }
}

.bottom-nav-item.is-active {
  color: var(--primary);
}

/* Indicateur d'onglet actif : pilule teintée derrière l'icône (pattern
   Material 3 / lisible aussi sur iOS). Pas que la couleur → reste un repère
   de forme distinct, conforme WCAG. */
.bottom-nav-icon {
  display: inline-flex;
  width: 46px;
  height: 28px;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  transition: background 0.14s ease;
}

.bottom-nav-item.is-active .bottom-nav-icon {
  background: color-mix(in srgb, var(--primary) 16%, transparent);
}

.bottom-nav-icon svg {
  width: 23px;
  height: 23px;
}

/* Libellés sous les icônes — réaffichés. Material recommande le texte, et ça
   lève l'ambiguïté des icônes (la cloche « Menu » notamment). */
.bottom-nav-label {
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0.1px;
}

/* Bouton central « Créer » : pastille corail (couleur d'accent de l'app),
   agrandie et surélevée façon FAB. L'anneau couleur-barre la détache du fond.
   Les marges négatives haut/bas la font déborder vers le haut tout en ne
   réservant qu'une hauteur d'icône normale → le libellé « Créer » reste aligné
   avec les autres. Pas d'état actif (ce n'est pas un onglet). */
.bottom-nav-add-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  margin: -18px 0 -6px;
  border-radius: 999px;
  background: var(--primary);
  color: #fff;
  border: 3px solid var(--topbar-bg);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--primary) 42%, transparent);
  /* Animé pour le feedback de press (le FAB « s'enfonce » au tap). */
  transition: transform 0.13s ease, box-shadow 0.13s ease;
}

/* Retour utilisateur au tap : le « + » s'enfonce légèrement (scale) et son
   ombre se resserre — feeling de bouton physique pressé. */
.bottom-nav-add:active .bottom-nav-add-icon {
  transform: scale(0.88);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--primary) 50%, transparent);
}

.bottom-nav-add-icon .ico {
  width: 26px;
  height: 26px;
}

.bottom-nav-add .bottom-nav-label {
  color: var(--primary);
  font-weight: 600;
}

/* ── Barre supérieure mobile + menu « Réglages » (roue) ─────────────────
   Réactive une top bar sur mobile (la .topbar desktop reste cachée ≤700px).
   La roue est un <details> qui ouvre le MÊME bottom-sheet que l'ex-onglet
   « Plus ». Sticky pour rester visible au scroll. */
.mobile-topbar {
  display: none;
  /* Sticky : reste visible en haut au scroll. Les vrais coupables des bugs nav
     iOS de la refonte (nav surélevée / float) étaient `viewport-fit=cover` +
     `min-height: 100dvh` sur le body, PAS cette barre sticky — cf CONSIGNES /
     mémoire. ⚠️ Si un flickering vertical réapparaît EN NAVIGATION, le suspect
     redevient ce `sticky` → repasser en `static`. */
  position: sticky;
  top: 0;
  z-index: 40;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 14px;
  padding-top: calc(8px + env(safe-area-inset-top, 0));
  background: var(--topbar-bg);
  border-bottom: 1px solid var(--border);
  /* Glisse hors champ en « mode écriture » (form recette) — cf fin de fichier. */
  transition: transform 0.22s ease;
}

.mobile-topbar-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  min-height: 44px;
  -webkit-tap-highlight-color: transparent;
}

.mobile-topbar-brand .brand-wordmark {
  font-size: 20px;
}

.mobile-topbar-actions {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.mobile-topbar-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--border);
  /* Icône en --muted au repos, comme les onglets de nav (cohérence du
     « poids » des icônes sur le thème) ; corail quand actif/hover. */
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  position: relative;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.mobile-topbar-btn .ico {
  width: 21px;
  height: 21px;
}

.mobile-topbar-btn:active {
  transform: scale(0.94);
}

/* État actif (page en cours, ex. Mon compte) : pastille corail. Par défaut
   les boutons de la top bar restent NEUTRES — l'actif est ainsi un vrai
   repère (avant, le compte était corail en permanence → on ne savait pas
   quand on y était). */
.mobile-topbar-btn.is-active {
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-2));
  border-color: var(--primary);
  color: var(--primary);
}

@media (hover: hover) {
  .mobile-topbar-btn:hover,
  .mobile-topbar-btn:focus-visible {
    background: var(--hover-bg);
    border-color: var(--primary);
    outline: none;
  }
}

.appbar-menu {
  position: relative;
  display: inline-flex;
}

.appbar-menu-summary {
  list-style: none;
  user-select: none;
}

.appbar-menu-summary::-webkit-details-marker { display: none; }
.appbar-menu-summary::marker { content: ""; }

.appbar-menu[open] .appbar-menu-summary {
  background: var(--hover-bg);
  border-color: var(--primary);
  color: var(--primary);
}

/* Backdrop + sheet : seulement visibles quand le details est ouvert. */

.bottom-nav-sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
  z-index: 60;
}

.appbar-menu[open] .bottom-nav-sheet-backdrop {
  opacity: 1;
  pointer-events: auto;
}

.bottom-nav-sheet {
  position: fixed;
  left: 0;
  right: 0;
  /* Le sheet vient se poser AU-DESSUS de la bottom-nav. La nav fait
     ~71px (item min-height 62 + padding-bottom 8 + border-top 1) +
     safe-area. 72px = juste au-dessus, sans recouvrir la nav (sinon le
     menu déborde sur les onglets — bug signalé après la nav surélevée). */
  bottom: calc(72px + env(safe-area-inset-bottom, 0));
  z-index: 70;
  background: var(--panel);
  border-top-left-radius: 18px;
  border-top-right-radius: 18px;
  border-top: 1px solid var(--border);
  /* Pas de padding-bottom : le sheet colle directement au top de la
     bottom-nav (qui assure déjà l'espace en dessous via son propre
     padding + safe-area). Évite le gap visuel entre les deux. */
  padding: 0 6px 0;
  box-shadow: 0 -8px 30px var(--shadow-color, rgba(0, 0, 0, 0.35));
  /* translate3d (au lieu de translateY) force un composite layer GPU dès
     le départ → le sheet est pré-rendu hors du layout normal, plus de
     flash où il apparaît brièvement dans la cellule du <details> avant
     de prendre sa position fixed pleine largeur. */
  transform: translate3d(0, calc(100% + 72px + env(safe-area-inset-bottom, 0)), 0);
  transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
  max-height: calc(80vh - 72px - env(safe-area-inset-bottom, 0));
  overflow-y: auto;
}

.appbar-menu[open] .bottom-nav-sheet {
  transform: translate3d(0, 0, 0);
}

.bottom-nav-sheet-header {
  /* padding-top généreux : compense le retrait de la « poignée » décorative,
     éloigne le titre du bord arrondi du sheet. */
  padding: 16px 14px 6px;
}

.bottom-nav-sheet-title {
  font-weight: 600;
  font-size: 14px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Footer : "Se déconnecter" à gauche prend toute la place restante,
   bouton × en bas-droite — proche du pouce qui a tapé "Plus" (4e onglet
   à droite de la bottom-nav). Évite tout missclick sur la déconnexion. */
.bottom-nav-sheet-footer {
  display: flex;
  align-items: stretch;
  gap: 10px;
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.bottom-nav-sheet-footer .bottom-nav-sheet-form {
  flex: 1 1 auto;
}

.bottom-nav-sheet-footer .bottom-nav-sheet-item {
  width: 100%;
}

.bottom-nav-sheet-close {
  flex: 0 0 auto;
  width: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  color: var(--text);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.12s ease;
}

.bottom-nav-sheet-close:hover,
.bottom-nav-sheet-close:focus-visible {
  background: var(--hover-bg);
  border-color: var(--primary);
  outline: none;
}

.bottom-nav-sheet-close:active {
  transform: scale(0.94);
}

.bottom-nav-sheet-item {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px 14px;
  border-radius: 12px;
  color: var(--text);
  text-decoration: none;
  font-size: 15px;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  transition: background 0.12s ease;
}

.bottom-nav-sheet-item:focus-visible {
  background: var(--hover-bg);
  outline: none;
}

@media (hover: hover) {
  .bottom-nav-sheet-item:hover {
    background: var(--hover-bg);
  }
}

.bottom-nav-sheet-item-danger {
  color: var(--danger);
}

.bottom-nav-sheet-icon {
  font-size: 22px;
  width: 28px;
  text-align: center;
  flex-shrink: 0;
}

.bottom-nav-sheet-form {
  margin: 0;
}

/* Activation à ≤700px ----------------------------------------- */

@media (max-width: 700px) {
  /* Topbar desktop cachée — la top bar mobile + la bottom nav prennent le relais */
  .topbar {
    display: none;
  }

  /* Barre supérieure mobile (nom + compte + roue Réglages) */
  .mobile-topbar {
    display: flex;
  }

  /* Bottom nav visible */
  .bottom-nav {
    display: flex;
  }

  /* Réserver de l'espace en bas du contenu pour ne pas être recouvert
     par la bottom nav (~60px hauteur + safe-area iOS) + un buffer de
     16px pour que les boutons en bas de page (Enregistrer, etc.) ne
     soient pas collés à la nav. */
  /* Réserver de l'espace en bas du contenu pour ne pas être recouvert par la
     bottom nav (~60px + safe-area iOS) + buffer de 16px.
     ⚠️ PAS de `min-height` ici : la version d'avant la refonte nav (qui
     marchait, commit 2229d30) n'avait QUE ce `padding-bottom`. Le
     `min-height: 100dvh` ajouté ensuite comme « fix » du float en était en fait
     la CAUSE : forcer le body à 100dvh fait ancrer la bottom-nav `fixed` au
     mauvais repère au scroll-top sur iOS (elle reste surélevée, et ne se recale
     qu'après un scroll). Le vrai correctif = revenir au seul padding-bottom.
     cf CONSIGNES « Pièges connus ». */
  body {
    padding-bottom: calc(72px + env(safe-area-inset-bottom, 0) + 16px);
  }
}

/* ============================================================== */
/* Pages /menu/* — admin mobile-first.                             */
/* Style "appli pro" : hero card, cards horizontales avec photo,   */
/* chips d'accompagnements, contrôles tactiles 44px minimum.       */
/* ============================================================== */

.menu-inline-form {
  margin: 0;
  display: inline-flex;
}

/* Hero card ----------------------------------------------------- */

.menu-hero {
  position: relative;
  /* Halo décoratif en haut à droite via radial-gradient stacké sur le
     linear, clippé naturellement par le border-radius — pas besoin
     d'overflow: hidden (qui empêcherait les dropdowns enfants comme
     le filtre durée de déborder). */
  background:
    radial-gradient(
      circle at calc(100% - 50px) -10px,
      color-mix(in srgb, var(--primary) 12%, transparent) 0%,
      transparent 35%
    ),
    linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 22px 22px 18px;
  margin-bottom: 18px;
  box-shadow: 0 6px 20px var(--shadow-color, rgba(0, 0, 0, 0.18));
}

.menu-hero-inner {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  position: relative;
}

/* .menu-hero-inner ne contient que {h1 titre | filtre durée} en row.
   Le H1 (block par défaut) devient flex item — il prend sa largeur
   naturelle (pas 100% comme en block), le filtre durée vient juste
   après lui, collé. */
.menu-hero-duration {
  flex: 0 0 auto;
  max-width: 100%;
}

/* Subtitle sort de l'inner pour être sur sa propre ligne en dessous
   — il n'élargit donc pas la ligne titre+durée. */
.menu-hero-subtitle {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.4;
}

/* Toolbar (recherche / panier / mémoire / vider) sur sa propre ligne
   en dessous du bloc titre, alignée à gauche. */
.menu-hero-toolbar {
  margin-top: 14px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  position: relative;
  justify-content: flex-start;
}

.menu-hero-title {
  margin: 0;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}

/* Le CTA "Proposer un menu" est maintenant SOUS le bloc inner
   (titre + toolbar), en pleine largeur du hero. */
.menu-hero-cta-form {
  width: 100%;
  margin-top: 16px;
}

.menu-hero-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  padding: 14px 22px;
  min-height: 56px;
  background: var(--primary);
  color: #ffffff;
  border: 0;
  border-radius: 14px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 35%, transparent);
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}

.menu-hero-cta:hover,
.menu-hero-cta:focus-visible {
  background: var(--primary-hover);
  transform: translateY(-1px);
  box-shadow: 0 8px 22px color-mix(in srgb, var(--primary) 40%, transparent);
  outline: none;
}

.menu-hero-cta:active {
  transform: translateY(0);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--primary) 30%, transparent);
}

.menu-hero-cta-emoji {
  font-size: 22px;
  line-height: 1;
}

.menu-hero-cta-label {
  white-space: nowrap;
}

.menu-toolbar-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 14px;
  background: var(--panel-sunken);
  /* LIBELLÉ en --text (lisible) + poids 500 = même traitement que .menu-action /
     .btn.secondary → le bouton se lit comme cliquable. ICÔNE en --muted via la
     règle ci-dessous (sauf variante -primary qui garde son icône corail). */
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.menu-toolbar-btn:not(.menu-toolbar-btn-primary) .ico {
  color: var(--muted);
}

/* Icône agrandie + trait épaissi (cf .menu-action / .recipe-action-icon). */
.menu-toolbar-btn .ico {
  width: 19px;
  height: 19px;
  stroke-width: 2.25;
}

.menu-toolbar-btn:hover,
.menu-toolbar-btn:focus-visible {
  background: var(--hover-bg);
  border-color: var(--primary);
  color: var(--text);
  outline: none;
}

.menu-toolbar-btn:hover .ico,
.menu-toolbar-btn:focus-visible .ico {
  color: var(--text);
}

.menu-toolbar-btn-danger:hover,
.menu-toolbar-btn-danger:focus-visible {
  border-color: var(--danger);
}

/* Variant "primary" du bouton toolbar (utilisé pour signaler l'action
   suivante : préparer la liste de courses). Fond neutre comme les autres
   boutons toolbar, mais bordure épaisse couleur primary du thème — le
   bouton se démarque visuellement sans changer la palette générale. */
.menu-toolbar-btn-primary {
  border-width: 2px;
  border-color: var(--primary);
  color: var(--primary);
  font-weight: 600;
}

.menu-toolbar-btn-primary:hover,
.menu-toolbar-btn-primary:focus-visible {
  border-color: var(--primary-hover);
  color: var(--primary-hover);
}

/* Filtres durée -------------------------------------------------- */

.menu-filter {
  position: relative;
}

.menu-filter-summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 14px;
  background: var(--panel-sunken);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.menu-filter-summary::-webkit-details-marker { display: none; }
.menu-filter-summary::marker { content: ""; }

.menu-filter-summary:hover,
.menu-filter-summary:focus-visible {
  background: var(--hover-bg);
  border-color: var(--primary);
  outline: none;
}

.menu-filter-active {
  border-color: var(--primary);
  color: var(--primary);
}

.menu-filter[open] > .menu-filter-summary {
  background: var(--hover-bg);
  border-color: var(--primary);
}

.menu-filter-form {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 30;
  min-width: 220px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  box-shadow: 0 10px 30px var(--shadow-color, rgba(0, 0, 0, 0.25));
}

.menu-filter-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.menu-filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

@media (hover: hover) {
  .menu-filter-pill:hover {
    border-color: var(--primary);
  }
}

.menu-filter-pill input { accent-color: var(--primary); }

.menu-filter-pill:has(input:checked) {
  background: var(--primary);
  color: #ffffff;
  border-color: var(--primary);
}

.menu-filter-actions {
  display: flex;
  justify-content: flex-end;
}

/* Sections ------------------------------------------------------- */

.menu-section {
  margin-top: 24px;
}

.menu-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.menu-section-title {
  margin: 0 0 12px;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 10px;
}

.menu-section-head .menu-section-title {
  margin-bottom: 0;
}

.menu-section-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 22px;
  padding: 0 8px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
}

.menu-stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Empty state ---------------------------------------------------- */

.menu-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 28px 18px;
  background: var(--panel);
  border: 1px dashed var(--border);
  border-radius: 14px;
  text-align: center;
  color: var(--muted);
}

.menu-empty-emoji {
  font-size: 32px;
  opacity: 0.6;
}

.menu-empty-text {
  font-size: 14px;
  max-width: 320px;
}

/* Cards menu ----------------------------------------------------- */

.menu-card {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px;
  box-shadow: 0 2px 8px var(--shadow-color, rgba(0, 0, 0, 0.12));
  transition: border-color 0.12s ease, transform 0.12s ease;
}

.menu-card:hover {
  border-color: var(--primary);
}

.menu-card-suggestion {
  border-color: var(--primary);
  background: linear-gradient(135deg, var(--panel) 0%, var(--panel) 60%, var(--panel-2) 100%);
}

.menu-badge {
  position: absolute;
  top: -10px;
  left: 14px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  background: var(--primary);
  color: #ffffff;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 35%, transparent);
}

.menu-card-body {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 14px;
  align-items: start;
}

.menu-card-body-noimg {
  grid-template-columns: 1fr;
}

.menu-card-thumb-link {
  display: block;
  border-radius: 12px;
  overflow: hidden;
  transition: transform 0.12s ease;
}

.menu-card-thumb-link:hover {
  transform: scale(1.02);
}

.menu-card-content {
  min-width: 0;
}

.menu-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}

.menu-card-title {
  font-size: 17px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  line-height: 1.25;
  flex: 1 1 auto;
  min-width: 0;
  word-break: break-word;
}

.menu-card-title:hover {
  color: var(--primary);
}

.menu-card-title-muted {
  color: var(--muted);
}

.menu-card-meta {
  /* Layout only — taille/couleur viennent de .recipe-meta (macro recipe_meta). */
  margin-bottom: 8px;
}

/* Thumbnails ----------------------------------------------------- */

.menu-thumb {
  position: relative;
  width: 96px;
  height: 96px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--panel-2);
  border: 1px solid var(--border);
  flex-shrink: 0;
}

.menu-thumb-lg {
  width: 96px;
  height: 96px;
}

.menu-thumb-sm {
  width: 56px;
  height: 56px;
}

.menu-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.menu-thumb-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--panel-2) 0%, var(--panel-sunken) 100%);
}

.menu-thumb-fallback {
  font-size: 32px;
  opacity: 0.4;
}

.menu-thumb-sm .menu-thumb-fallback {
  font-size: 22px;
}

/* Servings stepper ---------------------------------------------- */

.menu-servings {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
  flex-shrink: 0;
}

.menu-servings-btn {
  /* Bouton visible 36px (compact pour le stepper) avec zone de tap
     étendue à 44px via pseudo ::after (recommandation Apple/Google
     pour les accessibilité tactile). */
  position: relative;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease;
}

.menu-servings-btn::after {
  content: "";
  position: absolute;
  inset: -4px;
}

.menu-servings-btn:hover,
.menu-servings-btn:focus-visible {
  background: var(--hover-bg);
  outline: none;
}

.menu-servings-value {
  display: inline-flex;
  align-items: baseline;
  gap: 1px;
  min-width: 30px;
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}

.menu-servings-unit {
  font-size: 11px;
  color: var(--muted);
  font-weight: 500;
}

/* Vague 11 — recettes liées génériques (card menu) ---------------- */

.menu-linked-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  margin-top: 8px;
  padding: 6px 12px;
  background: var(--panel-2);
  border: 1px dashed var(--border);
  border-radius: 999px;
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.menu-linked-btn:hover,
.menu-linked-btn:focus-visible {
  border-color: var(--primary);
  color: var(--text);
  background: var(--hover-bg);
  outline: none;
}

.menu-linked-btn.is-active {
  border-style: solid;
  border-color: var(--primary);
  color: var(--text);
}

.menu-linked-btn-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--primary);
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
}

/* Picker recettes liées — lignes à cocher (multi-sélection) ------- */

.menu-picker-row-check {
  cursor: pointer;
}

.menu-picker-row-checkbox {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  accent-color: var(--primary);
  cursor: pointer;
}

.menu-picker-row-tick {
  flex: 0 0 auto;
  display: inline-flex;
  margin-left: auto;
  color: var(--primary);
  opacity: 0;
  transition: opacity 0.12s ease;
}

.menu-picker-row-check:has(.menu-picker-row-checkbox:checked) {
  border-color: var(--primary);
}

.menu-picker-row-check:has(.menu-picker-row-checkbox:checked) .menu-picker-row-tick {
  opacity: 1;
}

.menu-picker-list-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 14px;
}

/* Fiche recette — bloc « Recettes liées » (navigation) ----------- */

.menu-recipe-linked {
  margin: 0 0 18px;
}

.menu-recipe-linked-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.menu-recipe-linked-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  text-decoration: none;
  color: var(--text);
  transition: background 0.12s ease, border-color 0.12s ease;
}

.menu-recipe-linked-card:hover,
.menu-recipe-linked-card:focus-visible {
  border-color: var(--primary);
  background: var(--hover-bg);
  outline: none;
}

.menu-recipe-linked-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.menu-recipe-linked-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-recipe-linked-meta {
  /* 14px : méta type harmonisée (cf .recipe-row-meta). */
  font-size: 14px;
  color: var(--muted);
}

.menu-recipe-linked-arrow {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--muted);
}

/* Actions card --------------------------------------------------- */

.menu-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}

/* Quand on a un stepper personnes dans les actions (cards "sélection"),
   on pousse le stepper à gauche et le bouton "Retirer" à droite via
   justify-content: space-between. */
.menu-card-actions:has(.menu-servings) {
  justify-content: space-between;
}

.menu-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 14px;
  /* Fond --panel-sunken : identique aux boutons de référence .recipe-action-icon
     de /recipes (cohérence). Ce n'est PAS le fond qui faisait « pas cliquable »,
     c'était le libellé grisé (corrigé juste en dessous). */
  background: var(--panel-sunken);
  /* LIBELLÉ en --text (lisible) → on comprend que c'est un bouton cliquable.
     Avant : tout en --muted (gris) → le libellé paraissait « désactivé »
     (signalé : « on ne comprend pas que c'est cliquable »). L'ICÔNE reste en
     --muted (calme, thème) via la règle ci-dessous. Variantes pleines
     -primary/-success/-danger/-plan : couleur propre, non touchées. */
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

/* Icône en --muted UNIQUEMENT sur les boutons secondaires (non pleins) : les
   variantes corail/vert/rouge/planifié gardent leur icône (blanche/colorée). */
.menu-action:not(.menu-action-primary):not(.menu-action-success):not(.menu-action-danger):not(.menu-action-plan-is-planned) .ico {
  color: var(--muted);
}

/* Icône AGRANDIE (19px) + trait un peu plus épais → présence « bouton », même
   idée que les icônes de /recipes (.recipe-action-icon = 21px). Avant : 1em
   ≈ 14px → petit et « mou ». (Lucide pose stroke-width sur le <svg>, donc le
   CSS le surcharge bien, hérité par les <path>.) */
.menu-action .ico {
  width: 19px;
  height: 19px;
  stroke-width: 2.25;
}

/* Flèches de navigation semaine (boutons icône seule) : display:block pour
   annuler le vertical-align:-0.125em global de .ico, qui les décalait
   verticalement dans leur carré (paraissaient « trop hautes »). */
.menu-planning-nav .menu-action .ico {
  display: block;
}

.menu-action:hover,
.menu-action:focus-visible {
  background: var(--hover-bg);
  border-color: var(--primary);
  color: var(--text);
  outline: none;
}

.menu-action-primary {
  background: var(--primary);
  color: #ffffff;
  border-color: var(--primary);
}

.menu-action-primary:hover,
.menu-action-primary:focus-visible {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

.menu-action-success {
  background: var(--success);
  color: #ffffff;
  border-color: var(--success);
}

.menu-action-success:hover,
.menu-action-success:focus-visible {
  background: var(--success-hover);
  border-color: var(--success-hover);
}

.menu-action-ghost {
  background: transparent;
}

/* Action destructive (ex. Supprimer dans /recipes) : rouge dès le
   repos pour signaler le danger, fond plein au survol. Le :hover est
   isolé dans @media (hover: hover) — sinon iOS Safari demande un
   double-tap (cf note bottom-nav dans la ROADMAP). */
.menu-action-danger {
  color: var(--danger);
  border-color: var(--danger);
}

.menu-action-danger:focus-visible {
  background: var(--danger);
  color: #ffffff;
  border-color: var(--danger);
  outline: none;
}

@media (hover: hover) {
  .menu-action-danger:hover {
    background: var(--danger);
    color: #ffffff;
    border-color: var(--danger);
  }
}

.menu-action-label {
  white-space: nowrap;
}

/* Picker page --------------------------------------------------- */

.menu-picker-header {
  margin-bottom: 18px;
}

.menu-picker-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  margin-bottom: 12px;
  font-size: 14px;
  color: var(--muted);
  text-decoration: none;
  border-radius: 8px;
  transition: background 0.12s ease, color 0.12s ease;
}

.menu-picker-back:hover {
  background: var(--hover-bg);
  color: var(--text);
}

/* En-tête du form en ÉDITION : barre COLLANTE (sticky) sous la topbar. Contient
   UN seul bouton « Voir la recette » (ouvre la fiche). sticky = reste visible au
   scroll ET réserve sa propre hauteur (pas de recouvrement, pas de spacer).
   pointer-events:none sur la barre + auto sur ses enfants → les clics passent au
   travers des zones vides (la barre est plus large que le bouton). */
.edit-fixed-bar {
  position: sticky;
  /* Même écart que le bouton retour de la visu (variable partagée). En « mode
     écriture » (form recette), --float-back-top est réduit → la barre remonte
     sous l'encoche pour occuper la place de la topbar masquée ; on anime ce
     déplacement. */
  top: var(--float-back-top);
  transition: top 0.22s ease;
  z-index: 25;
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 6px;
  margin: 6px 0 14px;
  pointer-events: none;
}
.edit-fixed-bar > * {
  pointer-events: auto;
}
/* Icône livre centrée géométriquement (annule le vertical-align:-0.125em global
   de .ico qui la décalait). */
.edit-fixed-bar-view .ico {
  display: block;
}

.menu-picker-title {
  margin: 0 0 4px;
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
}

.menu-picker-subtitle {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

.menu-picker-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.menu-picker-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  transition: border-color 0.12s ease, transform 0.12s ease;
}

.menu-picker-row:hover {
  border-color: var(--primary);
}

.menu-picker-row-none {
  grid-template-columns: 1fr auto;
  background: var(--panel-sunken);
  border-style: dashed;
}

.menu-picker-row-info {
  min-width: 0;
}

.menu-picker-row-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.menu-picker-row-meta {
  /* Layout only — taille/couleur viennent de .recipe-meta (macro recipe_meta). */
  margin-top: 2px;
}

.menu-picker-row-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* Page ingredients --------------------------------------------- */

.menu-ingredients-hero {
  margin-bottom: 18px;
}

.menu-ingredients-hero-inner {
  background: linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 20px 22px;
  box-shadow: 0 4px 14px var(--shadow-color, rgba(0, 0, 0, 0.15));
}

.menu-ingredients-title {
  margin: 0 0 6px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}

.menu-ingredients-subtitle {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

.menu-ingredients-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.menu-ingredients-categories {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.menu-ingredient-cat {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px 16px;
  box-shadow: 0 2px 8px var(--shadow-color, rgba(0, 0, 0, 0.10));
}

.menu-ingredient-cat-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 10px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

.menu-ingredient-cat-emoji {
  font-size: 22px;
  line-height: 1;
}

.menu-ingredient-cat-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
  flex: 1 1 auto;
}

.menu-ingredient-cat-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 22px;
  padding: 0 8px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
}

.menu-ingredient-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.menu-ingredient-row {
  border-radius: 8px;
}

.menu-ingredient-row + .menu-ingredient-row {
  border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
}

.menu-ingredient-label {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 6px;
  cursor: pointer;
  min-height: 44px;
  transition: background 0.12s ease;
  border-radius: 8px;
}

.menu-ingredient-label:hover {
  background: var(--hover-bg);
}

.menu-ingredient-check {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.menu-ingredient-checkmark {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  border: 2px solid var(--border);
  background: var(--panel-sunken);
  position: relative;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.menu-ingredient-checkmark::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 6px;
  width: 6px;
  height: 11px;
  border-right: 2.5px solid #ffffff;
  border-bottom: 2.5px solid #ffffff;
  transform: rotate(45deg) scale(0);
  transition: transform 0.12s ease;
}

.menu-ingredient-check:checked + .menu-ingredient-checkmark {
  background: var(--primary);
  border-color: var(--primary);
}

.menu-ingredient-check:checked + .menu-ingredient-checkmark::after {
  transform: rotate(45deg) scale(1);
}

.menu-ingredient-check:focus-visible + .menu-ingredient-checkmark {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.menu-ingredient-text {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: wrap;
}

.menu-ingredient-name {
  font-size: 15px;
  color: var(--text);
  font-weight: 500;
}

.menu-ingredient-check:not(:checked) ~ .menu-ingredient-text .menu-ingredient-name {
  color: var(--muted);
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--muted) 50%, transparent);
}

.menu-ingredient-qty {
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.menu-ingredients-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  position: sticky;
  bottom: 12px;
  box-shadow: 0 6px 20px var(--shadow-color, rgba(0, 0, 0, 0.20));
  z-index: 5;
}

.menu-ingredients-actions .menu-action {
  min-height: 48px;
  font-size: 15px;
}

.menu-empty-link {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.menu-empty-link:hover {
  color: var(--primary-hover);
}

/* Page manual --------------------------------------------------- */

.menu-manual-hero {
  margin-bottom: 18px;
}

.menu-manual-hero-inner {
  background:
    radial-gradient(
      circle at calc(100% - 50px) -10px,
      color-mix(in srgb, var(--primary) 10%, transparent) 0%,
      transparent 35%
    ),
    linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 20px 22px;
  box-shadow: 0 4px 14px var(--shadow-color, rgba(0, 0, 0, 0.15));
}

.menu-manual-hero-head {
  margin-bottom: 16px;
}

.menu-manual-title {
  margin: 0 0 6px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}

.menu-manual-subtitle {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.4;
}

.menu-manual-search {
  display: flex;
  gap: 10px;
  margin-bottom: 14px;
}

.menu-manual-search-field {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0 14px;
  min-height: 48px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.menu-manual-search-field:focus-within {
  border-color: var(--primary);
  box-shadow: var(--field-focus-ring);
}

.menu-manual-search-icon {
  font-size: 16px;
  margin-right: 10px;
  color: var(--muted);
  flex-shrink: 0;
}

.menu-manual-search-input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: none;
  color: var(--text);
  font-size: 15px;
  padding: 0;
  min-height: 44px;
}

.menu-manual-search-input::placeholder {
  color: var(--muted);
  opacity: 0.7;
}

.menu-manual-search-input::-webkit-search-cancel-button {
  display: none;
}

.menu-manual-search-clear {
  /* Bouton 32px visible + zone tap 44 via ::after (le bouton croix
     est petit pour rester compact dans le champ recherche). */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin-left: 8px;
  background: var(--panel-2);
  border-radius: 50%;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
  text-decoration: none;
  flex-shrink: 0;
  transition: background 0.12s ease, color 0.12s ease;
}

.menu-manual-search-clear::after {
  content: "";
  position: absolute;
  inset: -6px;
}

.menu-manual-search-clear:hover {
  background: var(--danger);
  color: #ffffff;
}

.menu-manual-search-submit {
  flex-shrink: 0;
}

.menu-manual-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Filtres types : panneau plus large avec liste scrollable */

.menu-filter-form-types {
  min-width: 260px;
  max-height: 70vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.menu-filter-types-actions {
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
}

.menu-filter-types-actions .menu-action {
  flex: 1 1 0;
  min-height: 36px;
  font-size: 13px;
}

.menu-filter-types-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 12px;
  max-height: 45vh;
  overflow-y: auto;
  padding-right: 4px;
  /* Scrollbar discrète mais visible (utile vu le grand nombre de types) */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.menu-filter-types-list::-webkit-scrollbar {
  width: 6px;
}

.menu-filter-types-list::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 999px;
}

.menu-filter-pill-wide {
  width: 100%;
  justify-content: flex-start;
  padding: 10px 12px;
  border-radius: 8px;
  cursor: pointer;
}

/* Cards résultats (héritent de .menu-card mais variant compact) */

.menu-card-result .menu-card-actions {
  border-top: 1px solid var(--border);
}

/* Page recipe (lecture seule) ----------------------------------- */

.menu-recipe-hero {
  margin-bottom: 18px;
  position: relative;
}

/* Écart commun (visu + édition) entre le haut du viewport et le bouton retour
   flottant : SOUS la topbar sticky (mobile ~58px+safe / desktop ~60px), avec un
   petit jeu. Une seule variable → visu et édition ont le MÊME écart. */
:root {
  --float-back-top: calc(env(safe-area-inset-top, 0px) + 64px);
}
@media (min-width: 701px) {
  :root {
    --float-back-top: 76px;
  }
}

/* Bouton retour FLOTTANT : flèche seule, fixée en haut-gauche du viewport →
   reste visible quand on descend la page (au-dessus de la photo / des cartes).
   IMPORTANT : la topbar (mobile sticky ~58px+safe / desktop sticky ~60px) est
   opaque et au-dessus → on décale le FAB SOUS elle, sinon il est recouvert et
   invisible. Fond légèrement transparent + flou (effet verre dépoli) quand il
   flotte au-dessus du contenu. */
.menu-recipe-back-fab {
  position: fixed;
  top: var(--float-back-top);
  /* 14px : même retrait effectif que le bouton retour en édition (padding
     conteneur mobile 8px + retrait barre 6px), pour un placement identique. */
  left: 14px;
  z-index: 25;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  color: var(--text);
}
.menu-recipe-back-fab .ico {
  width: 24px;
  height: 24px;
  stroke-width: 2.25;
}
@media (hover: hover) {
  .menu-recipe-back-fab:hover {
    border-color: var(--primary);
    color: var(--primary);
  }
}
/* Desktop (>700px) : le décalage vertical vient de --float-back-top ; ici juste
   un peu plus de marge à gauche. */
@media (min-width: 701px) {
  .menu-recipe-back-fab {
    left: 16px;
  }
}
/* Variante EN FLUX (non flottante) du bouton retour rond : page « Partager »,
   etc. → même rond avec la flèche, mais posé normalement en haut du panneau
   (pas sticky/fixed). */
.menu-recipe-back-fab--static {
  position: static;
  margin-bottom: 14px;
}

/* Bandeau haut du formulaire de CRÉATION : bouton retour rond + pastille titre,
   look de la fiche recette MAIS SANS FLOU (demande explicite) → fond OPAQUE
   var(--bg), pas de backdrop-filter. Collant en haut au scroll. Sur mobile, la
   barre du haut (Ribouns) est masquée pour que ce bandeau soit l'en-tête, collé
   tout en haut À LA MÊME DISTANCE que la fiche (cf override mobile plus bas). */
.recipe-form-topbar {
  position: sticky;
  top: var(--float-back-top);
  transition: top 0.22s ease;
  z-index: 25;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  margin-bottom: 8px;
  background: var(--bg);
}
@media (max-width: 700px) {
  /* Création : on masque la barre Ribouns (comme la fiche est immersive) et on
     colle le bandeau tout en haut, encoche comprise → même distance du haut que
     la visu, et plus de gros écart. L'édition (.edit-fixed-bar) n'est pas
     concernée (classe page-recipe-create seulement). */
  body.page-recipe-create .mobile-topbar {
    display: none;
  }
  /* Barre du haut masquée → le container ne doit plus réserver d'espace en haut ;
     c'est le bandeau (collant, top:0) qui gère l'encoche via son padding-top. Sans
     ça, un saut de ~12px apparaîtrait entre l'état repos et l'état collé. */
  body.page-recipe-create .container {
    padding-top: 0;
  }
  body.page-recipe-create .recipe-form-topbar {
    top: 0;
    margin-top: 0;
    padding-top: calc(env(safe-area-inset-top, 0px) + 10px);
  }
}
.recipe-form-topbar .recipe-form-topbar-back {
  flex: 0 0 auto;
  margin-bottom: 0;
}
.recipe-form-topbar-title {
  min-width: 0;
  height: 42px;
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0 16px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  color: var(--text);
  font-weight: 700;
  font-size: 16px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Sans photo, le hero n'a pas d'image pour « accueillir » le bouton → on
   réserve de la place en haut pour qu'il ne recouvre pas le titre. */
.menu-recipe-hero-inner:not(.menu-recipe-hero-with-photo) {
  padding-top: 56px;
}

/* Boutons d'action ICÔNE SEULE (Modifier / Courses / Partager) : carré compact
   pour tenir sur une ligne avec le stepper personnes. */
.menu-recipe-icon-btn {
  width: 44px;
  padding: 0;
  flex: 0 0 auto;
}

/* Régime & allergies + nutrition : bloc visible seulement sur l'onglet
   Ingrédients (togglé par le JS des onglets). Espacement du haut. */
.menu-recipe-ingredients-aside {
  margin-top: 16px;
}

.menu-recipe-hero-inner {
  background:
    radial-gradient(
      circle at calc(100% - 50px) -10px,
      color-mix(in srgb, var(--primary) 10%, transparent) 0%,
      transparent 35%
    ),
    linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 22px;
  box-shadow: 0 4px 14px var(--shadow-color, rgba(0, 0, 0, 0.15));
}

.menu-recipe-hero-with-photo {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 22px;
  align-items: center;
  padding: 18px;
}

.menu-recipe-photo {
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  background: var(--panel-2);
  aspect-ratio: 1 / 1;
  position: relative;
}

.menu-recipe-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Vignette photo ÉDITABLE (fiche recette, hors mode édition) : <label>
   cliquable qui enregistre la photo instantanément (cf recipe_photo.js).
   Deux états pilotés par `.has-image`. */
.menu-recipe-photo--editable {
  display: block;
  cursor: pointer;
}

.menu-recipe-photo img[hidden] {
  display: none;
}

/* État VIDE : encadré pointillé « Ajouter une photo ». */
.menu-recipe-photo--editable:not(.has-image) {
  border: 2px dashed var(--border);
  background: var(--panel-2);
}

.menu-recipe-photo-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px;
  text-align: center;
  font-weight: 600;
  font-size: 13px;
  color: #ffffff;
  background: color-mix(in srgb, #000000 42%, transparent);
  opacity: 0;
  transition: opacity 0.15s ease;
  /* Décoratif : laisse passer les clics vers le <label> (ouvre le sélecteur)
     et vers le bouton source superposé. */
  pointer-events: none;
}

.menu-recipe-photo-overlay-icon > svg,
.menu-recipe-photo-overlay-icon .ico {
  width: 24px;
  height: 24px;
}

/* État vide : overlay = CTA toujours visible, en couleurs « muted » (pas de
   voile noir sur le fond clair pointillé). */
.menu-recipe-photo--editable:not(.has-image) .menu-recipe-photo-overlay {
  opacity: 1;
  color: var(--muted);
  background: transparent;
}

.menu-recipe-photo--editable:not(.has-image) .menu-recipe-photo-overlay-icon {
  color: var(--primary);
}

/* Le libellé « Ajouter une photo » n'apparaît qu'à l'état vide (avec une
   photo, l'icône seule suffit — pas de texte « Changer »). */
.menu-recipe-photo.has-image .menu-recipe-photo-overlay-label--add {
  display: none;
}

/* État plein : overlay « Changer » au survol (desktop). */
.menu-recipe-photo--editable.has-image:hover .menu-recipe-photo-overlay,
.menu-recipe-photo--editable.has-image:focus-within .menu-recipe-photo-overlay {
  opacity: 1;
}

/* Tactile (pas de hover) : overlay « Changer » discret, ancré en bas. */
@media (hover: none) {
  .menu-recipe-photo--editable.has-image .menu-recipe-photo-overlay {
    opacity: 1;
    flex-direction: row;
    align-items: flex-end;
    justify-content: flex-end;
    padding: 8px 10px;
    background: linear-gradient(
      to top,
      color-mix(in srgb, #000000 55%, transparent) 0%,
      transparent 55%
    );
  }
}

/* Spinner d'upload (réutilise l'anim ribouns-photo-spin définie plus bas). */
.menu-recipe-photo-spinner {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  background: color-mix(in srgb, #000000 35%, transparent);
}

.menu-recipe-photo-spinner::after {
  content: "";
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.35);
  border-top-color: #ffffff;
  animation: ribouns-photo-spin 0.7s linear infinite;
}

.menu-recipe-photo--editable.is-photo-busy {
  pointer-events: none;
}

.menu-recipe-photo--editable.is-photo-busy .menu-recipe-photo-spinner {
  display: grid;
}

/* Feedback texte (bandeau bas de vignette). */
.menu-recipe-photo-status {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 6px 8px;
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  color: #ffffff;
  background: color-mix(in srgb, var(--success) 88%, #000000);
}

.menu-recipe-photo-status.is-error {
  background: color-mix(in srgb, var(--danger) 88%, #000000);
}

.menu-recipe-photo-status[hidden] {
  display: none;
}

.menu-recipe-head {
  min-width: 0;
}

.menu-recipe-title {
  margin: 0 0 10px;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1.15;
  word-break: break-word;
}

/* Titre + bouton « Modifier » sur une même ligne (le titre prend l'espace,
   le bouton reste en haut à droite). Libère la ligne d'actions pour que le
   stepper personnes + le bouton « Courses » tiennent côte à côte sur tel. */
.menu-recipe-title-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.menu-recipe-title-row .menu-recipe-title {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
}

.menu-recipe-edit-btn {
  flex: 0 0 auto;
}

.menu-recipe-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}

.menu-recipe-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 13px;
  color: var(--text);
}

.menu-recipe-badge-servings {
  background: color-mix(in srgb, var(--primary) 12%, var(--panel-sunken));
  border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
  color: var(--primary);
  font-weight: 600;
}

/* Nutrition par portion (vague 8.4) : carte compacte kcal + 3 macros. */
.menu-recipe-nutrition {
  margin-bottom: 14px;
  padding: 12px 14px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 14px;
}

.menu-recipe-nutrition-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}

.menu-recipe-nutrition-head .ico {
  width: 16px;
  height: 16px;
  color: var(--muted);
}

/* Lien « ≈ incomplet » → renvoie vers la modif (section régime) où sont nommés
   les ingrédients non détectés. Corail = action cliquable. */
.menu-recipe-nutrition-approx {
  margin-left: auto;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 14%, transparent);
  font-size: 11px;
  color: var(--muted);
}

/* Ligne « Non compté : X (raison) » sous les stats nutrition. */
.menu-recipe-nutrition-skipped {
  margin: 10px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
}
.menu-recipe-nutrition-skipped-link {
  color: var(--primary);
  font-weight: 500;
  text-decoration: none;
}
@media (hover: hover) {
  .menu-recipe-nutrition-skipped-link:hover {
    text-decoration: underline;
  }
}
.menu-recipe-nutrition-skipped-reason {
  color: var(--muted);
}

/* Badge « Réservé admin » — marque un bloc visible SEULEMENT par le super-admin
   (ex. détail « Non compté »), pour qu'il sache ce qu'un utilisateur standard ne
   voit pas. Réutilisable sur n'importe quel bloc gated {% if is_super_admin %}. */
.admin-only-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: var(--panel-2);
  border: 1px solid var(--border);
  vertical-align: middle;
}
.admin-only-badge .ico {
  width: 12px;
  height: 12px;
  vertical-align: middle;
}

.menu-recipe-nutrition-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}

.menu-recipe-nutrition-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-align: center;
}

.menu-recipe-nutrition-value {
  font-size: 19px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.1;
}

.menu-recipe-nutrition-unit {
  margin-left: 1px;
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
}

.menu-recipe-nutrition-label {
  font-size: 12px;
  color: var(--muted);
}

.menu-recipe-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 8px;
}

/* Sur mobile, le bouton « Courses » de la fiche est réduit à son icône
   (libellé masqué) pour que stepper + Modifier + Courses tiennent sur une
   ligne. Le bouton reste accessible via aria-label/title. */
@media (max-width: 640px) {
  .menu-recipe-courses-btn .menu-recipe-courses-label {
    display: none;
  }
}

/* Cards ingrédients / consignes */

.menu-recipe-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 18px 20px;
  margin-bottom: 14px;
  box-shadow: 0 2px 8px var(--shadow-color, rgba(0, 0, 0, 0.10));
}

/* Tabs Ingrédients / Consignes — pattern segmented control. */
.menu-recipe-tabs {
  display: flex;
  gap: 4px;
  padding: 4px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 14px;
  margin-bottom: 14px;
  /* Collant sous la topbar : on garde le switch Ingrédients / Consignes
     accessible quand on scrolle dans la recette (--topbar-h posé par
     admin_common.js). Fond opaque → le contenu passe dessous proprement. */
  position: sticky;
  top: calc(var(--topbar-h, 0px) + 6px);
  z-index: 15;
}

.menu-recipe-tab {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 14px;
  background: transparent;
  /* Petit contour thémé autour de chaque onglet (les 3 boutons se lisent comme
     des boutons distincts). */
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--muted);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.menu-recipe-tab:hover,
.menu-recipe-tab:focus-visible {
  color: var(--text);
  outline: none;
}

.menu-recipe-tab.is-active {
  background: var(--panel);
  color: var(--primary);
  font-weight: 600;
  border-color: color-mix(in srgb, var(--primary) 30%, var(--border));
  box-shadow: 0 1px 3px var(--shadow-color, rgba(0, 0, 0, 0.10));
}

.menu-recipe-tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 7px;
  background: var(--panel-2);
  border-radius: 999px;
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
}

.menu-recipe-tab.is-active .menu-recipe-tab-count {
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  color: var(--primary);
}

/* Onglet « Info » : plus petit (icône seule) que Ingrédients / Consignes ; garde
   l'état actif commun (.is-active → fond panel + corail). */
.menu-recipe-tab-info {
  flex: 0 0 auto;
  width: 44px;
  padding: 0;
  gap: 0;
}
.menu-recipe-tab-info .ico {
  display: block; /* centrage géométrique (annule le vertical-align global) */
  width: 20px;
  height: 20px;
}
/* Desktop : onglet Info un peu plus large pour qu'on le repère mieux. */
@media (min-width: 701px) {
  .menu-recipe-tab-info {
    width: 76px;
  }
}

/* Panneau « Info » : conteneur (PAS une carte) empilant les blocs méta
   (régime → recettes liées → nutrition) avec un espacement régulier. */
.menu-recipe-info-pane {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* Espacement = UNIQUEMENT le gap : on annule les marges propres des cartes.
   Sélecteurs enfant-direct (spécificité 0,2,0) pour battre .menu-recipe-diet /
   .shopping-linked-recipes / .menu-recipe-nutrition (0,1,0, définies plus bas
   dans le fichier) — sinon gap + marge carte se cumulaient (~28px). */
.menu-recipe-info-pane > .menu-recipe-diet,
.menu-recipe-info-pane > .shopping-linked-recipes,
.menu-recipe-info-pane > .menu-recipe-nutrition,
.menu-recipe-info-pane > .menu-recipe-times,
.menu-recipe-info-pane > .menu-recipe-folders,
.menu-recipe-info-pane > .menu-empty {
  margin: 0;
}

/* Dossiers de la recette (onglet Info) : entête + pastilles cliquables (→
   /recipes filtré sur le dossier). Même carte que les autres blocs méta. */
.menu-recipe-folders {
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 2px 8px var(--shadow-color, rgba(0, 0, 0, 0.10));
}
.menu-recipe-folders-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  margin-bottom: 10px;
}
.menu-recipe-folders-head .ico {
  color: var(--muted);
}
.menu-recipe-folders-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.menu-recipe-folder-chip {
  display: inline-flex;
  align-items: center;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}
@media (hover: hover) {
  .menu-recipe-folder-chip:hover {
    border-color: var(--primary);
    color: var(--primary);
  }
}

/* Encadré « Temps » (préparation / repos / cuisson) en tête de l'onglet Info :
   3 stats côte à côte (icône + libellé + valeur), même look carte que les autres
   blocs méta. « — » quand un temps n'est pas renseigné. */
.menu-recipe-times {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  padding: 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 2px 8px var(--shadow-color, rgba(0, 0, 0, 0.10));
}
.menu-recipe-time {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
  min-width: 0;
}
.menu-recipe-time-icon {
  display: inline-flex;
  color: var(--muted);
}
.menu-recipe-time-icon .ico {
  width: 22px;
  height: 22px;
}
.menu-recipe-time-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}
.menu-recipe-time-value {
  font-size: 16px;
  font-weight: 800;
  color: var(--text);
  white-space: nowrap;
}
/* display:flex ci-dessus écrase l'attribut [hidden] (comme display:block) → sans
   ce garde, le panneau resterait visible sur TOUS les onglets. */
.menu-recipe-info-pane[hidden] {
  display: none;
}

/* Cartes « Notation » et « Notes » de l'onglet Info — même habillage que les
   autres cartes méta (panel-sunken, coin 14, en-tête 14px/600, icône muted). */
.menu-recipe-note-card {
  padding: 12px 14px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 14px;
}
.menu-recipe-note-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.menu-recipe-note-head .ico {
  width: 16px;
  height: 16px;
  color: var(--muted);
}

/* Notation — étoiles cliquables. Pleines = corail (sur-thème) ; vides = contour
   discret. */
.menu-recipe-rating-stars {
  display: flex;
  gap: 4px;
}
.menu-recipe-rating-star {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--border);
  -webkit-tap-highlight-color: transparent;
  transition: color 0.12s ease, transform 0.1s ease;
}
.menu-recipe-rating-star .ico {
  display: block;
  width: 30px;
  height: 30px;
  fill: none;
}
.menu-recipe-rating-star.is-filled {
  color: var(--primary);
}
.menu-recipe-rating-star.is-filled .ico {
  fill: currentColor;
}
.menu-recipe-rating-star:active {
  transform: scale(0.88);
}
@media (hover: hover) {
  .menu-recipe-rating-star:hover {
    color: var(--primary);
  }
}

/* Notes — textarea + bouton enregistrer + statut. */
.menu-recipe-notes-input {
  width: 100%;
  min-height: 72px;
  resize: vertical;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  line-height: 1.5;
}
.menu-recipe-notes-input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent);
}
.menu-recipe-notes-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}
.menu-recipe-notes-status {
  font-size: 13px;
  color: var(--muted);
}

/* Stepper personnes dans la rangée d'actions, à côté du bouton
   Modifier. Bordure primary subtile pour signaler son interactivité
   et le distinguer visuellement du bouton ghost à côté. */
.menu-recipe-actions .menu-recipe-servings {
  border: 1px solid color-mix(in srgb, var(--primary) 35%, var(--border));
}

.menu-recipe-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--border);
}

.menu-recipe-card-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
}

/* Stepper personnes pour la carte ingrédients */

.menu-recipe-servings {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
  flex-shrink: 0;
}

.menu-recipe-servings-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--text);
  font-size: 18px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s ease;
}

.menu-recipe-servings-btn:hover,
.menu-recipe-servings-btn:focus-visible {
  background: var(--hover-bg);
  outline: none;
}

.menu-recipe-servings-btn.is-disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.menu-recipe-servings-value {
  display: inline-flex;
  align-items: baseline;
  gap: 1px;
  min-width: 34px;
  justify-content: center;
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.menu-recipe-servings-unit {
  font-size: 11px;
  color: var(--muted);
  font-weight: 500;
}

/* Liste ingrédients */

.menu-recipe-ingredients {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.menu-recipe-ingredient {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px dashed color-mix(in srgb, var(--border) 60%, transparent);
}

.menu-recipe-ingredient:last-child {
  border-bottom: 0;
}

/* Nom de l'ingrédient : prend uniquement sa largeur naturelle (max-content)
   plutôt que pousser la quantité à droite via space-between. Sur desktop
   large, la quantité reste collée au nom au lieu d'être à 600px à droite. */
.menu-recipe-ingredient-name {
  font-size: 15px;
  color: var(--text);
  font-weight: 500;
  flex: 0 0 auto;
}

.menu-recipe-ingredient-qty {
  font-size: 14px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Séparateur visuel entre nom et quantité — virgule en grisé. */
.menu-recipe-ingredient-qty:not(:empty)::before {
  content: "— ";
  color: var(--muted);
  margin-right: 2px;
}

/* Liste consignes (étapes numérotées) */

.menu-recipe-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  counter-reset: recipe-step;
}

.menu-recipe-step {
  display: grid;
  grid-template-columns: 32px 1fr;
  gap: 14px;
  align-items: start;
  padding: 6px 10px;
  border-radius: 10px;
  cursor: pointer;
  user-select: none;
  transition: background 0.12s ease;
}

.menu-recipe-step:hover {
  background: color-mix(in srgb, var(--primary) 6%, transparent);
}

/* Toggle au tap : marque visuellement l'étape "faite" (suivi cuisine).
   Comportement repris du Pi (touch_recipe.js : classe .active). */
.menu-recipe-step.is-active {
  background: color-mix(in srgb, var(--primary) 14%, transparent);
}

.menu-recipe-step.is-active .menu-recipe-step-num {
  background: var(--success);
}

.menu-recipe-step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: var(--primary);
  color: #ffffff;
  border-radius: 50%;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
}

.menu-recipe-step-text {
  font-size: 15px;
  color: var(--text);
  line-height: 1.5;
  padding-top: 6px;
}

/* Mobile responsive --------------------------------------------- */

@media (max-width: 540px) {
  /* Plus de surface utile sur téléphone : on resserre les marges latérales
     du conteneur global (de 20px à 8px) pour récupérer ~24px de largeur,
     surtout utile pour la liste des recettes où les boutons d'action
     "Afficher/Modifier/Supprimer" débordaient. */
  .container {
    padding: 12px 8px;
  }

  /* Panels : padding réduit sur mobile (18px → 12px) */
  .panel {
    padding: 12px;
  }

  /* Liste des recettes : le panel qui wrap la <ul> ne sert à rien sur
     mobile — chaque .recipe-row a déjà son propre fond et bordure. On
     retire le padding/border/background du panel parent pour gagner
     ~24px de largeur utile (sinon on a panel + recipe-row qui doublent
     l'effet "bloc dans un bloc"). */
  .panel:has(> .recipes-list) {
    padding: 0;
    background: transparent;
    border: 0;
    box-shadow: none;
    margin-bottom: 0;
  }

  /* Liste des recettes : cards plus serrées (padding/gap portés par la
     surface glissante). Densité mobile resserrée. */
  .recipe-row-surface {
    padding: 8px 10px;
    gap: 7px;
  }

  .recipe-row-head {
    gap: 10px;
  }

  .menu-hero {
    padding: 16px 14px 14px;
  }

  /* Sur mobile : on garde le row {titre | toolbar} (comme en desktop)
     pour mettre les filtres directement à droite de "Que cuisiner ?".
     Le wrap permet à la toolbar de passer sous le titre si vraiment
     l'espace manque (très petit écran + beaucoup de boutons). */
  .menu-hero-inner {
    align-items: center;
    gap: 10px;
  }

  .menu-hero-cta-form {
    margin-top: 12px;
  }

  .menu-hero-cta {
    padding: 14px 22px;
    font-size: 16px;
  }

  .menu-hero-title {
    font-size: 22px;
    margin-bottom: 2px;
  }

  .menu-hero-subtitle {
    font-size: 14px;
  }

  .menu-hero-toolbar {
    /* Toolbar pleine largeur en dessous du titre, boutons à gauche. */
    justify-content: flex-start;
    margin-top: 12px;
  }

  /* Filtre durée en mobile : on garde juste l'icône ⏱ pour qu'il reste
     compact à droite de "Que cuisiner ?". Le label complet est dispo
     dans le popover quand on tape dessus. */
  .menu-hero-duration .menu-filter-summary > span:nth-child(2) {
    display: none;
  }

  .menu-hero-duration .menu-filter-summary {
    padding: 0;
    width: 44px;
    height: 44px;
    justify-content: center;
  }

  .menu-toolbar-btn-label {
    display: none;
  }

  .menu-toolbar-btn {
    width: 44px;
    padding: 0;
    justify-content: center;
  }

  .menu-card-body {
    grid-template-columns: 72px 1fr;
    gap: 12px;
  }

  .menu-thumb-lg {
    width: 72px;
    height: 72px;
  }

  .menu-card-title {
    font-size: 16px;
  }

  .menu-card-actions .menu-action {
    flex: 1 1 0;
  }

  /* Mais quand on a le stepper (cards sélection) : largeur naturelle pour
     que stepper à gauche + Retirer à droite restent compacts. */
  .menu-card-actions:has(.menu-servings) .menu-action {
    flex: 0 0 auto;
  }

  .menu-picker-row {
    grid-template-columns: auto 1fr;
  }

  .menu-picker-row-actions {
    grid-column: 1 / -1;
    justify-content: flex-end;
  }

  .menu-filter-form {
    right: 0;
    left: auto;
    min-width: 200px;
  }

  .menu-ingredients-hero-inner {
    padding: 16px 16px;
  }

  .menu-ingredients-title {
    font-size: 22px;
  }

  .menu-ingredient-cat {
    padding: 12px 12px;
  }

  .menu-ingredients-actions {
    padding: 8px 10px;
    /* Sur ≤700px la bottom-nav (position:fixed, ~50px + safe-area iOS)
       passe par-dessus le contenu. Le sticky doit donc s'arrêter
       au-dessus, pas à 8px du bord. */
    bottom: calc(50px + env(safe-area-inset-bottom, 0) + 12px);
  }

  /* Bouton Exporter (et autres actions de la barre sticky) un peu
     plus compact sur mobile pour gagner de la verticale — on garde
     44×44px pour rester dans la guideline tactile Apple/Google. */
  .menu-ingredients-actions .menu-action {
    min-height: 44px;
    font-size: 14px;
  }

  .menu-manual-hero-inner {
    padding: 16px 16px;
  }

  .menu-manual-title {
    font-size: 22px;
  }

  .menu-manual-search {
    flex-direction: column;
  }

  .menu-manual-search-submit {
    width: 100%;
  }

  .menu-filter-form-types {
    /* Sur mobile, le popover absolute occupe quasi toute la largeur
       (left: 0, right: 0). Pas besoin de min-width fixe. */
    min-width: 0;
    width: calc(100vw - 32px);
    max-width: 380px;
  }

  /* Vague 7.10 — Reposition des popovers de filtres en BOTTOM SHEET sur
     tel. Avant, position: absolute + left:0 (relatif au <details> parent
     position:relative) → quand le bouton « Types » se trouvait à droite
     de la rangée, le popover dépassait à gauche et en bas du viewport
     (signalé revue UX : « la fenêtre est placée n'importe où »). En
     mobile on bascule en `position: fixed` ancré bas+gauche+droite avec
     un max-height contenu et un overflow propre. La durée bénéficie du
     même fix. */
  .menu-filter[open] > .menu-filter-form {
    position: fixed;
    top: auto;
    bottom: max(16px, env(safe-area-inset-bottom, 16px));
    left: 16px;
    right: 16px;
    width: auto;
    max-width: none;
    min-width: 0;
    max-height: min(70vh, calc(100dvh - 32px));
    overflow-y: auto;
    z-index: 1000;
    overscroll-behavior: contain;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
    animation: ribouns-bottom-sheet-rise 200ms ease-out both;
  }

  /* Backdrop semi-transparent derrière la bottom sheet pour faire
     comprendre l'overlay (et que le click extérieur sera capté par le
     JS click-outside ci-dessous). */
  .menu-filter[open]::before {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.35);
    z-index: 999;
    animation: ribouns-freeze-fade-in 180ms ease-out both;
  }
}

@keyframes ribouns-bottom-sheet-rise {
  from { transform: translateY(20%); opacity: 0; }
  to   { transform: translateY(0);   opacity: 1; }
}

@media (max-width: 540px) {

  .menu-recipe-hero-with-photo {
    grid-template-columns: 1fr;
    gap: 14px;
    padding: 16px;
  }

  .menu-recipe-photo {
    max-width: 100%;
    aspect-ratio: 16 / 9;
  }

  .menu-recipe-hero-inner {
    padding: 16px;
  }

  .menu-recipe-title {
    font-size: 22px;
  }

  .menu-recipe-card {
    padding: 14px 16px;
  }

  .menu-recipe-step-text {
    font-size: 14px;
  }
}

/* ============================================================== */
/* Feedback :active tactile — sur écrans à pointer "coarse" (=     */
/* tactile), on ajoute un effet scale + background plus marqué sur */
/* le tap. Donne le feeling "app native" : le doigt sent que ça a  */
/* réagi, plus de double-tap par incertitude.                      */
/* Les boutons qui ont déjà leur propre :active (CTA hero, recipe- */
/* row-toggle, bottom-nav-sheet-close, etc.) gardent leur effet    */
/* spécifique par spécificité CSS — pas d'override.                */
/* ============================================================== */
@media (hover: none) and (pointer: coarse) {
  /* On exclut explicitement .bottom-nav-item du scale : ajouter un
     transform à un onglet qui CONTIENT un sheet position:fixed (l'onglet
     "Plus") créerait un containing block au tap, et le sheet se
     positionnerait par rapport à la cellule de l'onglet (étroite, à
     droite) au lieu du viewport — bug visible où le sheet apparaît
     décalé à droite. Le feedback :active sur les onglets passe par
     background uniquement. */
  .menu-action:active,
  .menu-toolbar-btn:active,
  .menu-servings-btn:active,
  .bottom-nav-sheet-item:active,
  .nav-item:active,
  .card-link:active,
  .recipe-row-thumb-link:active,
  .menu-card-thumb-link:active,
  .recipe-row-name:active,
  .menu-card-title:active {
    transform: scale(0.96);
    transition: transform 90ms ease;
  }

  .menu-action:active,
  .menu-toolbar-btn:active,
  .menu-servings-btn:active {
    background: var(--hover-bg);
  }

  .bottom-nav-sheet-item:active {
    background: var(--hover-bg);
  }

  /* Feedback tap des onglets de nav : PAS de gros rectangle gris (jugé
     « dégueu ») — on tinte en corail (texte + pastille d'icône arrondie),
     même langage visuel que l'onglet actif. Repère propre, pas un carré. */
  .bottom-nav-item:active {
    color: var(--primary);
  }
  .bottom-nav-item:active .bottom-nav-icon {
    background: color-mix(in srgb, var(--primary) 16%, transparent);
  }
}

/* ============================================================== */
/* Thème clair — activé par [data-theme="light"] sur <html>.       */
/* S'appuie sur les variables CSS racine pour basculer toutes les  */
/* surfaces sans dupliquer les sélecteurs.                         */
/* ============================================================== */

[data-theme="light"] {
  /* Palette off-white doux : fond légèrement crème, panels blancs purs,
     bordures et hover teintés chaud pour casser l'effet "feuille blanche". */
  --bg: #fbf9f4;
  --panel: #ffffff;
  --panel-2: #f5f1e8;
  --panel-sunken: #ffffff;
  --topbar-bg: #ffffff;
  --hover-bg: #ece7da;
  --text: #1f1d1a;
  --muted: #5c574f;
  --border: #d8d2c4;
  --primary: #e0664a;
  --primary-hover: #c9543a;
  --secondary: #64748b;
  --secondary-hover: #475569;
  --danger: #dc2626;
  --danger-hover: #b91c1c;
  --success: #3f9a63;
  --success-hover: #347f53;
  --shadow-color: rgba(40, 30, 10, 0.08);
}

/* Hover sur la nav admin (haut de page) — visible sur les deux thèmes. */
.brand a:hover,
.brand a:focus-visible,
.nav a:hover,
.nav a:focus-visible,
.nav-btn:hover,
.nav-btn:focus-visible,
.nav-more-summary:hover,
.nav-more-summary:focus-visible {
  background: var(--hover-bg);
  border-color: var(--primary);
  outline: none;
}

/* Raccourci « + » création rapide (topbar desktop) : accent corail (couleur
   d'action de l'app, cohérent avec le FAB « + » corail de la bottom-nav
   mobile) pour le distinguer des onglets de navigation. Spécificité
   .nav a.nav-item-add pour passer devant la base .nav a (fond panel) et son
   :hover. */
.nav a.nav-item-add {
  background: var(--primary);
  border-color: transparent;
  color: #fff;
}

.nav a.nav-item-add:hover,
.nav a.nav-item-add:focus-visible {
  background: var(--primary-hover);
  border-color: transparent;
  color: #fff;
}

/* Onglet de la barre du haut DESKTOP correspondant à la page courante : corail
   (même repère « je suis ici » que les onglets de la bottom-nav mobile et que
   le bouton compte). Spécificité .nav a.nav-item.is-active pour passer devant
   la base `.nav a` (fond panel-2). */
.nav a.nav-item.is-active {
  color: var(--primary);
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, var(--panel-2));
}

.nav a.nav-item.is-active .nav-icon {
  color: var(--primary);
}

.nav-login-link:hover,
.nav-login-link:focus-visible {
  background: var(--hover-bg);
  border-color: var(--primary);
  outline: none;
}

/* Flash messages — contraste adapté au mode clair. */
[data-theme="light"] .flash {
  background: #eff6ff;
  border-color: #93c5fd;
  color: #1e3a8a;
}

/* ============================================================== */
/* Thème rose pâle — activé par [data-theme="pink"] sur <html>.    */
/* Palette girly basée sur pink-50/100/300/500 de Tailwind.        */
/* ============================================================== */

[data-theme="pink"] {
  --bg: #fdf2f8;
  --panel: #ffffff;
  --panel-2: #fce7f3;
  --panel-sunken: #ffffff;
  --topbar-bg: #ffffff;
  --hover-bg: #fbcfe8;
  --text: #4a1d2e;
  --muted: #9c5775;
  --border: #f9a8d4;
  --primary: #e0664a;
  --primary-hover: #c9543a;
  --secondary: #a78bfa;
  --secondary-hover: #8b5cf6;
  --danger: #dc2626;
  --danger-hover: #b91c1c;
  --success: #3f9a63;
  --success-hover: #347f53;
  --shadow-color: rgba(190, 24, 93, 0.10);
}

[data-theme="pink"] .flash {
  background: #fdf2f8;
  border-color: #f9a8d4;
  color: #831843;
}

/* Styles du formulaire de choix du thème dans /account. */
.account-theme-form .theme-options {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 8px 0 14px;
}

.account-theme-form .theme-option {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  min-height: 44px;
  color: var(--text);
}

.account-theme-form .theme-option:hover {
  background: var(--hover-bg);
  border-color: var(--primary);
}

.account-theme-form .theme-option input[type="radio"] {
  /* Neutralise le style générique des champs (`input, textarea, select`
     { width:100% ; padding ; border ; background }) qui transforme sinon ce
     radio — le seul radio VISIBLE de l'app, les autres étant masqués en
     opacity:0 — en grosse boîte pleine largeur, éjectant pastille + libellé
     hors de la case. On restaure un radio natif compact. */
  -webkit-appearance: radio;
  appearance: radio;
  accent-color: var(--primary);
  width: auto;
  min-width: 0;
  height: auto;
  flex: 0 0 auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.account-theme-form .theme-option-label {
  white-space: nowrap;
}

/* Pastilles thèmes : pastille circulaire avec la couleur du thème.
   Évite les emoji 🌙 ☀️ 🌸 qui rendent différemment sur iOS / Android
   selon les versions (parfois en noir-blanc, parfois cassés).

   iOS Safari : un span vide en display:inline-block dans un parent
   inline-flex calcule sa baseline depuis le content (vide) → décalage
   vertical visible (le swatch n'est pas centré dans le bouton). Le
   fix : forcer le span en participant flex pleinement (block-level
   dans flex parent), couper la line-height qui ajoute des half-leadings
   verticaux, et expliciter align-self au cas où le parent change. */
.account-theme-form .theme-option-swatch {
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  flex: 0 0 20px;
  align-self: center;
  line-height: 0;
  border: 1px solid color-mix(in srgb, var(--text) 20%, transparent);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}

.account-theme-form .theme-option-swatch-dark {
  background: linear-gradient(135deg, #0f172a 0%, #1f2937 100%);
}

.account-theme-form .theme-option-swatch-light {
  background: linear-gradient(135deg, #fbf9f4 0%, #ffffff 100%);
  border-color: #d8d2c4;
}

.account-theme-form .theme-option-swatch-pink {
  background: linear-gradient(135deg, #fdf2f8 0%, #ec4899 100%);
  border-color: #f9a8d4;
}

.u-hidden {
  display: none !important;
}

/* Dialog d'export — taille et paddings UNIFORMES pour les 4 fenêtres
   avant-plan (signalé en revue UX : « toutes les fenêtres qui s'ouvrent
   en avant plan, on peut pas faire en sorte qu'elles fassent toutes la
   même taille »). Les marges sont resserrées par rapport à l'ancienne
   variante non-compacte pour gagner de la place sur smartphone. */
.export-dialog {
  width: min(540px, calc(100vw - 16px));
  max-height: calc(100vh - 24px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--panel);
  color: var(--text);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
  /* Vague 7.9 bugfix — `overflow: hidden` sur la dialog garantit que
     le scroll des contenus dépassants reste contenu dans `.export-
     dialog-body` (qui a son propre `overflow-y: auto`). Sinon, sur
     iOS et certains Android, le scroll touch « fuyait » vers le body
     de la page derrière. */
  overflow: hidden;
}

.export-dialog::backdrop {
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(2px);
}

.export-dialog-form {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 24px);
  gap: 0;
}

/* Texte de chargement transitoire (modal picker recettes liées /menu,
   injecté par fetch avant le remplacement par le fragment). */
.export-dialog-loading {
  margin: 0;
  padding: 18px 16px;
  color: var(--muted);
}

.export-dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
}

.export-dialog-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

.export-dialog-close {
  width: 36px;
  min-width: 36px;
  padding: 0;
  font-size: 18px;
  line-height: 1;
}

.export-dialog-tools {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}

/* Ligne recherche + bouton « Filtres » (durée/type) des pickers de recettes
   — cohérent avec le picker du planning. Le champ prend l'espace, le bouton
   reste à droite. */
.export-dialog-search-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.export-dialog-search-row .export-dialog-search {
  flex: 1 1 auto;
  min-width: 0;
}

.export-dialog-search-row .recipes-filters-btn {
  flex: 0 0 auto;
}

.export-dialog-search {
  width: 100%;
}

.export-dialog-actions-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.export-dialog-count {
  margin-left: auto;
  font-size: 14px;
}

.export-dialog-list {
  list-style: none;
  margin: 0;
  padding: 4px 0;
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 80px;
  max-height: 50vh;
}

.export-dialog-item {
  padding: 0;
}

.export-dialog-item[hidden] {
  display: none;
}

.export-dialog-item > label {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px;
  cursor: pointer;
  min-height: 32px;
}

.export-dialog-item > label:hover {
  background: var(--hover-bg);
}

.export-dialog-item input[type="checkbox"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  accent-color: var(--primary);
  cursor: pointer;
}

.export-dialog-item-name {
  flex: 1 1 auto;
  word-break: break-word;
}

.export-dialog-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 10px 14px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}

.export-dialog-footer .btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

@media (max-width: 540px) {
  .export-dialog-header,
  .export-dialog-tools,
  .export-dialog-footer {
    padding-left: 10px;
    padding-right: 10px;
  }

  .export-dialog-item > label {
    padding-left: 10px;
    padding-right: 10px;
  }

  .export-dialog-footer {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .export-dialog-footer .btn {
    width: 100%;
  }
}

/* Champ photo dans le formulaire de recette. */
.recipe-image-field {
  display: grid;
  gap: 8px;
}

/* Form recette — bloc « Temps » (préparation / repos / cuisson). 3 champs
   minutes côte à côte ; la catégorie Rapide/Normal/Long en est dérivée au
   submit (temps actif = prépa + cuisson). */
.recipe-times {
  margin: 0 0 14px;
  padding: 12px 14px 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: transparent;
}
.recipe-times-legend {
  padding: 0 6px;
  font-weight: bold;
}
.recipe-times-legend-note {
  font-weight: normal;
  color: var(--muted);
  font-size: 13px;
}
.recipe-times-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.recipe-time-field label {
  font-weight: 600;
  font-size: 14px;
}

.recipe-image-preview {
  max-width: 320px;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--panel-2);
}

.recipe-image-preview img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 220px;
  object-fit: cover;
}

.recipe-image-delete-btn {
  align-self: flex-start;
  max-width: 320px;
}

.recipe-image-input {
  max-width: 360px;
}

/* --- Photo recette : enregistrement INSTANTANÉ (cf recipe_photo.js) ------- */
/* La vignette-dropzone bascule via la classe `.has-image` (posée au rendu ET
   par le JS après upload/suppression). L'<img>, le placeholder et l'overlay
   « Changer » ne coexistent jamais : leur visibilité découle de cet état. */
.recipe-image-dropzone-img[hidden] {
  display: none;
}

.recipe-image-dropzone:not(.has-image) .recipe-image-dropzone-img,
.recipe-image-dropzone:not(.has-image) .recipe-image-dropzone-overlay {
  display: none;
}

.recipe-image-dropzone.has-image .recipe-image-dropzone-placeholder {
  display: none;
}

/* Bouton « Supprimer la photo » masqué tant qu'il n'y a pas d'image. */
.recipe-image-delete-btn[hidden] {
  display: none;
}

/* Spinner d'upload : voile + rond tournant au centre de la vignette. */
.recipe-image-dropzone-spinner {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  background: color-mix(in srgb, var(--panel) 55%, transparent);
}

.recipe-image-dropzone-spinner::after {
  content: "";
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--primary) 30%, transparent);
  border-top-color: var(--primary);
  animation: ribouns-photo-spin 0.7s linear infinite;
}

.recipe-image-dropzone.is-photo-busy {
  pointer-events: none;
}

.recipe-image-dropzone.is-photo-busy .recipe-image-dropzone-spinner {
  display: grid;
}

@keyframes ribouns-photo-spin {
  to { transform: rotate(360deg); }
}

/* Feedback texte sous la vignette (« Photo enregistrée ✓ » / erreur). */
.recipe-image-status.is-error {
  color: var(--danger);
}

/* Encadré-dropzone cliquable du form recette (photo au-dessus des
   consignes). Un <label> qui ouvre le sélecteur de fichier au clic. */
.recipe-image-dropzone {
  display: block;
  position: relative;
  max-width: 360px;
  border-radius: 12px;
  border: 2px dashed var(--border);
  background: var(--panel-sunken);
  cursor: pointer;
  overflow: hidden;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.recipe-image-dropzone:hover,
.recipe-image-dropzone:focus-within {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 8%, var(--panel-sunken));
}

/* Photo présente : bord plein (l'image remplit l'encadré). */
.recipe-image-dropzone.has-image {
  border-style: solid;
  border-color: var(--border);
}

.recipe-image-dropzone-img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 240px;
  object-fit: cover;
}

/* Placeholder « Ajouter une photo » (pas d'image). */
.recipe-image-dropzone-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 32px 16px;
  text-align: center;
  color: var(--muted);
}

.recipe-image-dropzone-icon {
  color: var(--primary);
  line-height: 1;
}

/* iOS Safari : SVG en flex item avec width:1em peut s'effondrer → taille px
   explicite (cf CONSIGNES « pièges Safari iOS »). */
.recipe-image-dropzone-icon > svg,
.recipe-image-dropzone-icon .ico {
  width: 30px;
  height: 30px;
}

.recipe-image-dropzone-text {
  font-weight: 600;
  color: var(--text);
}

.recipe-image-dropzone-hint {
  font-size: 13px;
}

/* Overlay « Changer la photo » au survol quand une image est présente. */
.recipe-image-dropzone-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 600;
  color: #ffffff;
  background: color-mix(in srgb, #000000 45%, transparent);
  opacity: 0;
  transition: opacity 0.15s ease;
}

.recipe-image-dropzone-overlay-icon > svg,
.recipe-image-dropzone-overlay-icon .ico {
  width: 20px;
  height: 20px;
  vertical-align: -0.2em;
}

.recipe-image-dropzone:hover .recipe-image-dropzone-overlay,
.recipe-image-dropzone:focus-within .recipe-image-dropzone-overlay {
  opacity: 1;
}

/* Tactile (pas de hover) : l'overlay « Changer la photo » est invisible tant
   qu'on ne survole pas → sur tel, l'utilisateur ne sait pas qu'on peut taper
   la photo. On l'affiche donc en permanence, allégé (dégradé bas + label
   coin) pour ne pas masquer la photo. */
@media (hover: none) {
  .recipe-image-dropzone.has-image .recipe-image-dropzone-overlay {
    opacity: 1;
    align-items: flex-end;
    justify-content: flex-end;
    padding: 8px 12px;
    font-size: 13px;
    background: linear-gradient(
      to top,
      color-mix(in srgb, #000000 58%, transparent) 0%,
      transparent 55%
    );
  }
}


/* ============================================================== */
/* Vague 7.8 — Planning hebdomadaire des repas.                    */
/* Override en fin de fichier (pattern projet) : styles de la page */
/* /menu/planning, du picker /menu/planning/picker, et de l'intég- */
/* ration des boutons 📅 dans menu_home et la liste des recettes.  */
/* ============================================================== */

/* Page /menu/planning ---------------------------------------------- */

.menu-planning-hero {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 0 18px;
}

.menu-planning-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: space-between;
}

.menu-planning-week-label {
  font-size: 15px;
  color: var(--text);
  flex: 1 1 auto;
  text-align: center;
}

.menu-planning-week-label strong {
  color: var(--primary);
}

.menu-planning-today-link {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
}

.menu-planning-day {
  padding: 14px;
}

.menu-planning-day-today {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

.menu-planning-day-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}

.menu-planning-day-label {
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
}

.menu-planning-day-date {
  font-weight: 400;
  color: var(--muted);
  margin-left: 6px;
  font-size: 14px;
}

.menu-planning-slots {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Variante 2 colonnes : Midi + Soir sur la même ligne (signalé en
   revue UX). On garde l'ancien display:flex column comme défaut au cas
   où d'autres consommateurs en dépendent ; .menu-planning-slots-grid
   override pour /menu/planning. */
.menu-planning-slots-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.menu-planning-slot {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
}

/* En 2-cols, le slot devient plus compact : label en haut, contenu en
   bas, et le bouton + (ou la poubelle) calé à droite. Évite le déba-
   gement horizontal des longs noms de recette. */
.menu-planning-slots-grid .menu-planning-slot {
  grid-template-columns: 1fr auto;
  gap: 8px;
  padding: 8px 10px;
}

.menu-planning-slots-grid .menu-planning-slot-label {
  grid-column: 1 / -1;
}

/* Sous 480px : on bascule à nouveau en 1 col pour respirer (les noms
   de recette doivent rester lisibles sur petit écran). */
@media (max-width: 480px) {
  .menu-planning-slots-grid {
    grid-template-columns: 1fr;
  }
  .menu-planning-slots-grid .menu-planning-slot {
    grid-template-columns: 64px 1fr auto;
  }
  .menu-planning-slots-grid .menu-planning-slot-label {
    grid-column: 1;
  }
}

/* `+` corail (= couleur d'action de l'app, cohérent avec le FAB « + » de la
   nav). Hover scopé hover-only pour ne pas coller sur tactile. */
.menu-planning-slot-add-btn {
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
  background: color-mix(in srgb, var(--primary) 10%, var(--panel-sunken));
}

@media (hover: hover) {
  .menu-planning-slot-add-btn:hover,
  .menu-planning-slot-add-btn:focus-visible {
    background: var(--primary);
    border-color: var(--primary);
    color: #ffffff;
  }
}

/* Le filled-content (titre + meta + poubelle) en colonne resserrée. */
.menu-planning-slot-filled {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 4px 6px;
}

.menu-planning-slot-title,
.menu-planning-slot-meta {
  grid-column: 1;
  min-width: 0;
  word-break: break-word;
}

.menu-planning-slot-clear-form {
  grid-column: 2;
  grid-row: 1 / span 2;
  margin: 0;
  display: inline-flex;
  align-items: center;
}

/* Survol scopé pour la poubelle de Vider — déjà couvert par les règles
   .recipe-action-danger globales avec @media (hover: hover). Pas besoin
   de re-spécifier ici. */

.menu-planning-slot-label {
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: 0.5px;
}

.menu-planning-slot-filled {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.menu-planning-slot-filled .menu-card-title {
  font-size: 15px;
  font-weight: 500;
}

.menu-planning-slot-filled .menu-inline-form {
  margin-top: 6px;
  align-self: flex-start;
}

.menu-planning-slot-add {
  width: 100%;
}

.menu-planning-slot-add > summary {
  list-style: none;
  cursor: pointer;
  width: auto;
}

.menu-planning-slot-add > summary::-webkit-details-marker { display: none; }
.menu-planning-slot-add > summary::marker { content: ""; }

.menu-planning-slot-add-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.menu-planning-slot-add-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
}

.menu-planning-slot-add-field select {
  min-height: 44px;
  padding: 0 10px;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
}

/* Picker /menu/planning/picker ------------------------------------ */

.menu-planning-picker-hero {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px 0 18px;
}

.planning-picker-recipe {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: 12px;
  align-items: center;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
}

.planning-picker-recipe .menu-thumb-lg {
  width: 72px;
  height: 72px;
}

.planning-picker-recipe-info {
  min-width: 0;
}

.planning-picker-recipe-name {
  margin: 0 0 4px;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
}

/* .planning-picker-recipe-meta : supprimé — le macro recipe_meta() sort
   directement .recipe-meta (style partagé), aucune classe layout nécessaire ici. */

.planning-picker-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding-bottom: 24px;
}

.planning-picker-section {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  background: var(--panel-2);
  margin: 0;
}

.planning-picker-section-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0 4px;
}

.planning-picker-week {
  margin-top: 10px;
}

.planning-picker-week-label {
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 6px;
}

.planning-picker-day-grid {
  display: grid;
  /* auto-fit + minmax permet au navigateur de wrapper automatiquement
     selon la largeur dispo : 7 colonnes sur écran large, puis 4-5-3
     selon le viewport, sans débordement horizontal. */
  grid-template-columns: repeat(auto-fit, minmax(64px, 1fr));
  gap: 6px;
}

.planning-picker-day-chip {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 56px;
  padding: 6px 4px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.planning-picker-day-chip input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.planning-picker-day-chip:hover,
.planning-picker-day-chip:focus-within {
  border-color: var(--primary);
}

.planning-picker-day-chip:has(input[type="radio"]:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: #ffffff;
}

.planning-picker-day-chip-today {
  border-color: var(--primary);
}

.planning-picker-day-chip-label {
  font-size: 11px;
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.5px;
}

.planning-picker-day-chip-date {
  font-size: 13px;
  font-weight: 500;
}

.planning-picker-slot-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 10px;
}

.planning-picker-slot-chip {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 56px;
  padding: 8px 12px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  font-size: 15px;
  font-weight: 500;
}

.planning-picker-slot-chip input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.planning-picker-slot-chip:hover,
.planning-picker-slot-chip:focus-within {
  border-color: var(--primary);
}

.planning-picker-slot-chip:has(input[type="radio"]:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: #ffffff;
}

.planning-picker-slot-emoji {
  font-size: 20px;
  line-height: 1;
}

.planning-picker-servings {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 10px;
}

.planning-picker-servings-field {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 6px 12px;
}

.planning-picker-servings-btn {
  /* Bouton visible 40px (un peu plus généreux que le stepper card pour
     le contexte modal) avec zone de tap étendue à 48px via pseudo
     ::after (guideline Apple/Google). */
  position: relative;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--text);
  font-size: 22px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease, transform 0.08s ease;
  flex-shrink: 0;
}

.planning-picker-servings-btn::after {
  content: "";
  position: absolute;
  inset: -4px;
}

.planning-picker-servings-btn:hover,
.planning-picker-servings-btn:focus-visible {
  background: var(--hover-bg);
  outline: none;
}

.planning-picker-servings-btn:active {
  transform: scale(0.95);
}

.planning-picker-servings-btn[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

.planning-picker-servings-field input[type="number"] {
  width: 64px;
  text-align: center;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  background: transparent;
  border: 0;
  outline: none;
  -moz-appearance: textfield;
}

.planning-picker-servings-field input[type="number"]::-webkit-outer-spin-button,
.planning-picker-servings-field input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.planning-picker-servings-unit {
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
}

.planning-picker-actions {
  display: flex;
  justify-content: center;
}

.planning-picker-submit {
  min-width: 220px;
  min-height: 52px;
  font-size: 16px;
}

/* Cards menu_home : grouper Planifier + Retirer à droite -------- */

.menu-card-actions-end {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Mobile : afficher uniquement l'emoji pour les actions secondaires
   (planifier 📅 / retirer 🗑) sur les cards menu, pour gagner de la
   place quand le stepper personnes prend déjà la moitié de la ligne.
   Emoji grossi à 20px pour rester lisible une fois le label retiré. */
@media (max-width: 700px) {
  .menu-action-icon-only-mobile {
    padding: 0 14px;
    min-width: 48px;
  }

  .menu-action-icon-only-mobile .menu-action-label {
    display: none;
  }

  .menu-action-icon-only-mobile [aria-hidden="true"] {
    font-size: 20px;
    line-height: 1;
  }
}

/* (refonte de la liste des recettes : voir
   « Liste des recettes — refonte boutons » en fin de fichier.) */

/* ============================================================== */
/* Vague 7.8 Lot D — Ingrédients du planning + mode "Ajouter à".  */
/* ============================================================== */

/* Picker "Liste cible" + radio "Mode" (commun /menu/ingredients +
   /menu/planning/ingredients) -------------------------------- */

.export-target-fieldset,
.export-mode-fieldset {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
  padding: 14px;
  margin: 14px 0 0;
}

.export-target-legend {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0 6px;
}

.export-target-select {
  width: 100%;
  min-height: 44px;
  padding: 0 10px;
  margin-top: 6px;
  background: var(--panel-sunken);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14px;
}

.export-mode-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 6px;
}

.export-mode-option {
  display: grid;
  grid-template-columns: 24px 1fr;
  grid-template-rows: auto auto;
  column-gap: 10px;
  row-gap: 4px;
  align-items: start;
  padding: 12px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.export-mode-option:hover,
.export-mode-option:focus-within {
  border-color: var(--primary);
}

.export-mode-option:has(input[type="radio"]:checked) {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 8%, var(--panel-sunken));
}

.export-mode-option input[type="radio"] {
  grid-column: 1;
  grid-row: 1 / span 2;
  width: 20px;
  height: 20px;
  margin: 2px 0 0;
  align-self: start;
  accent-color: var(--primary);
}

.export-mode-option-title {
  grid-column: 2;
  grid-row: 1;
  font-weight: 600;
  color: var(--text);
}

.export-mode-option-hint {
  grid-column: 2;
  grid-row: 2;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.4;
}

.export-mode-hint-global {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
  font-style: italic;
}

/* Page /menu/planning/ingredients : checkboxes jours en haut -- */

.planning-ingredients-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding-bottom: 24px;
}

.planning-ingredients-days {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
  padding: 14px;
  margin: 0;
}

.planning-ingredients-section-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 0 4px;
  margin: 0 0 10px;
}

.planning-ingredients-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 22px;
  padding: 0 8px;
  margin-left: 6px;
  background: var(--primary);
  color: #ffffff;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
}

.planning-ingredients-day-grid {
  display: grid;
  /* auto-fit + minmax : wrap automatique selon la largeur dispo.
     Évite le débordement horizontal sur petit mobile où 7 colonnes
     de "Mer 28/05 1 repas" ne tiennent pas. */
  grid-template-columns: repeat(auto-fit, minmax(76px, 1fr));
  gap: 6px;
}

.planning-ingredients-day-chip {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 76px;
  padding: 8px 4px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease, opacity 0.12s ease;
}

.planning-ingredients-day-chip input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.planning-ingredients-day-chip:hover,
.planning-ingredients-day-chip:focus-within {
  border-color: var(--primary);
}

.planning-ingredients-day-chip:has(input[type="checkbox"]:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: #ffffff;
}

.planning-ingredients-day-chip-today {
  border-color: var(--primary);
}

.planning-ingredients-day-chip-past {
  opacity: 0.5;
}

.planning-ingredients-day-chip-label {
  font-size: 11px;
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.5px;
}

.planning-ingredients-day-chip-date {
  font-size: 13px;
  font-weight: 500;
}

.planning-ingredients-day-chip-count {
  font-size: 10px;
  opacity: 0.85;
  margin-top: 2px;
}

.planning-ingredients-day-chip-empty {
  font-size: 11px;
  opacity: 0.55;
}

/* Petit écran : gap réduit + padding compact. Grid auto-fit définie
   plus haut s'occupe du wrap naturellement (4 colonnes sur ~320-400px,
   passant à 7 sur écrans plus larges). */
@media (max-width: 480px) {
  .planning-ingredients-day-grid {
    gap: 4px;
  }
  .planning-ingredients-day-chip {
    padding: 6px 4px;
    min-height: 64px;
  }
  .planning-ingredients-day-chip-count,
  .planning-ingredients-day-chip-empty {
    font-size: 10px;
  }
}


/* ============================================================== */
/* Vague 7.8 Lot B — Modal AJAX picker planning + toast.           */
/* ============================================================== */

.planning-picker-dialog {
  width: min(540px, calc(100vw - 24px));
  max-height: calc(100vh - 60px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--panel);
  color: var(--text);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.planning-picker-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
}

.planning-picker-dialog-form {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 60px);
}

.planning-picker-dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-sunken);
}

.planning-picker-dialog-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.planning-picker-dialog-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  flex: 0 0 auto;
}

.planning-picker-dialog-close:hover,
.planning-picker-dialog-close:focus-visible {
  background: var(--hover-bg);
  border-color: var(--border);
  color: var(--text);
  outline: none;
}

.planning-picker-dialog-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px 16px;
  overflow-y: auto;
}

.planning-picker-dialog-body .planning-picker-section {
  margin: 0;
}

.planning-picker-dialog-error {
  margin: 0;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--danger) 12%, var(--panel-sunken));
  border: 1px solid var(--danger);
  border-radius: 10px;
  color: var(--danger);
  font-size: 13px;
}

.planning-picker-dialog-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  background: var(--panel-sunken);
}

.planning-picker-dialog-submit {
  min-width: 140px;
}

/* Mobile : dialog quasi plein-écran, footer reste accessible. */
@media (max-width: 700px) {
  .planning-picker-dialog {
    width: calc(100vw - 16px);
    max-height: calc(100vh - 32px);
    border-radius: 14px;
  }

  .planning-picker-dialog-body {
    padding: 12px;
    gap: 12px;
  }

  .planning-picker-dialog-header,
  .planning-picker-dialog-footer {
    padding: 12px;
  }

  .planning-picker-dialog-submit {
    min-width: 0;
    flex: 1 1 auto;
  }
}

/* ───── Toast système global (Vague 7.6 Polish UI) ───── */
/* API window.showToast(message, kind, options). Cf static/js/toast.js. */

.toast-container {
  position: fixed;
  z-index: 60;
  /* Desktop : sous la topbar sticky (~56px de haut) pour ne pas
     recouvrir la navigation du haut. Sur mobile (≤700px) la topbar est
     remplacée par la bottom-nav → on remonte le toast (cf media query). */
  top: 72px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  max-width: calc(100vw - 24px);
}

.toast {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 240px;
  max-width: 100%;
  padding: 12px 18px;
  border-radius: 12px;
  font-size: 14px;
  font-weight: 500;
  color: #ffffff;
  background: var(--success);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.toast.is-hiding {
  opacity: 0;
  transform: translateY(-8px);
}

.toast-text {
  flex: 1 1 auto;
}

.toast-close {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  padding: 0;
  background: transparent;
  border: 0;
  color: inherit;
  opacity: 0.85;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.toast-close:hover,
.toast-close:focus-visible {
  opacity: 1;
  outline: none;
}

.toast-success { background: var(--success); }
.toast-error   { background: var(--danger); }
.toast-info    { background: var(--primary); }

@media (max-width: 700px) {
  .toast-container {
    top: 12px;
  }
  .toast {
    min-width: 0;
    width: 100%;
    text-align: center;
    justify-content: center;
  }
}


/* ============================================================== */
/* Vague 7.8 — Retour visuel "déjà au planning" sur les boutons    */
/* 📅 des cards /menu. Cards menu_home affichent .is-planned       */
/* quand la recette a une assignation dans la fenêtre 7j courante. */
/* ============================================================== */

.menu-action-plan-is-planned {
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
  border-color: var(--primary);
  color: var(--primary);
}

.menu-action-plan-is-planned:hover,
.menu-action-plan-is-planned:focus-visible {
  background: color-mix(in srgb, var(--primary) 22%, var(--panel-sunken));
  border-color: var(--primary);
  color: var(--primary);
}

.menu-action-plan-is-planned [aria-hidden="true"] {
  /* Léger glow primary sur l'icône — précédemment trop appuyé (signalé
     en revue UX, « un flou un peu bizarre, c'est un peu too much »).
     Réduit à 0.6px / 30% pour rester discret. */
  filter: drop-shadow(0 0 0.6px color-mix(in srgb, var(--primary) 30%, transparent));
}

/* Même état « déjà planifié » sur l'icône calendar de /recipes, adapté
   à la classe `.recipe-action-icon` (38–44px, icône Lucide). Hover scopé
   `(hover: hover)` cohérent avec les autres icônes de la rangée. */
.recipe-action-plan-is-planned {
  color: var(--primary);
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
}

.recipe-action-plan-is-planned .ico {
  filter: drop-shadow(0 0 0.6px color-mix(in srgb, var(--primary) 30%, transparent));
}

@media (hover: hover) {
  .recipe-action-plan-is-planned:hover,
  .recipe-action-plan-is-planned:focus-visible {
    background: color-mix(in srgb, var(--primary) 22%, var(--panel-sunken));
    border-color: var(--primary);
    color: var(--primary);
  }
}


/* ============================================================== */
/* Polish — /admin/logs : marges entre les blocs.                  */
/* Le bouton "Supprimer les journaux" était collé à la searchbar   */
/* et la pagination collée au tableau. Ajout d'espaces respirables.*/
/* ============================================================== */

.admin-logs-clear-form {
  margin-bottom: 18px;
}

.admin-pagination {
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}



/* Picker autocomplete : mise en gras des "plats principaux" (Plat
   complet / Plat seul) dans le menu de suggestions du champ "Type"
   du form recette. Mis aussi en tête de liste côté back. */
.autocomplete-item-primary {
  font-weight: 700;
}

/* Entête de groupe dans le menu autocomplete (« Tes ingrédients » /
   « Suggestions » — enrichissement Ciqual du champ nom d'ingrédient).
   Non-interactive, collante en haut du scroll pour rester visible, thémée
   (même fond que le menu → les items défilent dessous sans transparence). */
.autocomplete-group {
  position: sticky;
  top: 0;
  padding: 7px 12px 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--panel-sunken);
  border-bottom: 1px solid var(--border);
  user-select: none;
}

/* Entête « Suggestions » préfixée de l'icône régime & allergies : signale que
   ces termes (issus du référentiel Ciqual) sont reconnus → détection
   régime/allergies opérante quand on les sélectionne. */
.autocomplete-group-with-icon {
  display: flex;
  align-items: center;
  gap: 5px;
}
.autocomplete-group-with-icon .ico {
  width: 13px;
  height: 13px;
  color: var(--muted);
}

/* Item « Tes ingrédients » qui existe AUSSI au catalogue Ciqual : libellé à
   gauche + petite icône catalogue à droite (même salade que l'entête du
   groupe). Signale « déjà utilisé ET reconnu » sans changer la sélection. */
.autocomplete-item-with-icon {
  display: flex;
  align-items: center;
  gap: 8px;
}
.autocomplete-item-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.autocomplete-item-catalogue-icon.ico {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  color: var(--muted);
  opacity: 0.75;
}

/* ============================================================== */
/* Form recette — ajout d'ingrédient « une ligne » + cartes       */
/* repliables (nom · qté unité · magasin ; tap pour éditer).      */
/* ============================================================== */
.ingredient-add-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  /* Fond blanc (comme les champs nom/type) → on voit que c'est un champ à
     remplir, distinct des cartes d'ingrédients (crème, --panel-2). */
  background: var(--panel-sunken);
  margin-bottom: 6px;
}
/* Barre du bas : séparée du bas de la liste (pas de hint en dessous). */
.ingredient-add-bar[data-position="bottom"] {
  margin-top: 8px;
  margin-bottom: 0;
}
.ingredient-add-bar:focus-within {
  border-color: var(--primary);
  box-shadow: var(--field-focus-ring);
}
.ingredient-add-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--primary);
}
.ingredient-add-icon .ico { width: 18px; height: 18px; }
.ingredient-add-bar input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  color: var(--text);
  font-size: 15px;
  padding: 4px 0;
  height: auto;
}
.ingredient-add-bar input:focus {
  outline: none;
  box-shadow: none;
}
.ingredient-add-spinner {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: 2px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: ingredient-add-spin 0.6s linear infinite;
}
@keyframes ingredient-add-spin { to { transform: rotate(360deg); } }
.ingredient-add-hint {
  font-size: 12px;
  margin: 0 0 14px 2px;
}

#ingredients-container {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ingredient-card {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
}
.ingredient-card.is-expanded {
  border-color: var(--primary);
}
.ingredient-card-summary {
  display: flex;
  align-items: stretch;
  gap: 6px;
}
.ingredient-card-open {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 11px 12px;
  border: 0;
  background: transparent;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.ingredient-card-headline {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
}
.ingredient-card-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ingredient-card-name.is-empty {
  color: var(--muted);
  font-weight: 500;
}
.ingredient-card-catalogue,
.ingredient-card-custom {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--muted);
  opacity: 0.75;
}
.ingredient-card-custom { opacity: 0.55; }
.ingredient-card-catalogue .ico,
.ingredient-card-custom .ico { width: 14px; height: 14px; }
/* L'attribut `hidden` doit primer sur le display:inline-flex ci-dessus, sinon
   les deux icônes (catalogue / hors-catalogue) s'afficheraient en même temps. */
.ingredient-card-catalogue[hidden],
.ingredient-card-custom[hidden] { display: none; }
.ingredient-card-meta {
  font-size: 13px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.ingredient-card-remove {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  border: 0;
  background: transparent;
  color: var(--danger);
  cursor: pointer;
  border-radius: 0 12px 12px 0;
}
.ingredient-card-remove .ico { width: 18px; height: 18px; }
.ingredient-card-remove:hover { background: var(--panel-sunken); }

.ingredient-card-edit {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border-top: 1px solid var(--border);
}
/* L'attribut `hidden` (carte repliée) doit primer sur le display:flex ci-dessus. */
.ingredient-card-edit[hidden] {
  display: none;
}
.ingredient-card-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ingredient-card-field-label {
  font-size: 12px;
  color: var(--muted);
}
.ingredient-card-field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.ingredient-card-field input {
  width: 100%;
}


/* ============================================================== */
/* Vague 7.6 — Spinners boutons après clic.                        */
/* Pattern : sur les forms taggés [data-spinner], au submit le     */
/* bouton submitter passe en .is-loading → spinner CSS pur (cercle */
/* qui tourne via @keyframes), texte caché (color:transparent), et */
/* disabled. Empêche le double-clic + donne un feedback immédiat.  */
/* Les handlers AJAX qui existent déjà désactivent eux-mêmes le    */
/* bouton ; cette classe complète juste avec le spinner visuel.    */
/* ============================================================== */

.btn.is-loading,
.menu-action.is-loading,
.menu-toolbar-btn.is-loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
  cursor: progress;
}

/* Préserver l'icône cachée par color:transparent — sinon le span
   aria-hidden="true" qui contient un emoji devient aussi invisible. */
.btn.is-loading > *,
.menu-action.is-loading > *,
.menu-toolbar-btn.is-loading > * {
  visibility: hidden;
}

.btn.is-loading::after,
.menu-action.is-loading::after,
.menu-toolbar-btn.is-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: #ffffff;
  animation: btn-spin 0.7s linear infinite;
  visibility: visible;
}

/* Boutons sur fond clair (ghost / secondary) : spinner en couleur primary
   sinon le blanc disparaît dans le fond. */
.btn.secondary.is-loading::after,
.menu-action.menu-action-ghost.is-loading::after {
  color: var(--primary);
}

@keyframes btn-spin {
  to { transform: rotate(360deg); }
}


/* ============================================================== */
/* Vague 7.6 — Skeleton screens sur images recettes lazy-loaded.   */
/* Retravaillé : le JPEG baseline se peignait « de haut en bas »   */
/* PAR-DESSUS le skeleton (moche). Désormais le pulse est sur le    */
/* CONTENEUR, l'<img> reste invisible tant qu'elle n'est pas        */
/* entièrement décodée, puis se FOND en entier (opacity 0 → 1). On  */
/* voit donc : skeleton → image complète, jamais de balayage.       */
/* ============================================================== */

/* Pulse du skeleton sur le CONTENEUR (uniquement quand il y a une image —
   pas la vignette placeholder à icône). */
.menu-thumb:not(.menu-thumb-placeholder),
.recipe-row-thumb:not(.recipe-row-thumb-placeholder) {
  background-image:
    linear-gradient(
      90deg,
      color-mix(in srgb, var(--text) 6%, var(--panel-sunken)) 0%,
      color-mix(in srgb, var(--text) 12%, var(--panel-sunken)) 50%,
      color-mix(in srgb, var(--text) 6%, var(--panel-sunken)) 100%
    );
  background-size: 200% 100%;
  animation: skeleton-pulse 1.4s ease-in-out infinite;
}

/* Fondu d'apparition. L'état est piloté par `data-loaded`, posé par
   admin_common.js :
     - « 0 » (image en cours de chargement) → invisible, on ne voit que le
       skeleton du conteneur ;
     - « 1 » (image décodée, ou déjà en cache avant paint) → visible.
   SANS JS : aucun attribut → l'image reste visible par défaut (pas de
   régression, juste le rendu natif). Fonctionne aussi pour les images lazy
   sous la ligne de flottaison (le « 1 » n'est posé qu'au vrai load au scroll,
   pas sur un minuteur). */
.menu-thumb img,
.recipe-row-thumb img {
  transition: opacity 0.35s ease;
}

img[data-loaded="0"] {
  opacity: 0;
}

img[data-loaded="1"] {
  opacity: 1;
}

/* Image chargée → on coupe le pulse du conteneur (l'image le couvre, économie
   CPU). `:has()` : iOS 15.4+ / navigateurs modernes ; sinon le pulse continue
   derrière l'image opaque (invisible), sans conséquence visuelle. */
.menu-thumb:has(img[data-loaded="1"]),
.recipe-row-thumb:has(img[data-loaded="1"]) {
  animation: none;
  background-image: none;
}

@keyframes skeleton-pulse {
  0%   { background-position: 200% 50%; }
  100% { background-position: -200% 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .menu-thumb:not(.menu-thumb-placeholder),
  .recipe-row-thumb:not(.recipe-row-thumb-placeholder) {
    animation: none;
  }
  /* Pas de fondu : l'image apparaît d'un coup une fois prête (toujours pas de
     balayage haut→bas, juste sans transition). */
  .menu-thumb img,
  .recipe-row-thumb img {
    transition: none;
  }
}


/* ============================================================== */
/* Branding Ribouns — wordmark serif + splash d'ouverture.         */
/* Le wordmark de la topbar et le nom du splash utilisent une      */
/* serif système italique (Georgia) — élégant, zéro dépendance,    */
/* conforme à la CSP (font-src 'self').                            */
/* Le splash est un overlay plein écran affiché uniquement au      */
/* lancement de l'app installée (cf static/js/splash_init.js qui   */
/* pose data-splash sur <html> avant le premier paint).            */
/* ============================================================== */

.brand a.brand-link {
  background: transparent;
  border: 1px solid transparent;
  padding: 6px 6px;
  font-size: inherit;
}

.brand a.brand-link:hover,
.brand a.brand-link:focus-visible {
  background: transparent;
  border-color: transparent;
}

.brand-logo {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  display: block;
}

.brand-wordmark {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 600;
  font-size: 22px;
  letter-spacing: 0.3px;
  color: var(--text);
}

.app-splash {
  display: none;
}

html[data-splash] .app-splash {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 9999;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background: #f1ede4;
  transition: opacity 0.45s ease;
}

html[data-splash="done"] .app-splash {
  opacity: 0;
  pointer-events: none;
}

.app-splash-logo {
  width: 132px;
  height: 132px;
}

.app-splash-name {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 600;
  font-size: 42px;
  letter-spacing: 0.5px;
  color: #2f3037;
}

@media (prefers-reduced-motion: reduce) {
  html[data-splash] .app-splash {
    transition: none;
  }
}


/* ============================================================== */
/* Lot 2 — Design system : accent terracotta, boutons, badges,     */
/* chips sélectionnables, en-tête de la carte de connexion.        */
/* Toutes les couleurs dérivent des tokens sémantiques (var) ou de */
/* color-mix → adaptation automatique aux 3 thèmes.                */
/* ============================================================== */

/* Boutons : alias explicite primary + variante ghost. */
.btn.primary {
  background: var(--primary);
  color: #ffffff;
}

.btn.ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}

@media (hover: hover) {
  .btn.primary:hover {
    background: var(--primary-hover);
  }

  .btn.ghost:hover {
    background: var(--hover-bg);
    border-color: var(--primary);
  }
}

/* Badges sémantiques (états / catégories). */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  color: color-mix(in srgb, var(--primary) 70%, var(--text));
  border: 1px solid color-mix(in srgb, var(--primary) 32%, transparent);
}

.badge.is-success {
  background: color-mix(in srgb, var(--success) 15%, transparent);
  color: color-mix(in srgb, var(--success) 64%, var(--text));
  border-color: color-mix(in srgb, var(--success) 32%, transparent);
}

.badge.is-danger {
  background: color-mix(in srgb, var(--danger) 15%, transparent);
  color: color-mix(in srgb, var(--danger) 64%, var(--text));
  border-color: color-mix(in srgb, var(--danger) 32%, transparent);
}

.badge.is-warning {
  background: color-mix(in srgb, #d97706 16%, transparent);
  color: color-mix(in srgb, #d97706 62%, var(--text));
  border-color: color-mix(in srgb, #d97706 34%, transparent);
}

.badge.is-muted {
  background: var(--panel-2);
  color: var(--muted);
  border-color: var(--border);
}

/* Chip sélectionnable (catégorie active). */
.chip.is-selected {
  background: var(--primary);
  color: #ffffff;
  border-color: var(--primary);
}

/* En-tête de marque dans la carte de connexion / pages d'auth. */
.auth-card {
  padding: 26px 22px;
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;
}

.auth-brand-logo {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
}

.auth-brand-name {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 600;
  font-size: 28px;
  letter-spacing: 0.3px;
  color: var(--text);
}

/* Bascule connexion ↔ inscription (lien sous le formulaire d'auth). */
.auth-switch {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  text-align: center;
  margin-bottom: 0;
}

.auth-switch a {
  color: var(--primary);
  font-weight: 600;
  text-decoration: none;
}

.auth-switch a:hover {
  text-decoration: underline;
}

/* Encart pédagogique sur /register (modèle de partage d'espace). */
.auth-info {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 12px 14px;
  margin-bottom: 18px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: 10px;
  background: var(--panel-sunken);
  color: var(--text);
  font-size: 0.92rem;
  line-height: 1.4;
}

.auth-info-icon {
  display: inline-flex;
  color: var(--primary);
  flex: 0 0 auto;
  margin-top: 2px;
}

/* Renvoi du lien de confirmation depuis le login (compte non confirmé). */
.auth-resend-form {
  margin-top: 16px;
}

/* Liens légaux discrets sous les cartes d'auth (accessibles sans compte). */
.legal-footer {
  margin: 18px auto 0;
  text-align: center;
  font-size: 0.82rem;
  color: var(--muted);
}

.legal-footer a {
  color: var(--muted);
  text-decoration: none;
}

.legal-footer a:hover {
  color: var(--primary);
  text-decoration: underline;
}

/* Case de consentement à l'inscription (CGU + confidentialité). */
.auth-consent {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 16px 0 0;
  font-size: 0.88rem;
  color: var(--muted);
  line-height: 1.4;
}

.auth-consent input {
  /* Sans width:auto, la règle globale `input { width: 100% }` étire la case à
     cocher sur toute la largeur (case centrée, texte tassé à droite). */
  width: auto;
  margin-top: 2px;
  flex: 0 0 auto;
}

.auth-consent span {
  flex: 1 1 auto;
  min-width: 0;
}

.auth-consent a {
  color: var(--primary);
}

/* Zone « danger » du compte (suppression). */
.account-danger {
  border-color: color-mix(in srgb, var(--danger) 40%, var(--border));
}

.account-delete-form {
  display: grid;
  gap: 10px;
}

/* Pages légales (CGU / confidentialité) — lisibilité prose. */
.legal-page {
  max-width: 760px;
}

.legal-page h2 {
  margin-top: 22px;
  font-size: 1.1rem;
}

.legal-page ul {
  margin: 8px 0;
  padding-left: 20px;
}

.legal-page li {
  margin: 4px 0;
}

/* Bandeau « confirme ton adresse mail » (inscription libre). Discret mais
   visible, s'adapte aux thèmes via les variables partagées. */
.verify-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 16px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: 10px;
  background: var(--panel-sunken);
  color: var(--text);
}

.verify-banner-text {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.verify-banner-icon {
  display: inline-flex;
  align-items: center;
  color: var(--primary);
  flex: 0 0 auto;
}

.verify-banner-form {
  flex: 0 0 auto;
  margin: 0;
}

.verify-banner-btn {
  white-space: nowrap;
}

/* Panneau super-admin « Inscription libre » (toggle global). */
.admin-signup-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.admin-signup-state {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.9rem;
  border: 1px solid var(--border);
}

.admin-signup-state.is-on {
  color: var(--success);
  border-color: color-mix(in srgb, var(--success) 50%, var(--border));
  background: color-mix(in srgb, var(--success) 12%, transparent);
}

.admin-signup-state.is-off {
  color: var(--muted);
}

.auth-title {
  font-size: 26px;
  margin: 0 0 4px;
}


/* ============================================================== */
/* Lot 3 — Iconographie Lucide. .ico suit le font-size (1em) et la */
/* couleur (currentColor) du conteneur → remplacement direct des   */
/* anciens emojis sans changer les tailles.                        */
/* ============================================================== */
.ico {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.125em;
  flex: 0 0 auto;
}


/* ============================================================== */
/* Résumé repliable des recettes planifiées (page planning →       */
/* liste de courses) : liste les recettes des jours cochés.        */
/* ============================================================== */
.planning-recap {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: 16px;
  overflow: hidden;
}

.planning-recap-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
  font-weight: 600;
  color: var(--text);
}

.planning-recap-summary::-webkit-details-marker {
  display: none;
}

.planning-recap-icon {
  display: inline-flex;
  color: var(--primary);
  font-size: 18px;
}

.planning-recap-title {
  flex: 1;
}

.planning-recap-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  color: color-mix(in srgb, var(--primary) 72%, var(--text));
}

.planning-recap-chevron {
  display: inline-flex;
  color: var(--muted);
  font-size: 18px;
  transition: transform 0.18s ease;
}

.planning-recap[open] .planning-recap-chevron {
  transform: rotate(180deg);
}

.planning-recap-body {
  padding: 0 16px 14px;
  display: grid;
  gap: 14px;
}

.planning-recap-day-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
}

.planning-recap-day-date {
  opacity: 0.8;
}

.planning-recap-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
}

.planning-recap-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.planning-recap-slot {
  flex: 0 0 auto;
  min-width: 46px;
  font-size: 12px;
  color: var(--muted);
}

.planning-recap-name {
  flex: 1;
  font-weight: 500;
}

.planning-recap-servings {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--muted);
}

.planning-recap-empty {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

/* Item avec poubelle (AJAX) — utilisé sur /menu/ingredients pour
   retirer une recette de la sélection. Le form est calé à droite et
   le bouton est en mode icon-only compact (32×32). */
.planning-recap-remove-form {
  margin: 0;
  display: inline-flex;
  align-items: center;
}

.planning-recap-remove-btn {
  width: 32px;
  height: 32px;
  border-radius: 8px;
}

.planning-recap-remove-btn .ico {
  width: 16px;
  height: 16px;
}

@media (prefers-reduced-motion: reduce) {
  .planning-recap-chevron {
    transition: none;
  }
}


/* ============================================================== */
/* Page /admin/users (Vague 9.3) — barre de filtres, badges de      */
/* statut, colonne d'actions empilées, ligne grisée si désactivé.   */
/* ============================================================== */
.admin-users-filter-form {
  margin-bottom: 14px;
  align-items: start;
}

.admin-user-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.admin-user-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.admin-user-row-disabled {
  opacity: 0.6;
}


/* ============================================================== */
/* Loader animé d'import recette : carrousel d'icônes d'aliments    */
/* en orbite + label, en overlay sur le panneau d'import pendant    */
/* l'analyse (URL / texte / photo). Icônes Lucide, couleurs thème.  */
/* ============================================================== */
.recipe-import-panel {
  position: relative;
}

.recipe-import-loader {
  position: absolute;
  inset: 0;
  z-index: 6;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  border-radius: inherit;
  background: color-mix(in srgb, var(--panel) 86%, transparent);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

.recipe-import-loader:not([hidden]) {
  display: flex;
}

.recipe-import-loader-orbit {
  position: relative;
  width: 108px;
  height: 108px;
}

.recipe-import-loader-slot {
  position: absolute;
  top: 0;
  left: 50%;
  width: 34px;
  height: 34px;
  margin-left: -17px;
  transform-origin: 17px 54px;
  /* Chaque slot a SA propre animation (import-orbit-1…6) avec un angle de
     départ en valeur LITTÉRALE (0, 60, …, 300°). On évite à la fois
     l'animation-delay négatif ET le var()/calc() dans un keyframe : iOS Safari
     ne gère de façon fiable NI l'un NI l'autre sur un élément cloné
     dynamiquement (overlay de suppression) → les 6 icônes s'empilaient en
     « crotte ». Keyframes littérales = chaque icône part de son angle dès la
     1re frame, sans dépendre de delay ni de custom property. */
  animation: import-orbit-1 4.5s linear infinite;
  /* iOS Safari : force le compositing GPU pour que la rotation soit fluide. */
  will-change: transform;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

.recipe-import-loader-slot:nth-child(1) { animation-name: import-orbit-1; }
.recipe-import-loader-slot:nth-child(2) { animation-name: import-orbit-2; }
.recipe-import-loader-slot:nth-child(3) { animation-name: import-orbit-3; }
.recipe-import-loader-slot:nth-child(4) { animation-name: import-orbit-4; }
.recipe-import-loader-slot:nth-child(5) { animation-name: import-orbit-5; }
.recipe-import-loader-slot:nth-child(6) { animation-name: import-orbit-6; }

.recipe-import-loader-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  font-size: 26px;
  color: var(--primary);
  animation: import-orbit-pulse 1.5s ease-in-out infinite;
}

/* iOS Safari : un SVG en flex item avec width:1em (cf .ico) ne suit pas
   toujours le font-size du parent → s'effondre à 0×0. On force ici la
   taille en pixels SPÉCIFIQUEMENT pour les icônes du carousel (pas
   d'impact sur .ico ailleurs dans l'app). flex-shrink:0 empêche aussi
   un éventuel shrink en flex container étroit. */
.recipe-import-loader-icon > svg,
.recipe-import-loader-icon .ico {
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
}

.recipe-import-loader-slot:nth-child(odd) .recipe-import-loader-icon {
  color: color-mix(in srgb, var(--primary) 65%, var(--success));
}

.recipe-import-loader-label {
  margin: 0;
  font-weight: 600;
  color: var(--text);
}

@keyframes import-orbit-1 { from { transform: rotate(0deg);   } to { transform: rotate(360deg); } }
@keyframes import-orbit-2 { from { transform: rotate(60deg);  } to { transform: rotate(420deg); } }
@keyframes import-orbit-3 { from { transform: rotate(120deg); } to { transform: rotate(480deg); } }
@keyframes import-orbit-4 { from { transform: rotate(180deg); } to { transform: rotate(540deg); } }
@keyframes import-orbit-5 { from { transform: rotate(240deg); } to { transform: rotate(600deg); } }
@keyframes import-orbit-6 { from { transform: rotate(300deg); } to { transform: rotate(660deg); } }

@keyframes import-orbit-pulse {
  0%, 100% { transform: scale(0.82); opacity: 0.7; }
  50% { transform: scale(1.1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  /* Un loader DOIT tourner pour signaler « ça charge » → la rotation de
     l'orbite (.recipe-import-loader-slot) est un mouvement ESSENTIEL, on la
     garde même en mode réduit. On ne coupe que le pulse d'échelle des icônes,
     lui purement décoratif. Sans ça, sur iPhone (« Réduire les animations »
     activé, fréquent) le carousel restait figé en cercle sans tourner. */
  .recipe-import-loader-icon {
    animation: none;
  }
}


/* « Voir la source » (recette importée) : petit bouton rond en bas-droite de
   la photo, icône globe seule. Superposé au <label>-vignette. Fond CORAIL
   (`--primary`) car ouvrir la source EST une action (langage couleur des
   CONSIGNES : corail = action) ; liseré blanc pour ressortir sur toute
   photo, claire comme sombre. */
.menu-recipe-photo-source {
  position: absolute;
  right: 8px;
  bottom: 8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  color: #ffffff;
  background: var(--primary);
  border: 1.5px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  pointer-events: auto;
  transition: background 0.12s ease, transform 0.12s ease;
}

.menu-recipe-photo-source > svg,
.menu-recipe-photo-source .ico {
  width: 18px;
  height: 18px;
}

@media (hover: hover) {
  .menu-recipe-photo-source:hover {
    background: var(--primary-hover);
    transform: translateY(-1px);
  }
}


/* ============================================================== */
/* Délices UI — pack « polish ambiant » (animations CSS pures,     */
/* thémées, coupées si prefers-reduced-motion).                    */
/* ============================================================== */
@media (prefers-reduced-motion: no-preference) {
  /* Cartes en fondu-montée décalé à l'arrivée (rejoué aussi au re-render
     AJAX d'une carte rerollée → la nouvelle carte fond joliment en place ;
     le stepper +/- met à jour le nombre en place, donc ne re-déclenche
     PAS l'animation). */
  .menu-card,
  .recipe-row {
    animation: ui-card-enter 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
  }
  .menu-stack > *:nth-child(2) { animation-delay: 0.04s; }
  .menu-stack > *:nth-child(3) { animation-delay: 0.08s; }
  .menu-stack > *:nth-child(4) { animation-delay: 0.12s; }
  .menu-stack > *:nth-child(5) { animation-delay: 0.16s; }
  .menu-stack > *:nth-child(n + 6) { animation-delay: 0.2s; }
  .recipes-list > *:nth-child(2) { animation-delay: 0.03s; }
  .recipes-list > *:nth-child(3) { animation-delay: 0.06s; }
  .recipes-list > *:nth-child(4) { animation-delay: 0.09s; }
  .recipes-list > *:nth-child(5) { animation-delay: 0.12s; }
  .recipes-list > *:nth-child(n + 6) { animation-delay: 0.15s; }

  /* Icône d'état vide qui lévite doucement (dé, panier, plat…). */
  .menu-empty-emoji {
    animation: ui-float 3.2s ease-in-out infinite;
  }

  /* Splash d'ouverture : le logo « respire » légèrement. */
  html[data-splash] .app-splash-logo {
    animation: ui-breathe 2.4s ease-in-out infinite;
  }

  /* Fiche recette : le panneau actif (Ingrédients/Consignes) fond en place
     au chargement et à chaque changement d'onglet. */
  .menu-recipe-pane:not([hidden]) {
    animation: ui-pane-fade 0.28s ease both;
  }
}

@keyframes ui-card-enter {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

@keyframes ui-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}

@keyframes ui-breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}

@keyframes ui-pane-fade {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}


/* ============================================================== */
/* Délices UI — pack 2 « le dé qui roule » : au clic sur            */
/* « Proposer un menu », le dé du CTA tournoie ~0,5 s avant le      */
/* POST (classe .is-rolling posée en JS sur .menu-hero-cta-emoji,   */
/* cf admin_common.js). Coupé si prefers-reduced-motion.           */
/* ============================================================== */
@media (prefers-reduced-motion: no-preference) {
  .menu-hero-cta-emoji.is-rolling .ico {
    animation: ui-dice-roll 0.25s cubic-bezier(0.45, 0.05, 0.3, 1);
  }
}

@keyframes ui-dice-roll {
  0% { transform: rotate(0deg) scale(1); }
  20% { transform: rotate(160deg) scale(1.2); }
  45% { transform: rotate(360deg) scale(0.94); }
  70% { transform: rotate(560deg) scale(1.12); }
  100% { transform: rotate(720deg) scale(1); }
}


/* ============================================================== */
/* Délices UI — pack 3 « célébration courses finies » : quand le   */
/* dernier article passe coché, une pluie de confettis thémés      */
/* (overlay .ui-celebrate injecté en JS, cf shopping_list_detail.js)*/
/* + un toast « Tout est coché ! ». Les pièces n'ont aucun style    */
/* inline : couleur / position / dérive viennent du :nth-child     */
/* (conforme CSP stricte). Coupé si prefers-reduced-motion (le JS   */
/* n'injecte alors rien, seul le toast reste).                      */
/* ============================================================== */
.ui-celebrate {
  position: fixed;
  inset: 0;
  z-index: 55;
  overflow: hidden;
  pointer-events: none;
}

.ui-celebrate-piece {
  position: absolute;
  top: -6vh;
  width: 9px;
  height: 14px;
  border-radius: 2px;
  opacity: 0;
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: no-preference) {
  .ui-celebrate-piece {
    animation: ui-confetti-fall 1.5s cubic-bezier(0.3, 0.7, 0.5, 1) forwards;
  }
}

@keyframes ui-confetti-fall {
  0% { opacity: 0; transform: translate3d(0, 0, 0) rotate(0deg); }
  8% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(var(--ui-drift, 0), 112vh, 0) rotate(560deg); }
}

/* Couleurs thémées en cycle (terracotta / vert / crème / mélange). */
.ui-celebrate-piece:nth-child(4n + 1) { background: var(--primary); }
.ui-celebrate-piece:nth-child(4n + 2) { background: var(--success); }
.ui-celebrate-piece:nth-child(4n + 3) { background: color-mix(in srgb, #ffffff 72%, var(--primary)); }
.ui-celebrate-piece:nth-child(4n) { background: color-mix(in srgb, var(--primary) 55%, var(--success)); }

/* Une pièce sur trois est ronde, pour casser la régularité. */
.ui-celebrate-piece:nth-child(3n) { width: 9px; height: 9px; border-radius: 50%; }

/* Position de départ, cadence et dérive horizontale par pièce. */
.ui-celebrate-piece:nth-child(1)  { left: 6%;  animation-delay: 0s;    animation-duration: 1.25s; --ui-drift: -14px; }
.ui-celebrate-piece:nth-child(2)  { left: 14%; animation-delay: 0.08s; animation-duration: 1.5s;  --ui-drift: 10px; }
.ui-celebrate-piece:nth-child(3)  { left: 22%; animation-delay: 0.02s; animation-duration: 1.15s; --ui-drift: -8px; }
.ui-celebrate-piece:nth-child(4)  { left: 30%; animation-delay: 0.14s; animation-duration: 1.4s;  --ui-drift: 16px; }
.ui-celebrate-piece:nth-child(5)  { left: 38%; animation-delay: 0.05s; animation-duration: 1.3s;  --ui-drift: -12px; }
.ui-celebrate-piece:nth-child(6)  { left: 46%; animation-delay: 0.18s; animation-duration: 1.55s; --ui-drift: 8px; }
.ui-celebrate-piece:nth-child(7)  { left: 54%; animation-delay: 0s;    animation-duration: 1.2s;  --ui-drift: 14px; }
.ui-celebrate-piece:nth-child(8)  { left: 62%; animation-delay: 0.12s; animation-duration: 1.45s; --ui-drift: -10px; }
.ui-celebrate-piece:nth-child(9)  { left: 70%; animation-delay: 0.06s; animation-duration: 1.32s; --ui-drift: 12px; }
.ui-celebrate-piece:nth-child(10) { left: 78%; animation-delay: 0.2s;  animation-duration: 1.5s;  --ui-drift: -16px; }
.ui-celebrate-piece:nth-child(11) { left: 86%; animation-delay: 0.03s; animation-duration: 1.18s; --ui-drift: 6px; }
.ui-celebrate-piece:nth-child(12) { left: 94%; animation-delay: 0.15s; animation-duration: 1.42s; --ui-drift: -6px; }
.ui-celebrate-piece:nth-child(13) { left: 10%; animation-delay: 0.24s; animation-duration: 1.6s;  --ui-drift: 18px; }
.ui-celebrate-piece:nth-child(14) { left: 50%; animation-delay: 0.1s;  animation-duration: 1.28s; --ui-drift: -18px; }
.ui-celebrate-piece:nth-child(15) { left: 34%; animation-delay: 0.22s; animation-duration: 1.38s; --ui-drift: 10px; }
.ui-celebrate-piece:nth-child(16) { left: 66%; animation-delay: 0.17s; animation-duration: 1.48s; --ui-drift: -12px; }


/* ============================================================== */
/* Délices UI — pack 4 « micro-feedback » : petits effets « le     */
/* temps d'une action ». Classes posées/retirées en JS (reroll,    */
/* stepper personnes, planifier, cochage). Coupés si               */
/* prefers-reduced-motion.                                          */
/* ============================================================== */
@media (prefers-reduced-motion: no-preference) {
  /* Stepper personnes : le nombre « pop » à chaque +/- (MAJ en place). */
  .menu-servings-value.is-bump {
    animation: ui-pop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  /* Planifier : l'icône calendrier « pop » à la confirmation. */
  .ui-pop {
    animation: ui-pop 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  /* Cochage d'un article : pop de la case + barré qui balaie le nom. */
  body.page-shopping-list-detail .shopping-row.is-just-checked .shopping-check {
    animation: ui-check-pop 0.42s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  body.page-shopping-list-detail .shopping-row.is-just-checked .shopping-line-name {
    position: relative;
  }
  body.page-shopping-list-detail .shopping-row.is-just-checked .shopping-line-name::after {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 100%;
    height: 2px;
    background: currentColor;
    transform-origin: left center;
    animation: ui-strike 0.42s ease forwards;
    pointer-events: none;
  }
}

@keyframes ui-spin {
  to { transform: rotate(360deg); }
}

@keyframes ui-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.4); }
  100% { transform: scale(1); }
}

@keyframes ui-check-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.32); }
  100% { transform: scale(1); }
}

@keyframes ui-strike {
  0% { transform: scaleX(0); opacity: 0.85; }
  70% { opacity: 0.85; }
  100% { transform: scaleX(1); opacity: 0; }
}


/* ============================================================== */
/* Délices UI — « repère-moi » : quand toutes les recettes du      */
/* filtre ont déjà été proposées, menu.random pose .menu-toolbar-  */
/* btn-attention sur le bouton « Mémoire » (recyclage) le temps de */
/* la page → quelques pulsations (halo terracotta + léger scale)   */
/* pour que l'utilisateur trouve le bouton à toucher. Coupé sous   */
/* prefers-reduced-motion (le message reste, lui).                 */
/* ============================================================== */
@media (prefers-reduced-motion: no-preference) {
  .menu-toolbar-btn-attention {
    animation: ui-attention 1.1s ease-in-out 3;
  }
}

@keyframes ui-attention {
  0% {
    transform: scale(1);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 50%, transparent);
  }
  35% {
    transform: scale(1.07);
    box-shadow: 0 0 0 8px color-mix(in srgb, var(--primary) 0%, transparent);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 0%, transparent);
  }
}

/* ============================================================== */
/* Liste des recettes — refonte boutons (icônes Lucide, 1 ligne)  */
/* ============================================================== */

/* La card en colonne (head + actions row), la grille du head devient
   simplement « thumb + info » (le toggle a déménagé dans les actions). */
.recipe-row-head {
  grid-template-columns: 72px 1fr;
}

/* Une seule rangée d'icônes en bas. Les 5 boutons se suivent (modifier,
   planifier, toggle menu, toggle courses, supprimer) — la poubelle est
   simplement en dernière position, sans margin-left:auto qui l'ancrait
   à l'extrême droite. */
.recipe-actions {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  justify-content: flex-start;
  flex-wrap: nowrap;
}

.recipe-action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: var(--panel-sunken);
  /* Icône en --muted au repos, comme les onglets de nav (au lieu du --text
     « noir plein » qui ressortait trop) ; corail au hover / actif. */
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.14s ease, color 0.14s ease, border-color 0.14s ease, transform 0.12s ease;
}

.recipe-action-icon .ico {
  width: 21px;
  height: 21px;
}

.recipe-action-icon:active {
  transform: scale(0.96);
}

/* Le form wrapper du Supprimer / du toggle Menu ne doit pas casser le
   layout flex (les <form> sont en flex item, comme les <a> / <button>). */
.recipe-action-delete-form,
.recipe-action-toggle-form {
  margin: 0;
  display: inline-flex;
}
/* Poubelle Supprimer ancrée à DROITE de la ligne (place de l'ancien menu « ⋮ »). */
.recipe-action-delete-form {
  margin-left: auto;
}

.recipe-action-danger {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 30%, var(--border));
}

/* Toggles Menu / Courses — NEUTRES au repos (ils héritent de
   .recipe-action-icon, comme les autres icônes de la ligne et de la nav au
   repos). Le vert était hors palette : on le réserve au vrai feedback de
   succès (toasts, badges). L'état actif (au menu / en courses) passe en
   corail + drop-shadow, exactement comme le bouton « planifié ». */

/* Vague 7.10 — Toggles Menu/Courses actifs : MÊME couleur que le
   bouton de planification (`--primary` corail) au lieu du rouge danger,
   et drop-shadow PERCEPTIBLE (signalé revue UX : « pas la petite ombre
   comme sur le planning, on peut d'ailleurs mettre la même couleur que
   le planning quand c'est déjà coché »). Cohérence visuelle complète
   entre les 3 boutons d'état d'une card recette (planifié / au menu /
   en courses) — tous corail, tous avec drop-shadow équivalente. */
.recipe-action-toggle-menu.is-active,
.recipe-action-toggle-courses.is-active {
  color: var(--primary);
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 22%, var(--panel-sunken));
}

.recipe-action-toggle-menu.is-active .ico,
.recipe-action-toggle-courses.is-active .ico {
  /* Shadow plus appuyée (1.2px / 55%) que .recipe-action-plan-is-planned
     pour que ce soit vraiment visible — l'utilisateur avait signalé que
     la version 0.8px/40% précédente ne ressortait pas assez. */
  filter: drop-shadow(0 0 1.2px color-mix(in srgb, var(--primary) 55%, transparent));
}

/* Hover states scopés aux pointeurs SOUVRIS — sur tactile, :hover reste
   sticky après tap iOS/Android et la couleur du clic ne se relâche pas
   (bug remonté en revue). On garde le focus-visible (clavier) qui ne
   colle pas sur tactile. */
@media (hover: hover) {
  .recipe-action-icon:hover,
  .recipe-action-icon:focus-visible {
    background: color-mix(in srgb, var(--primary) 12%, var(--panel-sunken));
    border-color: var(--primary);
    color: var(--primary);
    outline: none;
  }

  .recipe-action-danger:hover,
  .recipe-action-danger:focus-visible {
    background: var(--danger);
    border-color: var(--danger);
    color: #ffffff;
  }

  /* Toggles Menu/Courses au repos : pas de hover spécifique → ils utilisent
     le hover corail de base (.recipe-action-icon:hover ci-dessus). */

  /* Vague 7.10 — Hover des toggles actifs : intensifie le fond primary
     au lieu de passer en danger (cohérence avec le nouvel état actif). */
  .recipe-action-toggle-menu.is-active:hover,
  .recipe-action-toggle-menu.is-active:focus-visible,
  .recipe-action-toggle-courses.is-active:hover,
  .recipe-action-toggle-courses.is-active:focus-visible {
    background: var(--primary);
    border-color: var(--primary);
    color: #ffffff;
  }
}

/* Petit label sous le titre de la modale Courses qui rappelle quelle
   recette on ajoute. Padding resserré pour rapprocher du « Liste cible ». */
.recipe-shopping-recipe-label {
  margin: 0 0 6px;
  font-size: 14px;
}

/* Plus que 4 éléments sur la rangée (3 toggles + « ⋮ ») au lieu de 5 :
   on garde 44px de cible partout, y compris iPhone SE 320px (4×44 + gaps
   tiennent largement). On ne rétrécit plus en dessous de 44 (reco
   Apple/Google : 44px mini — l'ancien 40/36px était le bug remonté). */

/* Le menu « ⋮ » (Modifier / Supprimer) est poussé à droite, séparant
   visuellement les 3 toggles d'état de l'overflow. */
.recipe-row-more {
  position: relative;
  margin-left: auto;
}

/* On masque la flèche native du <summary> (l'icône Lucide « ⋮ » suffit). */
.recipe-row-more-summary {
  list-style: none;
}
.recipe-row-more-summary::-webkit-details-marker {
  display: none;
}
.recipe-row-more-summary::marker {
  content: "";
}

/* Menu déroulant ancré sous le « ⋮ », aligné à droite. Flotte au-dessus
   des cards voisines (z-index + ombre). */
.recipe-row-more-menu {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 30;
  min-width: 184px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.28);
}

.recipe-row-more[open] .recipe-row-more-menu {
  display: block;
}

/* Items du menu : icône + libellé, cible ≥ 44px de haut. */
.recipe-row-more-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  margin: 0;
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.recipe-row-more-icon {
  display: inline-flex;
}

.recipe-row-more-item .ico {
  width: 19px;
  height: 19px;
}

.recipe-row-more-item-danger {
  color: var(--danger);
}

/* Icône du « Modifier » (stylo) en --muted comme les autres icônes au repos.
   On exclut l'item danger pour garder la poubelle en rouge. */
.recipe-row-more-item:not(.recipe-row-more-item-danger) .recipe-row-more-icon {
  color: var(--muted);
}

@media (hover: hover) {
  .recipe-row-more-item:hover,
  .recipe-row-more-item:focus-visible {
    background: var(--panel-sunken);
    outline: none;
  }
  .recipe-row-more-item-danger:hover,
  .recipe-row-more-item-danger:focus-visible {
    background: color-mix(in srgb, var(--danger) 14%, var(--panel-sunken));
  }
}

/* Le <form> Supprimer dans le menu ne doit pas casser l'empilement vertical
   des items (les .recipe-action-*-form sont inline-flex par défaut). */
.recipe-row-more-menu .recipe-action-delete-form {
  margin: 0;
  display: block;
}

/* ============================================================== */
/* /recipes — header collant + recherche instantanée + menu « ⋯ »  */
/* ============================================================== */

/* Le header (recherche + Filtres + ⋯) colle sous la barre supérieure mobile.
   --appbar-h est posée par recipes_toolbar.js (= hauteur de .mobile-topbar,
   0 sur desktop → pas de décalage). */
@media (max-width: 700px) {
  .recipes-toolbar {
    /* Colle sous la barre du haut sticky (--appbar-h = sa hauteur, posée par
       recipes_toolbar.js ; 0 sur desktop → pas de décalage). */
    position: sticky;
    top: var(--appbar-h, 0px);
    z-index: 30;
  }

  /* Tout sur une seule ligne : champ qui s'étire + Filtres + ⋯ (on gagne de
     la hauteur, précieux maintenant qu'il y a une barre du haut). */
  .recipes-toolbar .searchbar {
    grid-template-columns: 1fr auto;
    align-items: center;
  }

  .recipes-toolbar .search-actions {
    justify-content: flex-end;
  }
}

.searchbar-input {
  width: 100%;
}

/* Menu « ⋯ » : <details> avec dropdown ancré à droite du bouton. Fermeture au
   clic dehors + Échap via le handler générique [data-nav-more]. */
.recipes-more {
  position: relative;
  display: inline-flex;
}

.recipes-more-summary {
  list-style: none;
}

.recipes-more-summary::-webkit-details-marker { display: none; }
.recipes-more-summary::marker { content: ""; }

.recipes-more-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 220px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 30px var(--shadow-color, rgba(0, 0, 0, 0.25));
  display: grid;
  gap: 2px;
}

.recipes-more-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 11px 12px;
  border-radius: 9px;
  background: transparent;
  border: 0;
  color: var(--text);
  font-size: 15px;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.recipes-more-item:focus-visible {
  background: var(--hover-bg);
  outline: none;
}

@media (hover: hover) {
  .recipes-more-item:hover {
    background: var(--hover-bg);
  }
}

.recipes-more-icon {
  display: inline-flex;
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  color: var(--muted);
}

.recipes-more-icon .ico {
  width: 20px;
  height: 20px;
}

/* Recherche instantanée : .recipe-row est en display:flex, qui l'emporte sur
   l'attribut [hidden] → on rétablit explicitement le masquage des lignes
   filtrées (même piège que pour les autres composants flex). */
.recipe-row[hidden] {
  display: none;
}


/* ============================================================== */
/* Liste de courses (édition) — bloc « Recettes liées » repliable */
/* ============================================================== */

.shopping-linked-recipes {
  margin: 0 0 12px;
  padding: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}

.shopping-linked-recipes-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  cursor: pointer;
  list-style: none;
  user-select: none;
  gap: 12px;
  /* Pas de flash gris/bleu au tap (iOS/Android) sur le <summary>. */
  -webkit-tap-highlight-color: transparent;
}

.shopping-linked-recipes-summary::-webkit-details-marker {
  display: none;
}

/* Fond de survol réservé aux pointeurs fins (souris) : sur écran tactile le
   :hover « colle » après le tap → effet de surbrillance disgracieux. */
@media (hover: hover) {
  .shopping-linked-recipes-summary:hover {
    background: color-mix(in srgb, var(--primary) 6%, transparent);
  }
}

.shopping-linked-recipes-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: var(--text);
}

.shopping-linked-recipes-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 7px;
  border-radius: 11px;
  background: color-mix(in srgb, var(--primary) 18%, var(--panel-sunken));
  color: var(--primary);
  font-size: 12px;
  font-weight: 700;
}

.shopping-linked-recipes-chevron {
  display: inline-flex;
  transition: transform 0.18s ease;
  color: var(--muted);
}

.shopping-linked-recipes-chevron .ico {
  width: 18px;
  height: 18px;
}

.shopping-linked-recipes[open] .shopping-linked-recipes-chevron {
  transform: rotate(180deg);
}

.shopping-linked-recipes-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0 10px 12px;
}

.shopping-linked-recipes-empty {
  margin: 0;
  padding: 8px 4px;
  font-size: 13px;
  line-height: 1.5;
}

/* Bouton « + » à droite de « Recettes liées » dans le summary du bloc.
   Toujours visible (que le bloc soit plié ou déplié) → point d'entrée
   rapide vers le picker sans avoir à déplier d'abord. Le clic sur le
   bouton stoppe la propagation (cf shopping_add_recipes_dialog.js) pour
   ne pas toggler le <details>. Couleur **corail** = couleur d'action de l'app
   (cohérent avec le FAB « + » de la nav et les autres `+` d'ajout). */
.shopping-linked-recipes-summary-add {
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
  background: color-mix(in srgb, var(--primary) 10%, var(--panel-sunken));
  width: 34px;
  height: 34px;
  border-radius: 9px;
}

.shopping-linked-recipes-summary-add .ico {
  width: 18px;
  height: 18px;
}

@media (hover: hover) {
  .shopping-linked-recipes-summary-add:hover,
  .shopping-linked-recipes-summary-add:focus-visible {
    background: var(--primary);
    border-color: var(--primary);
    color: #ffffff;
  }
}

.shopping-linked-recipes-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.shopping-linked-recipe-row {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--panel);
  border: 1px solid var(--border);
}

.shopping-linked-recipe-thumb-link {
  display: block;
  border-radius: 8px;
  overflow: hidden;
  transition: transform 0.12s ease;
}

.shopping-linked-recipe-thumb-link:hover {
  transform: scale(1.05);
}

.shopping-linked-recipe-thumb {
  width: 44px;
  height: 44px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  position: relative;
}

.shopping-linked-recipe-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Le form d'édition clone une vignette avec un <img data-thumb-img hidden> que
   le JS ne dé-masque que si la recette a une photo. Sans ce garde, la règle
   `display:block` ci-dessus écrase l'attribut [hidden] → image cassée affichée
   par-dessus le fallback (couverts) pour les recettes sans photo. */
.shopping-linked-recipe-thumb img[hidden] {
  display: none;
}

.shopping-linked-recipe-thumb-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--panel-2) 0%, var(--panel-sunken) 100%);
}

.shopping-linked-recipe-thumb-fallback {
  opacity: 0.45;
}

.shopping-linked-recipe-thumb-fallback .ico {
  width: 20px;
  height: 20px;
}

.shopping-linked-recipe-name {
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
  line-height: 1.3;
  word-break: break-word;
  min-width: 0;
}

.shopping-linked-recipe-name:hover {
  color: var(--primary);
}

.shopping-linked-recipe-unlink-form {
  margin: 0;
  display: inline-flex;
}

/* Bouton × rouge à droite de chaque recette liée — retire les ingrédients
   de la recette de la liste (best-effort par nom, items non cochés
   uniquement) ET le lien. Pattern danger plein au hover. */
.shopping-linked-recipe-unlink {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, var(--border));
  background: color-mix(in srgb, var(--danger) 10%, var(--panel-sunken));
}

@media (hover: hover) {
  .shopping-linked-recipe-unlink:hover,
  .shopping-linked-recipe-unlink:focus-visible {
    background: var(--danger);
    border-color: var(--danger);
    color: #ffffff;
  }
}

/* Ancien bouton + (re-add ingredients) retiré — conservé inerte ici au
   cas où des tests ou intégrations le référencent encore. */
.shopping-linked-recipe-add {
  color: var(--success);
  border-color: color-mix(in srgb, var(--success) 40%, var(--border));
  background: color-mix(in srgb, var(--success) 10%, var(--panel-sunken));
}

.shopping-linked-recipe-add:hover,
.shopping-linked-recipe-add:focus-visible {
  background: var(--success);
  border-color: var(--success);
  color: #ffffff;
}

/* ============================================================== */
/* Export-dialog — variante compacte (padding réduit + bouton     */
/* flottant en bas, icône courses). Appliquée via la classe       */
/* `.export-dialog-compact` aux 3 dialogs : toggle Courses sur    */
/* /recipes, picker « Ajouter des recettes » sur /shopping-lists. */
/* ============================================================== */

/* Le « body » regroupe les fieldsets target/mode du toggle Courses pour
   un padding cohérent (les fieldsets nus n'ont pas de cadre).

   Vague 7.9 bugfix — `flex: 1 1 auto; min-height: 0; overflow-y: auto`
   pour que le body soit la zone scrollable de la dialog. Sans ça, quand
   le contenu de la dialog dépasse la hauteur du viewport, le scroll
   passait à la page derrière au lieu de scroller le body de la dialog
   (signalé en revue UX : « ça scroll la page de derrière, je ne peux
   pas descendre pour choisir le type de repas »). Le header et le footer
   restent figés en haut/bas. */
.export-dialog-body {
  padding: 10px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Évite que le scroll touch propage au body de la page derrière sur
     iOS Safari (qui ne respecte pas le scroll-lock natif de <dialog>). */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.export-dialog .export-target-fieldset,
.export-dialog .export-mode-fieldset {
  padding: 8px 10px;
}

/* Footer flottant : le bouton de soumission est large, success vif,
   icône `shopping-cart` à gauche du label — copie conforme du sticky
   bottom de /menu/ingredients (cf `.menu-ingredients-actions`). */
.export-dialog-footer-floating {
  padding: 10px 14px;
  background: var(--panel);
  border-top: 1px solid var(--border);
  box-shadow: 0 -6px 12px color-mix(in srgb, var(--shadow-color, rgba(0, 0, 0, 0.18)) 70%, transparent);
  position: sticky;
  bottom: 0;
}

.export-dialog-submit-floating {
  width: 100%;
  min-height: 46px;
  font-size: 15px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.export-dialog-submit-floating .ico {
  width: 18px;
  height: 18px;
}

@media (max-width: 540px) {
  .export-dialog-body,
  .export-dialog-footer-floating {
    padding-left: 10px;
    padding-right: 10px;
  }
}

/* Label discret « Recette : <name> » dans la modale Courses de /recipes —
   on évite la doublure du title de la modale, padding contenu. */
.recipe-shopping-recipe-label:empty {
  display: none;
}

/* ============================================================== */
/* /menu/ingredients & /menu/planning/ingredients — hero compact  */
/* + bouton flottant qui ouvre la dialog d'export (uniforme avec  */
/* les autres flows : /recipes Courses toggle, /shopping-lists    */
/* Ajouter des recettes).                                         */
/* ============================================================== */

/* Hero « Ingrédients à acheter » — réduction sensible des paddings
   (énormes sur mobile signalés en revue). */
.menu-ingredients-hero-compact {
  margin-bottom: 10px;
}

.menu-ingredients-hero-compact .menu-ingredients-hero-inner {
  padding: 12px 14px;
  border-radius: 14px;
}

.menu-ingredients-hero-compact .menu-ingredients-title {
  font-size: 20px;
  margin-bottom: 4px;
}

.menu-ingredients-hero-compact .menu-ingredients-subtitle {
  font-size: 13px;
}

/* Form devient plus serré : gap entre sections réduit. */
.menu-ingredients-form {
  gap: 12px;
}

/* Dialog d'export — réduction supplémentaire des paddings sur mobile
   (le user a signalé « écart énorme » entre les éléments). */
@media (max-width: 540px) {
  .menu-ingredients-hero-compact .menu-ingredients-hero-inner {
    padding: 10px 12px;
  }
  .menu-ingredients-hero-compact .menu-ingredients-title {
    font-size: 18px;
  }
  .menu-ingredients-hero-compact .menu-ingredients-subtitle {
    font-size: 12px;
  }
  .menu-planning-hero {
    padding: 8px 0 12px;
  }
  /* Tools / body / footer encore plus resserrés sous 540 */
  .export-dialog-compact .export-dialog-header {
    padding: 8px 10px;
  }
  .export-dialog-compact .export-dialog-body {
    padding: 8px 10px;
    gap: 8px;
  }
  .export-dialog-footer-floating {
    padding: 8px 10px;
  }
  .export-dialog-compact .export-target-fieldset,
  .export-dialog-compact .export-mode-fieldset {
    padding: 6px 8px;
  }
  /* Options « append / replace » resserrées pour ne pas faire monter
     la dialog en hauteur — un padding visuel sur chaque label suffit. */
  .export-dialog-compact .export-mode-option {
    padding: 6px 8px;
  }
}


/* ================================================================== */
/* Vague 7.9 — Refonte planning : `+` par jour + dialog 4 slots +     */
/* multi-recettes par créneau. L'ancien layout `menu-planning-slots-   */
/* grid` (midi/soir en 2 colonnes, 1 recette par slot) est remplacé    */
/* par une liste plate de lignes "Slot : Recette  Servings  🗑".       */
/* L'ajout passe par un seul `+` vert à droite du libellé du jour, qui */
/* ouvre la dialog `#planning-assign-dialog` (slot choisi à l'intérieur).*/
/* ================================================================== */

.menu-planning-day-head {
  /* Étend les règles existantes (.menu-planning-day-head ligne ~5228)
     pour caler le `+` vert à droite et le badge "Aujourd'hui" inline
     avec le libellé du jour. */
  flex-wrap: wrap;
  align-items: center;
}

.menu-planning-day-today-badge {
  font-size: 11px;
  padding: 2px 8px;
  margin-left: 8px;
  vertical-align: middle;
}

/* Bouton `+` corail à droite du libellé du jour. Hérite de .recipe-action-icon.
   Corail = couleur d'action de l'app (cohérent avec le FAB « + » de la nav et
   les autres `+` ajout). Le vert est réservé au feedback de succès. */
.menu-planning-day-add-btn {
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
  background: color-mix(in srgb, var(--primary) 10%, var(--panel-sunken));
}

@media (hover: hover) {
  .menu-planning-day-add-btn:hover,
  .menu-planning-day-add-btn:focus-visible {
    background: var(--primary);
    border-color: var(--primary);
    color: #ffffff;
  }
}

/* Liste des recettes planifiées du jour — une ligne par recette
   (multi-recettes par slot autorisé, ex. apéro avec focaccia + fougasse).
   Réinitialise le padding/style par défaut des <ul>. */
.menu-planning-day-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.menu-planning-day-row {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  min-width: 0;
}

.menu-planning-day-slot {
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: 0.5px;
  white-space: nowrap;
}

.menu-planning-day-recipe {
  font-size: 15px;
  color: var(--text);
  text-decoration: none;
  font-weight: 500;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-planning-day-recipe:hover,
.menu-planning-day-recipe:focus-visible {
  color: var(--primary);
}

.menu-planning-day-recipe-muted {
  color: var(--muted);
  font-style: italic;
}

.menu-planning-day-servings {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}

.menu-planning-day-clear-form {
  margin: 0;
  display: inline-flex;
  align-items: center;
}

/* Bouton poubelle plus compact dans cette liste (pas besoin du 44×44
   standard, on est dans une ligne dense). */
.menu-planning-day-clear-btn {
  width: 36px;
  height: 36px;
  border-radius: 8px;
}

.menu-planning-day-clear-btn .ico {
  width: 17px;
  height: 17px;
}

.menu-planning-day-empty {
  margin: 4px 0 0;
  padding: 8px 10px;
  /* 14px : cohérent avec les métas/textes secondaires harmonisés. */
  font-size: 14px;
  color: var(--muted);
  background: var(--panel-sunken);
  border: 1px dashed var(--border);
  border-radius: 10px;
}

/* Sur tel : slot en label au-dessus du nom de recette pour gagner de
   la place horizontale (les noms longs ne sont pas écrasés). */
@media (max-width: 540px) {
  .menu-planning-day-row {
    grid-template-columns: 1fr auto auto;
  }
  .menu-planning-day-slot {
    grid-column: 1 / -1;
    margin-bottom: 2px;
  }
  .menu-planning-day-recipe {
    grid-column: 1;
  }
}


/* ================================================================== */
/* Vague 7.9 — Slots de planification (4 options : petit-déjeuner /    */
/* déjeuner / apéro / dîner). Pattern radio-chips réutilisé dans       */
/* `#planning-assign-dialog` (page /menu/planning) et                  */
/* `#planning-picker-dialog` (cross-page /menu + /recipes).            */
/* ================================================================== */

.planning-assign-slots {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}

.planning-assign-slot-option {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 8px 10px;
  cursor: pointer;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  text-align: center;
  position: relative;
  font-size: 14px;
  color: var(--text);
  transition: border-color 120ms ease, background-color 120ms ease;
}

.planning-assign-slot-option input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.planning-assign-slot-option:has(input[type="radio"]:checked) {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
  color: var(--primary);
  font-weight: 600;
}

@media (hover: hover) {
  .planning-assign-slot-option:hover {
    border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
  }
}

@media (max-width: 380px) {
  /* On GARDE 2 colonnes même sur petit écran (demande UX : petit-déj +
     déjeuner côte à côte, apéro + dîner côte à côte). Avant, on basculait
     à 1 colonne dès 380px « pour ne pas tronquer Petit-déjeuner », mais ça
     changeait trop tôt. On resserre juste padding + police : le libellé
     tient sur une ligne, et à défaut il casse au trait d'union (jamais de
     débordement horizontal, la case grandit). */
  .planning-assign-slot-option {
    padding: 8px 6px;
    font-size: 13px;
  }
}


/* ================================================================== */
/* Vague 7.9 — Picker planning (dialog cross-page) : navigation        */
/* semaine par flèches + grille de jours en boutons cliquables         */
/* (au lieu de radios cachés derrière des <label>). La grille est      */
/* rebuild en JS à chaque navigateWeek(), donc ces règles ciblent      */
/* aussi bien les chips serveur que ceux générés client.               */
/* ================================================================== */

.planning-picker-week-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}

.planning-picker-week-nav .planning-picker-week-label {
  flex: 1;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}

.planning-picker-week-nav-btn {
  /* Hérite de .recipe-action-icon ; juste affiner le rendu visuel
     pour éviter qu'il ressemble à une action danger ou success. */
  color: var(--text);
  border-color: var(--border);
  background: var(--panel-sunken);
}

@media (hover: hover) {
  .planning-picker-week-nav-btn:hover,
  .planning-picker-week-nav-btn:focus-visible {
    background: color-mix(in srgb, var(--primary) 10%, var(--panel-sunken));
    border-color: var(--primary);
    color: var(--primary);
  }
}

/* Le picker historique utilisait des <label> autour de <input radio>.
   Vague 7.9 : `data-planning-picker-day` est un <button>, donc on
   redéfinit les états ici (le sélecteur :has(radio:checked) ne
   s'applique plus). */
.planning-picker-day-chip[data-planning-picker-day] {
  border: 1px solid var(--border);
  background: var(--panel-sunken);
  color: var(--text);
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}

.planning-picker-day-chip[data-planning-picker-day].is-selected {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
  color: var(--primary);
}

@media (hover: hover) {
  .planning-picker-day-chip[data-planning-picker-day]:hover {
    border-color: color-mix(in srgb, var(--primary) 40%, var(--border));
  }
}


/* ================================================================== */
/* Vague 7.9 — Boost visuel de `is-planned` sur /recipes pour aligner  */
/* sur le rendu /menu (signalé en revue UX : « l'icône de planification*/
/* dans la liste des recettes doit être comme l'icône sur le menu     */
/* quand il y a planification, avec la petite shadow »). On garde le   */
/* drop-shadow déjà défini ligne ~6207 ET on renforce le contraste via */
/* un fond + une bordure primary plus marqués (pour que l'état soit    */
/* lisible même sur l'icône-seule 44×44 sans label texte).             */
/* ================================================================== */

.recipe-action-plan-is-planned {
  /* Réécrit les valeurs ligne ~6201 pour un rendu plus appuyé,
     comparable au bouton /menu (qui est plus large donc l'état "fond
     corail" est très visible). */
  background: color-mix(in srgb, var(--primary) 22%, var(--panel-sunken));
  border-color: var(--primary);
  color: var(--primary);
}

.recipe-action-plan-is-planned .ico {
  /* Drop-shadow légèrement plus marqué qu'avant (0.8px/40% au lieu de
     0.6px/30%) pour que l'effet de glow soit perceptible sur la petite
     icône 21×21 de la rangée d'actions /recipes. Reste discret — pas
     un « flou bizarre » comme l'ancienne version 1px/60%. */
  filter: drop-shadow(0 0 0.8px color-mix(in srgb, var(--primary) 40%, transparent));
}


/* ================================================================== */
/* Vague 7.9 — Listes de courses (index) : carte mobile retravaillée.  */
/*  - Nom de la liste cliquable (devient le lien d'ouverture, plus     */
/*    besoin du bouton "Ouvrir").                                      */
/*  - Bouton "Supprimer" remplacé par une poubelle rouge classique     */
/*    (cohérent avec le pattern du reste de l'app).                    */
/*  - Sur tel, la poubelle est placée sous le badge "Édition/Courses". */
/* ================================================================== */

.shopping-list-title-link {
  /* Le <a> qui enveloppe le titre — large surface de clic, pas de
     soulignement décoratif (le bloc fait office de bouton). */
  display: inline-block;
  color: inherit;
  text-decoration: none;
}

.shopping-list-title-link:hover .shopping-list-title,
.shopping-list-title-link:focus-visible .shopping-list-title {
  color: var(--primary);
}

.shopping-list-title-link:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 4px;
  border-radius: 4px;
}

.shopping-list-delete-form {
  margin: 0;
}


/* ================================================================== */
/* Vague 7.9 — Desktop : labels texte à côté des icônes sur les pages  */
/* qui n'en avaient que des icônes (gain de clarté sur grand écran,    */
/* le mobile reste icon-only pour économiser la place — cf revue UX :  */
/* « sur ordinateur on peut afficher du texte en plus des icônes »).   */
/* Pattern : `.recipe-action-icon-label` est `display:none` par défaut */
/* et bascule à `inline` au-dessus du breakpoint MD (>= 760px).        */
/* ================================================================== */

.recipe-action-icon-label {
  display: none;
}

@media (min-width: 760px) {
  /* On élargit `.recipe-action-icon` pour qu'il accommode le texte —
     plus carré, devient pill horizontal. Le label suit le `.ico`
     avec un gap de 6px. */
  .recipe-action-icon:has(.recipe-action-icon-label) {
    width: auto;
    min-width: 44px;
    padding: 0 12px;
    gap: 6px;
  }
  .recipe-action-icon-label {
    display: inline;
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
  }
}


/* ================================================================== */
/* Vague 7.9 — Body scroll-lock quand une dialog modale est ouverte.   */
/* `<dialog>.showModal()` empêche le scroll au clavier mais sur iOS    */
/* Safari & certains Android, un scroll touch peut passer au body de  */
/* la page derrière. Le `:has()` natif modernise ça côté CSS pur.     */
/* ================================================================== */

html:has(dialog[open]),
body:has(dialog[open]) {
  overflow: hidden;
}

/* Même verrou pour le bottom-sheet « Réglages » (un <details>, pas un
   <dialog> : la règle ci-dessus ne le couvre pas). Sinon on pouvait
   scroller la page derrière le menu ouvert — incohérent avec les dialogs. */
html:has(.appbar-menu[open]),
body:has(.appbar-menu[open]) {
  overflow: hidden;
}

/* ================================================================== */
/* Modale de confirmation maison (.confirm-dialog) — remplace le       */
/* window.confirm() natif (popup système hors thème). Construite en JS  */
/* (cf static/js/confirm_dialog.js), stylée comme les autres dialogs.   */
/* ================================================================== */
.confirm-dialog {
  width: min(420px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--panel);
  color: var(--text);
  box-shadow: 0 24px 60px var(--shadow-color, rgba(0, 0, 0, 0.45));
}

.confirm-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

/* Le conteneur reçoit le focus à l'ouverture (cf confirm_dialog.js) : pas
   d'outline dessus (sinon halo « autour » de la modale / d'Annuler). */
.confirm-dialog:focus,
.confirm-dialog:focus-visible {
  outline: none;
}

.confirm-dialog-body {
  padding: 22px 22px 6px;
}

.confirm-dialog-title {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 600;
}

.confirm-dialog-message {
  margin: 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.5;
}

.confirm-dialog-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  padding: 18px 22px 20px;
}

.confirm-dialog-actions .btn {
  min-width: 104px;
  justify-content: center;
}

@media (prefers-reduced-motion: no-preference) {
  .confirm-dialog[open] {
    animation: confirm-dialog-pop 0.16s ease-out;
  }
  @keyframes confirm-dialog-pop {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to { opacity: 1; transform: none; }
  }
}


/* ================================================================== */
/* Vague 7.9 — Desktop : searchbar /recipes — réafficher les icônes à  */
/* gauche des labels (signalé en revue UX : « rajouter les icônes de   */
/* tel à gauche des textes 'rechercher' 'effacer' etc. »), et ne plus  */
/* ancrer les boutons à droite (« sur ordinateur il faut arrêter       */
/* d'ancrer les boutons à droite »). Le pattern .btn (display:         */
/* inline-flex; gap: 6px) gère naturellement icône + label côte à      */
/* côte une fois `.search-icon` ré-affichée.                           */
/* ================================================================== */

@media (min-width: 701px) {
  /* On override le `display: none` desktop par défaut (cf bloc top du
     fichier ligne ~479) pour réafficher les icônes à côté du label. */
  .search-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Le label reste visible (default desktop) — on réaffirme juste pour
     la lisibilité du bloc. */
  .search-label {
    display: inline;
  }

  /* Boutons d'actions de la searchbar : alignement à gauche (au plus
     proche du champ de recherche) plutôt qu'à droite. Plus intuitif
     sur grand écran (signalé revue UX). */
  .search-actions {
    justify-content: flex-start;
  }
}


/* ================================================================== */
/* Vague 7.9 — Overlay plein écran « page figée » pendant l'unlink     */
/* d'une recette d'une liste de courses. Le mini spinner inline de la  */
/* 1re itération n'était pas assez visible (signalé revue UX : « le    */
/* carousel ne marche pas ») et le DOM swap n'actualisait pas le mode  */
/* édition (`.shopping-categories` n'existe qu'en mode locked). On     */
/* fige donc la page min 1.5 s avec un gros carousel central pendant   */
/* qu'on POST + reload — le backend a le temps de traiter et le        */
/* feedback est sans ambiguïté.                                        */
/* ================================================================== */

.shopping-freeze-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--panel) 70%, rgba(0, 0, 0, 0.55));
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: ribouns-freeze-fade-in 180ms ease-out both;
}

.shopping-freeze-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 28px 36px;
  border-radius: 18px;
  background: var(--panel);
  border: 1px solid var(--border);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
  min-width: 220px;
  max-width: min(360px, calc(100vw - 32px));
}

/* Vague 7.10 — Le carousel orbit hérite des styles de .recipe-import-
   loader-orbit (cf bloc « Loader animé d'import recette ») dès qu'on
   clone le <template>. Pas de surcharge nécessaire — les icônes
   tournent en rond, taille fixe 108×108. On centre juste dans la carte. */

.shopping-freeze-spinner {
  width: 56px;
  height: 56px;
  border: 5px solid color-mix(in srgb, var(--primary) 25%, transparent);
  border-top-color: var(--primary);
  border-radius: 50%;
  /* Réutilise `btn-spin` (ligne ~6360) — keyframe déjà définie, on
     évite d'en dupliquer une autre. */
  animation: btn-spin 0.85s linear infinite;
}

.shopping-freeze-label {
  text-align: center;
  font-size: 14px;
  color: var(--text);
  font-weight: 500;
}

@keyframes ribouns-freeze-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}


/* ================================================================== */
/* Vague 7.10 — /recipes filtres (Durée + Type de plat).               */
/* L'ancien fork /menu/manual a été retiré (signalé revue UX : « les   */
/* popups étaient archi dégueu »). Nouveau design : bouton « Filtres » */
/* dans la searchbar (badge primary si actif) qui ouvre une dialog     */
/* .export-dialog (cohérence avec les autres avant-plans de l'app),    */
/* avec des chips bien espacés, header titre, footer Reset/Appliquer.  */
/* ================================================================== */

/* Bouton Filtres : badge primary affichant le nombre de filtres actifs. */
.recipes-filters-btn.is-active {
  color: var(--primary);
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
}

.recipes-filters-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 4px;
  border-radius: 999px;
  background: var(--primary);
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
  vertical-align: middle;
}

/* À 0 filtre actif, le badge est marqué [hidden] côté JS. Sans ça, le
   display:inline-flex ci-dessus écraserait le display:none de [hidden] et
   laisserait un badge vide (« boule orange ») ou un « 0 » résiduel. */
.recipes-filters-count[hidden],
.planning-assign-filters-count[hidden] {
  display: none;
}

/* Le dialog hérite de .export-dialog (sizing/padding uniformes). On
   pose juste les chips et la mise en page sections. */
.recipes-filters-dialog .export-dialog-body {
  /* Plus d'espacement entre sections (Durée vs Type de plat) pour la
     lisibilité — pattern « card resserrée » des autres dialogs ne
     convient pas ici, on veut respirer. */
  gap: 18px;
  /* Les chips wrappent déjà ; on coupe tout micro-scroll horizontal parasite
     (sous-pixel / ombres) qui donnait un petit « mouvement latéral ». */
  overflow-x: hidden;
}

.recipes-filters-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.recipes-filters-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.recipes-filters-section-title .ico {
  width: 18px;
  height: 18px;
  color: var(--muted);
}

.recipes-filters-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.recipes-filters-chip {
  display: inline-flex;
  align-items: center;
  position: relative;
  padding: 9px 14px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  user-select: none;
  font-size: 14px;
  color: var(--text);
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
  min-height: 38px;
}

.recipes-filters-chip input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.recipes-filters-chip-label {
  position: relative;
  z-index: 1;
}

.recipes-filters-chip:has(input[type="checkbox"]:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: #ffffff;
  font-weight: 600;
}

@media (hover: hover) {
  .recipes-filters-chip:hover {
    border-color: color-mix(in srgb, var(--primary) 50%, var(--border));
  }
  .recipes-filters-chip:has(input[type="checkbox"]:checked):hover {
    background: color-mix(in srgb, var(--primary) 88%, #000);
  }
}

/* Footer du dialog filtres : Apply à gauche (action principale), Reset
   à droite (action secondaire). Signalé revue UX : « Appli à gauche
   et reset à droite ». L'ordre dans le DOM reste Reset-puis-Apply (tab
   order naturel), on inverse uniquement à l'affichage via flex-row-
   reverse. */
.recipes-filters-footer {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-direction: row-reverse;
  /* Vague 9.5+++ — Bug fix : `.export-dialog-footer` parent a
     `flex-wrap: wrap`. On force nowrap pour garder les 2 boutons côte
     à côte. */
  flex-wrap: nowrap;
}

.recipes-filters-reset-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  /* Match la hauteur du bouton Apply (`.export-dialog-submit-floating
     { min-height: 46px }`) pour que les 2 boutons soient alignés
     visuellement, et pas Reset rikiki à côté d'un Apply 2× plus haut
     (signalé revue UX : « le bouton reset est encore tout petit,
     il fait 2px de haut »). */
  min-height: 46px;
}

.recipes-filters-footer .export-dialog-submit-floating {
  flex: 1 1 auto;
  /* Override le `width: 100%` du pattern de base (qui forçait le wrap
     dans le footer 2-boutons). */
  width: auto;
}

@media (max-width: 480px) {
  /* Sur tel : reset devient compact (icône seule), apply prend la place. */
  .recipes-filters-reset-btn span:not([aria-hidden]) {
    display: none;
  }
  .recipes-filters-reset-btn {
    /* Carré 46×46 pour matcher la hauteur du bouton Apply
       (.export-dialog-submit-floating min-height: 46px). */
    width: 46px;
    min-width: 46px;
    min-height: 46px;
    padding: 0;
    justify-content: center;
  }
}

/* Vague 9.5+++ — Réduction du padding de la dialog filtres pour gagner
   ~16-20 px verticaux (signalé revue UX : « on peut gagner de la place
   en haut et en bas si jamais sur le padding »). Ciblé uniquement sur
   `.recipes-filters-dialog` — les autres `.export-dialog` (assign,
   shopping, etc.) gardent leur padding standard. */
.recipes-filters-dialog .export-dialog-header {
  padding: 8px 12px;
}

.recipes-filters-dialog .export-dialog-body {
  padding: 8px 12px;
  gap: 14px;
}

.recipes-filters-dialog .export-dialog-footer-floating {
  padding: 8px 12px;
}

.recipes-filters-dialog .recipes-filters-section {
  gap: 8px;
}

/* Vague 9.5+++ — Override mobile : le pattern par défaut
   `.export-dialog-footer` à <= 540 px passe en `flex-direction:
   column-reverse` + `.export-dialog-footer .btn { width: 100% }` (le
   submitter prend toute la largeur, Reset wrap en dessous). C'est
   bien pour les dialogs 1-bouton, mais pour le footer 2-boutons
   filtres on veut Reset + Apply côte à côte aussi sur tel, avec
   Apply à GAUCHE et Reset à droite. Signalé revue UX deux fois :
   « le bouton reset est côte à côte sur ordi mais pas sur tel » puis
   « Appli à gauche et reset à droite ». */
@media (max-width: 540px) {
  .recipes-filters-footer.export-dialog-footer {
    /* row-reverse comme sur desktop → Apply à gauche, Reset à droite.
       (Avant : `flex-direction: row` qui annulait le row-reverse parent.) */
    flex-direction: row-reverse;
    align-items: center;
    flex-wrap: nowrap;
  }
  .recipes-filters-footer.export-dialog-footer .btn {
    width: auto;
  }
  /* Reset reste compact (icône seule, 44 px) — déjà géré par le bloc
     @media (max-width: 480px) plus haut. */
}


/* ================================================================== */
/* View Transitions (transitions de page) — DÉSACTIVÉES.               */
/*                                                                     */
/* Historique : la « Vague 7.10 » avait ajouté l'API View Transitions  */
/* (fondu entre pages, tâche #49). C'est une source RÉCURRENTE de bugs */
/* sur iOS/Safari dès qu'une barre `position: fixed`/`sticky` (top bar,*/
/* bottom-nav) est impliquée : pendant la navigation, la barre est     */
/* soit DÉPLACÉE (lift vertical / « dart » horizontal via le morph du  */
/* ::view-transition-group), soit ESTOMPÉE (cross-fade). Aucun réglage */
/* (animation:none, isolation par view-transition-name, opacité seule) */
/* ne la stabilise de façon fiable sur Safari.                         */
/*                                                                     */
/* Décision : on RETIRE complètement les View Transitions. La          */
/* navigation redevient instantanée (comportement web classique) → la  */
/* nav ne peut plus bouger d'un pixel. NE PAS réintroduire             */
/* `@view-transition` tant que la nav contient des éléments            */
/* fixed/sticky. Cf CONSIGNES « Pièges connus » (ligne View            */
/* Transitions).                                                       */
/* ================================================================== */



/* ================================================================== */
/* Vague 7.10 — Animation de départ/arrivée d'une ligne shopping     */
/* quand on coche / décoche un article en mode locked (vue courses). */
/* Signalé revue UX : « l'ingrédient disparaît un peu violemment ».  */
/* Le pattern combine 3 phases :                                     */
/*   1. is-just-checked (existant) : pop de la case + strike du nom  */
/*   2. is-leaving (nouveau) : la ligne glisse de ~24px vers le bas  */
/*      et fade out + shrink léger pendant 320 ms                    */
/*   3. is-arriving (nouveau) : à l'arrivée dans le bloc « Articles  */
/*      cochés » (ou retour au magasin si décoche), petit fade-in    */
/*      de 220 ms pour signaler que la ligne a atterri               */
/* Coupé sous prefers-reduced-motion (le user qui demande à réduire  */
/* le mouvement aura le DOM swap instantané, comme avant 7.10).      */
/* ================================================================== */

@media (prefers-reduced-motion: no-preference) {
  body.page-shopping-list-detail .shopping-row.is-leaving {
    animation: shopping-row-leave 320ms cubic-bezier(0.4, 0, 0.2, 1) both;
    pointer-events: none;
  }

  body.page-shopping-list-detail .shopping-row.is-arriving {
    animation: shopping-row-arrive 220ms cubic-bezier(0, 0, 0.2, 1) both;
  }
}

@keyframes shopping-row-leave {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  40% {
    opacity: 0.6;
  }
  100% {
    opacity: 0;
    transform: translateY(24px) scale(0.96);
  }
}

@keyframes shopping-row-arrive {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}


/* ================================================================== */
/* Vague 9.5+ — Picker recette enrichi dans le dialog d'assignation    */
/* planning (search + filtres durée/type, façon /recipes). Remplace    */
/* l'ancien <select> tout simple qui devenait inutilisable dès 30+    */
/* recettes. Filtrage 100% client via menu_planning_assign_dialog.js. */
/* ================================================================== */

/* Vague 9.5+++ — Bug fix containment fieldset : avant, le <fieldset>
   avait `display: flex; flex-direction: column` directement, et la
   liste avec `max-height: 38vh` débordait visuellement sous le bord
   inférieur du fieldset (les fieldsets en flex ont des bugs de
   containment connus dans tous les browsers). Maintenant le fieldset
   reste un `block` normal et c'est `.planning-assign-recipe-content`
   (wrapper interne dans le template) qui gère le flex column. */
.planning-assign-recipe-picker {
  display: block;
  /* Override `min-width: min-content` par défaut des fieldsets, qui
     empêche le fieldset de shrink correctement sur petit écran. */
  min-width: 0;
}

/* Wrapper interne (barre d'outils + liste). Ciblé en ENFANT DIRECT du
   fieldset picker : la classe `.planning-assign-recipe-content` est
   réutilisée plus bas sur le <span> du nom de recette (gap: 2px). Sans le
   combinateur `>`, la règle du <span> l'emportait (même spécificité, plus
   bas dans le fichier) et rabaissait ce gap à 2px — la liste se collait
   alors à la barre de recherche. */
.planning-assign-recipe-picker > .planning-assign-recipe-content {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
}

/* Espacement des deux fieldsets DANS cette dialog uniquement : le
   `margin-top: 14px` générique de `.export-*-fieldset` (partagé avec les
   autres dialogs d'export) s'additionnait au `gap: 10px` du body → ~24px
   au-dessus du 1er bloc « Moment du repas » et entre les deux blocs, trop
   aéré ici. On retombe sur le seul gap du body (10px). */
#planning-assign-dialog .export-mode-fieldset,
#planning-assign-dialog .export-target-fieldset {
  margin-top: 0;
}

/* Vague 9.5++ — Toolbar refactor : layout horizontal (search prend
   l'espace restant, bouton Filtres à droite). Sur tel le label du
   bouton se cache pour laisser le bouton en icône seule. Pattern
   identique à la searchbar de /recipes. */
.planning-assign-recipe-tools {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

.planning-assign-recipe-tools .planning-assign-recipe-search-field {
  flex: 1 1 auto;
  min-width: 0;
  /* Ce champ est un <label> → il héritait du `margin-bottom: 6px` global des
     labels, ce qui le décalait dans la rangée flex (bouton Filtres qui paraît
     plus bas). On l'annule. */
  margin: 0;
}

.planning-assign-filters-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  /* Même hauteur que le champ de recherche à sa gauche → alignés (avant, le
     bouton dépassait vers le haut). */
  height: 44px;
}

.planning-assign-filters-btn.is-active {
  color: var(--primary);
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
}

.planning-assign-filters-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--primary);
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
}

@media (max-width: 480px) {
  .planning-assign-filters-btn-label {
    display: none;
  }
  .planning-assign-filters-btn {
    width: 44px;
    min-width: 44px;
    padding: 0;
    justify-content: center;
  }
}

/* Champ de recherche : input + icône à gauche, pattern réutilisé de
   .menu-manual-search-field (cf bloc plus haut). */
.planning-assign-recipe-search-field {
  position: relative;
  display: flex;
  align-items: center;
}

.planning-assign-recipe-search-icon {
  position: absolute;
  left: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  pointer-events: none;
}

.planning-assign-recipe-search-icon .ico {
  width: 16px;
  height: 16px;
}

.planning-assign-recipe-search-input {
  width: 100%;
  height: 44px;
  padding: 10px 12px 10px 34px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14px;
  color: var(--text);
}

.planning-assign-recipe-search-input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--field-focus-ring);
}

/* Vague 9.5++ — Les anciens <details> inline filter blocks
   `.planning-assign-recipe-filters`, `.planning-assign-filter-*` ont
   été retirés. Les filtres durée+type vivent désormais dans une dialog
   dédiée `#planning-assign-filters-dialog` qui réutilise les classes
   `.recipes-filters-section`, `.recipes-filters-chips`,
   `.recipes-filters-chip` du picker /recipes. Cohérence visuelle et
   pas de débordement sur tel.

   Liste des recettes : scrollable, radio sélection unique. */
.planning-assign-recipe-list {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1 1 auto;
  min-height: 80px;
  max-height: 38vh;
  overflow-y: auto;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.planning-assign-recipe-item {
  border-bottom: 1px solid var(--border);
}

.planning-assign-recipe-item:last-child {
  border-bottom: none;
}

.planning-assign-recipe-item[hidden] {
  display: none;
}

.planning-assign-recipe-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  cursor: pointer;
  min-height: 44px;
  transition: background 120ms ease;
}

.planning-assign-recipe-row input[type="radio"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  accent-color: var(--primary);
  cursor: pointer;
}

.planning-assign-recipe-content {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.planning-assign-recipe-name {
  font-size: 14px;
  color: var(--text);
  font-weight: 500;
  word-break: break-word;
}

.planning-assign-recipe-meta {
  font-size: 12px;
  color: var(--muted);
}

@media (hover: hover) {
  .planning-assign-recipe-row:hover {
    background: var(--hover-bg);
  }
}

/* Row sélectionnée : fond corail discret pour confirmer le choix. */
.planning-assign-recipe-item:has(input[type="radio"]:checked) .planning-assign-recipe-row {
  background: color-mix(in srgb, var(--primary) 14%, var(--panel-sunken));
}

.planning-assign-recipe-empty,
.planning-assign-recipe-no-match {
  margin: 12px 0 0;
  padding: 12px;
  text-align: center;
  font-size: 13px;
  background: var(--panel-sunken);
  border: 1px dashed var(--border);
  border-radius: 10px;
}

@media (max-width: 480px) {
  .planning-assign-recipe-list {
    max-height: 32vh;
  }
}

/* /menu/ingredients — rows en lecture seule (décision 2026-06 : plus de
   sélection par ingrédient, on importe tout). Le label n'est plus un
   contrôle cliquable → on neutralise curseur main + survol. */
.menu-ingredient-label-static {
  cursor: default;
}
.menu-ingredient-label-static:hover {
  background: transparent;
}

/* ============================================================== */
/* Form recette — éditeur de consignes en blocs (1 étape = 1 bloc) */
/* Chaque étape : numéro à gauche + zone de texte editable + bouton */
/* supprimer. Un « + » discret entre deux blocs insère une étape.   */
/* Cf static/js/recipe_steps_editor.js.                             */
/* ============================================================== */

.steps-editor {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 4px;
}

.step-block {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.step-block:focus-within {
  border-color: var(--primary);
  box-shadow: var(--field-focus-ring);
}

/* Poignée de glisser-déposer (réordonner les étapes). Étendue sur toute la
   hauteur du bloc → grande cible tactile. touch-action:none pour que le
   geste de drag ne déclenche pas le scroll de la page sur mobile. */
.step-block-handle {
  flex: 0 0 auto;
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  margin: -8px 0 -8px -4px;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  border-radius: 8px;
  cursor: grab;
  touch-action: none;
  transition: color 0.15s ease, background 0.15s ease;
}
.step-block-handle:active {
  cursor: grabbing;
}
.step-block-handle .ico {
  width: 18px;
  height: 18px;
}
@media (hover: hover) {
  .step-block-handle:hover {
    color: var(--primary);
    background: var(--hover-bg);
  }
}

/* Bloc en cours de glissement : surélevé. Les rangées « + » sont masquées
   pendant le réordonnancement pour un déplacement fluide bloc-à-bloc. */
.step-block.is-dragging {
  position: relative;
  z-index: 2;
  border-color: var(--primary);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
  opacity: 0.97;
}
.steps-editor.is-reordering {
  cursor: grabbing;
}
.steps-editor.is-reordering .step-insert {
  display: none;
}

/* Numéro d'étape — pastille ronde primary, alignée en haut du bloc. */
.step-block-num {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  margin-top: 2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 16%, transparent);
  color: var(--primary);
  font-weight: 700;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  user-select: none;
}

.step-block-text {
  flex: 1 1 auto;
  min-height: 28px;
  padding: 4px 6px;
  border-radius: 8px;
  line-height: 1.5;
  color: var(--text);
  outline: none;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.step-block-text:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
  pointer-events: none;
}

/* Bouton supprimer l'étape — compact, aligné en haut. */
.step-block-remove {
  flex: 0 0 auto;
  margin-top: 0;
  padding: 6px 8px;
  min-height: 0;
  line-height: 0;
}

/* Rangée « + » entre deux blocs : discrète, se révèle au survol /
   focus de la zone éditeur pour ne pas surcharger visuellement. */
.step-insert {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 14px;
  position: relative;
}

/* Trait + bouton « + » TOUJOURS visibles (desktop ET mobile). Sur tel il
   n'y a pas de survol, donc on n'attend plus le hover pour les révéler —
   ils restent affichés en permanence (couleur sobre au repos, primary au
   survol/focus sur ordi). */
.step-insert::before {
  content: "";
  position: absolute;
  left: 38px;
  right: 12px;
  top: 50%;
  height: 1px;
  background: var(--border);
}

.step-insert-btn {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.step-insert-btn:hover,
.step-insert-btn:focus-visible {
  color: var(--primary);
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, var(--panel));
  outline: none;
}

.step-insert-btn .ico {
  width: 14px;
  height: 14px;
}

/* Bouton « Ajouter une étape » en bas de l'éditeur. */
.steps-add {
  margin-top: 8px;
}

.steps-add-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Mobile : pastille + bouton un peu plus compacts. */
@media (max-width: 480px) {
  .step-block {
    gap: 8px;
    padding: 7px;
  }
  .step-block-num {
    width: 24px;
    height: 24px;
    font-size: 12px;
  }
}

/* --------------------------------------------------------------------------- */
/* Partage de recettes (vague 13) — page d'envoi, boîte de réception, landing. */
/* --------------------------------------------------------------------------- */

/* Lien « Partager cette recette » sous la ligne d'actions de la fiche. */
/* Bouton « Partager » : style hérité de .menu-action (cohérent avec
   Modifier/Courses). On ne garde ici que le layout (marge + sa propre ligne). */
.menu-recipe-share-link {
  margin-top: 12px;
  align-self: flex-start;
}

/* Page « Partager une recette ». */
.share-form {
  margin: 14px 0;
}
.share-label {
  display: block;
  font-weight: 600;
  margin-bottom: 6px;
}
.share-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.share-row input {
  flex: 1 1 auto;
  min-width: 0;
}
.share-row .btn {
  flex: 0 0 auto;
  white-space: nowrap;
}
.share-hint {
  margin-top: 6px;
  font-size: 13px;
}
.share-sep {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0;
  color: var(--muted);
  font-size: 13px;
}
.share-sep::before,
.share-sep::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border);
}
.share-link-box {
  margin-top: 14px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
}

/* Zone « texte brut » : recette lisible en lecture seule. pre-wrap conserve
   les sauts de ligne du texte tout en laissant les longues lignes se
   replier (pas de scroll horizontal sur mobile). */
.share-plain-text {
  width: 100%;
  box-sizing: border-box;
  min-height: 200px;
  resize: vertical;
  margin-top: 6px;
  padding: 12px 14px;
  font: inherit;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.share-text-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.share-text-actions .btn {
  flex: 0 0 auto;
}

/* Boîte de réception « Recettes reçues ». */
.share-inbox {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 14px;
}
.share-inbox-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
}
.share-inbox-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.share-inbox-name {
  font-weight: 700;
}
.share-inbox-from {
  font-size: 13px;
  color: var(--muted);
}
.share-inbox-actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}

/* Landing publique d'un lien de partage. */
.share-landing {
  text-align: center;
}
.share-landing-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0 auto 8px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 15%, transparent);
  color: var(--primary);
}
.share-landing-icon .ico {
  width: 28px;
  height: 28px;
}
.share-landing-recipe {
  font-size: 18px;
  font-weight: 700;
  margin: 4px 0 14px;
}
.share-landing-cta {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Badge compteur + pastille de notification (boîte « Recettes reçues »). */
.share-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: auto;
  border-radius: 9px;
  background: var(--primary);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}
.nav-label .share-badge {
  margin-left: 4px;
}
.appbar-menu-summary,
.nav-more-summary {
  position: relative;
}
.share-dot {
  position: absolute;
  top: 2px;
  right: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 0 0 2px var(--panel-2);
  pointer-events: none;
}

/* --------------------------------------------------------------------------- */
/* Régime & allergies (vague 13.x) — section du form recette.                  */
/* --------------------------------------------------------------------------- */
/* Encadré « assistant » : conteneur (colonne) = ligne SUGGESTION + note info. */
.recipe-diet-suggest {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border: 1px solid color-mix(in srgb, var(--primary) 40%, var(--border));
  border-radius: 12px;
  background: color-mix(in srgb, var(--primary) 8%, transparent);
}
/* Ligne SUGGESTION (verte, actionnable) — masquée après « Appliquer » / si la
   recette est déjà taguée. La note info, elle, reste. */
.recipe-diet-suggest-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.recipe-diet-suggest-main.is-applied {
  display: none;
}
/* Quand la ligne suggestion est masquée, le conteneur ne garde plus qu'une
   note : on retire alors le fond/bordure corail (info neutre, pas d'action). */
.recipe-diet-suggest:not(:has(.recipe-diet-suggest-main:not(.is-applied))) {
  border-color: var(--border);
  background: var(--panel-2);
}
.recipe-diet-suggest-text {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
}
.recipe-diet-suggest-text .ico {
  width: 16px;
  height: 16px;
  color: var(--primary);
}
/* Note info secondaire (hors catalogue / à préciser) : discrète, icône info. */
.recipe-diet-suggest-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}
.recipe-diet-suggest-note .ico {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  margin-top: 1px;
  color: var(--muted);
}

.recipe-diet-group {
  border: none;
  margin: 0 0 12px;
  padding: 0;
  min-width: 0;
}
.recipe-diet-group-title {
  padding: 0;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.recipe-diet-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.recipe-diet-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel-2);
  cursor: pointer;
  font-size: 14px;
  user-select: none;
  transition: border-color .12s, background .12s;
}
/* Annule le `input { width:100% }` global (sinon la case déborde le chip). */
.recipe-diet-chip input {
  width: auto;
  margin: 0;
  accent-color: var(--primary);
}
.recipe-diet-chip:has(input:checked) {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  font-weight: 600;
}
.recipe-diet-hint {
  margin-top: 4px;
  font-size: 13px;
}

/* Dossiers (form recette) : réutilise les chips régime ; le champ « nouveau
   dossier » est juste espacé sous les chips. */
.recipe-folders-empty {
  margin: 0 0 10px;
}
.recipe-folders-new {
  margin-top: 12px;
}
.recipe-folders-new label {
  display: block;
  margin-bottom: 6px;
  font-weight: 600;
  font-size: 14px;
}

/* Écran de gestion des dossiers (/recipes/folders). */
.folder-create-form label {
  display: block;
  margin-bottom: 6px;
  font-weight: 600;
}
.folder-create-row {
  display: flex;
  gap: 10px;
}
.folder-create-row input {
  flex: 1 1 auto;
  min-width: 0;
}
.folder-create-row .btn {
  flex: 0 0 auto;
}
.folder-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.folder-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.folder-rename-form {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.folder-rename-form input {
  flex: 1 1 auto;
  min-width: 0;
}
.folder-rename-save {
  flex: 0 0 auto;
}
.folder-row-count {
  flex: 0 0 auto;
  font-size: 13px;
  white-space: nowrap;
}
.folder-delete-form {
  flex: 0 0 auto;
  margin: 0;
}

/* Filtres /recipes — accordéon (sections repliables + compteur d'actifs). */
.recipes-filters-section-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  padding: 2px 0;
}
.recipes-filters-section-summary::-webkit-details-marker {
  display: none;
}
.recipes-filters-section-summary .recipes-filters-section-title {
  flex: 1 1 auto;
  min-width: 0;
}
.recipes-filters-section-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: 9px;
  background: var(--primary);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}
.recipes-filters-section-chevron {
  display: inline-flex;
  color: var(--muted);
  transition: transform .15s ease;
}
.recipes-filters-section-chevron .ico {
  width: 18px;
  height: 18px;
}
.recipes-filters-section[open] .recipes-filters-section-chevron {
  transform: rotate(180deg);
}

/* Badges régime/allergies sur la fiche recette (vert = compatible). */
.menu-recipe-diet-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.menu-recipe-diet-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text);
  background: var(--panel-sunken);
  border: 1px solid var(--border);
}
.menu-recipe-diet-badge .ico {
  width: 13px;
  height: 13px;
  color: var(--muted);
}

/* Bloc régime/allergies de la fiche : CARTE pleine largeur (même habillage que
   la carte nutrition juste en dessous), repliée par défaut. */
.menu-recipe-diet {
  margin-bottom: 14px;
  padding: 12px 14px;
  background: var(--panel-sunken);
  border: 1px solid var(--border);
  border-radius: 14px;
}
.menu-recipe-diet-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  cursor: pointer;
  list-style: none;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  -webkit-tap-highlight-color: transparent;
}
.menu-recipe-diet-summary::-webkit-details-marker {
  display: none;
}
.menu-recipe-diet-summary .ico {
  width: 16px;
  height: 16px;
  color: var(--muted);
}
.menu-recipe-diet-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  color: var(--text);
  font-size: 11px;
  font-weight: 600;
}
.menu-recipe-diet-chevron {
  margin-left: auto; /* poussé à droite, comme un en-tête de section */
  color: var(--muted);
  transition: transform .15s ease;
}
.menu-recipe-diet-chevron .ico {
  width: 18px;
  height: 18px;
}
.menu-recipe-diet[open] .menu-recipe-diet-chevron {
  transform: rotate(180deg);
}

/* « Recettes liées » — habillage unifié (opt-in via .menu-recipe-linked) :
   fiche recette, édition ET détail d'une liste de courses. Aligné sur les cartes
   régime / nutrition (fond sunken, coin 14, compteur neutre, icône muted). Les
   éléments .shopping-linked-recipes SANS cette classe gardent le style de base. */
.menu-recipe-linked {
  background: var(--panel-sunken);
  border-radius: 14px;
}
.menu-recipe-linked .shopping-linked-recipes-label {
  font-size: 14px;
}
.menu-recipe-linked .shopping-linked-recipes-label .ico {
  color: var(--muted);
}
.menu-recipe-linked .shopping-linked-recipes-count {
  min-width: 18px;
  height: 18px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  color: var(--text);
  font-size: 11px;
}

/* #8 — la dialog filtres /recipes n'a pas de wrapper .export-dialog-form :
   on en fait directement un conteneur flex-column pour que .export-dialog-body
   (flex:1 + overflow-y:auto) devienne la zone scrollable quand l'accordéon est
   déplié (sinon le contenu débordait, clippé par overflow:hidden).
   ⚠️ Scopé à [open] : sinon `display:flex` écrase le `display:none` natif d'un
   <dialog> fermé → la dialog s'afficherait en plein milieu de la page. */
.recipes-filters-dialog[open] {
  display: flex;
  flex-direction: column;
}

/* Animation d'entrée d'un panneau de recette (Ingrédients/Consignes) : swipe
   = glissement dans le sens du doigt, tap = fondu. Coupée si l'utilisateur
   préfère moins d'animations. */
@media (prefers-reduced-motion: no-preference) {
  [data-recipe-pane].is-pane-enter-next { animation: recipe-pane-from-right .22s ease; }
  [data-recipe-pane].is-pane-enter-prev { animation: recipe-pane-from-left .22s ease; }
  [data-recipe-pane].is-pane-enter-fade { animation: recipe-pane-fade .16s ease; }
}
@keyframes recipe-pane-from-right {
  from { opacity: 0; transform: translateX(22px); }
  to { opacity: 1; transform: none; }
}
@keyframes recipe-pane-from-left {
  from { opacity: 0; transform: translateX(-22px); }
  to { opacity: 1; transform: none; }
}
@keyframes recipe-pane-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ================================================================= */
/* Champs de saisie — FOCUS UNIFIÉ (bordure + halo corail « hollow »).*/
/* Un seul style partout : fini l'outline noir du navigateur et les   */
/* halos incohérents (Nom/Type/Durée, recherches, imports, etc.).     */
/* `--field-focus-ring` = le halo partagé (réutilisé par les wrappers  */
/* de recherche via :focus-within — cf .menu-manual-search-field,     */
/* .ingredient-add-bar…). Un seul endroit à changer.                  */
/* ================================================================= */
:root {
  --field-focus-ring: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
input:not([type="checkbox"]):not([type="radio"]):focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--field-focus-ring);
}
/* Inputs SANS bordure à l'intérieur d'un wrapper : le halo est déjà porté
   par le wrapper (:focus-within) → on retire celui de l'input (pas de double). */
.menu-manual-search-input:focus,
.ingredient-add-bar input:focus {
  box-shadow: none;
}

/* iOS Safari AUTO-ZOOME au focus d'un champ dont le `font-size` est < 16px
   (pour rendre le texte « lisible ») → petit zoom parasite au tap, très visible.
   On force 16px sur les appareils TACTILES (pointer: coarse) pour TOUS les
   champs de saisie → plus de zoom. Le desktop (pointer fin) garde sa densité.
   Écrase les tailles ad-hoc (base 15px, searchbar 14px, add-bar 15px…) via la
   spécificité (`input:not(...)` > `.classe input`). */
@media (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([disabled]),
  textarea:not([disabled]),
  select:not([disabled]) {
    font-size: 16px;
  }
}


/* ============================================================== */
/* Mode IMMERSION — fiche recette + liste de courses.             */
/* Plein écran : on masque la barre du haut (mobile ET desktop)   */
/* ET la nav du bas. Chaque page garde son bouton retour flottant */
/* (.menu-recipe-back-fab) → toujours un chemin de sortie.        */
/* L'écran reste allumé (wakeLock, immersive.js).                 */
/* ============================================================== */
body.is-immersive .topbar,
body.is-immersive .mobile-topbar,
body.is-immersive .bottom-nav {
  display: none;
}

/* Nav du bas masquée → les FAB flottants (verrou liste de courses) se
   ré-ancrent au bas réel : ils s'appuient sur var(--bottom-nav-h). */
body.is-immersive {
  --bottom-nav-h: 0px;
}

/* Fiche recette : aucun FAB en bas → on récupère l'espace jadis réservé à la
   nav, et on respecte l'encoche en haut (plus de barre pour la gérer). */
body.page-menu-recipe {
  padding-bottom: calc(env(safe-area-inset-bottom, 0) + 16px);
  /* Barre du haut masquée → le bouton retour flottant n'a plus à se décaler
     SOUS elle : on le remonte juste sous l'encoche (base = +64px pour la
     topbar, ici +12px). */
  --float-back-top: calc(env(safe-area-inset-top, 0px) + 12px);
}

body.page-menu-recipe .container {
  padding-top: calc(14px + env(safe-area-inset-top, 0));
}

/* --- Bandeau flottant du haut (bouton retour + nom au scroll) ------------- */
/* Rangée fixée en haut : bouton retour rond à gauche, nom du plat à droite. Le
   conteneur est en pointer-events:none → les taps « à vide » traversent vers la
   photo / le contenu dessous ; seul le bouton retour (interactif) reçoit les
   clics. Le nom n'est révélé qu'au scroll (.is-revealed, cf menu_recipe.js). */
body.page-menu-recipe .menu-recipe-float-top {
  position: fixed;
  top: var(--float-back-top);
  left: 0;
  right: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 14px;
  pointer-events: none;
}
@media (min-width: 701px) {
  body.page-menu-recipe .menu-recipe-float-top {
    padding: 0 16px;
  }
}

/* Bouton retour : on neutralise son position:fixed (le bandeau l'est déjà) et
   on lui rend les clics (le conteneur les coupe). */
body.page-menu-recipe .menu-recipe-float-top .menu-recipe-back-fab {
  position: static;
  top: auto;
  left: auto;
  flex: 0 0 auto;
  pointer-events: auto;
}

/* Voile flou plein-largeur DERRIÈRE le bouton retour + le nom : au scroll, le
   haut de l'écran (jusqu'à quelques px au-dessus des onglets) devient du verre
   dépoli → le contenu qui défile dessous est flouté au lieu d'apparaître net
   dans les jours autour des pastilles. Masque dégradé en bas → pas de ligne
   nette, un petit jour reste entre le voile et le bloc ingrédients. Posé en
   z-index:-1 dans le bandeau (donc derrière retour + nom), apparaît avec le nom
   (.is-scrolled). */
body.page-menu-recipe .menu-recipe-float-top::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* Le voile descend JUSQU'AUX onglets (tabs à --float-back-top + 50px) puis
     18px de plus, cachés DERRIÈRE la barre d'onglets (z-index relevé plus bas).
     Le flou reste donc PLEIN jusqu'au bloc onglets — plus de bande de texte
     net « dégueu » entre le voile et les onglets. Le fondu (mask) se joue
     sous les onglets opaques → pas de ligne dure non plus sur les côtés. */
  height: calc(var(--float-back-top) + 50px + 18px);
  z-index: -1;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
  opacity: 0;
  transition: opacity 0.18s ease;
  pointer-events: none;
}
body.page-menu-recipe .menu-recipe-float-top.is-scrolled::before {
  opacity: 1;
}

/* Nom du plat : pastille « verre » (même look que le bouton retour), alignée
   sur lui, masquée par défaut et révélée en fondu quand le titre du hero passe
   sous le bandeau. display:block (pas flex) → text-overflow: ellipsis marche sur
   le nœud texte ; centrage vertical par line-height (contenu = 42px − 2px de
   bordure). Une seule ligne, tronquée « … » si trop long. */
body.page-menu-recipe .menu-recipe-float-title {
  min-width: 0;
  height: 42px;
  display: block;
  padding: 0 15px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  color: var(--text);
  font-weight: 700;
  font-size: 16px;
  line-height: 40px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  pointer-events: none;
}
body.page-menu-recipe .menu-recipe-float-top.is-scrolled .menu-recipe-float-title {
  opacity: 1;
  transform: none;
}

/* Onglets Ingrédients / Consignes : en immersion la topbar est masquée, ils
   remontaient donc tout en haut et le bouton retour flottant les recouvrait. On
   les gare JUSTE SOUS le bandeau retour (hauteur du bouton 42px + petit jeu) →
   plus d'overlap, le bandeau (retour + nom) reste au-dessus. */
body.page-menu-recipe .menu-recipe-tabs {
  top: calc(var(--float-back-top) + 42px + 8px);
  /* Au-dessus du voile flou (z-index:30 du bandeau) pour que le fondu du
     voile passe DERRIÈRE les onglets (fond opaque) et non par-dessus le texte
     des onglets. */
  z-index: 31;
}


/* ============================================================== */
/* Liste de courses — polish visuel (plus « app pro »).           */
/* CSS seul : aucune modif de markup / JS. Overrides de fin de     */
/* fichier → s'appliquent à toutes largeurs.                       */
/* ============================================================== */

/* Case à cocher CUSTOM : carré arrondi, vert plein + coche blanche une fois
   cochée (article coché = fait = succès → vert, cohérent avec la barre de
   progression). Remplace la case native (accent-color). Coche dessinée en CSS
   (pas de data-URI) → compatible CSP. */
body.page-shopping-list-detail .shopping-check,
body.page-shopping-list-detail .shopping-edit-check {
  appearance: none;
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  margin: 1px 0 0;
  flex: 0 0 auto;
  border-radius: 8px;
  border: 2px solid color-mix(in srgb, var(--success) 42%, var(--border));
  background: var(--panel-sunken);
  position: relative;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

body.page-shopping-list-detail .shopping-check:checked,
body.page-shopping-list-detail .shopping-edit-check:checked {
  background: var(--success);
  border-color: var(--success);
}

body.page-shopping-list-detail .shopping-check:checked::after,
body.page-shopping-list-detail .shopping-edit-check:checked::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 3px;
  width: 6px;
  height: 11px;
  border: solid #ffffff;
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg);
}

body.page-shopping-list-detail .shopping-check:focus-visible,
body.page-shopping-list-detail .shopping-edit-check:focus-visible {
  outline: none;
  box-shadow: var(--field-focus-ring);
}

/* Entête magasin : icône du rayon dans une pastille arrondie discrète + le
   compteur en pastille → vrai look « section ». */
body.page-shopping-list-detail .shopping-category-title h2 {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 18px;
  font-weight: 700;
}

body.page-shopping-list-detail .shopping-category-title h2 .ico {
  width: 30px;
  height: 30px;
  padding: 6px;
  box-sizing: border-box;
  border-radius: 9px;
  background: var(--panel-sunken);
  color: var(--muted);
}

body.page-shopping-list-detail .shopping-category-count {
  background: var(--panel-sunken);
  border-radius: 999px;
  padding: 3px 11px;
  font-weight: 600;
}

/* Ligne cochée : léger voile vert (en plus du barré) → feedback net. */
body.page-shopping-list-detail .shopping-row.is-checked {
  background: color-mix(in srgb, var(--success) 7%, transparent);
}

/* Barre de progression un chouïa plus épaisse. */
body.page-shopping-list-detail .shopping-progress {
  height: 10px;
}

/* Voile flou plein-largeur DERRIÈRE le haut flottant (même effet que la fiche
   recette) : le contenu qui défile est flouté sous le bandeau. Fixé au viewport
   (le .shopping-float-top est transformé, il ne peut pas le porter). z-index 35
   = au-dessus du contenu, sous le bandeau (z 40). Masque dégradé sur les 18
   derniers px → bord doux, petit jour avant le contenu. Toujours présent : au
   repos il recouvre le fond uni (donc invisible), il ne « floute » qu'à partir
   du moment où du contenu passe dessous au scroll. */
body.page-shopping-list-detail .shopping-float-veil {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: calc(env(safe-area-inset-top, 0px) + 70px);
  z-index: 35;
  background: color-mix(in srgb, var(--bg) 66%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
  pointer-events: none;
}

/* --- Haut FLOTTANT de la liste (façon fiche recette) --------------------- */
/* Bouton retour rond à gauche ; à droite le NOM (au-dessus) + la barre de
   progression, dans un conteneur FIXE → restent visibles au scroll. Centré +
   borné à la largeur du contenu (760px). `align-items: flex-end` → le BAS de
   la barre s'aligne sur le bas du bouton retour. */
body.page-shopping-list-detail .shopping-float-top {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 10px);
  left: 50%;
  transform: translateX(-50%);
  width: min(760px, calc(100% - 28px));
  z-index: 40;
  display: flex;
  align-items: flex-end;
  gap: 12px;
}

/* Réutilise le look du bouton retour de la fiche recette, mais neutralise son
   position:fixed (le conteneur .shopping-float-top l'est déjà). */
body.page-shopping-list-detail .shopping-float-top .menu-recipe-back-fab {
  position: static;
  top: auto;
  left: auto;
  flex: 0 0 auto;
}

/* Colonne droite : nom (au-dessus) + barre de progression, même largeur. */
body.page-shopping-list-detail .shopping-float-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

body.page-shopping-list-detail .shopping-float-title-form {
  width: 100%;
  min-width: 0;
  margin: 0;
}

/* Nom : pastille « verre » (lisible au-dessus du contenu qui défile), même
   largeur que la barre, tronqué si trop long (pas de retour à la ligne). En
   édition c'est un <input> éditable, en courses un <div> d'affichage : hauteur
   ET taille de police FIGÉES (16px = valeur imposée aux inputs sur mobile pour
   éviter le zoom iOS) → le haut a exactement la même hauteur dans les 2 modes.
   La hauteur passe par `height` + `line-height` égaux (centrage vertical qui
   marche pour un div comme pour un input). */
body.page-shopping-list-detail .shopping-float-title {
  box-sizing: border-box;
  display: block;
  width: 100%;
  height: 30px;
  margin: 0;
  /* Reset l'appearance native de l'<input> (iOS ajoute une hauteur/ombre
     intrinsèque) → boîte réellement identique au <div> d'affichage. */
  appearance: none;
  -webkit-appearance: none;
  padding: 0 11px;
  border-radius: 10px;
  /* Un <input> n'hérite PAS de la police du body → `inherit` = Hanken Grotesk
     comme partout (sinon rendu en police système = aspect « autre taille »). */
  font-family: inherit;
  font-size: 16px;
  font-weight: 800;
  line-height: 28px;
  color: var(--text);
  background: color-mix(in srgb, var(--panel) 84%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Mode courses : l'<input> est en lecture seule → pas de curseur / focus au
   tap (c'est juste un affichage). */
body.page-shopping-list-detail input.shopping-float-title[readonly] {
  pointer-events: none;
  cursor: default;
}

/* Barre de progression flottante : plus haute + ombre pour ressortir. */
body.page-shopping-list-detail .shopping-float-progress {
  flex: 0 0 auto;
  width: 100%;
  height: 14px;
  margin: 0;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}


/* ============================================================== */
/* Form recette — « mode écriture » (mobile).                     */
/* recipe_form.js pose body.is-typing quand un champ de saisie a  */
/* le focus (clavier virtuel ouvert). On masque le chrome :       */
/*  - barre du haut (glisse vers le haut) → gagne de la place ;   */
/*  - nav du bas (glisse hors champ) → n'apparaît plus AU-DESSUS   */
/*    du clavier iOS.                                             */
/* Le bouton flottant Enregistrer, LUI, reste : il se replace     */
/* juste au-dessus du clavier (via --kb-inset) → on peut valider  */
/* sans fermer le clavier. Le bandeau retour + « Voir la recette » */
/* reste aussi et remonte sous l'encoche. Mobile seulement        */
/* (topbar/nav sont desktop-cachées).                             */
/* ============================================================== */
@media (max-width: 700px) {
  body.page-recipe-form.is-typing .mobile-topbar {
    transform: translateY(-100%);
    pointer-events: none;
  }
  body.page-recipe-form.is-typing .bottom-nav {
    transform: translateY(110%);
    opacity: 0;
    pointer-events: none;
  }
  /* Topbar masquée → on remonte le bandeau retour/voir juste sous l'encoche. */
  body.page-recipe-form.is-typing {
    --float-back-top: calc(env(safe-area-inset-top, 0px) + 8px);
  }
}

/* ============================================================== */
/* Onboarding « Premiers pas » — carte checklist + modale de      */
/* bienvenue (Sommaire). Corail (--primary) = progression/action ;*/
/* vert (--success) réservé à l'état terminé (vrai succès).       */
/* Variables partagées → s'adapte seul aux thèmes clair/sombre.   */
/* ============================================================== */
.onboarding-card {
  position: relative;
  overflow: hidden;
  margin: 0 0 18px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--panel-2);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
}
.onboarding-card.is-complete {
  border-color: color-mix(in srgb, var(--success) 45%, var(--border));
}
.onboarding-card.is-leaving {
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.4s ease, transform 0.4s ease;
}

.onboarding-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.onboarding-card-heading {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.onboarding-card-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 12px;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  font-size: 20px;
}
.onboarding-card.is-complete .onboarding-card-icon {
  color: var(--success);
  background: color-mix(in srgb, var(--success) 16%, transparent);
}
.onboarding-card-title {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
}
.onboarding-card-sub {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--muted);
}
.onboarding-progress-count {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 700;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.onboarding-card.is-complete .onboarding-progress-count {
  color: var(--success);
}

.onboarding-progress {
  margin: 12px 0 4px;
  height: 8px;
  border-radius: 999px;
  background: var(--panel-sunken);
  overflow: hidden;
}
.onboarding-progress-bar {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
  transition: width 0.5s ease;
}
.onboarding-card.is-complete .onboarding-progress-bar {
  background: var(--success);
}
/* Largeurs par palier (CSP : pas de style inline sur la barre). */
.onboarding-progress-bar.is-0 { width: 0%; }
.onboarding-progress-bar.is-1 { width: 20%; }
.onboarding-progress-bar.is-2 { width: 40%; }
.onboarding-progress-bar.is-3 { width: 60%; }
.onboarding-progress-bar.is-4 { width: 80%; }
.onboarding-progress-bar.is-5 { width: 100%; }

.onboarding-missions {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.onboarding-mission-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 12px;
  text-decoration: none;
  color: var(--text);
  background: var(--panel-sunken);
  transition: background-color 120ms ease;
}
@media (hover: hover) {
  .onboarding-mission-link:hover {
    background: var(--hover-bg);
  }
}
.onboarding-mission-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  font-size: 16px;
}
.onboarding-mission.is-done .onboarding-mission-check {
  color: #fff;
  background: var(--success);
}
.onboarding-mission-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
}
.onboarding-mission-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.onboarding-mission.is-done .onboarding-mission-label {
  color: var(--muted);
  text-decoration: line-through;
}
.onboarding-mission-desc {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.3;
}
.onboarding-mission-arrow {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 18px;
}

.onboarding-card-actions {
  display: flex;
  justify-content: center;
  margin-top: 12px;
}
.onboarding-card-actions form {
  margin: 0;
}
.onboarding-dismiss-btn {
  border: none;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  padding: 6px 12px;
  border-radius: 8px;
  cursor: pointer;
}
@media (hover: hover) {
  .onboarding-dismiss-btn:hover {
    color: var(--text);
    background: var(--panel-sunken);
  }
}
.onboarding-card.is-complete .onboarding-dismiss-btn {
  color: var(--success);
  font-weight: 600;
}

/* Point discret sur l'entrée « Tutoriel » des menus nav (tuto en cours). */
.onboarding-menu-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--primary);
  vertical-align: middle;
}

/* Pastille de progression du tuto dans la barre du haut (mobile) : chip corail
   distinct des boutons ronds neutres — c'est un repère temporaire, il disparaît
   quand le tuto est masqué/terminé. Surcharge le carré 42px en pilule auto. */
.onboarding-topbar-btn {
  width: auto;
  gap: 6px;
  padding: 0 12px;
  border-radius: 999px;
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 32%, var(--border));
  background: color-mix(in srgb, var(--primary) 12%, var(--panel-2));
}
.onboarding-topbar-btn .ico {
  width: 18px;
  height: 18px;
}
.onboarding-topbar-badge {
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}

/* Confettis de complétion : pièces créées en JS, chute animée en CSS. */
.onboarding-confetti {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.onboarding-confetti-piece {
  position: absolute;
  top: -14px;
  width: 8px;
  height: 12px;
  border-radius: 2px;
  animation-name: onboarding-confetti-fall;
  animation-timing-function: ease-in;
  animation-iteration-count: 1;
}
@keyframes onboarding-confetti-fall {
  0% { transform: translateY(0) rotate(0deg); opacity: 1; }
  100% { transform: translateY(260px) rotate(560deg); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .onboarding-confetti { display: none; }
}

/* Modale de bienvenue. */
.onboarding-welcome {
  width: min(440px, calc(100vw - 32px));
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 0;
  background: var(--panel-2);
  color: var(--text);
}
.onboarding-welcome::backdrop {
  background: rgba(0, 0, 0, 0.5);
}
.onboarding-welcome-body {
  padding: 22px 22px 20px;
  text-align: center;
}
.onboarding-welcome-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  margin-bottom: 8px;
  border-radius: 16px;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  font-size: 26px;
}
.onboarding-welcome-title {
  margin: 4px 0 6px;
  font-size: 20px;
  font-weight: 700;
}
.onboarding-welcome-lead {
  margin: 0 0 14px;
  font-size: 14px;
  color: var(--muted);
}
.onboarding-welcome-pillars {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  text-align: left;
}
.onboarding-welcome-pillars li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
}
.onboarding-welcome-pillar-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--primary);
}
.onboarding-welcome-tip {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--panel-sunken);
  font-size: 13px;
  color: var(--muted);
  text-align: left;
}
.onboarding-welcome-tip-icons {
  display: inline-flex;
  gap: 4px;
  flex: 0 0 auto;
  color: var(--primary);
}
.onboarding-welcome-outro {
  margin: 0 0 16px;
  font-size: 13px;
  color: var(--muted);
}
.onboarding-welcome-cta {
  width: 100%;
}
