/**
 * Socle visuel — jetons de design et composants communs.
 * Chargé par toutes les pages. Les styles propres à une page vont dans son
 * propre fichier (kanban.css, pilotage.css, admin.css…).
 */

:root{
  --bg:#f5f6f8;
  --surface:#ffffff;
  --surface-2:#fafbfc;
  /* Fond translucide des barres collantes. Il ne peut pas s'écrire en dur
     dans une page : une requête média ne voit pas un thème choisi à la main. */
  --glass:rgba(255,255,255,.86);
  --line:#e4e6eb;
  --line-strong:#cfd4dd;
  --txt:#16181d;
  --txt-2:#5a6270;
  --txt-3:#8b93a1;
  --accent:#185FA5;
  --accent-soft:#e6f1fb;
  --ok:#3B6D11;
  --warn:#B45309;
  --danger:#A32D2D;
  --info:#0F6E56;
  --radius:10px;
  --radius-lg:14px;
  --shadow:0 1px 2px rgba(16,24,40,.05), 0 4px 12px rgba(16,24,40,.04);
  --shadow-lg:0 8px 32px rgba(16,24,40,.12);
  /* Ombre portée d'un bouton principal, teintée de l'accent : une ombre
     grise sous un aplat coloré donne toujours un rendu sale. */
  --shadow-accent:0 1px 2px rgba(24,95,165,.18), 0 4px 12px rgba(24,95,165,.16);
  --font:-apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;

  /**
   * Le symbole de la marque.
   *
   * Une flèche montante enroulée dans une boucle, qui se lit aussi comme un
   * P. Elle remplace l'aiguille de boussole des premières versions.
   *
   * Le tracé fourni était une vectorisation automatique : il suivait le
   * contour du pixel, une marche d'escalier par point, mille points pour
   * trois formes. Il a été simplifié — les marches retirées, les angles
   * gardés — et l'encoche intérieure supprimée. Cette encoche se referme
   * sous vingt-quatre pixels et transformait le symbole en tache bleue ; le
   * favicon d'un onglet en fait seize. Sans elle, la flèche se lit à cette
   * taille, et le dessin reste le même en grand.
   *
   * Il vit ici, en un seul endroit, parce que .mark est stylé dans quatre
   * fichiers différents (barre latérale, site public, page de connexion,
   * barre supérieure). Chacun garde sa taille et son fond ; aucun n'a à
   * connaître le dessin.
   *
   * Il est monochrome, et c'est ce qui permet son inversion : blanc sur
   * l'accent en thème clair, presque noir sur l'accent clair en thème
   * sombre. Le dégradé de la charte ne peut pas faire ça — posé sur la
   * pastille bleue, il disparaîtrait. Il vit dans /favicon.svg, là où le
   * fond est neutre.
   *
   * La couleur du tracé est écrite dans l'image, et non appliquée par CSS.
   * Une première version passait par un masque, ce qui permettait d'hériter
   * de la couleur du texte — mais le raccourci « mask: … center / 56% » était
   * rejeté par certains navigateurs qui déclarent pourtant savoir masquer.
   * Le masque devenait alors inexistant, et un masque absent laisse tout
   * voir : le symbole se transformait en carré plein. Une image de fond ne
   * peut pas échouer de cette façon.
   *
   * Le même tracé existe en dégradé dans /favicon.svg, à une tolérance de
   * simplification plus fine. Les deux sortent du même fichier source par le
   * même script : ce ne sont pas deux dessins à tenir d'accord.
   *
   * À ne pas confondre avec la mascotte, plus bas : le symbole est
   * l'identité, la mascotte n'est qu'une illustration d'écran vide.
   */
  --marque:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-34 -17 263 263'%3E%3Cg fill='%23ffffff' fill-rule='evenodd'%3E%3Cpath d='M111 80L110 81L100 81L93 83L80 84L71 87L63 88L39 96L22 106L13 115L9 121L5 130L3 139L4 160L14 194L16 197L23 220L26 225L28 225L30 223L37 210L37 208L52 176L63 147L67 143L99 122L116 109L129 96L137 85L137 82L125 81L124 80L112 80Z'/%3E%3Cpath d='M40 3L39 6L42 12L45 15L54 31L73 60L79 62L102 63L103 64L123 66L133 69L137 69L149 74L153 78L153 83L135 117L133 124L118 154L107 182L117 182L131 178L145 172L158 164L169 155L179 143L184 135L190 119L191 96L187 82L178 66L161 52L127 35L105 27L100 24L87 20L82 17L68 13L58 8L41 3Z'/%3E%3C/g%3E%3C/svg%3E");
}

