/* =====================================================================
   TT2 Recipe Optimizer - Public stylesheet
   Sommaire :
     1. Variables CSS (thèmes clair/sombre)
     2. Reset universel + contrôles natifs (inputs, selects, options)
     3. Layout général (body, container, header)
     4. Sélecteurs flottants (theme-toggle, lang-selector, admin-link)
     5. Sections et titres
     6. Grille d'ingrédients et cartes
     7. Contrôles principaux (objectif, quantité, boutons)
     8. Mode expert (multi-objectifs)
     9. Section "ingrédients connus"
    10. Résultats d'optimisation
    11. Avertissement "discover"
    12. Animations
    13. Utilitaires (helpers)
    14. Media queries (responsive)
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Variables CSS - palette, ombres, transitions
   --------------------------------------------------------------------- */
:root {
    /* Fonds */
    --bg-primary: #f8fafc;
    --bg-secondary: #ffffff;
    --bg-tertiary: #f1f5f9;
    --bg-input: #ffffff;
    --bg-gradient-start: #5566cc;
    --bg-gradient-end: #764ba2;
    /* Textes */
    --text-primary: #0f172a;
    --text-secondary: #475569;
    --text-muted: #5b6778;
    --text-on-accent: #fff; /* Texte blanc sur fond accent/coloré (boutons actifs, entête) */
    /* Bordures */
    --border-color: #e2e8f0;
    --border-strong: #cbd5e1;
    /* Accents */
    --accent-primary: #5566cc;
    /* La même teinte, mais lue COMME TEXTE sur le fond de la page.
       --accent-primary doit rester foncée : elle sert de fond sous du
       texte blanc, qui y est à 5,05 et tomberait à 2,98 si on l'éclaircissait.
       En thème sombre, la même couleur en texte ne rend que 3,54 sur le fond
       de page et 2,90 sur une carte : il faut donc deux valeurs, pas une. */
    --accent-texte: #5566cc;
    --accent-secondary: #764ba2;
    /* États */
    --success-bg: #dcfce7;
    --craft-bg: #fef3c7;
    --success-border: #22c55e;
    --success-text: #166534;
    --danger: #ef4444;
    --danger-text: #dc2626;
    --danger-soft: rgba(239, 68, 68, 0.08);
    --warn-bg: #fef3c7;
    --warn-border: #f59e0b;
    --warn-text: #78350f;
    /* Effets */
    --card-hover: rgba(102, 126, 234, 0.08);
    --white-30: rgba(255, 255, 255, 0.3); /* Blanc translucide : bordure/ombre/glow sur l'entête */
    /* Voile des commandes posées SUR l'entête (langue, bascule de thème).
       Sombre en thème clair : un voile blanc y éclaircissait un violet déjà
       trop clair et faisait tomber le texte blanc à 2,94. Ici : 5,95. */
    --voile-controle: rgba(0, 0, 0, 0.10);
    --voile-controle-actif: rgba(0, 0, 0, 0.20);
    --focus-ring: 0 0 0 3px rgba(102, 126, 234, 0.25);
    --shadow-sm: 0 1px 2px 0 rgba(15, 23, 42, 0.05);
    --shadow-md: 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.05);
    --shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, 0.08), 0 4px 6px -4px rgba(15, 23, 42, 0.05);
    --shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, 0.1), 0 8px 10px -6px rgba(15, 23, 42, 0.05);
    --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    /* Synchronise le rendu natif (scrollbars, autofill) avec le thème */
    color-scheme: light;
}

[data-theme="dark"] {
    /* Le voile reste BLANC ici : l'entête sombre rend déjà le texte à 9,08,
       et un voile sombre y effacerait la commande au lieu de la détacher. */
    --voile-controle: rgba(255, 255, 255, 0.15);
    --voile-controle-actif: rgba(255, 255, 255, 0.25);
    /* Indigo clair. #818cf8 ne rendait que 4,21 sur la carte la plus claire
       du thème sombre (#273449) : le pire des trois fonds sombres décide,
       pas le plus foncé. Ici 6,29 au pire. */
    --accent-texte: #a5b4fc;
    --bg-primary: #0f172a;
    --bg-secondary: #1e293b;
    --bg-tertiary: #273449;
    --bg-input: #1e293b;
    /* L'ardoise pure ne disait plus rien de la marque : le degrade sombre
       reprend l'indigo et le violet du thème clair, en version nuit. Blanc
       dessus : 13,0 et 12,2, très au-dessus des 4,5 demandes. */
    --bg-gradient-start: #232a52;
    --bg-gradient-end: #3a2a58;
    --text-primary: #f1f5f9;
    --text-secondary: #cbd5e1;
    --text-muted: #94a3b8;
    --border-color: #334155;
    --border-strong: #475569;
    --accent-soft: rgba(129, 140, 248, 0.18);
    --success-bg: #14532d;
    --craft-bg: rgba(234, 179, 8, 0.22);
    --success-border: #22c55e;
    --success-text: #86efac;
    --danger: #f87171;
    --danger-text: #f87171;
    --danger-soft: rgba(248, 113, 113, 0.12);
    --warn-bg: rgba(245, 158, 11, 0.15);
    --warn-border: #f59e0b;
    --warn-text: #fde68a;
    --card-hover: rgba(129, 140, 248, 0.15);
    --focus-ring: 0 0 0 3px rgba(129, 140, 248, 0.35);
    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
    --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.6);
    color-scheme: dark;
}

/* ---------------------------------------------------------------------
   2. Reset universel + contrôles natifs
   Corrige les fonds noirs en mode clair sur OS sombre.
   --------------------------------------------------------------------- */
* { box-sizing: border-box; margin: 0; padding: 0; }

input, select, textarea, button {
    font-family: inherit;
    color: var(--text-primary);
}
input[type="number"], input[type="text"], textarea {
    background-color: var(--bg-input);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
}
input[type="text"], textarea {
    -webkit-appearance: none;
    appearance: none;
}
/* Les fleches du champ nombre, et le piege qui les faisait revenir.
   « -moz-appearance » et « appearance » sont la MÊME propriété pour Firefox
   moderne : ecrire « appearance: none » APRÈS « -moz-appearance: textfield »
   annulait donc la première, et les fleches revenaient. Firefox les affiche
   en permanence la ou Chrome ne les montre qu'au survol : le défaut etait
   invisible ici et bien visible a l'usage.
   « appearance: textfield » est la forme standard qui les retire. */
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
/* Selects custom : flèche SVG injectée, sauf .lang-selector qui garde son look */
select:not(.lang-selector) {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    background-color: var(--bg-input);
    color: var(--text-primary);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='%2364748b' d='M6 8 0 0h12z'/></svg>");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    padding-right: 2rem;
}
[data-theme="dark"] select:not(.lang-selector) {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='%2394a3b8' d='M6 8 0 0h12z'/></svg>");
}
option {
    background-color: var(--bg-input);
    color: var(--text-primary);
}
::placeholder {
    color: var(--text-secondary);
    opacity: 1;
}
/* Évite le fond jaune du remplissage automatique des navigateurs WebKit */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--text-primary);
    -webkit-box-shadow: 0 0 0 1000px var(--bg-input) inset;
    transition: background-color 5000s ease-in-out 0s;
}

/* ---------------------------------------------------------------------
   3. Layout général
   --------------------------------------------------------------------- */
body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
    min-height: 100vh;
    padding: 1.5rem;
    color: var(--text-primary);
    transition: var(--transition);
    box-shadow: inset 0 0 6rem rgba(0, 0, 0, 0.15);
}
.container {
    max-width: 1200px;
    margin: 0 auto;
    background: var(--bg-gradient-start);
    background-clip: padding-box;
    border-radius: 1.25rem;
    box-shadow: var(--shadow-xl), 0 0 4rem rgba(0, 0, 0, 0.25);
    overflow: hidden;
    transition: var(--transition);
    position: relative;
    z-index: 1;
}
.header {
    background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
    color: white;
}
.header p {
    opacity: 0.95;
    font-size: 0.875rem;
    font-weight: 500;
}
.content {
    padding: 1.25rem 1.5rem;
    background: var(--bg-primary);
    transition: var(--transition);
}

/* ---------------------------------------------------------------------
   4. Sélecteurs flottants : thème, langue, admin
   --------------------------------------------------------------------- */
.theme-toggle {
    position: absolute;
    top: 1.25rem;
    right: 1.25rem;
    background: var(--voile-controle);
    border: none;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    cursor: pointer;
    font-size: 1.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: var(--transition);
    backdrop-filter: blur(10px);
    z-index: 100;
}
.theme-toggle:hover {
    background: var(--voile-controle-actif);
    transform: scale(1.1) rotate(15deg);
}
.theme-toggle:active { transform: scale(0.95); }

/* Même géométrie que .theme-toggle : même diametre, même rondeur, même voile,
   pas de bordure. Les deux commandes se ressemblaient de loin sans jamais se
   ressembler vraiment - un rond de 44 px face a un carre arrondi borde, avec un
   remplissage asymetrique herite d'une fleche qui n'existe plus. Deux formes
   voisines mais differentes se lisent comme une erreur, pas comme une intention.

   « text-align-last » est nécessaire en plus de « text-align » : sur un <select>,
   le premier seul ne centre pas le libelle affiche dans tous les navigateurs. */
