/* =====================================================================
   Grow Manager — interface v2
   =====================================================================

   UNE SEULE FEUILLE. Le style d'une page ne vit ici que s'il est
   reutilisable ; ce qui n'appartient qu'a une page reste dans sa balise
   <style>, et seulement dans ce cas. La v1 avait 975 lignes ici plus des
   blocs de 90 lignes dans index.php et edit_preset.php, souvent
   redondants entre eux et parfois divergents.

   DEUX THEMES, TROIS ETATS. Le visiteur peut avoir choisi clair, choisi
   sombre, ou n'avoir rien choisi — et ce troisieme cas est le plus
   frequent. On declare donc TOUTES les couleurs sur :root, on ne
   redefinit que les jetons sous prefers-color-scheme, et on les
   redefinit une troisieme fois sous [data-theme] pour que le bouton
   l'emporte dans les deux sens. Une couleur qui n'existerait que dans
   un bloc @media ne s'appliquerait jamais a l'etat par defaut.

   LE VERT RESTE. Ce n'est pas une couleur d'accent choisie au hasard :
   c'est un produit de culture. La v1 utilisait #2ecc71 sur blanc, qui
   ne passe pas le contraste pour du texte ; on garde la teinte et on
   fonce la variante portante.
   ===================================================================== */

:root {
  /* --- Rythme, typographie : independants de la palette -------------- */
  --r-xs: .25rem;  --r-sm: .45rem;  --r-md: .7rem;
  --r-lg: .95rem;  --r-xl: 1.5rem;  --r-2xl: 2.25rem;

  /* Angles adoucis, comme le demande la direction artistique — mais sans
     rendre a l'interieur des cartes l'air qu'on venait d'en retirer. On
     adoucit la FORME, pas la densite : ce sont deux reglages differents, et
     les confondre etait le plus court chemin vers une page a moitie vide. */
  --arrondi-sm: 10px;
  --arrondi:    14px;
  --arrondi-lg: 22px;
  --arrondi-plein: 999px;

  --largeur:      1280px;
  --largeur-texte: 68ch;

  --police: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
            Arial, sans-serif;
  --police-chiffres: ui-monospace, SFMono-Regular, "SF Mono", Menlo,
            Consolas, monospace;

  --transition: 160ms cubic-bezier(.4, 0, .2, 1);

  /* --- Echelle de texte -------------------------------------------------
     HUIT tailles, et pas une de plus. La feuille en comptait treize, posees
     au fur et a mesure des composants : .68, .7, .72, .78, .8, .85, .875,
     .9, .95, .98, 1.02, 1.08, 1.4, 1.5. Personne ne pouvait dire laquelle
     etait la bonne pour un element neuf, donc chacun en ajoutait une.
     Les ecarts de moins d'un pixel ne se voyaient pas un par un ; ensemble,
     ils donnaient une page dont rien n'etait aligne.

       2xs  etiquette de grille, entete de calendrier, tuile de panneau
       xs   etiquette de mesure, entete de tableau, pastille
       sm   aide, note, jour de calendrier, ligne de palette
       md   texte secondaire dense, tableau, libelle de champ
       base texte courant, h3, titre de l'en-tete
       lg   h2
       xl   h1
       2xl  valeur d'une mesure — le chiffre est l'objet principal        */
  --t-2xs:  .7rem;
  --t-xs:   .78rem;
  --t-sm:   .85rem;
  --t-md:   .9rem;
  --t-base: 1rem;
  --t-lg:   1.1rem;
  --t-xl:   1.4rem;
  --t-2xl:  1.5rem;

  /* Quatre graisses. La feuille en avait sept, dont 620 et 650 cote a cote
     sur deux titres voisins — un ecart que l'oeil ne lit pas comme une
     hierarchie, seulement comme une irregularite. */
  --g-normal: 500;
  --g-moyen:  550;
  --g-fort:   600;
  --g-titre:  650;

  /* --- Elevation ---------------------------------------------------------
     TROIS niveaux, choisis par ROLE et jamais par gout :

       0  pose      bordure seule .............. .carte, .mesure, .tuile
       1  flottant  --ombre .................... .carte-posee
       2  superpose --ombre-haute .............. .panneau, .modale-boite

     Un objet ne monte d'un niveau que s'il recouvre reellement ce qui est
     dessous. Une ombre sur chaque bloc ne cree pas de relief : elle le
     supprime, parce que plus rien ne se detache de rien.                  */
}

/* PALETTES:DEBUT — genere par tools/gen_palettes.py, ne pas editer a la main */
/*
   Cinq palettes, chacune en clair et en sombre.

   Le sombre est ecrit DEUX fois a dessein : sous prefers-color-scheme pour
   qui n'a rien choisi — le cas le plus frequent — et sous [data-theme="dark"]
   pour que le bouton l'emporte sur un systeme regle en clair. Une couleur qui
   n'existerait que dans le bloc @media ne s'appliquerait jamais a l'etat par
   defaut, et la page melangerait les deux themes.

   Les fonds ne sont jamais des gris purs : chacun porte une pointe de la
   teinte de son accent. C'est ce qui separe une interface de jardin d'un
   tableau de bord d'instrumentation.
*/

:root, :root[data-palette="chlorophylle"] {
  --accent: #1d6b45;
  --accent-vif: #2a8a5a;
  --accent-doux: #e7f3ea;
  --accent-fonce: #12502f;
  --accent-2: #7a5cc4;
  --fond: #f6f1e6;
  --surface: #fffdf7;
  --surface-haute: #fffdf7;
  --texte: #1e241f;
  --texte-doux: #6a7168;
  --bordure: #e6dfcd;
  --bordure-forte: #d2c9b2;
  --danger: #c0392b;
  --danger-fond: #fbeae7;
  --alerte: #a06a08;
  --alerte-fond: #fdf3e0;
  --info: #2f6f9e;
  --info-fond: #eaf1f7;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(60, 45, 30, .06), 0 4px 12px rgba(60, 45, 30, .05);
  --ombre-haute: 0 8px 30px rgba(60, 45, 30, .16);
  color-scheme: light;
}

:root[data-palette="serre"] {
  --accent: #3f8f5f;
  --accent-vif: #4faa72;
  --accent-doux: #e9f4ec;
  --accent-fonce: #2c6b46;
  --accent-2: #6f6bbd;
  --fond: #f7f6f1;
  --surface: #fffdf8;
  --surface-haute: #fffdf8;
  --texte: #23261f;
  --texte-doux: #6c7266;
  --bordure: #e7e3d9;
  --bordure-forte: #d3cec0;
  --danger: #c0392b;
  --danger-fond: #fbeae7;
  --alerte: #a06a08;
  --alerte-fond: #fdf3e0;
  --info: #2f6f9e;
  --info-fond: #eaf1f7;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(60, 45, 30, .06), 0 4px 12px rgba(60, 45, 30, .05);
  --ombre-haute: 0 8px 30px rgba(60, 45, 30, .16);
  color-scheme: light;
}

:root[data-palette="terre"] {
  --accent: #b0603c;
  --accent-vif: #c5714b;
  --accent-doux: #fbeee7;
  --accent-fonce: #8a4527;
  --accent-2: #6b8f5e;
  --fond: #faf6f2;
  --surface: #fffcf9;
  --surface-haute: #fffcf9;
  --texte: #2a221e;
  --texte-doux: #75665e;
  --bordure: #ece2d9;
  --bordure-forte: #d9c9bc;
  --danger: #c0392b;
  --danger-fond: #fbeae7;
  --alerte: #a06a08;
  --alerte-fond: #fdf3e0;
  --info: #2f6f9e;
  --info-fond: #eaf1f7;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(60, 45, 30, .06), 0 4px 12px rgba(60, 45, 30, .05);
  --ombre-haute: 0 8px 30px rgba(60, 45, 30, .16);
  color-scheme: light;
}

:root[data-palette="miel"] {
  --accent: #a5711a;
  --accent-vif: #c08a2a;
  --accent-doux: #fdf3df;
  --accent-fonce: #7f560d;
  --accent-2: #7d6aa8;
  --fond: #faf7ef;
  --surface: #fffdf7;
  --surface-haute: #fffdf7;
  --texte: #2a2519;
  --texte-doux: #736b57;
  --bordure: #ece5d3;
  --bordure-forte: #dbd0b6;
  --danger: #c0392b;
  --danger-fond: #fbeae7;
  --alerte: #a06a08;
  --alerte-fond: #fdf3e0;
  --info: #2f6f9e;
  --info-fond: #eaf1f7;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(60, 45, 30, .06), 0 4px 12px rgba(60, 45, 30, .05);
  --ombre-haute: 0 8px 30px rgba(60, 45, 30, .16);
  color-scheme: light;
}