/**
 * Thème sombre.
 *
 * Deux écritures du même jeu de valeurs, et c'est volontaire :
 *   — la première s'applique quand theme.js a posé data-theme sur <html>,
 *     c'est-à-dire dès que l'utilisateur a fait un choix explicite ;
 *   — la seconde ne sert qu'aux pages ouvertes sans JavaScript, où il faut
 *     bien retomber sur la préférence du système.
 * Toute modification doit être reportée dans les deux blocs.
 */
:root[data-theme="dark"]{
  --bg:#131417;
  --surface:#1c1d21;
  --surface-2:#212328;
  --glass:rgba(19,20,23,.88);
  --line:#2c2e33;
  --line-strong:#3d4048;
  --txt:#e9eaec;
  --txt-2:#a8aeb9;
  --txt-3:#787f8b;
  --accent:#5B9BD5;
  --accent-soft:rgba(91,155,213,.14);
  --ok:#7CB342;
  --warn:#E0A82E;
  --danger:#E06C6C;
  --info:#4DB6A0;
  --shadow:0 1px 2px rgba(0,0,0,.3), 0 4px 14px rgba(0,0,0,.25);
  --shadow-lg:0 12px 40px rgba(0,0,0,.45);
  --shadow-accent:0 1px 2px rgba(0,0,0,.35), 0 4px 14px rgba(91,155,213,.18);
  /* L'accent clair du thème sombre demande un tracé foncé, comme les boutons */
  --marque:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-34 -17 263 263'%3E%3Cg fill='%230d1117' fill-rule='evenodd'%3E%3Cpath d='M111 80L110 81L100 81L93 83L80 84L71 87L63 88L39 96L22 106L13 115L9 121L5 130L3 139L4 160L14 194L16 197L23 220L26 225L28 225L30 223L37 210L37 208L52 176L63 147L67 143L99 122L116 109L129 96L137 85L137 82L125 81L124 80L112 80Z'/%3E%3Cpath d='M40 3L39 6L42 12L45 15L54 31L73 60L79 62L102 63L103 64L123 66L133 69L137 69L149 74L153 78L153 83L135 117L133 124L118 154L107 182L117 182L131 178L145 172L158 164L169 155L179 143L184 135L190 119L191 96L187 82L178 66L161 52L127 35L105 27L100 24L87 20L82 17L68 13L58 8L41 3Z'/%3E%3C/g%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark){
  :root:not([data-theme]){
    --bg:#131417;
    --surface:#1c1d21;
    --surface-2:#212328;
    --glass:rgba(19,20,23,.88);
    --line:#2c2e33;
    --line-strong:#3d4048;
    --txt:#e9eaec;
    --txt-2:#a8aeb9;
    --txt-3:#787f8b;
    --accent:#5B9BD5;
    --accent-soft:rgba(91,155,213,.14);
    --ok:#7CB342;
    --warn:#E0A82E;
    --danger:#E06C6C;
    --info:#4DB6A0;
    --shadow:0 1px 2px rgba(0,0,0,.3), 0 4px 14px rgba(0,0,0,.25);
    --shadow-lg:0 12px 40px rgba(0,0,0,.45);
    --shadow-accent:0 1px 2px rgba(0,0,0,.35), 0 4px 14px rgba(91,155,213,.18);
    --marque:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-34 -17 263 263'%3E%3Cg fill='%230d1117' fill-rule='evenodd'%3E%3Cpath d='M111 80L110 81L100 81L93 83L80 84L71 87L63 88L39 96L22 106L13 115L9 121L5 130L3 139L4 160L14 194L16 197L23 220L26 225L28 225L30 223L37 210L37 208L52 176L63 147L67 143L99 122L116 109L129 96L137 85L137 82L125 81L124 80L112 80Z'/%3E%3Cpath d='M40 3L39 6L42 12L45 15L54 31L73 60L79 62L102 63L103 64L123 66L133 69L137 69L149 74L153 78L153 83L135 117L133 124L118 154L107 182L117 182L131 178L145 172L158 164L169 155L179 143L184 135L190 119L191 96L187 82L178 66L161 52L127 35L105 27L100 24L87 20L82 17L68 13L58 8L41 3Z'/%3E%3C/g%3E%3C/svg%3E");
  }
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--bg); color:var(--txt);
  font:15px/1.55 var(--font);
  -webkit-font-smoothing:antialiased;
}
a{color:var(--accent); text-decoration:none}
a:hover{text-decoration:underline}