.lang-selector {
    position: absolute;
    top: 1.25rem;
    right: 4.5rem;
    background: var(--voile-controle);
    border: none;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border-radius: 50%;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 600;
    color: white;
    text-align: center;
    text-align-last: center;
    transition: var(--transition);
    backdrop-filter: blur(10px);
    z-index: 100;
    appearance: none;
    -webkit-appearance: none;
}
.lang-selector:hover { background: var(--voile-controle-actif); }
.lang-selector:focus {
    outline: none;
    box-shadow: 0 0 0 3px var(--white-30);
}
.lang-selector option {
    background: var(--bg-secondary);
    color: var(--text-primary);
}

/* Acces admin : dans le flux, tout en bas de page, et NON fixe a l'écran.
   Il etait epingle en bas a droite et suivait donc le lecteur du haut jusqu'en
   bas, en permanence par-dessus le contenu. C'est un lien qui ne sert qu'a
   l'administrateur, quelques fois par saison : il n'a pas a occuper un coin de
   l'écran en continu. Etant déjà le dernier élément de la page, il suffit de le
   remettre dans le flux pour qu'on ne le voie qu'arrive en bas. */
.admin-link {
    display: flex;
    justify-content: flex-end;
    margin-top: 1.5rem;
}
.admin-link a {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    background: rgba(102, 126, 234, 0.1);
    border-radius: 50%;
    text-decoration: none;
    font-size: 1.125rem;
    transition: var(--transition);
    backdrop-filter: blur(10px);
    border: 1px solid var(--border-color);
}
.admin-link a:hover {
    background: rgba(102, 126, 234, 0.2);
    transform: scale(1.1) rotate(90deg);
}
/* === Liens réseaux sociaux (publics, discrets) === */
.social-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    margin: 2rem auto 0.5rem;
}
.social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    color: var(--text-secondary, #888);
    background: rgba(102, 126, 234, 0.08);
    border: 1px solid var(--border-color);
    transition: var(--transition);
}
.social-link svg { width: 1.15rem; height: 1.15rem; fill: currentColor; display: block; }
.social-link:hover,
.social-link:focus-visible {
    color: var(--accent-color, #667eea);
    background: rgba(102, 126, 234, 0.18);
    transform: translateY(-2px);
    outline: none;
}
.social-link:focus-visible { box-shadow: 0 0 0 2px var(--accent-color, #667eea); }
@media (max-width: 480px) {
    .social-link { width: 2.5rem; height: 2.5rem; }
    .social-link svg { width: 1.25rem; height: 1.25rem; }
}
/* ---------------------------------------------------------------------
   5. Sections et titres
   --------------------------------------------------------------------- */
.section {
    background: var(--bg-secondary);
    padding: 1.75rem;
    border-radius: 1rem;
    margin-bottom: 1.5rem;
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
    transition: var(--transition);
}
.section:hover { box-shadow: var(--shadow-md); }
.section h2 {
    color: var(--text-primary);
    margin-bottom: 0.5rem;
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: -0.025em;
}
.section-desc {
    color: var(--text-muted);
    font-size: 0.875rem;
    margin-bottom: 1.25rem;
}
/* En-tête de section avec actions à droite (ex: bouton reset) */
.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.section-toggle {
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.section-toggle .toggle-icon {
    font-size: 0.875rem;
    color: var(--text-muted);
    transition: var(--transition);
}
.section-collapsible {
    overflow: hidden;
    transition: max-height 0.3s ease;
}

/* ---------------------------------------------------------------------
   6. Grille d'ingrédients et cartes
   --------------------------------------------------------------------- */
.ingredients-grid {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 1rem;
}
.ingredient-card {
    background: var(--bg-tertiary);
    padding: 1rem;
    border-radius: 0.75rem;
    text-align: center;
    border: 2px solid var(--border-color);
    /* Une ombre douce detache la carte du fond. Sans elle, le thème sombre
       n'a qu'un trait d'un pixel pour separer seize cartes d'un fond presque
       de la même couleur, et la page parait plate. */
    box-shadow: var(--shadow-sm);
    transition: var(--transition);
}
.ingredient-card:hover {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 3px var(--card-hover);
    transform: translateY(-2px);
}
.ingredient-card img {
    width: 48px;
    height: 48px;
    margin-bottom: 0.5rem;
    border-radius: 0.5rem;
    transition: var(--transition);
}
.ingredient-card:hover img { transform: scale(1.1); }
.ingredient-card label {
    display: block;
    font-weight: 600;
    font-size: 0.8125rem;
    margin-bottom: 0.5rem;
    color: var(--text-primary);
}
.ingredient-card input {
    width: 70px;
    padding: 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 0.5rem;
    text-align: center;
    font-size: 0.875rem;
    background-color: var(--bg-input);
    color: var(--text-primary);
    font-weight: 600;
    transition: var(--transition);
}
/* Pas-a-pas de quantité : deux touches de part et d'autre du champ.
   A la souris elles ne servent a rien - le clavier va plus vite - et elles ne
   tiendraient de toute façon pas dans une carte de 76 px de large. Elles ne
   s'affichent donc qu'au doigt, ou la grille est en même temps elargie pour
   leur faire place (voir la section 17). */
.qty-stepper {
    display: flex;
    align-items: stretch;
    justify-content: center;
    /* 1 px et non 2 : les colonnes de la grille tombent sur des largeurs
       fractionnaires (88,3281 px au téléphone), et la somme des trois éléments
       depassait alors la carte d'un demi-pixel. Invisible a l'oeil, mais la
       carte debordait pour de bon. */
    gap: 1px;
}
.btn-step { display: none; }

.ingredient-card input:hover { border-color: var(--border-strong); }
.ingredient-card input:focus {
    outline: none;
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
    background-color: var(--bg-secondary);
}

/* ---------------------------------------------------------------------
   7. Contrôles principaux (objectif, quantité, boutons)
   --------------------------------------------------------------------- */
.controls {
    display: flex;
    gap: 1rem;
    align-items: center;
    flex-wrap: wrap;
}
.controls label {
    font-weight: 600;
    color: var(--text-secondary);
    font-size: 0.875rem;
}
.controls select {
    padding: 0.75rem 2.25rem 0.75rem 1rem;
    border: 1px solid var(--border-color);
    border-radius: 0.625rem;
    min-width: 180px;
    background-color: var(--bg-input);
    color: var(--text-primary);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: var(--transition);
}
.controls select:hover { border-color: var(--border-strong); }
.controls select:focus {
    outline: none;
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}

.objective-qty {
    width: 90px;
    padding: 0.75rem;
    border: 1px solid var(--border-color);
    border-radius: 0.625rem;
    background-color: var(--bg-input);
    color: var(--text-primary);
    font-size: 0.875rem;
    font-weight: 600;
    text-align: center;
    transition: var(--transition);
}
.objective-qty:hover { border-color: var(--border-strong); }
.objective-qty:focus {
    outline: none;
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}

.btn {
    padding: 0.75rem 1.75rem;
    border: none;
    border-radius: 0.625rem;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: var(--transition);
    box-shadow: var(--shadow-md);
}
.btn-primary {
    /* Le fond est range dans une variable pour que le reflet (section 18)
       puisse se superposer SANS avoir a recopier le degrade : deux copies du
       même fond finiraient par diverger le jour ou l'une des deux est retouchee. */
    --fond-bouton: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%);
    background: var(--fond-bouton);
    color: white;
}
.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}
.btn-primary:active { transform: translateY(0); }
.btn-primary:disabled {
    background: var(--border-color);
    cursor: not-allowed;
    transform: none;
    opacity: 0.6;
}
/* Bouton "soustraire les ingrédients consommés" (rouge plein largeur) */
.btn-subtract {
    /* Rouge assombri : le blanc n'atteignait que 3,76 sur #ef4444. */
    --fond-bouton: linear-gradient(135deg, #dc2626, #b91c1c);
    background: var(--fond-bouton);
    color: white;
    width: 100%;
}
/* Bouton secondaire pour reset/cocher tout/décocher tout */
.btn-reset {
    padding: 0.5rem 1rem;
    border: 1px solid var(--border-color);
    border-radius: 0.5rem;
    background: var(--bg-tertiary);
    color: var(--text-muted);
    font-size: 0.8125rem;
    cursor: pointer;
    transition: var(--transition);
    white-space: nowrap;
}
.btn-reset:hover {
    color: var(--danger-text);
    border-color: var(--danger);
    background: var(--danger-soft);
}
/* Wrapper qty + bouton infini */
.qty-wrapper {
    position: relative;
    display: inline-flex;
    align-items: stretch;
}
.qty-wrapper .objective-qty {
    padding-right: 2.25rem;
}
.btn-qty-clear {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    width: 1.75rem;
    height: 1.75rem;
    border: none;
    background: var(--bg-tertiary);
    color: var(--text-secondary);
    border-radius: 0.375rem;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: var(--transition);
}
.btn-qty-clear:hover {
    background: var(--accent-primary);
    color: var(--text-on-accent);
}
.btn-qty-clear:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
/* ---------------------------------------------------------------------
   7b. Sélecteur de mode (radio segmenté : standard / expert / tiers)
   --------------------------------------------------------------------- */
.mode-toggle-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1rem;
    background: var(--bg-tertiary);
    padding: 0.375rem;
    border-radius: 0.625rem;
    border: 1px solid var(--border-color);
}
.mode-toggle {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.mode-toggle input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.mode-toggle span {
    display: block;
    width: 100%;
    text-align: center;
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
    border-radius: 0.4rem;
    transition: var(--transition);
    user-select: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.mode-toggle input[type="radio"]:checked + span {
    background: var(--accent-primary);
    color: var(--text-on-accent);
    box-shadow: var(--shadow-sm);
}
.mode-toggle input[type="radio"]:focus-visible + span {
    box-shadow: var(--focus-ring);
}
.mode-toggle:hover input[type="radio"]:not(:checked) + span {
    background: var(--card-hover);
    color: var(--text-primary);
}

#expert-section {
    display: none;
    margin-top: 0.75rem;
    padding: 1rem;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 0.625rem;
}
.expert-help {
    font-size: 0.8125rem;
    color: var(--text-muted);
    margin: 0 0 0.75rem 0;
}

#expert-targets-list {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}
.expert-target-row {
    display: grid;
    grid-template-columns: 1fr 110px 24px;
    gap: 0.5rem;
    align-items: center;
    background: var(--bg-secondary);
    padding: 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 0.5rem;
    transition: var(--transition);
}
.expert-target-row:focus-within {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.expert-target-row .expert-target-name {
    font-size: 0.875rem;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.expert-target-row input {
    padding: 0.5rem 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 0.4rem;
    background-color: var(--bg-input);
    color: var(--text-primary);
    font-size: 0.875rem;
    width: 100%;
    text-align: right;
    transition: var(--transition);
}
.expert-target-row input:focus {
    outline: none;
    border-color: var(--accent-primary);
}
.expert-target-unit {
    font-size: 1rem;
    text-align: center;
    color: var(--text-muted);
    user-select: none;
}

.expert-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 0.75rem;
    gap: 0.5rem;
    flex-wrap: wrap;
}

#expert-sum-info {
    font-size: 0.8125rem;
    font-weight: 600;
}
#expert-sum-info.invalid { color: var(--danger-text); }
#expert-sum-info.valid { color: var(--success-text); }

/* ---------------------------------------------------------------------
   8b. Mode tiers (priorités lexicographiques)
   --------------------------------------------------------------------- */
#tiers-section {
    display: none;
    margin-top: 0.75rem;
    padding: 1rem;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 0.625rem;
}
.tiers-columns {
    display: grid;
    /* minmax(0, 1fr) et non 1fr : « 1fr » signifie minmax(auto, 1fr), donc
       une colonne garde au moins la largeur de son contenu. Au téléphone, la
       liste des objectifs depassait de 30 px le bord droit de l'écran, sans
       que la page defile : ces 30 px etaient hors d'atteinte. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1rem;
    margin: 0.75rem 0;
}
.tiers-column {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 0.5rem;
    padding: 0.75rem;
    min-height: 80px;
}
.tiers-col-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 0.5rem 0;
}
.tiers-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    counter-reset: tier-counter;
}
.tiers-list-ordered .tier-item::before {
    counter-increment: tier-counter;
    content: counter(tier-counter);
    background: var(--accent-primary);
    color: var(--text-on-accent);
    font-weight: 700;
    font-size: 0.75rem;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-right: 0.5rem;
}
.tier-item {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.5rem;
    background: var(--bg-input);
    border: 1px solid var(--border-color);
    border-radius: 0.4rem;
    font-size: 0.875rem;
    color: var(--text-primary);
    transition: var(--transition);
}
.tier-item:hover {
    border-color: var(--accent-primary);
    background: var(--card-hover);
}
.tier-name {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.tier-controls {
    display: inline-flex;
    gap: 0.25rem;
    flex-shrink: 0;
}
.tier-btn {
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 1px solid var(--border-color);
    background: var(--bg-tertiary);
    color: var(--text-secondary);
    border-radius: 0.3rem;
    cursor: pointer;
    font-size: 0.75rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: var(--transition);
}
.tier-btn:hover:not(:disabled) {
    background: var(--accent-primary);
    color: var(--text-on-accent);
    border-color: var(--accent-primary);
}
.tier-btn:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.tier-btn-add:hover {
    background: var(--success-border);
    border-color: var(--success-border);
}
.tier-btn-remove:hover {
    background: var(--danger);
    border-color: var(--danger);
}
#tiers-info {
    font-size: 0.8125rem;
    font-weight: 600;
}
#tiers-info.valid { color: var(--success-text); }
#tiers-info.invalid { color: var(--danger-text); }

/* Barre de sous-mode (priorité / absolu / pourcentage) + bouton d'aide */
.tiers-submode-bar {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.tiers-submode-group {
    flex: 1 1 auto;
    margin: 0;
}
.tiers-help-btn {
    flex: 0 0 auto;
    width: 2.25rem;
    align-self: center;
    height: 2.25rem;
    padding: 0;
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    color: var(--accent-texte);
    border-radius: 50%;
    cursor: pointer;
    font-size: 1.125rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: var(--transition);
}
.tiers-help-btn:hover {
    background: var(--accent-primary);
    color: var(--text-on-accent);
    border-color: var(--accent-primary);
}
.tiers-help-btn:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.tiers-help-zone {
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    border-radius: 0.5rem;
    padding: 0.625rem 0.75rem;
    margin-bottom: 0.5rem;
    font-size: 0.8125rem;
    line-height: 1.45;
}
.tiers-help-zone p { margin: 0; }

/* Champ de saisie par tier (cible absolue ou part en %) */
.tier-value-wrap {
    display: inline-flex;
    align-items: center;
    gap: 0.125rem;
    flex-shrink: 0;
    margin: 0 0.375rem;
}
.tier-value {
    width: 4.5rem;
    padding: 0.25rem 0.375rem;
    border: 1px solid var(--border-color);
    background: var(--bg-input);
    color: var(--text-primary);
    border-radius: 0.3rem;
    font-size: 0.8125rem;
    text-align: right;
}
.tier-value:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-color: var(--accent-primary);
}
.tier-value-unit {
    font-size: 0.8125rem;
    color: var(--text-muted);
}

/* Indicateur de somme des pourcentages + avertissement valeur nulle */
.tiers-percent-sum {
    font-size: 0.8125rem;
    font-weight: 600;
}
.tiers-percent-sum.valid { color: var(--success-text); }
.tiers-percent-sum.invalid { color: var(--danger-text); }
.tiers-zero-warn {
    margin: 0.5rem 0 0 0;
    padding: 0.5rem 0.625rem;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    line-height: 1.4;
}

/* Récapitulatif de la valeur diamant (mode pourcentage) */
.tiers-percent-recap {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 0.5rem;
    padding: 0.625rem 0.75rem;
    margin-top: 0.5rem;
    min-width: 12rem;
}
.tiers-recap-title {
    font-size: 0.8125rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 0.375rem;
}
.tiers-recap-row {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    font-size: 0.8125rem;
    padding: 0.125rem 0;
}
.tiers-recap-name {
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.tiers-recap-val { flex-shrink: 0; color: var(--text-primary); }
.tiers-recap-foot {
    margin-top: 0.375rem;
    padding-top: 0.375rem;
    border-top: 1px solid var(--border-color);
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* Mobile : empiler les 2 colonnes du mode tiers */
@media (max-width: 640px) {
    .tiers-columns {
        grid-template-columns: minmax(0, 1fr);
    }
    .tiers-submode-group {
        flex-wrap: wrap;
    }
    .tier-value { width: 3.75rem; }
    .tier-value-wrap { margin: 0 0.25rem; }
    /* Le nom de l'objectif etait ecrase par ce qui l'entoure : le numéro, le
       champ de valeur et les trois boutons ne se reduisent pas, et ils prennent
       toute la place. Mesure au téléphone : 7 px de large pour « Skill Points »,
       et ZÉRO en mode pourcentage. On ne savait plus de quel objectif parlait la
       ligne.

       On lui garantit donc une largeur lisible, et la rangee passe a la ligne :
       le nom reste entier, les commandes descendent en dessous. */
    .tier-item { flex-wrap: wrap; row-gap: 0.375rem; }
    .tier-name { font-size: 0.8125rem; min-width: 8rem; }
}

/* ---------------------------------------------------------------------
   9. Section "ingrédients connus"
   --------------------------------------------------------------------- */
.known-count {
    font-size: 0.875rem;
    color: var(--text-muted);
}
.known-actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
    flex-wrap: wrap;
}
.known-grid-spaced { margin-top: 1rem; }
/* La checkbox réelle est masquée, la carte entière sert de zone cliquable */
.known-checkbox {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}
.known-card {
    cursor: pointer;
    user-select: none;
    position: relative;
    transition: var(--transition);
}
.known-checked {
    border-color: var(--success-border);
    background: var(--success-bg);
}
.known-checked img {
    filter: drop-shadow(0 0 4px var(--success-border));
}

/* ---------------------------------------------------------------------
   10. Résultats d'optimisation
   --------------------------------------------------------------------- */
.results {
    margin-top: 1.5rem;
    animation: fadeIn 0.3s ease-out;
}
.results-header {
    background: var(--success-bg);
    padding: 1.5rem;
    border-radius: 0.75rem;
    border-left: 4px solid var(--success-border);
    margin-bottom: 1rem;
}
.results-header h3 {
    color: var(--success-text);
    margin-bottom: 1rem;
    font-size: 1.125rem;
    font-weight: 600;
}
.totals {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    align-items: stretch;
}
.totals-left-column {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
@media (min-width: 900px) {
    /* Layout 3 colonnes : pile de totaux | consommés | bouton.
       Tous les enfants sont alignés en haut sans cellule fantôme. */
    .totals {
        display: grid;
        grid-template-columns: minmax(180px, max-content) 1fr auto;
        column-gap: 1rem;
        align-items: start;
    }
    .totals > .consumed-block { margin-top: 0; }
    .totals > .subtract-row   { margin-top: 0; }
}
.total-item {
    background: var(--bg-secondary);
    padding: 0.75rem 1rem;
    border-radius: 0.5rem;
    font-size: 0.875rem;
    border: 1px solid var(--border-color);
}
.total-item strong {
    color: var(--accent-texte);
    font-size: 1.25rem;
    margin-right: 0.5rem;
}
/* Affichage des manquants en rouge */
.total-shortfall { color: var(--danger-text); }

.steps-list {
    display: grid;
    gap: 0.75rem;
}
.step-group {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    background: var(--bg-tertiary);
    padding: 1rem;
    border-radius: 0.75rem;
    border: 1px solid var(--border-color);
    transition: var(--transition);
}
.step-group:hover {
    border-color: var(--accent-primary);
    box-shadow: var(--shadow-sm);
}
.step-formula {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.375rem 0.625rem;
    font-size: 0.875rem;
    flex: 1;
    min-width: 0;
}
/* Une image et son nom ne font qu'un. Dans une rangee qui se replie, deux
   éléments voisins se separent des que la place manque : au téléphone, 19
   couples sur 53 avaient l'image en fin de ligne et le nom au debut de la
   suivante. Reunis, ils ne peuvent plus être coupes, et le repli se fait la
   ou il doit, avant le plus ou la fleche. */
.step-item {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}
.step-formula img {
    width: 32px;
    height: 32px;
    border-radius: 0.375rem;
    object-fit: contain;
}
.step-formula .op {
    font-weight: 700;
    color: var(--text-muted);
}
.step-formula strong { color: var(--accent-texte); }
.step-badge {
    flex-shrink: 0;
    background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%);
    color: white;
    padding: 0.375rem 0.875rem;
    border-radius: 9999px;
    font-weight: 700;
    font-size: 0.875rem;
}
.step-group.step-clickable { cursor: pointer; }
.step-group.step-clickable:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.step-group.step-done { opacity: 0.55; }
.step-group.step-done .step-formula {
    text-decoration: line-through;
    text-decoration-color: var(--text-muted);
}
.step-group.step-done .step-formula strong { color: var(--text-muted); }
.step-group.step-done .step-badge { filter: grayscale(1); }

/* Bloc "ingrédients consommés" affiché après l'optimisation */
.consumed-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 0.5rem;
}
.consumed-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    gap: 0.5rem;
}
.consumed-block {
    margin-top: 0.75rem;
    padding: 0.75rem;
    background: var(--bg-tertiary);
    border-radius: 0.5rem;
    border: 1px solid var(--border-color);
}
.subtract-row {
    margin-top: 0.5rem;
}
/* Bordure rouge pour un total avec manquant */
.total-item.shortfall { border-color: var(--danger); }
.consumed-item {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    background: var(--bg-secondary);
    padding: 0.25rem 0.625rem;
    border-radius: 0.4rem;
    border: 1px solid var(--border-color);
}
.consumed-item img {
    width: 20px;
    height: 20px;
    border-radius: 3px;
}
.consumed-item-text {
    font-size: 0.8125rem;
}
.consumed-item-text strong { color: var(--accent-texte); }
.consumed-item-pct {
    font-size: 0.6875rem;
    color: var(--text-muted);
}
.no-results-msg {
    text-align: center;
    color: var(--text-muted);
}

/* ---------------------------------------------------------------------
   11. Avertissement "discover" (recettes incomplètes)
   --------------------------------------------------------------------- */
/* Avertissement de configuration : même habillage que l'avertissement de
   decouverte, mais toujours affiche quand il est present dans la page. Il
   est pose par le serveur, jamais montre ou cache par un script. */
.config-warning {
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    padding: 0.75rem 1rem;
    border-radius: 0.625rem;
    font-size: 0.875rem;
    margin: 0 0 1rem;
}

/* Réserve « plan non prouve le meilleur ». Même famille visuelle que les
   autres avertissements : c'est une précision, pas une erreur. */
.optimum-reserve {
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    padding: 0.75rem 1rem;
    border-radius: 0.625rem;
    font-size: 0.875rem;
    margin: 0 0 1rem;
}

.discover-warning {
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    padding: 0.75rem 1rem;
    border-radius: 0.625rem;
    font-size: 0.875rem;
    margin-top: 0.75rem;
    display: none;
}

/* Fin d'événement proche, sous le titre de saison.
   Même famille de couleurs que les autres avertissements, pour que ce soit
   reconnu comme une précision et non comme une panne. Il vit dans l'en-tête,
   donc en pastille centree : une bande pleine couperait le degrade en deux.
   Le fond de la famille est opaque en thème clair et translucide en thème
   sombre, ce qui convient aux deux fonds d'en-tête sans règle supplementaire. */
.event-ending {
    display: inline-block;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    padding: 0.375rem 0.875rem;
    border-radius: 999px;
    font-size: 0.875rem;
    font-weight: 700;
    opacity: 1;               /* annule l'attenuation de .header p */
    margin: 0.625rem auto 0;
    max-width: 100%;
}

/* ---------------------------------------------------------------------
   12. Animations
   --------------------------------------------------------------------- */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

/* ---------------------------------------------------------------------
   13. Utilitaires (helpers réutilisables, à l'origine en style="")
   --------------------------------------------------------------------- */
.is-hidden { display: none !important; }

/* Commande hors d'atteinte pour l'instant, et qui le dit.
   La cacher serait plus propre a l'oeil et pire a l'usage : le visiteur ne
   saurait ni que l'outil existe, ni comment l'obtenir. Le curseur barre et
   l'infobulle repondent a la question avant qu'on ait clique.
   L'attribut title porte le texte ; c'est le script qui retire le href, donc
   rien ici ne depend du style pour empecher le clic. */
.btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
}
.btn.is-disabled:hover {
    transform: none;
    filter: none;
}

/* ---------------------------------------------------------------------
   Animation de brillance sur le titre + structure d'entête
   (placé AVANT les media queries pour que le mobile puisse écraser)
   --------------------------------------------------------------------- */
/* 1. LE TITRE (Texte + Halo) */
.header h1 {
    font-size: 2.5rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: 0.5rem;
    text-transform: uppercase;
    color: var(--text-on-accent);
    display: inline-block;
    position: relative;
    filter: drop-shadow(0 0 4px var(--white-30));
    animation: glow-pulse 5s ease-in-out infinite;
}

/* 2. LA BARRE DÉCORATIVE (Sous le titre) */
.header h1::after {
    content: "";
    display: block;
    width: 60px;
    height: 4px;
    background: var(--accent-primary);
    margin: 12px auto 0;
    border-radius: 10px;
    animation: expandBar 4s ease-in-out infinite;
}

/* --- KEYFRAMES --- */
@keyframes glow-pulse {
    0%, 100% {
        filter: drop-shadow(0 0 8px var(--white-30))
                drop-shadow(0 0 15px rgba(255, 255, 255, 0.1));
    }
    50% {
        filter: drop-shadow(0 0 15px rgba(255, 255, 255, 0.6))
                drop-shadow(0 0 25px rgba(255, 255, 255, 0.2));
    }
}

@keyframes expandBar {
    0%, 100% { width: 40px; opacity: 0.7; }
    50% { width: 80px; opacity: 1; }
}

/* --- STRUCTURE DE L'ENTÊTE --- */
.header {
    position: relative;
    overflow: hidden;
    z-index: 1;
    /* En-tête resserree : 3rem laissait une bande vide plus haute que le titre
       lui-même, au detriment du contenu. La butee basse est le bouton de thème,
       pose a 1,25rem du haut sur 2,75rem de haut : sous 2rem il mord sur le titre. */
    padding: 2rem 1rem 1.5rem;
    text-align: center;
}

/* Lueur d'ambiance en arrière-plan */
.header::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 150%;
    height: 300%;
    background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 60%);
    transform: translate(-50%, -50%);
    animation: pulseGlow 8s ease-in-out infinite;
    z-index: -1;
    pointer-events: none;
}