:root[data-palette="mousse"] {
  --accent: #4a7c3f;
  --accent-vif: #5b9150;
  --accent-doux: #edf4e8;
  --accent-fonce: #35602c;
  --accent-2: #a8763c;
  --fond: #f6f7f2;
  --surface: #fffefa;
  --surface-haute: #fffefa;
  --texte: #21261c;
  --texte-doux: #697061;
  --bordure: #e5e8db;
  --bordure-forte: #cfd4c1;
  --danger: #c0392b;
  --danger-fond: #fbeae7;
  --alerte: #a06a08;
  --alerte-fond: #fdf3e0;
  --info: #2f6f9e;
  --info-fond: #eaf1f7;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(60, 45, 30, .06), 0 4px 12px rgba(60, 45, 30, .05);
  --ombre-haute: 0 8px 30px rgba(60, 45, 30, .16);
  color-scheme: light;
}

:root[data-palette="grenat"] {
  --accent: #9c4055;
  --accent-vif: #b25068;
  --accent-doux: #fbecef;
  --accent-fonce: #782c3e;
  --accent-2: #4f7f7a;
  --fond: #faf5f5;
  --surface: #fffbfb;
  --surface-haute: #fffbfb;
  --texte: #2a1f22;
  --texte-doux: #776166;
  --bordure: #eddfe1;
  --bordure-forte: #dcc6ca;
  --danger: #c0392b;
  --danger-fond: #fbeae7;
  --alerte: #a06a08;
  --alerte-fond: #fdf3e0;
  --info: #2f6f9e;
  --info-fond: #eaf1f7;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(60, 45, 30, .06), 0 4px 12px rgba(60, 45, 30, .05);
  --ombre-haute: 0 8px 30px rgba(60, 45, 30, .16);
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-palette]) {
    --accent: #3fd98a;
    --accent-vif: #35c078;
    --accent-doux: #12261b;
    --accent-fonce: #8bf0b8;
    --accent-2: #b39cf5;
    --fond: #0d0f0d;
    --surface: #161a16;
    --surface-haute: #1e231e;
    --texte: #eaf0ea;
    --texte-doux: #94a094;
    --bordure: #232922;
    --bordure-forte: #333c32;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
  :root:not([data-theme="light"])[data-palette="chlorophylle"] {
    --accent: #3fd98a;
    --accent-vif: #35c078;
    --accent-doux: #12261b;
    --accent-fonce: #8bf0b8;
    --accent-2: #b39cf5;
    --fond: #0d0f0d;
    --surface: #161a16;
    --surface-haute: #1e231e;
    --texte: #eaf0ea;
    --texte-doux: #94a094;
    --bordure: #232922;
    --bordure-forte: #333c32;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
  :root:not([data-theme="light"])[data-palette="serre"] {
    --accent: #7ecb92;
    --accent-vif: #6dbb82;
    --accent-doux: #1c261f;
    --accent-fonce: #a9e6b8;
    --accent-2: #a79bf0;
    --fond: #14160f;
    --surface: #1d211a;
    --surface-haute: #252a21;
    --texte: #eceee4;
    --texte-doux: #9aa392;
    --bordure: #2b3127;
    --bordure-forte: #3d4537;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
  :root:not([data-theme="light"])[data-palette="terre"] {
    --accent: #e79170;
    --accent-vif: #d2805f;
    --accent-doux: #2a1c15;
    --accent-fonce: #f6bda6;
    --accent-2: #9dc98f;
    --fond: #17120f;
    --surface: #211a16;
    --surface-haute: #2a221d;
    --texte: #f1e8e2;
    --texte-doux: #a89689;
    --bordure: #33281f;
    --bordure-forte: #463830;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
  :root:not([data-theme="light"])[data-palette="miel"] {
    --accent: #e6b45c;
    --accent-vif: #cf9f49;
    --accent-doux: #2a2313;
    --accent-fonce: #f7d79a;
    --accent-2: #b6a3e0;
    --fond: #16140d;
    --surface: #201c13;
    --surface-haute: #29241a;
    --texte: #f2ece0;
    --texte-doux: #a89c82;
    --bordure: #322c1e;
    --bordure-forte: #453d2a;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
  :root:not([data-theme="light"])[data-palette="mousse"] {
    --accent: #92c97f;
    --accent-vif: #80b76e;
    --accent-doux: #1d2618;
    --accent-fonce: #b9e4a8;
    --accent-2: #e0b071;
    --fond: #131610;
    --surface: #1c2118;
    --surface-haute: #242a1f;
    --texte: #eaefe3;
    --texte-doux: #98a38d;
    --bordure: #2a3124;
    --bordure-forte: #3b4433;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
  :root:not([data-theme="light"])[data-palette="grenat"] {
    --accent: #e58a9d;
    --accent-vif: #cd788a;
    --accent-doux: #2a1a1e;
    --accent-fonce: #f4b6c3;
    --accent-2: #8fc3bd;
    --fond: #171113;
    --surface: #211a1c;
    --surface-haute: #2a2124;
    --texte: #f2e8ea;
    --texte-doux: #a89297;
    --bordure: #332529;
    --bordure-forte: #463338;
    --danger: #ff8a7a;
    --danger-fond: #2b1815;
    --alerte: #f0b95f;
    --alerte-fond: #2a2113;
    --info: #7fbde0;
    --info-fond: #13212b;
    --ok: var(--accent);
    --ok-fond: var(--accent-doux);
    --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
    --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
}

:root[data-theme="dark"]:not([data-palette]) {
  --accent: #3fd98a;
  --accent-vif: #35c078;
  --accent-doux: #12261b;
  --accent-fonce: #8bf0b8;
  --accent-2: #b39cf5;
  --fond: #0d0f0d;
  --surface: #161a16;
  --surface-haute: #1e231e;
  --texte: #eaf0ea;
  --texte-doux: #94a094;
  --bordure: #232922;
  --bordure-forte: #333c32;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

:root[data-theme="dark"][data-palette="chlorophylle"] {
  --accent: #3fd98a;
  --accent-vif: #35c078;
  --accent-doux: #12261b;
  --accent-fonce: #8bf0b8;
  --accent-2: #b39cf5;
  --fond: #0d0f0d;
  --surface: #161a16;
  --surface-haute: #1e231e;
  --texte: #eaf0ea;
  --texte-doux: #94a094;
  --bordure: #232922;
  --bordure-forte: #333c32;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

:root[data-theme="dark"][data-palette="serre"] {
  --accent: #7ecb92;
  --accent-vif: #6dbb82;
  --accent-doux: #1c261f;
  --accent-fonce: #a9e6b8;
  --accent-2: #a79bf0;
  --fond: #14160f;
  --surface: #1d211a;
  --surface-haute: #252a21;
  --texte: #eceee4;
  --texte-doux: #9aa392;
  --bordure: #2b3127;
  --bordure-forte: #3d4537;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

:root[data-theme="dark"][data-palette="terre"] {
  --accent: #e79170;
  --accent-vif: #d2805f;
  --accent-doux: #2a1c15;
  --accent-fonce: #f6bda6;
  --accent-2: #9dc98f;
  --fond: #17120f;
  --surface: #211a16;
  --surface-haute: #2a221d;
  --texte: #f1e8e2;
  --texte-doux: #a89689;
  --bordure: #33281f;
  --bordure-forte: #463830;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

:root[data-theme="dark"][data-palette="miel"] {
  --accent: #e6b45c;
  --accent-vif: #cf9f49;
  --accent-doux: #2a2313;
  --accent-fonce: #f7d79a;
  --accent-2: #b6a3e0;
  --fond: #16140d;
  --surface: #201c13;
  --surface-haute: #29241a;
  --texte: #f2ece0;
  --texte-doux: #a89c82;
  --bordure: #322c1e;
  --bordure-forte: #453d2a;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

:root[data-theme="dark"][data-palette="mousse"] {
  --accent: #92c97f;
  --accent-vif: #80b76e;
  --accent-doux: #1d2618;
  --accent-fonce: #b9e4a8;
  --accent-2: #e0b071;
  --fond: #131610;
  --surface: #1c2118;
  --surface-haute: #242a1f;
  --texte: #eaefe3;
  --texte-doux: #98a38d;
  --bordure: #2a3124;
  --bordure-forte: #3b4433;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

:root[data-theme="dark"][data-palette="grenat"] {
  --accent: #e58a9d;
  --accent-vif: #cd788a;
  --accent-doux: #2a1a1e;
  --accent-fonce: #f4b6c3;
  --accent-2: #8fc3bd;
  --fond: #171113;
  --surface: #211a1c;
  --surface-haute: #2a2124;
  --texte: #f2e8ea;
  --texte-doux: #a89297;
  --bordure: #332529;
  --bordure-forte: #463338;
  --danger: #ff8a7a;
  --danger-fond: #2b1815;
  --alerte: #f0b95f;
  --alerte-fond: #2a2113;
  --info: #7fbde0;
  --info-fond: #13212b;
  --ok: var(--accent);
  --ok-fond: var(--accent-doux);
  --ombre: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 14px rgba(0, 0, 0, .3);
  --ombre-haute: 0 10px 34px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

/* PALETTES:FIN */

/* =====================================================================
   Base
   ===================================================================== */

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

/* CACHE VEUT DIRE CACHE.
   Le navigateur applique `[hidden] { display: none }` dans SA feuille, et
   n'importe quelle regle ecrite ici l'emporte sur elle, quelle que soit la
   specificite : une feuille d'auteur passe toujours devant celle de l'agent.
   Il suffisait donc d'un `.champ { display: flex }` pour qu'un element marque
   hidden reste visible — le champ de saisie manuelle du reseau s'affichait en
   permanence, et le bloc de la pompe a nutriment ne disparaissait plus quand
   on debranchait l'arrosage. Le defaut ne se voit pas a la lecture : les deux
   regles sont dans deux fichiers differents, et JavaScript croit avoir cache
   l'element puisque la propriete hidden est bien posee.
   Une seule regle ici evite d'avoir a y penser composant par composant. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  font-family: var(--police);
  font-size: 16px;
  line-height: 1.55;
  color: var(--texte);
  background: var(--fond);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 var(--r-sm); line-height: 1.2; text-wrap: balance; }
h1 { font-size: var(--t-xl);   font-weight: var(--g-titre); letter-spacing: -.02em; }
h2 { font-size: var(--t-lg);   font-weight: var(--g-titre); letter-spacing: -.01em; }
h3 { font-size: var(--t-base); font-weight: var(--g-fort); }
p  { margin: 0 0 var(--r-md); }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Un focus visible partout : c'est la seule facon de naviguer au clavier. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--arrondi-sm);
}

img, svg { max-width: 100%; }
svg { display: block; }

/* Legende, unite, mention discrete. */
.doux { color: var(--texte-doux); }
.petit { font-size: var(--t-md); }
.chiffres { font-variant-numeric: tabular-nums; font-family: var(--police-chiffres); }

/* Reserve au lecteur d'ecran : sert aux libelles des boutons-icones. */
.sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* =====================================================================
   Ossature : en-tete colle, contenu large
   ===================================================================== */

.app-entete {
  position: sticky; top: 0; z-index: 40;
  /* Repere pour les panneaux qui s'ouvrent sous un bouton de l'en-tete. */
  display: flex; align-items: center; gap: var(--r-md);
  padding: var(--r-sm) var(--r-lg);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--bordure);
}
.app-entete .titre { font-size: var(--t-base); font-weight: var(--g-fort); }
.app-entete .pousse { margin-left: auto; }
/* ENFANT DIRECT, pas descendant. `.app-entete nav` attrapait aussi le <nav>
   de la grille d'icones du panneau d'administration — qui vit dans l'en-tete —
   et son display:flex ecrasait la grille : les huit tuiles prenaient des
   largeurs de 27 a 86 px sur une seule ligne au lieu de trois colonnes
   regulieres. Une specificite de (0,1,1) contre (0,1,0), invisible a la
   lecture des deux regles prises separement. */
.app-entete > nav { display: flex; align-items: center; gap: var(--r-xs); }

/* Le bouton de menu n'existe que replie : partout ailleurs la barre a la
   place de montrer ses commandes. */
#gm-menu { display: none; }
@media (max-width: 560px) and (orientation: portrait) {
  /* Ne restent que le nom de la box et son voyant. Les huit commandes se
     partageaient 360 px et n'etaient plus que des points ; elles passent dans
     le menu. Le titre prend la place et se coupe proprement s'il est long. */
  .app-entete > a.btn-icone,
  .app-entete > #gm-admin,
  .app-entete > nav.pousse { display: none; }
  #gm-menu { display: inline-flex; margin-left: auto; }
  .app-entete .titre {
    flex: 1 1 auto; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* Les panneaux ancres sous un bouton masque s'etalent sur la largeur. */
  #panneau-admin, #panneau-palette { left: var(--r-md); right: var(--r-md); width: auto; }
}
/* Le menu : une liste, une commande par ligne, l'icone devant. Les entrees
   sont des boutons ou des liens ; ils ont le meme aspect. */
.menu-replie { display: flex; flex-direction: column; gap: 2px; }
.menu-replie > a, .menu-replie > button {
  display: flex; align-items: center; gap: var(--r-sm);
  padding: .55rem .7rem; border: 0; border-radius: var(--arrondi-sm);
  background: transparent; color: var(--texte); font: inherit;
  text-align: left; text-decoration: none; cursor: pointer;
}
.menu-replie > a:hover, .menu-replie > button:hover { background: var(--accent-doux); color: var(--accent); }
/* La bascule de version, en tete du menu : visible sans chercher. */
.menu-replie > a.bascule-version { background: var(--accent-doux); color: var(--accent); font-weight: var(--g-fort); margin-bottom: var(--r-xs); }
.menu-replie svg { width: 20px; height: 20px; flex: none; color: var(--texte-doux); }
.menu-replie > a:hover svg, .menu-replie > button:hover svg { color: var(--accent); }
.menu-replie .lettres {
  width: 20px; flex: none; text-align: center;
  font-size: var(--t-2xs); font-weight: var(--g-fort); color: var(--texte-doux);
}

/* Panneau ouvert sous un bouton de l'en-tete — l'administration aujourd'hui.
   Carre et compact : c'est une grille d'icones, pas une liste de liens. Il se
   pose PAR-DESSUS la page au lieu de la pousser, sinon l'ouvrir deplacerait
   tout ce qu'on etait en train de lire. */
.panneau {
  position: absolute; top: calc(100% + 6px); left: var(--r-lg);
  z-index: 50; width: min(360px, calc(100vw - 2 * var(--r-md)));
  padding: var(--r-md);
  background: var(--surface-haute);
  border: 1px solid var(--bordure);
  border-radius: var(--arrondi-lg);
  box-shadow: var(--ombre-haute);
}
.panneau[hidden] { display: none; }
/* Panneau accroche a DROITE : le bouton de palette vit dans la barre d'outils
   de droite ; l'ouvrir a gauche l'aurait fait apparaitre a l'oppose du doigt
   qui vient de le toucher. */
.panneau-droite { left: auto; right: var(--r-lg); }
@media (max-width: 480px) { .panneau-droite { left: var(--r-md); right: var(--r-md); } }

.palettes { display: grid; gap: 2px; }
.palette {
  display: flex; align-items: center; gap: var(--r-sm);
  padding: .4rem .5rem;
  font: inherit; font-size: var(--t-sm); color: var(--texte);
  background: none; border: 0; border-radius: var(--arrondi-sm);
  cursor: pointer; text-align: left; width: 100%;
}
.palette:hover { background: var(--accent-doux); }
.palette[aria-current="true"] { font-weight: var(--g-titre); background: var(--accent-doux); }
/* La pastille montre la couleur d'accent ET le fond de la palette : choisir
   sur un seul disque reviendrait a choisir a l'aveugle son fond de page. */
.palette-pastille {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--bordure-forte);
}
.pal-chlorophylle { background: linear-gradient(135deg, #1d6b45 50%, #f6f1e6 50%); }
.pal-serre  { background: linear-gradient(135deg, #3f8f5f 50%, #f7f6f1 50%); }
.pal-terre  { background: linear-gradient(135deg, #b0603c 50%, #faf6f2 50%); }
.pal-miel   { background: linear-gradient(135deg, #a5711a 50%, #faf7ef 50%); }
.pal-mousse { background: linear-gradient(135deg, #4a7c3f 50%, #f6f7f2 50%); }
.pal-grenat { background: linear-gradient(135deg, #9c4055 50%, #faf5f5 50%); }
/* En sombre, la pastille montre les couleurs SOMBRES de la palette : sinon on
   choisirait sur un apercu clair un theme qu'on verra sombre. Le bloc @media
   couvre « rien de choisi », le bloc [data-theme] couvre le bouton. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .pal-chlorophylle { background: linear-gradient(135deg, #3fd98a 50%, #0d0f0d 50%); }
  :root:not([data-theme="light"]) .pal-serre  { background: linear-gradient(135deg, #7ecb92 50%, #14160f 50%); }
  :root:not([data-theme="light"]) .pal-terre  { background: linear-gradient(135deg, #e79170 50%, #17120f 50%); }
  :root:not([data-theme="light"]) .pal-miel   { background: linear-gradient(135deg, #e6b45c 50%, #16140d 50%); }
  :root:not([data-theme="light"]) .pal-mousse { background: linear-gradient(135deg, #92c97f 50%, #131610 50%); }
  :root:not([data-theme="light"]) .pal-grenat { background: linear-gradient(135deg, #e58a9d 50%, #171113 50%); }
}
:root[data-theme="dark"] .pal-chlorophylle { background: linear-gradient(135deg, #3fd98a 50%, #0d0f0d 50%); }
:root[data-theme="dark"] .pal-serre  { background: linear-gradient(135deg, #7ecb92 50%, #14160f 50%); }
:root[data-theme="dark"] .pal-terre  { background: linear-gradient(135deg, #e79170 50%, #17120f 50%); }
:root[data-theme="dark"] .pal-miel   { background: linear-gradient(135deg, #e6b45c 50%, #16140d 50%); }
:root[data-theme="dark"] .pal-mousse { background: linear-gradient(135deg, #92c97f 50%, #131610 50%); }
:root[data-theme="dark"] .pal-grenat { background: linear-gradient(135deg, #e58a9d 50%, #171113 50%); }

.panneau-titre {
  font-size: var(--t-2xs); font-weight: var(--g-titre); letter-spacing: .08em;
  color: var(--texte-doux); margin-bottom: var(--r-sm);
}
/* TROIS colonnes pour neuf destinations : le panneau est alors exactement
   carre, comme demande, et aucune ligne ne reste a moitie vide. En quatre
   colonnes les huit premieres tenaient juste, mais la neuvieme tombait seule
   au rang suivant — et chaque tuile n'avait que 84 px pour porter un mot
   comme « Precommandes ». */
.panneau .tuiles { grid-template-columns: repeat(3, 1fr); gap: var(--r-xs); }
.panneau .tuile { padding: var(--r-sm) var(--r-xs); }
.panneau .tuile svg { width: 20px; height: 20px; }
.panneau .tuile span { font-size: var(--t-2xs); }
@media (max-width: 480px) {
  .panneau { left: var(--r-md); right: var(--r-md); width: auto; }
}

.app-corps {
  max-width: var(--largeur);
  margin: 0 auto;
  padding: var(--r-lg);
}
@media (max-width: 700px) { .app-corps { padding: var(--r-md); } }

.texte-lisible { max-width: var(--largeur-texte); }

/* =====================================================================
   Grilles
   ===================================================================== */

/* Les cartes remplissent la largeur disponible sans jamais laisser une
   ligne a moitie vide : auto-fit ramene les colonnes au contenu. */
.grille {
  display: grid;
  gap: var(--r-md);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
/* Les mesures sont compactes : a 260 px la quatrieme tombait seule au rang
   suivant sur un ecran courant. A 200 px les quatre tiennent sur une ligne —
   mais un telephone n'en mettait alors qu'une par rang, soit quatre ecrans
   pour quatre chiffres. On descend a 160 px : quatre de front sur un
   ordinateur, deux par deux sur un telephone. */
.grille-mesures { grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr)); }
.grille-large { --min: 340px; grid-template-columns: repeat(auto-fit, minmax(min(var(--min), 100%), 1fr)); }
.pile { display: flex; flex-direction: column; gap: var(--r-md); }

/* Deux blocs cote a cote, le premier plus large que le second : un programme
   porte une liste, un arrosage porte un champ. Leur donner la meme largeur
   etirerait le second sur du vide. Ils s'empilent des que la place manque. */
.duo {
  display: grid; gap: var(--r-md);
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  /* Les deux cartes font la meme hauteur : posees cote a cote, deux cadres
     de hauteurs differentes se lisent comme un defaut d'alignement, pas
     comme deux contenus. */
  align-items: stretch;
}
@media (max-width: 900px) { .duo { grid-template-columns: 1fr; } }
.rang { display: flex; align-items: center; gap: var(--r-sm); flex-wrap: wrap; }

/* =====================================================================
   Surfaces
   ===================================================================== */

.carte {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--arrondi-lg);
  padding: var(--r-lg);
}
/* L'ombre distingue ce qui FLOTTE. La donner a toutes les cartes aplatit
   la hierarchie au lieu de la creer. */
.carte-posee { box-shadow: var(--ombre); }

.carte-entete {
  display: flex; align-items: center; gap: var(--r-sm);
  margin-bottom: var(--r-md);
}
.carte-entete h2, .carte-entete h3 { margin: 0; }
.carte-entete .pousse { margin-left: auto; }

/* =====================================================================
   Boutons
   ===================================================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--r-xs);
  padding: .6rem 1.1rem;
  font: inherit; font-weight: var(--g-moyen);
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--arrondi);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition),
              transform var(--transition);
}
.btn:hover { border-color: var(--accent); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

.btn-fort {
  color: #fff; background: var(--accent); border-color: var(--accent);
}
.btn-fort:hover { background: var(--accent-fonce); border-color: var(--accent-fonce); }
:root[data-theme="dark"] .btn-fort,
:root:not([data-theme="light"]) .btn-fort { color: #06120b; }

.btn-danger { color: var(--danger); border-color: var(--danger); background: transparent; }
.btn-danger:hover { background: var(--danger-fond); }

/* LA ZONE IRREVERSIBLE, EN BAS ET A DROITE.
   Elle se detache par un filet plutot que par une couleur : rouge sur rouge,
   l'avertissement et le bouton se seraient annules. Le bouton est a droite,
   loin des champs qu'on vient de remplir, pour qu'on ne l'atteigne pas dans
   la lancee du formulaire precedent. */
.zone-danger {
  display: flex; flex-direction: column; align-items: flex-end;
  gap: var(--r-sm); text-align: right;
  margin-top: var(--r-md); padding-top: var(--r-md);
  border-top: 1px solid var(--bordure);
}
.zone-danger p { margin: 0; max-width: 46ch; }

/* Bouton-icone : la cible tactile fait 44 px meme si l'icone en fait 20. */
.btn-icone {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  color: var(--texte-doux);
  background: transparent; border: 0; border-radius: var(--arrondi);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.btn-icone:hover { background: var(--accent-doux); color: var(--accent); }
.btn-icone svg { width: 20px; height: 20px; }
/* La petite : pour une commande posee a cote d'une ligne de texte, ou 44 px
   de cote pesent plus lourd que le texte lui-meme. Le rond de 32 px reste une
   cible correcte au pouce. */
/* Le rond : un « + » cercle, pose a cote du crayon dans l'en-tete d'une
   carte. Il ouvre, il n'enregistre rien — d'ou le trait plutot que le plein,
   qui serait lu comme l'action principale de la carte. */
.btn-icone.btn-rond {
  width: 34px; height: 34px;
  /* UN TRAIT DE 2 PX, ET DE LA COULEUR DU « + ».
     A 1 px de --bordure, le cercle se perdait : cette teinte est a deux
     points du fond de carte, si bien que l'epaissir n'y changeait rien — un
     trait epais invisible reste invisible. L'anneau reprend donc l'accent,
     comme le signe qu'il entoure. */
  border: 2px solid var(--accent); border-radius: 50%;
  font-size: 1.15rem; line-height: 1; font-weight: 600;
  color: var(--accent);
}
.btn-icone.btn-rond:hover { background: var(--accent-doux); border-color: var(--accent); }

.btn-icone-sm { width: 32px; height: 32px; border-radius: var(--arrondi-sm); }
.btn-icone-sm svg { width: 17px; height: 17px; }
/* Une commande destructive garde sa couleur au survol : un rouge qui devient
   vert au moment de cliquer dirait le contraire de ce qu'on va faire. */
/* L'INFOBULLE : un bouton-icone qui montre son texte au survol ET au clavier.
   Le texte vient de data-texte ; il double l'aria-label, pour que le lecteur
   d'ecran l'ait sans survol. On la pose en position absolue sous le bouton,
   alignee a droite : c'est un « i » en coin de carte, la bulle doit rester
   dans la carte. Pas d'attribut title : le navigateur en ferait une seconde
   bulle par-dessus la notre. */
.infobulle { position: relative; }
.infobulle::after {
  content: attr(data-texte);
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
  width: max-content; max-width: min(22rem, 80vw);
  padding: .55rem .75rem;
  font-size: var(--t-sm); font-weight: var(--g-normal); line-height: 1.45;
  text-align: left; white-space: normal;
  color: var(--texte); background: var(--surface-haute);
  border: 1px solid var(--bordure); border-radius: var(--arrondi);
  box-shadow: var(--ombre-haute);
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity var(--transition), transform var(--transition), visibility 0s linear .15s;
  pointer-events: none;
}
.infobulle:hover::after, .infobulle:focus-visible::after {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity var(--transition), transform var(--transition);
}
.btn-icone.danger { color: var(--danger); }
.btn-icone.danger:hover { background: var(--danger-fond); color: var(--danger); }

/* Le crayon d'edition, pose a cote d'un titre. */
.btn-stylo { width: 34px; height: 34px; }
.btn-stylo svg { width: 16px; height: 16px; }

/* =====================================================================
   Tuile de navigation : la maille du choix de box
   ===================================================================== */

.tuiles {
  display: grid; gap: var(--r-sm);
  /* 88 px, pas 96 : les cinq destinations d'une box tiennent alors sur UNE
     ligne dans une carte de largeur courante. A 96 px la cinquieme tombait
     seule au rang suivant, et une ligne a moitie vide se lit comme un oubli. */
  grid-template-columns: repeat(auto-fit, minmax(min(88px, 100%), 1fr));
}
.tuile {
  display: flex; flex-direction: column; align-items: center; gap: var(--r-xs);
  padding: var(--r-md) var(--r-sm);
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--arrondi);
  text-align: center;
  transition: border-color var(--transition), background var(--transition);
}
.tuile:hover { border-color: var(--accent); background: var(--accent-doux); text-decoration: none; }
.tuile svg { width: 26px; height: 26px; color: var(--accent); }
.tuile { position: relative; }
.tuile span { font-size: var(--t-xs); font-weight: var(--g-moyen); line-height: 1.25; }
/* Pastille d'alerte, posee sur le coin de la tuile. Elle ne dit pas ce qui se
   passe — elle dit qu'il y a quelque chose a lire, et ou.

   EN BAS a droite, pas en haut. Sur l'ecran « Mes box », la tuile « Journaux »
   est la derniere de la rangee, et son coin superieur droit se trouve juste
   sous la pastille « En ligne » de l'en-tete de carte : deux points de couleur
   a un centimetre l'un de l'autre, et l'oeil les lisait comme un seul
   indicateur — le rouge semblait contredire le vert. En bas, la pastille
   n'a plus de voisine. */
.tuile-pastille {
  position: absolute; bottom: 6px; right: 6px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--danger);
  box-shadow: 0 0 0 2px var(--surface);
}

/* =====================================================================
   Arrosage manuel : une pilule par pompe
   =====================================================================
   Le bouton et sa duree dans le meme cadre. Le LIBELLE ne se coupe jamais :
   il est en flex 1 et peut passer a la ligne ; c'est la duree, a largeur
   fixe, qui reste en bout. La v1 faisait l'inverse et affichait « E… ». */
.pilules-arrosage { display: flex; flex-wrap: wrap; gap: var(--r-sm); }
.pilule-arrosage {
  display: flex; align-items: stretch; gap: 0;
  flex: 1 1 15rem; margin: 0;
  border: 1px solid var(--bordure); border-radius: var(--arrondi);
  overflow: hidden;
}
.pilule-arrosage .btn {
  flex: 1 1 auto; min-width: 0;
  display: inline-flex; align-items: center; gap: .5rem;
  justify-content: flex-start;
  border: 0; border-radius: 0;
  white-space: normal; text-align: left;
}
.pilule-arrosage .btn svg { width: 20px; height: 20px; flex: none; }
.pilule-arrosage .duree {
  display: inline-flex; align-items: center; gap: .25rem;
  flex: none; padding: 0 .6rem;
  border-left: 1px solid var(--bordure);
  /* Pas de fond propre : la pilule est UN objet, le filet suffit a separer la
     duree du bouton. Un fond distinct dessinait un second bloc dans le bloc. */
}
.pilule-arrosage .duree input {
  width: 3.4rem; border: 0; background: transparent; padding: .35rem 0;
  text-align: right; font-family: var(--police-chiffres); font-size: var(--t-base);
  color: var(--texte);
}
.pilule-arrosage .duree input:focus { outline: 2px solid var(--accent); border-radius: var(--arrondi-sm); }
.pilule-arrosage .unite { font-size: var(--t-sm); color: var(--texte-doux); }
/* La pilule des nutriments se distingue par sa teinte d'information, pas par
   une couleur en dur : elle suit le theme et la palette. */
.pilule-arrosage.nutriments .btn { color: var(--info); }
.pilule-arrosage.nutriments .btn svg { color: var(--info); }

/* =====================================================================
   Mesures
   ===================================================================== */

.mesure {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--arrondi);
}
/* Une mesure cliquable ouvre son reglage : elle doit le montrer. */
button.mesure, a.mesure {
  font: inherit; color: inherit; text-align: left; cursor: pointer; width: 100%;
}
button.mesure:hover, a.mesure:hover { border-color: var(--accent); text-decoration: none; }
.mesure .etiquette { font-size: var(--t-xs); font-weight: var(--g-fort); letter-spacing: .04em;
                     text-transform: uppercase; color: var(--texte-doux); }
.mesure .valeur { font-size: var(--t-2xl); font-weight: var(--g-titre); line-height: 1.1;
                  font-variant-numeric: tabular-nums; }
.mesure .unite { font-size: var(--t-base); font-weight: var(--g-normal); color: var(--texte-doux); }
.mesure .note { font-size: var(--t-sm); color: var(--texte-doux); }

/* L'etat de la lecture, en tete de la fenetre de calibration : ce qu'on
   regarde est une vraie mesure de l'instant, une mesure agee, ou une panne. */
.etat-sonde { margin: 0; font-size: var(--t-sm); color: var(--texte-doux);
  display: flex; align-items: center; gap: 8px; }
.etat-sonde::before { content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--texte-doux); flex: none; }
.etat-sonde.directe { color: var(--accent); }
.etat-sonde.directe::before { background: var(--accent); animation: battement 3s ease-in-out infinite; }
.etat-sonde.agee::before { background: var(--alerte); }
.etat-sonde.en-panne { color: var(--danger); }
.etat-sonde.en-panne::before { background: var(--danger); }
/* Un bouton qui se lit comme un lien : « Recharger », propose la ou la
   panne est annoncee plutot que dans un message a dechiffrer. */
.lien-bouton { background: none; border: 0; padding: 0; margin-left: 6px;
  color: inherit; font: inherit; text-decoration: underline; cursor: pointer; }
.lien-bouton:hover { color: var(--accent); }
@keyframes battement { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .etat-sonde.directe::before { animation: none; } }
/* Une saisie refusee : le champ le montre, et l'aide le dit. */
.champ input.refuse { border-color: var(--danger); }
.champ .aide.refus { color: var(--danger); }
.mesure.absente .valeur { color: var(--texte-doux); }

/* =====================================================================
   Etats
   ===================================================================== */

/* --- LA LISTE DES PROGRAMMES (modale du tableau de bord) ---
 *
 * Cette fenetre repond a UNE question : lequel de mes programmes tourne ?
 * Elle portait dix controles pour cinq programmes — un « Editer », un
 * « Utiliser » vert et une corbeille par ligne — et l'information se lisait
 * entre les boutons. La ligne EST desormais le controle : la choisir, c'est
 * l'activer, comme un bouton radio. Le reste passe en icones discretes.
 *
 * Le vert ne dit plus qu'une chose : l'etat. Il servait aussi a « Utiliser »
 * et au gros bouton d'ajout, si bien qu'il ne signifiait plus rien.
 */
.prog-liste { display: flex; flex-direction: column; }

.prog-ligne {
  display: flex; align-items: center; gap: var(--r-xs, 6px);
  border-bottom: 1px solid var(--bordure);
}
.prog-ligne:last-child { border-bottom: 0; }

/* La zone cliquable d'une ligne : tout le nom, pas une pastille de 80 px. */
.prog-choix, .prog-fixe {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: var(--r-md);
  padding: 10px 8px;
  text-align: left; color: inherit; font: inherit;
  background: transparent; border: 0; border-radius: var(--arrondi-sm);
}
.prog-choix { cursor: pointer; transition: background var(--transition); }
.prog-choix:hover { background: var(--accent-doux); }
.prog-choix:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* L'indicateur : plein pour celui qui tourne, creux pour un choix possible,
   rouge pour un programme que la box refusera. */
.prog-point {
  flex: none; width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--texte-doux); box-sizing: border-box;
}
.prog-ligne.est-actif   .prog-point { border-color: var(--accent); background: var(--accent); }
.prog-ligne.est-casse   .prog-point { border-color: var(--danger); }
.prog-ligne.est-vide    .prog-point { border-style: dashed; }

.prog-texte { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.prog-nom { display: flex; align-items: center; gap: var(--r-sm); flex-wrap: wrap; }
.prog-sous { font-size: var(--t-sm); color: var(--texte-doux); }
.prog-ligne.est-casse .prog-sous { color: var(--danger); }

/* Les actions secondaires : meme largeur pour toutes les lignes, donc un
   bord droit net. Elles ne prennent leur couleur qu'au survol. */
/* Toutes les lignes n'ont pas de corbeille — seules celles qu'on peut
   supprimer. Sans emplacement reserve, le crayon changeait de colonne d'une
   ligne a l'autre et le bord droit partait en dents de scie. */
.prog-actions { flex: none; display: flex; align-items: center; }
.prog-vide-action { display: block; width: 44px; height: 1px; }

/* L'ajout est une action RARE : il n'a pas a etre le vert le plus grand de
   la fenetre. En contour, il se trouve sans voler la vedette a l'etat. */
.prog-ajout {
  display: flex; align-items: center; justify-content: center; gap: .5em;
  margin-top: var(--r-md); padding: 12px;
  border: 1px dashed var(--bordure); border-radius: var(--arrondi);
  color: var(--accent); font-weight: var(--g-fort);
  transition: background var(--transition), border-color var(--transition);
}
.prog-ajout:hover { background: var(--accent-doux); border-color: var(--accent); }

.pastille {
  display: inline-flex; align-items: center; gap: .4em;
  padding: .18em .7em;
  font-size: var(--t-xs); font-weight: var(--g-fort);
  border-radius: var(--arrondi-plein);
  background: var(--accent-doux); color: var(--accent);
}
.pastille::before {
  content: ""; width: .5em; height: .5em; border-radius: 50%;
  background: currentColor;
}
.pastille.eteint  { background: var(--bordure); color: var(--texte-doux); }
.pastille.alerte  { background: var(--alerte-fond); color: var(--alerte); }
.pastille.danger  { background: var(--danger-fond); color: var(--danger); }
.pastille.info    { background: var(--info-fond);   color: var(--info); }

/* Etat de connexion de la box, a cote de son nom.
 *
 * On REUTILISE le composant .pastille defini plus bas — meme pilule, meme
 * point, memes variantes de couleur. Ma premiere version en redefinissait un
 * second sous le meme nom : le point heritait alors du composant existant et
 * s'affichait en gros pave vert, quel que soit l'etat.
 *
 * Il ne reste donc ici que ce qui est propre a l'en-tete : la place, le temps
 * ecoule, et le battement qui distingue « ca vit en ce moment » d'une simple
 * etiquette. */
.app-entete .etat-box { margin-left: .7rem; vertical-align: middle; }
.etat-box .etat-depuis { font-weight: var(--g-normal); opacity: .72; }

.etat-box:not(.danger)::before { animation: gm-battement 2.4s ease-in-out infinite; }
@keyframes gm-battement { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
/* =====================================================================
   Pages de compte : connexion, inscription, mot de passe, activation
   =====================================================================
   Une seule colonne etroite, la carte au milieu, le logo au-dessus. Elles
   portent l'en-tete v2 en mode public : le theme et la langue se choisissent
   au meme endroit que partout ailleurs. */
.compte { width: min(460px, 100%); margin: 2vh auto 0; }
.compte-large { width: min(640px, 100%); }
.compte-logo {
  width: 92px; height: 92px; border-radius: 50%; margin: 0 auto var(--r-md);
  display: grid; place-items: center;
  background: var(--accent-doux); border: 2px solid var(--accent);
}
.compte-logo img { height: 56px; width: auto; }
.compte h1 { text-align: center; font-size: var(--t-xl); margin: 0 0 var(--r-xs); }
.compte .sous { text-align: center; color: var(--texte-doux); margin: 0 0 var(--r-lg); }
.compte form { display: flex; flex-direction: column; gap: var(--r-md); }
.compte .btn-fort { width: 100%; padding: .8rem 1.2rem; font-size: var(--t-base); }
.compte-pied {
  display: flex; flex-direction: column; gap: var(--r-xs); align-items: center;
  margin-top: var(--r-sm); font-size: var(--t-sm); color: var(--texte-doux); text-align: center;
}
.compte-section {
  margin-top: var(--r-sm); padding-top: var(--r-sm); border-top: 1px solid var(--bordure);
  font-size: var(--t-xs); font-weight: var(--g-fort); letter-spacing: .06em;
  text-transform: uppercase; color: var(--texte-doux);
}
.compte-deux { display: grid; grid-template-columns: 1fr 1fr; gap: var(--r-md); }
@media (max-width: 480px) { .compte-deux { grid-template-columns: 1fr; } }

/* Une case a cocher et son explication, sur la meme ligne. */
.case { display: flex; align-items: flex-start; gap: var(--r-sm); }
.case input[type="checkbox"] { margin-top: .3rem; flex: none; }
.case label { display: flex; flex-direction: column; gap: 2px; cursor: pointer; }
.case label small { color: var(--texte-doux); font-size: var(--t-sm); }

/* Liste de suggestions sous un champ (adresse, entreprise). */
.suggere { position: relative; }
.suggestions {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 20;
  background: var(--surface-haute); border: 1px solid var(--bordure-forte);
  border-radius: var(--arrondi-sm); box-shadow: var(--ombre-haute);
  max-height: 220px; overflow-y: auto;
}
.suggestions:empty { display: none; }
.suggestion { padding: .5rem .7rem; cursor: pointer; font-size: var(--t-md); border-bottom: 1px solid var(--bordure); }
.suggestion:last-child { border-bottom: 0; }
.suggestion:hover { background: var(--accent-doux); color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .etat-box:not(.danger)::before { animation: none; }
}

@media (max-width: 560px) {
  /* Sur telephone la barre est disputee : le point suffit, les mots partent.
   * On rend la pilule ronde plutot que de la laisser en pilule vide. */
  .etat-box .etat-mot, .etat-box .etat-depuis { display: none; }
  .etat-box { padding: .35em; gap: 0; }
}

/* Message d'etat.
   Pas de rail colore a gauche : ce motif est devenu la signature visuelle des
   interfaces produites a la chaine, et il coupe le bloc en deux pour ne rien
   dire de plus qu'un fond teinte. Ici la teinte porte le sens, une bordure
   complete ferme le bloc, et une pastille de couleur pleine tient lieu de
   marqueur — comme les etats ailleurs dans la page. */
.bandeau {
  display: flex; gap: var(--r-sm); align-items: baseline;
  padding: var(--r-sm) var(--r-md);
  border-radius: var(--arrondi);
  border: 1px solid color-mix(in srgb, var(--info) 32%, transparent);
  background: var(--info-fond);
  color: var(--texte);
  transition: opacity 400ms ease, transform 400ms ease;
}
.bandeau::before {
  content: ""; flex: none;
  width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--info);
  transform: translateY(-1px);
}
.bandeau.alerte { border-color: color-mix(in srgb, var(--alerte) 34%, transparent);
                  background: var(--alerte-fond); }
.bandeau.alerte::before { background: var(--alerte); }
.bandeau.danger { border-color: color-mix(in srgb, var(--danger) 34%, transparent);
                  background: var(--danger-fond); }
.bandeau.danger::before { background: var(--danger); }
.bandeau.ok     { border-color: color-mix(in srgb, var(--accent) 34%, transparent);
                  background: var(--accent-doux); }
.bandeau.ok::before { background: var(--accent); }
.bandeau p:last-child { margin-bottom: 0; }

/* Un message de confirmation a fait son travail des qu'il a ete lu. Il
   s'efface, sans faire sauter la page : on retire d'abord l'opacite, puis la
   hauteur. Les messages d'ALERTE, eux, restent — ils decrivent un etat qui
   dure, pas une action reussie. */
.bandeau.efface { opacity: 0; transform: translateY(-4px); }

/* =====================================================================
   Formulaires
   ===================================================================== */

.champ { display: flex; flex-direction: column; gap: var(--r-xs); }
.champ > label { font-size: var(--t-md); font-weight: var(--g-fort); }
.champ .aide { font-size: var(--t-sm); color: var(--texte-doux); }

/* UN CHAMP ET SON BOUTON, SUR LA MEME LIGNE.
   Pose a cote du bloc .champ tout entier, un bouton s'aligne sur le milieu de
   ce bloc — libelle au-dessus et phrase d'aide en dessous compris. Il tombait
   donc plus bas que la zone de saisie a laquelle il repond, d'autant plus bas
   que l'aide etait longue.
   Ici le libelle reste au-dessus du duo et l'aide en dessous : seuls la saisie
   et le bouton partagent la ligne, et `stretch` leur donne la meme hauteur
   quelle que soit celle du champ. */
.champ-action { display: flex; gap: var(--r-sm); align-items: stretch; }
.champ-action > input,
.champ-action > select,
.champ-action > textarea { flex: 1; min-width: 0; }
.champ-action > .btn { flex: none; white-space: nowrap; }
/* Sous 420 px, le duo ne tient plus : le bouton passe dessous, pleine largeur,
   plutot que de comprimer la saisie a trois caracteres. */
@media (max-width: 420px) {
  .champ-action { flex-direction: column; }
}

/* TOUS LES CHAMPS DE SAISIE, PAR EXCLUSION.
   L'enumeration precedente listait six types, et manquait donc tout le reste :
   `datetime-local` sur la mise a l'heure de la box, et surtout les <input>
   SANS attribut type — un selecteur `input[type="text"]` ne les atteint pas,
   meme si le navigateur les traite comme du texte. Le champ de renommage d'une
   box et celui de l'heure s'affichaient ainsi bruts, au milieu de champs
   habilles, sans que rien dans la feuille ne l'explique.
   On liste desormais ce qui N'EST PAS un champ de saisie : la liste est courte
   et, elle, ne s'allongera plus.

   DANS UN :where(), ET C'EST DECISIF. Chaque :not([type=...]) compte pour une
   classe : dix d'affilee donnaient a cette regle une specificite de (0,10,1),
   celle de dix classes. Toute regle de page ecrite normalement — `.duree
   input`, `.champ-secret input` — perdait contre elle sans un mot. La cellule
   de duree de l'arrosage gardait un fond et une bordure qu'on lui avait
   retires ; le champ du mot de passe n'avait jamais recu la marge qui laisse
   la place au bouton de l'oeil. :where() ramene la regle a (0,0,1) : le poids
   d'un simple `input`, ce qu'une regle de base doit peser. */
input:where(:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="file"]):not([type="range"]):not([type="color"]):not([type="image"]):not([type="hidden"])),
select, textarea {
  width: 100%;
  padding: .55rem .75rem;
  font: inherit;
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--arrondi-sm);
  transition: border-color var(--transition);
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); outline: none; }
/* ... mais le clavier, lui, garde son anneau. `input:focus` a une specificite
   de (0,1,1) et ecrasait la regle :focus-visible generale, qui n'en a que
   (0,1,0) : un champ atteint au clavier ne changeait donc que de couleur de
   bordure, ce qui ne se remarque pas au milieu d'un formulaire entier. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* AUCUN filtre sur les icones de calendrier et d'horloge des champs.
   Chaque palette declare color-scheme, et le navigateur rend deja ces icones
   claires sur un fond sombre. Le invert(1) qu'on avait pose les repeignait en
   NOIR — exactement l'inverse du but. Le seul reglage necessaire est
   color-scheme, qui est deja la. */

/* Interrupteur : « j'utilise cette sonde », « alertes par e-mail »... */
.bascule { display: inline-flex; align-items: center; gap: var(--r-sm); cursor: pointer; }
.bascule input { position: absolute; opacity: 0; width: 0; height: 0; }
.bascule .piste {
  position: relative; flex: none; width: 42px; height: 24px;
  background: var(--bordure-forte); border-radius: var(--arrondi-plein);
  transition: background var(--transition);
}
.bascule .piste::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 18px; height: 18px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3);
  transition: transform var(--transition);
}
.bascule input:checked + .piste { background: var(--accent); }
.bascule input:checked + .piste::after { transform: translateX(18px); }
.bascule input:focus-visible + .piste { outline: 2px solid var(--accent); outline-offset: 2px; }

/* =====================================================================
   Modales — la v2 en vit : reglages du programme, calibration des sondes,
   configuration des prises. Elles evitent autant de pages.
   ===================================================================== */

.modale {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  padding: var(--r-md);
  background: rgba(8, 18, 13, .55);
  backdrop-filter: blur(3px);
}
.modale[hidden] { display: none; }
.modale-boite {
  width: min(680px, 100%);
  max-height: min(86vh, 900px);
  display: flex; flex-direction: column;
  background: var(--surface-haute);
  border: 1px solid var(--bordure);
  border-radius: var(--arrondi-lg);
  box-shadow: var(--ombre-haute);
}
.modale-entete, .modale-pied {
  display: flex; align-items: center; gap: var(--r-sm);
  padding: var(--r-md) var(--r-lg);
}
.modale-entete { border-bottom: 1px solid var(--bordure); }
.modale-entete h2 { margin: 0; }
.modale-entete .pousse, .modale-pied .pousse { margin-left: auto; }
.modale-corps { padding: var(--r-lg); overflow-y: auto; }
/* L'editeur de programme a besoin de place : c'est une page entiere posee
   dans une fenetre. On lui donne presque tout l'ecran plutot que de le
   comprimer dans la largeur des autres modales. */
.modale-large { width: min(1500px, 100%); height: min(94vh, 1100px); }
/* L'editeur de programme : la page la plus importante du parcours. Elle
   prend l'ecran entier, a une marge pres — les regles s'y ecrivent sur une
   ligne, et le programme se lit d'un coup d'oeil. */
.modale-plein-ecran { width: 100%; height: 100%; max-height: 100%; }

/* Une fenetre POSEE SUR une autre : le detail d'un jour par-dessus le
   calendrier. L'ordre du document suffirait a l'empiler, mais il tient alors
   a la position d'un bloc dans un fichier — on le dit explicitement, pour que
   deplacer le balisage ne renvoie pas la fenetre derriere celle qu'elle
   recouvre. */
.modale-par-dessus { z-index: 110; }
.modale-pied { border-top: 1px solid var(--bordure); }

/* Plein ecran sur telephone : une modale de reglages a besoin de place. */
@media (max-width: 640px) {
  .modale { padding: 0; }
  .modale-boite { width: 100%; max-height: 100%; height: 100%; border-radius: 0; border: 0; }
}

/* =====================================================================
   Calendrier de culture
   =====================================================================
   Une grille de sept colonnes, et des pastilles pour dire qu'il s'est passe
   quelque chose ce jour-la. La pastille ne dit pas QUOI : le detail est
   liste sous le calendrier, parce qu'un survol n'existe pas sur telephone. */

.calendrier {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.cal-entete {
  text-align: center; font-size: var(--t-2xs); font-weight: var(--g-titre);
  color: var(--texte-doux); padding: .2rem 0;
}
.cal-jour {
  aspect-ratio: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 3px;
  border: 1px solid var(--bordure); border-radius: var(--arrondi-sm);
  font-size: var(--t-sm); font-variant-numeric: tabular-nums;
}
.cal-jour.aujourdhui { border-color: var(--accent); font-weight: var(--g-titre); }
.cal-jour.depart {
  background: var(--accent-doux); border-color: var(--accent);
}
.cal-marques { display: flex; gap: 3px; min-height: 6px; }
.cal-pt {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--texte-doux); vertical-align: middle;
}
.cal-pt.eau    { background: var(--info); }
.cal-pt.action { background: var(--accent-2); }
.cal-pt.depart { background: var(--accent); }
.cal-pt.note   { background: var(--alerte); }

/* LA GRILLE DEMANDE DE LA LARGEUR.
   Sept colonnes dans 360 px donnent des cases de 40 px : les pastilles s'y
   confondent et viser un jour au doigt tient du hasard. En portrait etroit on
   remplace donc la grille par une invitation a tourner l'appareil. On ne
   FORCE pas la rotation — les navigateurs mobiles la refusent hors plein
   ecran, et l'imposer retirerait au visiteur le controle de son telephone.
   Le reste de la fenetre reste debout : la date de depart et le detail du
   mois sont des lignes, elles se lisent tres bien en portrait. */
.invite-rotation { display: none; }
@media (max-width: 820px) and (orientation: portrait) {
  /* La grille RESTE affichee. Elle etait masquee au profit de la seule
     invitation, et le resultat etait pire que le probleme : on ouvrait le
     calendrier sur un telephone tenu debout et il n'y avait rien a voir, juste
     un telephone qui tourne. Une grille serree reste consultable ; une grille
     absente ne l'est pas.
     L'invitation demeure, au-dessus, pour dire qu'on lira mieux a
     l'horizontale — c'est une proposition, plus un barrage. */
  .calendrier { gap: 1px; }
  .cal-jour { font-size: var(--t-2xs); border-radius: var(--arrondi-sm); }
  .cal-pt { width: 5px; height: 5px; }
  .invite-rotation {
    display: flex; flex-direction: column; align-items: center;
    gap: var(--r-sm); text-align: center;
    padding: var(--r-xl) var(--r-md);
    border: 1px dashed var(--bordure-forte);
    border-radius: var(--arrondi);
  }
}
/* Le telephone pivote d'un quart de tour, marque le temps a plat, puis
   revient : c'est le geste demande, montre plutot qu'ecrit. */
.telephone-pivote {
  display: block; color: var(--accent);
  transform-origin: 50% 50%;
  animation: pivoter 3s ease-in-out infinite;
}
@keyframes pivoter {
  0%, 20%   { transform: rotate(0deg); }
  40%, 72%  { transform: rotate(-90deg); }
  92%, 100% { transform: rotate(0deg); }
}

/* LE JOUR EST CLIQUABLE : il ouvre le detail de la journee et sa note.
   C'est donc un vrai bouton, pour que le clavier l'atteigne et que le lecteur
   d'ecran l'annonce — mais il doit continuer a ressembler a une case de
   calendrier, d'ou la remise a zero de ce que le navigateur ajoute aux
   boutons. La regle reste moins specifique que .cal-jour.depart, qui garde
   donc son fond. */
button.cal-jour {
  font-family: inherit; color: inherit;
  background: var(--surface); cursor: pointer;
  padding: 0;
  transition: border-color var(--transition), background var(--transition);
}
button.cal-jour:hover { border-color: var(--accent-vif); }
/* Le jour ouvert. Une bordure interieure double le trait sans deplacer la
   case d'un pixel : agrandir la bordure ferait sauter toute la grille. */
.cal-jour.choisi {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* =====================================================================
   Tableaux — journaux, historiques
   ===================================================================== */

.tableau-cadre { overflow-x: auto; border-radius: var(--arrondi); }
table { width: 100%; border-collapse: collapse; font-size: var(--t-md); }
th, td { padding: .38rem .6rem; text-align: left; border-bottom: 1px solid var(--bordure); }
th { font-size: var(--t-xs); font-weight: var(--g-titre); letter-spacing: .03em;
     text-transform: uppercase; color: var(--texte-doux); }
tbody tr:last-child td { border-bottom: 0; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* =====================================================================
   Orientation
   =====================================================================
   On ne FORCE pas le paysage : les navigateurs mobiles refusent le
   verrouillage hors plein ecran, et l'imposer retirerait au visiteur le
   controle de son propre appareil. On affiche une invitation, visible
   seulement quand elle a un sens — telephone tenu debout — et seulement
   sur les pages qui la demandent. */

/* Invitation a tourner le telephone. Elle passait pour une legende grise
   qu'on ne lisait pas ; elle a desormais l'allure d'un message, avec son
   icone. On n'IMPOSE toujours pas la rotation : les navigateurs mobiles
   refusent le verrouillage hors plein ecran, et l'imposer retirerait au
   visiteur le controle de son propre appareil. */
.paysage-invite { display: none; }
@media (max-width: 820px) and (orientation: portrait) {
  .demande-paysage .paysage-invite {
    display: flex; align-items: center; gap: var(--r-sm);
    margin: 0 0 var(--r-md);
    padding: var(--r-sm) var(--r-md);
    font-size: var(--t-sm);
    color: var(--texte);
    background: var(--info-fond);
    border: 1px solid color-mix(in srgb, var(--info) 32%, transparent);
    border-radius: var(--arrondi);
  }
  .demande-paysage .paysage-invite svg { flex: none; color: var(--info); }
  /* Debout, le graphique n'est pas montre du tout : il n'est pas lisible a
     cette largeur, et un graphique illisible n'informe pas, il occupe. Le
     canvas reste dans la page, masque, pour reapparaitre des qu'on tourne. */
  .demande-paysage .zone-graphique { display: none; }
  .demande-paysage .invite-rotation {
    display: flex; flex-direction: column; align-items: center;
    gap: var(--r-sm); text-align: center;
    padding: var(--r-xl) var(--r-md);
    border: 1px dashed var(--bordure-forte);
    border-radius: var(--arrondi);
  }
}

/* =====================================================================
   Respect des preferences
   ===================================================================== */

/* =====================================================================
   Mouvement
   =====================================================================
   Trois animations, et trois seulement : chacune dit quelque chose.
   Au-dela, le mouvement devient du bruit sur une page qu'on consulte
   plusieurs fois par jour. */

/* 1. Passage clair/sombre. Sans transition le basculement claque ; avec une
      transition sur TOUT, la page entiere ondule a chaque chargement. On la
      pose donc sur les seules proprietes concernees, et seulement pendant la
      bascule — la classe est retiree juste apres. */
.bascule-theme, .bascule-theme * {
  transition: background-color 280ms ease, border-color 280ms ease,
              color 280ms ease !important;
}

/* 2. Chargement : une pousse qui grandit, plutot qu'un disque qui tourne.
      Le disque ne dit rien ; la pousse dit de quoi parle ce site. */
.pousse-chargement { display: inline-block; width: 28px; height: 28px; }
/* display:inline-block l'emporte sur le [hidden] du navigateur, qui n'a que
   la specificite d'un attribut : sans cette ligne la pousse reste affichee en
   permanence a cote de chaque bouton d'arrosage. */
.pousse-chargement[hidden] { display: none; }
/* La meme pousse, en grand : dans une fenetre d'attente, 28 px se perdent au
   milieu du vide et on ne sait plus si quelque chose travaille. */
.pousse-grande { width: 48px; height: 48px; }
.pousse-chargement path, .pousse-chargement circle {
  stroke-dasharray: 40; stroke-dashoffset: 40;
  animation: pousser 1.6s ease-in-out infinite;
}
.pousse-chargement .feuille-2 { animation-delay: .35s; }
.pousse-chargement .tige      { animation-delay: 0s; }
@keyframes pousser {
  0%        { stroke-dashoffset: 40; opacity: .25; }
  45%, 70%  { stroke-dashoffset: 0;  opacity: 1; }
  100%      { stroke-dashoffset: 0;  opacity: .25; }
}

/* 3. Le trace des courbes est confie a Chart.js : il connait la longueur de
      chaque ligne, le CSS non. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .app-entete, .btn, .btn-icone, .modale { display: none !important; }
  body { background: #fff; color: #000; }
}