/**
 * Titres.
 *
 * Le resserrement de l'interlettrage augmente avec le corps : une police
 * système est dessinée pour du texte courant, et posée en grand elle paraît
 * lâche. Un titre d'écran gagne à être serré ; un intertitre de 15 pixels,
 * non — le serrer le rendrait juste moins lisible.
 *
 * text-wrap:balance répartit les mots sur les lignes d'un titre au lieu de
 * laisser un mot seul en bas. Ignoré par les navigateurs qui ne le
 * connaissent pas, sans conséquence.
 */
h1,h2,h3{margin:0; font-weight:600; letter-spacing:-.02em; text-wrap:balance}
h1{font-size:23px; font-weight:650; letter-spacing:-.032em}
h2{font-size:17px}
h3{font-size:15px; letter-spacing:-.01em}
p{margin:0}

/**
 * Le symbole de la marque.
 *
 * Le pictogramme d'origine reste dans le HTML et sert de repli : seul celui
 * de la boussole est remplacé. La console d'administration, qui affiche
 * volontairement un cadenas, garde le sien.
 *
 * :has() sélectionne le conteneur d'après son contenu. Un navigateur qui ne
 * le connaît pas ignore les deux règles et affiche la boussole d'origine —
 * c'est un repli acceptable, jamais un carré vide.
 */
.mark:has(> i.ti-compass){position:relative}
.mark:has(> i.ti-compass) > i{visibility:hidden}
.mark:has(> i.ti-compass)::before{
  content:"";
  position:absolute; inset:0;
  background-image:var(--marque);
  background-repeat:no-repeat;
  background-position:center;
  background-size:56%;
}

/* Anneau de focus au clavier uniquement : visible pour qui navigue au
   clavier, invisible pour qui clique. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:2px;
}

/* ---------- Mise en page ---------- */
.page{max-width:1280px; margin:0 auto; padding:18px 20px 48px}
.page-narrow{max-width:760px}
.stack{display:flex; flex-direction:column; gap:16px}
.row{display:flex; align-items:center; gap:10px}
.row-end{margin-left:auto}
.grid-auto{display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px}

/* ---------- En-tête applicatif ---------- */
.topbar{
  display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  padding:12px 20px; background:var(--surface); border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:40;
}
.topbar .brand{display:flex; align-items:center; gap:9px; font-weight:600; letter-spacing:-.02em}
.topbar .brand .mark{
  width:30px; height:30px; border-radius:8px; background:var(--accent); color:#fff;
  display:grid; place-items:center; font-size:16px; flex:none;
}
.topbar nav{display:flex; gap:2px; margin-left:12px}
.topbar nav a{
  padding:7px 12px; border-radius:8px; color:var(--txt-2);
  font-size:14px; font-weight:500; text-decoration:none;
}
.topbar nav a:hover{background:var(--surface-2); color:var(--txt); text-decoration:none}
.topbar nav a.active{background:var(--accent-soft); color:var(--accent)}
@media(max-width:720px){ .topbar nav{width:100%; margin-left:0; overflow-x:auto} }

/* ---------- Cartes ---------- */
.card{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius-lg); padding:18px; box-shadow:var(--shadow);
}
.card-head{display:flex; align-items:center; gap:9px; margin-bottom:14px}
.card-head h2{flex:1}
.card-head .count{font-size:12px; color:var(--txt-3); font-weight:500}

/* ---------- Statistiques ---------- */
.stat{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:13px 15px}
.stat .label{font-size:11.5px; text-transform:uppercase; letter-spacing:.4px; color:var(--txt-3); font-weight:600}
/* Chiffres à chasse fixe : sans cela, une valeur qui se met à jour en direct
   fait danser la carte entière à chaque changement de largeur de chiffre. */
.stat .value{
  font-size:23px; font-weight:650; letter-spacing:-.03em; margin-top:5px;
  font-variant-numeric:tabular-nums;
}
.stat .sub{font-size:11.5px; color:var(--txt-3); margin-top:2px}