@keyframes pulseGlow {
    0%, 100% { opacity: 0.5; transform: translate(-50%, -50%) scale(1); }
    50% { opacity: 1; transform: translate(-50%, -50%) scale(1.1); }
}

/* ---------------------------------------------------------------------
   14. Responsive (tablettes & mobiles)
   --------------------------------------------------------------------- */
@media (max-width: 768px) {
    body { padding: 1rem; }
    /* Haut inchange : le titre a 2rem de marge pour passer sous le bouton
       de thème, rogner ici le ferait remonter contre lui. Seul le bas cede. */
    .header { padding: 1.5rem 1rem 1rem; }
    .header h1 { font-size: 1.5rem; margin-top: 2rem; }
    .header p { font-size: 0.8125rem; }
    /* Les deux gardent 2,75 rem, soit 44 px : la règle « pointer: coarse » plus
       bas les y ramenait de toute façon, et deux tailles ecrites pour un même
       résultat finissent par diverger. */
    .theme-toggle { top: 0.75rem; right: 0.75rem; }
    .lang-selector { top: 0.75rem; right: 3.75rem; font-size: 0.75rem; }
    .content { padding: 1rem; }
    /* Le bouton d'aide des tiers etait centre VERTICALEMENT sur son groupe.
       Tant que les trois sous-modes tiennent sur une ligne, cela revient au
       même ; des qu'ils passent a deux lignes, au téléphone, il se retrouvait
       a cheval sur l'ecart entre les deux rangees, aligne sur aucune et
       flottant seul a droite.

       Il se cale donc sur la PREMIÈRE rangee : même hauteur qu'un bouton de
       sous-mode, decale du remplissage du groupe. Au passage il gagne les
       44 px attendus sous le doigt, contre 36 auparavant. */
    .tiers-help-btn {
        align-self: flex-start;
        margin-top: 0.625rem;
    }
    /* La section garde 1,75rem de remplissage sur grand écran. Au
       téléphone, c'est 56 px pris sur 320 : la grille des ingrédients
       n'avait plus que 198 px pour trois colonnes, et les noms les plus
       longs forcaient leur colonne a grossir. */
    .section { padding: 1rem; }
    .ingredients-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
    .controls { flex-direction: column; align-items: stretch; }
    .controls select { width: 100%; }
    /* En colonne, les éléments de la rangee sont etires sur toute la largeur.
    Le conteneur du champ de quantité l'etait aussi, alors que la croix est
    posee par rapport a LUI, a 4 px de son bord droit : elle se retrouvait a
    160 px du champ, seule au milieu de rien. Le conteneur garde donc sa
    taille, et la croix revient dans le champ comme sur grand écran. */
    .qty-wrapper { align-self: flex-start; }
    /* Touch targets ≥ 44px sur mobile (recommandation WCAG 2.2 / Apple HIG) */
    .btn-reset { padding: 0.625rem 1rem; min-height: 44px; }
    /* Quand le titre de section prend toute la largeur, le bouton passe a la
       ligne et se retrouvait cale a GAUCHE, juste au-dessus du premier champ :
       on le lisait comme une commande de la grille, pas de la section. Colle a
       droite, il reste rattache a son titre et s'eloigne des champs qu'il vide.
       Il demande confirmation de toute façon, ce n'est donc pas une garde mais
       une question de lecture. */
    .section-header .btn-reset {
        margin-left: auto;
        /* Quand le bouton passe a la ligne, il devient le DERNIER élément de
           l'en-tête, et plus rien ne le separe de la grille : mesure a 0 px, il
           touchait la première carte. Sur grand écran il reste sur la ligne du
           titre et c'est la description qui menage l'ecart, 20 px ; on reprend
           la même valeur pour que la grille commence au même endroit dans les
           deux dispositions. */
        margin-bottom: 1.25rem;
    }
    .objective-qty { min-height: 44px; }
    .ingredient-card input { min-height: 40px; font-size: 1rem; }
}

@media (max-width: 480px) {
    /* Header compact sur petit écran */
    .header { padding: 1.25rem 0.75rem 0.875rem; }
    /* Sous 480 px le titre occupe presque toute la largeur et passe donc
       DERRIÈRE le bouton de thème et le selecteur de langue, qui flottent en
       haut a droite. Mesure : le cadre du titre montait a 68 px quand celui des
       commandes descend a 72. Les lettres ne se touchaient pas, mais il ne
       restait rien comme marge, et un titre plus long aurait mordu. */
    .header h1 { font-size: 1.25rem; letter-spacing: 0; margin-top: 2.5rem; }
    /* Cartes ingrédients compactes : padding/img/input réduits pour ne pas tronquer */
    /* Trois colonnes plutot que deux : saisir seize nombres demandait 2,8
       ecrans de defilement, le pouce quittait le clavier a chaque ligne.
       La carte maigrit en consequence, mais le CHAMP garde sa hauteur : c'est
       lui qu'on vise, pas la carte. */
    .ingredients-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.375rem; }
    .ingredient-card { padding: 0.5rem 0.25rem; }
    .ingredient-card img { width: 32px; height: 32px; margin-bottom: 0.25rem; }
    .ingredient-card label { font-size: 0.6875rem; margin-bottom: 0.25rem; }
    .ingredient-card input { width: 100%; max-width: 100%; padding: 0.375rem 0.25rem; }
    .expert-target-row { grid-template-columns: 1fr 85px 20px; gap: 0.375rem; }
    .expert-target-row .expert-target-name { font-size: 0.8125rem; }
}