/* ---------- Formulaires ---------- */
.field{display:flex; flex-direction:column; margin-bottom:14px}
.field label{font-size:13px; font-weight:500; margin-bottom:6px; color:var(--txt)}
.field .hint{font-size:12px; color:var(--txt-3); margin-top:5px}
input[type=text],input[type=email],input[type=tel],input[type=password],
input[type=number],input[type=date],select,textarea{
  width:100%; padding:10px 13px; font:inherit; font-size:14.5px;
  color:var(--txt); background:var(--surface); border:1px solid var(--line-strong);
  border-radius:var(--radius); transition:border-color .15s, box-shadow .15s;
}
textarea{resize:vertical; min-height:96px; line-height:1.6}
select{cursor:pointer}
input:focus,select:focus,textarea:focus{
  outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft);
}
input.invalid,select.invalid,textarea.invalid{border-color:var(--danger)}
.field-error{font-size:12px; color:var(--danger); min-height:14px; margin-top:5px}

/* ---------- Boutons ----------
 *
 * Le survol ne colore plus la bordure en accent : quand quatre boutons se
 * suivent, cela donnait l'impression que chacun était sélectionné au passage.
 * Il soulève légèrement, ce qui suffit à signaler qu'on peut cliquer.
 */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  padding:9px 15px; font:inherit; font-size:14px; font-weight:550;
  border-radius:9px; border:1px solid var(--line-strong);
  background:var(--surface); color:var(--txt); cursor:pointer;
  white-space:nowrap;
  transition:background .14s, border-color .14s, box-shadow .14s, transform .08s;
}
.btn:hover:not(:disabled){background:var(--surface-2); box-shadow:var(--shadow); text-decoration:none}
.btn:active:not(:disabled){transform:translateY(1px); box-shadow:none}
.btn:disabled{opacity:.5; cursor:not-allowed}