/* Très petits écrans (≤ 360 px : iPhone SE 1, vieux Android) */
@media (max-width: 360px) {
    .header { padding: 1rem 0.5rem; }
    /* L'entête est plus courte de 4 px ici : le titre a besoin d'autant de
       marge en plus pour garder le même écart avec les commandes flottantes. */
    .header h1 { font-size: 1rem; margin-top: 2.75rem; }
    /* Remplissage lateral reduit, comme au cran précédent : a 320 px la
       carte fait 70 px et « Mushroom » en demande 55. Avec 8 px de chaque
       cote il n'en restait que 50, et le nom se coupait. */
    .ingredient-card { padding: 0.5rem 0.25rem; }
    .ingredient-card img { width: 36px; height: 36px; }
    .ingredient-card label { font-size: 0.6875rem; }
    .ingredient-card input { font-size: 0.875rem; padding: 0.25rem; }
}

/* ---------------------------------------------------------------------
   15. Bulles décoratives - montent depuis le bas et éclatent vers 50-66% de hauteur
   --------------------------------------------------------------------- */
.bubbles {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: 50vh;
    pointer-events: none;
    z-index: 0;
    overflow: hidden;
}
.bubbles span {
    position: absolute;
    bottom: -2rem;
    display: block;
    width: 0.5rem;
    height: 0.5rem;
    background: radial-gradient(circle at 30% 30%,
        rgba(255, 255, 255, 0.9),
        var(--white-30) 60%,
        rgba(255, 255, 255, 0.05) 100%);
    border-radius: 50%;
    box-shadow: inset 0 0 2px rgba(255, 255, 255, 0.6);
    opacity: 0;
    animation: bubble-rise 6s linear infinite;
}
/* Position horizontale, taille et délai variables pour casser la régularité */
.bubbles span:nth-child(1)  { left:  5%; width: 0.4rem; height: 0.4rem; animation-duration: 5s;  animation-delay: 0s;   }
.bubbles span:nth-child(2)  { left: 12%; width: 0.6rem; height: 0.6rem; animation-duration: 7s;  animation-delay: 1.2s; }
.bubbles span:nth-child(3)  { left: 20%; width: 0.3rem; height: 0.3rem; animation-duration: 4.5s;animation-delay: 0.5s; }
.bubbles span:nth-child(4)  { left: 28%; width: 0.5rem; height: 0.5rem; animation-duration: 6s;  animation-delay: 2s;   }
.bubbles span:nth-child(5)  { left: 35%; width: 0.7rem; height: 0.7rem; animation-duration: 8s;  animation-delay: 0.8s; }
.bubbles span:nth-child(6)  { left: 43%; width: 0.4rem; height: 0.4rem; animation-duration: 5.5s;animation-delay: 1.5s; }
.bubbles span:nth-child(7)  { left: 52%; width: 0.5rem; height: 0.5rem; animation-duration: 6.5s;animation-delay: 0.3s; }
.bubbles span:nth-child(8)  { left: 60%; width: 0.3rem; height: 0.3rem; animation-duration: 4s;  animation-delay: 2.5s; }
.bubbles span:nth-child(9)  { left: 68%; width: 0.6rem; height: 0.6rem; animation-duration: 7.5s;animation-delay: 0.6s; }
.bubbles span:nth-child(10) { left: 76%; width: 0.4rem; height: 0.4rem; animation-duration: 5s;  animation-delay: 1.8s; }
.bubbles span:nth-child(11) { left: 83%; width: 0.5rem; height: 0.5rem; animation-duration: 6s;  animation-delay: 0.4s; }
.bubbles span:nth-child(12) { left: 90%; width: 0.7rem; height: 0.7rem; animation-duration: 8s;  animation-delay: 2.2s; }
.bubbles span:nth-child(13) { left: 95%; width: 0.3rem; height: 0.3rem; animation-duration: 4.5s;animation-delay: 1s;   }
.bubbles span:nth-child(14) { left: 15%; width: 0.5rem; height: 0.5rem; animation-duration: 7s;  animation-delay: 3s;   }
.bubbles span:nth-child(15) { left: 72%; width: 0.4rem; height: 0.4rem; animation-duration: 6s;  animation-delay: 2.8s; }

/*  0%   : invisible, en bas
   10%   : apparition
   60-80%: ascension (jusqu'au tiers/moitié inférieure de .bubbles, soit ~33-50% de l'écran)
   85%   : éclatement (scale + fade)
   100%  : disparue */
@keyframes bubble-rise {
    0%   { transform: translateY(0) scale(1);    opacity: 0; }
    10%  { opacity: 0.7; }
    70%  { transform: translateY(-40vh) scale(1); opacity: 0.7; }
    85%  { transform: translateY(-45vh) scale(1.8); opacity: 0; }
    100% { transform: translateY(-45vh) scale(1.8); opacity: 0; }
}

/* Accessibilité : respect du "reduced motion" */
@media (prefers-reduced-motion: reduce) {
    .bubbles { display: none; }
}

/* Mobile : moins de bulles visibles, hauteur réduite */
@media (max-width: 640px) {
    .bubbles span:nth-child(n+10) { display: none; }
}
/* ---------------------------------------------------------------------
   16. Éclairs décoratifs aléatoires (générés en JS) - partent des coins
   supérieurs vers le centre. Tablette + PC uniquement (≥768px).
   pointer-events:none, z-index:0.
   --------------------------------------------------------------------- */
.lightnings {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    overflow: hidden;
    display: none; /* activé en JS seulement sur tablette/PC */
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 70%);
    mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 70%);
}
.lightnings .bolt {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    filter: drop-shadow(0 0 5px rgba(150, 200, 255, 0.95))
            drop-shadow(0 0 12px rgba(120, 170, 255, 0.6));
    animation: bolt-flash 0.7s ease-out forwards;
}
.lightnings .bolt path {
    fill: none;
    stroke: #eaf3ff;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke; /* épaisseur constante quel que soit le viewBox */
}
.lightnings .bolt path.main   { stroke-width: 2.2; }
.lightnings .bolt path.branch { stroke-width: 1.4; opacity: 0.8; }

/* Flash bref avec une petite réplique */
@keyframes bolt-flash {
    0% { opacity: 0; }
    1% { opacity: 1; }      /* Premier impact ultra-rapide */
    5% { opacity: 0.2; }    /* Extinction partielle */
    7% { opacity: 1; }      /* Deuxième retour de courant */
    12% { opacity: 0.1; }   /* Extinction */
    15% { opacity: 0.9; }   /* Troisième lueur plus faible */
    25% { opacity: 0.8; }
    100% { opacity: 0; filter: drop-shadow(0 0 0px rgba(0,0,0,0)); } /* Évanouissement progressif */
}

/* Accessibilité : pas d'animation si l'utilisateur la refuse */
@media (prefers-reduced-motion: reduce) {
    .lightnings { display: none !important; }
}
/* =====================================================================
   Tableau des recettes - plein écran (lecture seule)
   ===================================================================== */
.recipe-table-launch { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 0.75rem; margin: 0.5rem 0 1rem; }
.recipe-table-launch .btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.375rem; text-decoration: none; }
.expert-weight-tools { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.75rem; }
.btn-open-recipe {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border-strong);
}
.btn-open-recipe:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
    border-color: var(--accent-primary);
}
.btn-open-recipe:active { transform: translateY(0); }
.btn-open-recipe:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.recipe-matrix-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    background: rgba(15, 23, 42, 0.55);
    /* Marge minimale autour du tableau : la place gagnee ici est de la
       place de lecture en plus. */
    padding: 0.125rem;
}
.recipe-matrix-overlay[hidden] { display: none; }
html.recipe-modal-open { overflow: hidden; }

.recipe-matrix-dialog {
    margin: auto;
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    max-width: 1400px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-strong);
    border-radius: 0.75rem;
    box-shadow: var(--shadow-xl);
    overflow: hidden;
}
.recipe-matrix-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    /* Marge reduite : chaque pixel rendu ici est une ligne de plus du
       tableau visible sans defilement. */
    padding: 0.375rem 1rem;
    border-bottom: 1px solid var(--border-color);
    background: var(--bg-tertiary);
}
.recipe-matrix-bar h2 { font-size: 1.125rem; margin: 0; color: var(--text-primary); }
.recipe-matrix-titlewrap {
    display: flex;
    align-items: baseline;
    gap: 0.25rem 0.75rem;
    flex-wrap: wrap;
    min-width: 0;
}
.recipe-matrix-stats {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-secondary);
    white-space: nowrap;
}
.recipe-matrix-close {
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-radius: 0.5rem;
    width: 2.25rem;
    height: 2.25rem;
    font-size: 1.125rem;
    cursor: pointer;
    transition: var(--transition);
}
.recipe-matrix-close:hover { color: var(--danger-text); border-color: var(--danger); }
.recipe-matrix-close:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Bouton "Adapter à l'écran" + mode mise à l'échelle (tout le tableau visible) */
.recipe-matrix-fit {
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-radius: 0.5rem;
    height: 2.25rem;
    padding: 0 0.75rem;
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: var(--transition);
}
.recipe-matrix-fit:hover { border-color: var(--accent-primary); }
.recipe-matrix-fit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.recipe-matrix-fit[aria-pressed="true"] {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: var(--text-on-accent);
}
.recipe-matrix-wrapper.is-fit { overflow: hidden; }

.recipe-matrix-wrapper { flex: 1; overflow: auto; -webkit-overflow-scrolling: touch; }

.recipe-matrix-table {
    border-collapse: separate;
    border-spacing: 0;
    background: var(--bg-secondary);
    width: 100%;
    table-layout: fixed;
    --hl: var(--card-hover);
}
.recipe-matrix-table th,
.recipe-matrix-table td {
    border-right: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
    text-align: center;
    padding: 2px;
    color: var(--text-primary);
    font-size: 0.875rem;
    overflow: hidden;
    word-break: break-word;
}
.recipe-matrix-table th {
    background: var(--bg-tertiary);
    font-weight: 600;
    position: sticky;
    z-index: 8;
    padding: 4px 4px;
}
.recipe-matrix-table th[scope="col"] { top: 0; z-index: 10; }
.recipe-matrix-table th[scope="row"] { left: 0; z-index: 9; }
.recipe-matrix-table th:first-child { top: 0; left: 0; z-index: 11; }
/* Bords externes (mode separate) : haut sur la 1re ligne, gauche sur la 1re colonne */
.recipe-matrix-table tr:first-child th { border-top: 1px solid var(--border-color); }
.recipe-matrix-table th:first-child { border-left: 1px solid var(--border-color); }
.recipe-matrix-table .matrix-corner {
    text-align: center;
    line-height: 1.15;
    white-space: nowrap;
    padding: 4px 6px;
}
.recipe-matrix-table .matrix-corner .corner-frac,
.recipe-matrix-table .matrix-corner .corner-pct {
    display: block;
}
.recipe-matrix-table .matrix-corner .corner-frac {
    font-size: 0.8125em;
    font-weight: 700;
}
.recipe-matrix-table .matrix-corner .corner-pct {
    font-size: 0.75em;
    font-weight: 600;
    opacity: .8;
}
.recipe-matrix-table img {
    width: 24px;
    height: 24px;
    display: block;
    margin: 0 auto 1px;
    border-radius: 3px;
    object-fit: contain;
}
.recipe-matrix-table small {
    font-size: 11px;
    line-height: 1.15;
    word-break: break-word;
    display: block;
}
.recipe-matrix-table .recipe-out-img { width: 24px; height: 24px; margin: 0 auto; }
.recipe-matrix-table .recipe-obj {
    display: block;
    max-width: 100%;
    margin: 0 auto;
    font-size: 12px;
    line-height: 1.15;
    word-break: break-word;
    overflow-wrap: anywhere;
    hyphens: auto;
}
.recipe-matrix-table td.filled { background: var(--success-bg); }
.recipe-matrix-table td.filled.is-craft { background: var(--craft-bg); }
.recipe-matrix-table td.diagonal-same { background: var(--border-strong); }

/* Surlignage croisé ligne + colonne */
.recipe-matrix-table td.row-hl,
.recipe-matrix-table td.col-hl { background: var(--hl); }
.recipe-matrix-table th.row-hl,
.recipe-matrix-table th.col-hl {
    /* Teinte translucide empilée sur le fond opaque de l'en-tête :
       le résultat reste opaque et masque le contenu qui défile dessous. */
    background:
        linear-gradient(var(--card-hover), var(--card-hover)),
        var(--bg-tertiary);
    box-shadow: inset 0 -3px 0 var(--accent-primary);
}
.recipe-matrix-table td.filled.row-hl,
.recipe-matrix-table td.filled.col-hl {
    background: linear-gradient(var(--hl), var(--hl)), var(--success-bg);
}
.recipe-matrix-table td.filled.is-craft.row-hl,
.recipe-matrix-table td.filled.is-craft.col-hl {
    background: linear-gradient(var(--hl), var(--hl)), var(--craft-bg);
}
.recipe-matrix-table td.diagonal-same.row-hl,
.recipe-matrix-table td.diagonal-same.col-hl {
    background: linear-gradient(var(--hl), var(--hl)), var(--border-strong);
}

@media (hover: hover) and (pointer: fine) {
    .recipe-matrix-table td:hover {
        outline: 2px solid var(--accent-primary);
        outline-offset: -2px;
    }
}

/* Infobulle */
.matrix-tip {
    position: fixed;
    z-index: 3000;
    transform: translate(-50%, -100%);
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    padding: 6px 8px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    pointer-events: none;
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transition: opacity 0.12s ease;
}
.matrix-tip.visible { opacity: 1; }
.matrix-tip-ing { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.matrix-tip-ing img { width: 28px; height: 28px; border-radius: 3px; object-fit: contain; }
.matrix-tip-ing small { font-size: 11px; font-weight: 600; }
.matrix-tip-plus { opacity: 0.6; font-weight: 700; }
.matrix-tip-obj { font-weight: 700; color: var(--accent-texte); }

@media (max-width: 600px) {
    .recipe-matrix-table th,
    .recipe-matrix-table td { padding: 2px; }
    .recipe-matrix-table img,
    .recipe-matrix-table .recipe-out-img { width: 22px; height: 22px; }
    .recipe-matrix-table .recipe-obj { font-size: 11px; }
    .recipe-matrix-bar h2 { font-size: 1rem; }
}
/* ====================================================================
   Mode LISTE DE DÉCOUVERTE (__discover_list__)
   Cartes de recettes proposées au choix de l'utilisateur.
   ==================================================================== */
.discover-list-intro {
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 8px;
}
.discover-option {
    border: 1px solid var(--border-color);
    border-radius: 12px;
    background: var(--bg-tertiary);
    padding: 12px;
    margin-bottom: 12px;
}
.discover-option:hover { border-color: var(--border-strong); }
.discover-option-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 6px;
}
.discover-option-title {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 1rem;
    color: var(--text-primary);
}
.discover-option-cost {
    font-size: 0.875rem;
    color: var(--text-muted);
    white-space: nowrap;
}
.discover-option-steps { margin: 8px 0; }
.discover-option .subtract-row { margin-top: 8px; }
@media (max-width: 600px) {
    .discover-option-head { align-items: flex-start; }
    .discover-option .btn-subtract { width: 100%; }
}