.btn-primary{background:var(--accent); border-color:var(--accent); color:#fff; box-shadow:var(--shadow-accent)}
.btn-primary:hover:not(:disabled){filter:brightness(1.07); background:var(--accent); box-shadow:var(--shadow-accent)}

.btn-danger{color:var(--danger); border-color:var(--line-strong); background:var(--surface)}
.btn-danger:hover:not(:disabled){border-color:var(--danger); background:rgba(163,45,45,.07)}

.btn-ghost{border-color:transparent; background:transparent; color:var(--txt-2); box-shadow:none}
.btn-ghost:hover:not(:disabled){background:var(--surface-2); color:var(--txt); box-shadow:none}

.btn-block{width:100%}
.btn-sm{padding:6px 11px; font-size:13px; border-radius:8px}
/* Bouton ne portant qu'une icône : carré, sans le rembourrage d'un libellé */
.btn-icon{padding:9px; width:36px}
.btn-icon.btn-sm{padding:6px; width:30px}

/* En thème sombre, un aplat clair a besoin d'un texte franchement foncé :
   #fff sur --accent clair passerait sous le seuil de lisibilité. */
:root[data-theme="dark"] .btn-primary{color:#0d1117}
@media (prefers-color-scheme: dark){
  :root:not([data-theme]) .btn-primary{color:#0d1117}
}

/* ---------- Badges ---------- */
.badge{
  display:inline-flex; align-items:center; gap:5px;
  padding:3px 9px; border-radius:99px; font-size:12px; font-weight:600;
  background:var(--surface-2); color:var(--txt-2); border:1px solid var(--line);
}
.badge-ok{background:rgba(59,109,17,.12); color:var(--ok); border-color:transparent}
.badge-warn{background:rgba(180,83,9,.12); color:var(--warn); border-color:transparent}
.badge-danger{background:rgba(163,45,45,.12); color:var(--danger); border-color:transparent}
.badge-info{background:var(--accent-soft); color:var(--accent); border-color:transparent}

/* ---------- Tableaux ---------- */
.table{width:100%; border-collapse:collapse; font-size:14px}
.table th{
  text-align:left; padding:0 10px 9px; font-size:11px; font-weight:600;
  text-transform:uppercase; letter-spacing:.4px; color:var(--txt-3);
}
.table td{padding:10px; border-top:1px solid var(--line); vertical-align:middle}
.table tbody tr:hover{background:var(--surface-2)}
.table .num{text-align:right; font-variant-numeric:tabular-nums}

/* ---------- Modale ---------- */
.overlay{
  position:fixed; inset:0; background:rgba(10,14,22,.55); z-index:100;
  display:flex; align-items:center; justify-content:center; padding:18px;
  overflow-y:auto;
  animation:overlay-in .14s ease-out;
}
@keyframes overlay-in{from{opacity:0}to{opacity:1}}
.modal{
  background:var(--surface); border-radius:var(--radius-lg); box-shadow:var(--shadow-lg);
  width:100%; max-width:540px; max-height:90vh; overflow-y:auto;
  animation:modal-in .18s cubic-bezier(.2,.8,.3,1);
}
@keyframes modal-in{from{opacity:0; transform:translateY(10px) scale(.99)}to{opacity:1; transform:none}}
.modal-head{
  display:flex; align-items:center; gap:12px; padding:18px 20px;
  border-bottom:1px solid var(--line); position:sticky; top:0; background:var(--surface); z-index:1;
}
.modal-head h2{flex:1}
.modal-body{padding:20px}

/**
 * Pied de modale.
 *
 * Toutes les actions avaient la même largeur : quatre rectangles gris
 * identiques, sans hiérarchie, où l'action principale se noyait au milieu
 * des autres — et où le bouton de suppression était aussi visible qu'elle.
 *
 * Désormais l'action principale occupe la place restante, les actions
 * secondaires font la largeur de leur texte, et la destructive part à droite,
 * séparée du reste.
 */
.modal-foot{
  display:flex; align-items:center; gap:8px;
  padding:14px 20px; border-top:1px solid var(--line);
  flex-wrap:wrap;
}
.modal-foot .btn{flex:0 1 auto}
.modal-foot .btn-primary{flex:1 1 auto; min-width:160px}
.modal-foot .btn-danger{margin-left:auto}
@media(max-width:520px){
  .modal-foot .btn{flex:1 1 auto}
  .modal-foot .btn-danger{margin-left:0}
}

/* ---------- Feuille d'actions ----------
   Sur téléphone, le glisser-déposer n'existe pas : on choisit dans une liste.
   Elle monte du bas sur mobile, se centre sur grand écran. */
.sheet-back{
  position:fixed; inset:0; z-index:180;
  background:rgba(10,14,22,.5);
  display:flex; align-items:flex-end; justify-content:center;
  animation:sheet-fade .15s ease-out;
}
.sheet{
  background:var(--surface); width:100%; max-width:520px;
  border-radius:18px 18px 0 0; box-shadow:var(--shadow-lg);
  max-height:82vh; display:flex; flex-direction:column;
  animation:sheet-up .2s cubic-bezier(.2,.8,.3,1);
  padding-bottom:env(safe-area-inset-bottom, 0px);
}
.sheet-grip{
  width:38px; height:4px; border-radius:99px; background:var(--line-strong);
  margin:9px auto 3px; flex:none;
}
.sheet-head{padding:8px 20px 12px; border-bottom:1px solid var(--line); flex:none}
.sheet-head b{display:block; font-size:16px; font-weight:600; letter-spacing:-.02em}
.sheet-head span{display:block; font-size:12.5px; color:var(--txt-3); margin-top:2px}
.sheet-body{overflow-y:auto; padding:8px; -webkit-overflow-scrolling:touch}
.sheet-item{
  display:flex; align-items:center; gap:12px; width:100%;
  padding:13px 14px; border-radius:var(--radius);
  background:none; border:none; cursor:pointer; text-align:left;
  font:inherit; font-size:15px; color:var(--txt);
  transition:background .12s;
}
.sheet-item:hover{background:var(--surface-2)}
.sheet-item .dot{width:10px; height:10px; border-radius:99px; flex:none}
.sheet-item i.lead{font-size:19px; color:var(--txt-3); flex:none}
.sheet-item .lbl{flex:1; min-width:0}
.sheet-item .lbl b{display:block; font-weight:550}
.sheet-item .lbl span{display:block; font-size:12.5px; color:var(--txt-3); margin-top:1px}
.sheet-item.actif{background:var(--accent-soft); color:var(--accent)}
.sheet-item.actif .lbl b{font-weight:650}
.sheet-item .coche{margin-left:auto; color:var(--accent); font-size:18px; flex:none}
.sheet-item.danger{color:var(--danger)}
.sheet-foot{padding:8px 12px 14px; border-top:1px solid var(--line); flex:none}
@keyframes sheet-up{from{transform:translateY(100%)}to{transform:none}}
@keyframes sheet-fade{from{opacity:0}to{opacity:1}}
@media(min-width:640px){
  .sheet-back{align-items:center}
  .sheet{border-radius:var(--radius-lg); max-height:76vh; animation:toast-in .18s ease-out}
  .sheet-grip{display:none}
  .sheet-head{padding-top:16px}
}

/* ---------- États vides ---------- */
.empty{text-align:center; color:var(--txt-3); padding:34px 16px; font-size:14px}
.empty i{font-size:28px; display:block; margin-bottom:8px; opacity:.6}

/**
 * La mascotte dans les écrans vides.
 *
 * Elle n'apparaît que là où il n'y a rien à faire. Un écran vide est un
 * moment mort — l'illustration y remplit un silence. Sur un écran plein elle
 * volerait l'attention à ce que la personne est venue lire, et sur cet outil
 * ce qu'on vient lire, c'est de l'argent en attente.
 *
 * Elle n'est pas le symbole de la marque. La flèche reste dans la barre
 * latérale et dans l'onglet du navigateur ; une mascotte se promène, un
 * symbole tient en place.
 *
 * Le choix se fait d'après le pictogramme déjà posé par le JavaScript, avec
 * :has(). Cela évite de toucher aux fichiers de page, mais crée un couplage
 * silencieux : changer un <i class="ti ..."> dans un état vide fait
 * disparaître son illustration sans qu'aucune erreur ne le signale. Voir
 * img/mascotte/LISEZMOI.md.
 *
 * Un navigateur qui ignore :has() affiche le pictogramme d'origine. C'est
 * un repli acceptable — jamais un trou.
 */
.empty:has(> i.ti-coffee),
.empty:has(> i.ti-checklist),
.empty:has(> i.ti-check),
.empty:has(> i.ti-inbox){padding-top:22px}

.empty:has(> i.ti-coffee) > i,
.empty:has(> i.ti-checklist) > i,
.empty:has(> i.ti-check) > i,
.empty:has(> i.ti-inbox) > i{display:none}

.empty:has(> i.ti-coffee)::before,
.empty:has(> i.ti-checklist)::before,
.empty:has(> i.ti-check)::before,
.empty:has(> i.ti-inbox)::before{
  content:"";
  display:block;
  height:150px;
  margin:0 auto 14px;
  background-repeat:no-repeat;
  background-position:center bottom;
  /* contain, pas cover : les poses n'ont pas le même rapport de forme, et
     une mascotte rognée aux pieds fait accident plutôt que décor */
  background-size:contain;
}

/* Rien à faire aujourd'hui : c'est une bonne nouvelle, elle salue. */
.empty:has(> i.ti-coffee)::before{background-image:url("/img/mascotte/salut.webp")}
/* Aucune tâche : elle présente une liste, vide mais prête. */
.empty:has(> i.ti-checklist)::before{background-image:url("/img/mascotte/tablette.webp")}
/* Rien de terminé encore : elle attend, assise. */
.empty:has(> i.ti-check)::before{background-image:url("/img/mascotte/assise.webp")}
/* Aucune affaire dans cette étape : elle cherche. */
.empty:has(> i.ti-inbox)::before{background-image:url("/img/mascotte/loupe.webp")}

/* Sur petit écran, la place manque : on réduit plutôt que de faire défiler
   un état vide, ce qui serait le comble. */
@media(max-width:560px){
  .empty:has(> i.ti-coffee)::before,
  .empty:has(> i.ti-checklist)::before,
  .empty:has(> i.ti-check)::before,
  .empty:has(> i.ti-inbox)::before{height:110px}
}

/* ---------- Toasts ---------- */
.ui-toasts{position:fixed; bottom:18px; right:18px; z-index:200; display:flex; flex-direction:column; gap:8px}
.ui-toast{
  padding:11px 16px; border-radius:var(--radius); font-size:14px; font-weight:500;
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--shadow-lg);
  animation:toast-in .22s ease-out;
}
.ui-toast.ok{border-left:3px solid var(--ok)}
.ui-toast.ko{border-left:3px solid var(--danger)}
.ui-toast.out{opacity:0; transform:translateX(8px); transition:.3s}
@keyframes toast-in{from{opacity:0; transform:translateY(8px)}to{opacity:1; transform:none}}

/* ---------- Chargement plein écran ---------- */
.boot{display:grid; place-items:center; min-height:70vh; color:var(--txt-3); gap:12px}
.spinner{
  width:22px; height:22px; border:2px solid var(--line-strong);
  border-top-color:var(--accent); border-radius:50%; animation:spin .7s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* ---------- Divers ---------- */
.mono{font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:13px}
.muted{color:var(--txt-3)}
.sr-only{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0)}
@media(prefers-reduced-motion:reduce){*{animation-duration:.01ms !important; transition-duration:.01ms !important}}