/* Mode cases vides : éléments spécifiques */
.discover-unknown {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--accent-texte);
}
.discover-option-obtain {
    font-size: 0.875rem;
    color: var(--text-secondary);
    margin: 6px 0 4px;
}
.discover-option-have {
    font-size: 0.875rem;
    color: var(--text-secondary);
    margin-top: 6px;
}
.discover-option-title .op { margin: 0 2px; }

/* Mode cases vides : barre d'actions (Réalisé / Déjà connue) */
.discover-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}
.discover-actions .btn { flex: 1 1 auto; }
.count-wrapper {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.count-wrapper input { width: 4.5rem; }
@media (max-width: 600px) {
    .discover-actions .btn { width: 100%; flex-basis: 100%; }
}

/* Bouton secondaire (ex : "Déjà connue") */
.btn-secondary {
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
}
.btn-secondary:hover { border-color: var(--border-strong); }

/* ------------------------------------------------------------------ *
 * Cibles tactiles : au doigt, une commande doit mesurer au moins 44px.
 * Ne s'applique qu'aux ecrans sans souris, la mise en page a la souris
 * reste donc inchangee.
 * ------------------------------------------------------------------ */
@media (pointer: coarse) {
    .theme-toggle,
    .lang-selector {
        min-width: 44px;
        min-height: 44px;
    }
    .ingredient-card input,
    .ingredient-input {
        min-height: 44px;
    }
    /* La case a cocher ne fait que 13px : c'est son etiquette qu'on vise. */
    .mode-toggle {
        min-height: 44px;
    }
    .social-link {
        min-width: 44px;
        min-height: 44px;
    }
    /* Zone de contact agrandie par un calque : rien ne bouge a l'écran,
       seul le doigt gagne de la place autour du bouton.

       PAS de position:relative ici. Elle sortait la croix du champ, ou elle
       est posee en absolu, et la renvoyait dans le flux : au téléphone, le
       signe d'effacement flottait a cote du champ au lieu d'être dedans. Un
       élément en position absolue ancre tout aussi bien son propre calque. */
    .btn-qty-clear::after {
        content: '';
        position: absolute;
        inset: -8px;
    }
    /* Même calque pour le bouton d'aide des tiers : 36 px a l'écran,
       52 px sous le doigt. L'elargir pour de bon aurait vole sa place
       au groupe voisin, qui serait alors passe sur trois lignes. */
    .tiers-help-btn { position: relative; }
    .tiers-help-btn::after {
        content: '';
        position: absolute;
        inset: -8px;
    }
}

/* ---------------------------------------------------------------------
   17. Pas-a-pas de quantité au doigt (tablette et mobile)

   Mesure faite avant d'ecrire une ligne, largeur UTILE d'une carte :

     | écran   | grille actuelle | utile  | grille au doigt | utile  |
     |---------|-----------------|--------|-----------------|--------|
     | 375 px  | 3 colonnes      |  76 px | 2 colonnes      | 124 px |
     | 600 px  | 3 colonnes      | 105 px | 2 colonnes      | 181 px |
     | 768 px  | 3 colonnes      | 174 px | 3 colonnes      | 174 px |
     | 1024 px | 6 colonnes      |  93 px | 4 colonnes      | 166 px |

   Deux touches de 40 px en demandent 80 : a 76 px d'utile, la demande
   d'origine - des touches DE PART ET D'AUTRE du champ - etait donc
   irrealisable telle quelle. C'est la grille qu'on elargit, pas les touches
   qu'on retrecit : des touches de 28 px rentraient, mais sous le seuil de
   44 px elles se manquent au pouce, et on aurait remplace une saisie penible
   par une saisie imprecise.

   La tablette en paysage (1024 px) est le cas qu'on aurait rate en ne
   regardant que le téléphone : elle a le moins de place de toutes avec ses
   six colonnes, alors qu'elle a l'écran le plus large.

   Seule #ingredients-grid est elargie. La grille des ingrédients CONNUS
   partage la classe mais n'a que des cases a cocher : lui faire perdre une
   colonne aurait allonge la page sans rien résoudre.
   --------------------------------------------------------------------- */
@media (pointer: coarse) {
    .btn-step {
        display: flex;
        align-items: center;
        justify-content: center;
        /* La touche CEDE la place, jamais le nombre. A 375 px le champ ne
           faisait que 40 px et "9999" y etait coupe de 6 px - sans barre de
           defilement, sans erreur : on lisait "999" et on croyait avoir 999.
           Les touches peuvent donc descendre de 40 a 32 px, et c'est le champ
           qui garde son minimum. */
        flex: 0 1 40px;
        min-width: 32px;
        min-height: 44px;
        position: relative;
        padding: 0;
        border: 1px solid var(--border-color);
        border-radius: 0.5rem;
        background: var(--bg-tertiary);
        color: var(--text-primary);
        font-size: 1.25rem;
        font-weight: 600;
        line-height: 1;
        cursor: pointer;
        /* Un appui maintenu sur une touche ne doit pas selectionner le signe
           ni ouvrir le menu de copie : on repete des appuis, pas du texte. */
        user-select: none;
        -webkit-user-select: none;
        -webkit-tap-highlight-color: transparent;
        transition: var(--transition);
    }
    .btn-step:active {
        background: var(--accent-primary);
        color: var(--text-on-accent);
        border-color: var(--accent-primary);
    }
    .btn-step:focus-visible {
        outline: none;
        box-shadow: var(--focus-ring);
    }
    /* Zone de contact elargie par un calque, comme pour la croix d'effacement :
       retrecie a 32 px la touche reste sous le seuil de 44 px au pouce. Le
       calque ne s'affiche pas et ne deplace rien - seul le doigt gagne de la
       place.

       Il est ASYMETRIQUE, et c'est une correction payee : un calque centre
       (inset: 0 -6px) debordait la carte de 1 px par la droite. Invisible, mais
       un debordement reel, que la mesure a vu et pas l'oeil. Vers l'exterieur
       il ne prend donc que les 4 px de marge interieure de la carte ; les 8 px
       restants vont vers le champ, qui garde plus de 40 px directement
       touchables. */
    .btn-step::after {
        content: '';
        position: absolute;
    }
    .btn-step:first-child::after { inset: 0 -8px 0 -4px; }
    .btn-step:last-child::after  { inset: 0 -4px 0 -8px; }
    /* Le champ prend ce qui reste. min-width:0 est indispensable : sans lui un
       élément de formulaire refuse de descendre sous sa largeur intrinseque et
       fait deborder la carte au lieu de se laisser retrecir. */
    .qty-stepper .ingredient-input {
        flex: 1 1 auto;
        width: auto;
        /* 48 px : la largeur mesuree pour que "9999" tienne en entier a la
           taille de police du téléphone, plus deux pixels de marge. C'est ce
           minimum qui force les touches a se retrecir, et non l'inverse. */
        min-width: 48px;
    }

    #ingredients-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (pointer: coarse) and (min-width: 700px) {
    #ingredients-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (pointer: coarse) and (min-width: 900px) {
    #ingredients-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ---------------------------------------------------------------------
   18. Reflet qui suit le pointeur, sur les deux boutons d'action

   C'est une COUCHE DE FOND, pas un calque pose par-dessus. Un pseudo-élément
   en position absolue se peindrait AU-DESSUS du libelle et en baisserait le
   contraste ; un fond passe dessous par construction.

   La forme a ete imposee par la mesure : un halo centre sous le pointeur
   eclaircit le fond LA OU EST LE TEXTE, et le contraste du libelle blanc tombe
   sous le seuil des 4,5 quelle que soit l'opacite choisie - il n'y a que 0,33
   de marge sans reflet. Le halo est donc ancre au bord haut, avec un rayon
   vertical qui s'eteint a 6,7 px alors que le texte commence a 12 px. Aucun
   caractère n'est touche, et le reflet peut rester franc.

   A la souris seulement : un écran tactile n'a pas de survol, le reflet y
   resterait colle après un appui. Et rien ne repose dessus - :hover, :active
   et :focus-visible sont intacts.
   --------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
    .btn-primary,
    .btn-subtract {
        /* La position par défaut est hors du bouton : tant que le pointeur
           n'est pas entre, aucun reflet n'est visible, même une fraction de
           seconde au premier survol. */
        --reflet-x: -999px;
    }
    .btn-primary:hover:not(:disabled),
    .btn-subtract:hover:not(:disabled) {
        background-image:
            radial-gradient(
                8rem 0.6rem at var(--reflet-x) 0,
                rgba(255, 255, 255, 0.30),
                rgba(255, 255, 255, 0) 70%
            ),
            var(--fond-bouton);
    }
}

/* Mouvement reduit : le reflet SUIT le pointeur, c'est donc du mouvement, et
   il disparait entierement. Le bouton retrouve exactement son fond d'origine. */
@media (prefers-reduced-motion: reduce) {
    .btn-primary:hover:not(:disabled),
    .btn-subtract:hover:not(:disabled) {
        background-image: var(--fond-bouton);
    }
}

/* ---------------------------------------------------------------------
   19. Au téléphone : une application, pas une page reduite

   Deux mesures sur un écran de 320 x 700 ont decide de cette section.

   En largeur, 98 des 320 px partaient en marges emboitees - 16 px de chaque
   cote sur le corps, puis sur .content, puis sur .section. Il restait 96 px
   utiles dans une carte la ou les touches + et - en demandent 114 : c'est ce
   qui les faisait deborder.

   En hauteur, la grille des ingrédients ne commencait qu'a 369 px, soit plus
   de la moitie du premier écran passee en en-tête avant le moindre champ.

   Sur un ordinateur ces marges aerent une page large ; sur un téléphone tenu a
   la main, elles prennent la place de ce qu'on est venu faire. La place se
   reprend donc sur les marges et les decorations, jamais sur le contenu : les
   phrases d'explication et la ligne de saison restent affichees, resserrees.
   --------------------------------------------------------------------- */
@media (max-width: 600px) {
    /* 1. Le corps de page ne garde AUCUNE marge : sur un téléphone, toute la
       largeur appartient au contenu.

       Consequence directe, et c'est pour cela que les deux vont ensemble : le
       degrade de fond du corps n'est visible QUE dans ces marges, puisque le
       conteneur le recouvre entierement. Sans marges, il n'a plus un pixel
       pour s'afficher. On le retire donc, ainsi que l'ombre interieure qui
       dessinait le même liséré : peindre un degrade que personne ne verra
       coute du travail a l'affichage et ne rend rien.

       Le conteneur perd du même coup ses coins arrondis et son ombre portee :
       colle aux bords de l'écran, un coin arrondi laisse voir un angle de fond
       et une ombre portee n'a aucune surface ou tomber. */
    body {
        padding: 0;
        /* Vaut zéro hors encoche ; ne reprend de la largeur qu'en paysage
           sur écran a encoche. */
        padding-left: env(safe-area-inset-left, 0px);
        padding-right: env(safe-area-inset-right, 0px);
        background: var(--bg-gradient-start);
        box-shadow: none;
    }
    .container {
        border-radius: 0;
        box-shadow: none;
    }
    .content { padding-left: 0.5rem; padding-right: 0.5rem; }
    .section {
        padding: 0.75rem;
        margin-bottom: 0.75rem;
        border-radius: 0.75rem;
    }

    /* 2. La grille des ingrédients va d'un bord a l'autre.
       Demande explicitement : on rogne la marge A GAUCHE du premier ingrédient
       et A DROITE du dernier, sans toucher a l'espace entre eux. Les marges
       negatives annulent exactement le remplissage de .section, donc le texte
       reste en retrait et seules les cartes touchent les bords. */
    #ingredients-grid {
        margin-left: -0.75rem;
        margin-right: -0.75rem;
    }

    /* 3. En-tête compacte, et les deux commandes en miroir.

       Elles etaient cote a cote dans le coin droit, ou elles se disputaient la
       place avec le titre : sur un écran etroit, celui-ci passait dessous. Une
       de chaque cote, a la même hauteur et a la même distance du bord, elles
       encadrent le titre au lieu de le pousser.

       « right: auto » est indispensable : sans lui, l'élément porterait un bord
       gauche ET un bord droit, et sa largeur ne serait plus celle qu'on croit. */
    .lang-selector {
        left: 0.5rem;
        right: auto;
    }
    .theme-toggle { right: 0.5rem; }

    .header { padding: 0.75rem 0.5rem 0.625rem; }
    .header h1 {
        font-size: 1.375rem;
        margin-bottom: 0.25rem;
        /* La lueur pulsee attire l'oeil vers le titre, c'est-a-dire vers ce
           qu'on a déjà lu. Sur un petit écran elle ne fait que bouger. */
        animation: none;
    }
    .header h1::after { display: none; }
    .header p { font-size: 0.75rem; }
    .header::before { display: none; }

    /* 4. Les phrases d'explication restent AFFICHEES, simplement resserrees.
       Je les avais mises hors écran pour gagner de la hauteur : c'etait une
       perte d'information, et elle a ete refusee. Gagner de la place ne doit
       jamais se payer en contenu retire au lecteur - la place se prend sur les
       marges et les decorations, pas sur ce qui se lit. */
    .section-desc {
        font-size: 0.75rem;
        line-height: 1.35;
        margin-bottom: 0.5rem;
    }

    /* 5. Titres de section resserres. */
    .section h2 { font-size: 1.0625rem; margin-bottom: 0.5rem; }

    /* 6. La ligne de saison garde TOUT : numéro de saison, date de mise a jour
       et taux de completude. J'en avais masque la fin pour gagner une ligne ;
       c'etait une perte d'information et elle a ete refusee, a juste titre. On
       la resserre donc au lieu de la couper. */
    .header p { font-size: 0.6875rem; line-height: 1.3; }
}

/* ---------------------------------------------------------------------
   20. Barre d'onglets du téléphone - TÉLÉPHONE UNIQUEMENT

   Trois vues, une seule affichee : Inventaire, Objectif, Résultat. On ne fait
   plus defiler entre les sections, on passe de l'une a l'autre.

   Tout est enferme dans cette requête de media. Au-dessus de 600 px, ni la
   barre ni les règles de masquage n'existent : l'attribut data-onglet peut
   être pose sur <body>, il ne produit rien. La tablette et l'ordinateur sont
   donc intouchables par construction.
   --------------------------------------------------------------------- */
.onglets { display: none; }
.results-empty { display: none; }

@media (max-width: 600px) {
    .onglets {
        display: flex;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 200;
        background: var(--bg-secondary);
        border-top: 1px solid var(--border-color);
        /* Encoche et barre de geste des telephones recents : sans cela, le
           dernier onglet se retrouve sous la barre du système. */
        padding-bottom: env(safe-area-inset-bottom, 0px);
        padding-left: env(safe-area-inset-left, 0px);
        padding-right: env(safe-area-inset-right, 0px);
    }
    .onglet {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        min-height: 56px;
        padding: 0.375rem 0.25rem;
        border: 0;
        background: none;
        color: var(--text-muted);
        font-size: 0.6875rem;
        font-weight: 600;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        transition: var(--transition);
    }
    .onglet-icone { font-size: 1.125rem; line-height: 1; }
    .onglet[aria-selected="true"] {
        color: var(--accent-texte);
        /* Un trait plein en haut de l'onglet actif : la couleur seule ne suffit
           pas a qui la distingue mal. */
        box-shadow: inset 0 3px 0 0 var(--accent-primary);
    }
    .onglet:focus-visible {
        outline: none;
        box-shadow: var(--focus-ring);
    }

    /* Réserve sous la barre d'onglets, posee en position fixe par-dessus la
       page. L'inset s'ajoute aux 72 px : la barre le réserve pour elle-même,
       et l'omettre ici cacherait le dernier élément du contenu. */
    body { padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }

    /* Une seule vue a la fois.
       ON NE MASQUE QUE CE QUI N'APPARTIENT PAS A L'ONGLET, et on ne touche
       JAMAIS au display de ce qui reste visible.

       La première version faisait l'inverse : tout masquer, puis remettre en
       « display: block » ce qui devait paraitre. Elle a casse l'espacement des
       boutons « Tableau des recettes » et « Scan d'inventaire », qui sont un
       conteneur FLEX : repasses en block, leur « gap: 12px » ne s'appliquait
       plus du tout et les deux boutons se touchaient. Aucune erreur, aucun
       debordement - juste deux boutons colles, et il a fallu un oeil pour le
       voir.

       Remettre « display: flex » sur ce seul conteneur aurait repare ce
       cas-la et laisse le piege en place pour le suivant : toute règle qui
       impose un display efface celui que l'élément avait choisi. */
    body[data-onglet="objectif"] #inventory-section,
    body[data-onglet="objectif"] #known-section,
    body[data-onglet="objectif"] #discover-warning,
    body[data-onglet="objectif"] .recipe-table-launch,
    body[data-onglet="objectif"] #results,
    body[data-onglet="resultat"] #inventory-section,
    body[data-onglet="resultat"] #known-section,
    body[data-onglet="resultat"] #discover-warning,
    body[data-onglet="resultat"] .recipe-table-launch,
    body[data-onglet="resultat"] #objective-section,
    body[data-onglet="inventaire"] #objective-section,
    body[data-onglet="inventaire"] #results {
        display: none;
    }
    /* Seule exception, et elle est legitime : une règle plus haut masque ce
       paragraphe a toutes les largeurs, il faut donc bien l'allumer. C'est un
       simple <p>, « block » est sa valeur naturelle. */
    body[data-onglet="resultat"][data-resultats="vide"] #results-empty {
        display: block;
    }

    .results-empty {
        margin: 2rem 0.5rem;
        padding: 1rem;
        border: 1px dashed var(--border-color);
        border-radius: 0.75rem;
        color: var(--text-muted);
        font-size: 0.875rem;
        text-align: center;
    }
}
