  :root {
    --bleu-fonce: #11285F;
    --bleu-moyen: #184093;
    --bleu-clair: #005EAA;
    --vert: #5EB245;
    --ambre: #E8A317;
    --texte: #1D1D18;
  }
  * { box-sizing: border-box; }
  html, body {
    margin: 0; padding: 0;
    font-family: -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    color: var(--texte);
    background: #F4F6FA;
  }
  /* Colonne pleine hauteur pour que la vue ?vue=repertoire (issue #405) puisse
     donner à son contenu exactement la hauteur restante sous l'en-tête, en CSS
     pur (flex:1 plus bas), sans mesurer quoi que ce soit en JS. */
  body { display: flex; flex-direction: column; min-height: 100vh; }
  /* Un composant cache/affiche toujours via l'attribut hidden natif : sans
     cette règle, une classe qui pose display:flex/block sur ce même élément
     gagnerait sur le [hidden] du navigateur et le rendrait visible quand même. */
  [hidden] { display: none !important; }
  header {
    background: var(--bleu-fonce);
    color: #fff;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    flex: none;
  }
  .bouton-retour {
    flex: none;
    width: 44px;
    height: 44px;
    line-height: 44px;
    text-align: center;
    border-radius: 10px;
    color: #fff;
    text-decoration: none;
    font-size: 22px;
    background: rgba(255, 255, 255, 0.14);
  }
header img.logo-emv { height: 40px; width: auto; flex: none; display: block; background: #fff; border-radius: 12px; padding: 5px 7px; }
  header h1 {
    margin: 0;
    font-size: 22px;
    font-weight: 800;
    letter-spacing: 0.02em;
  }
  .sous-titre {
    padding: 10px 20px;
    font-size: 13px;
    color: #5b5b55;
    background: #fff;
    border-bottom: 1px solid #e3e3de;
  }
  .bandeau-horsligne {
    background: var(--ambre);
    color: var(--texte);
    padding: 10px 20px;
    font-size: 13px;
    font-weight: 700;
    flex: none;
  }
  #ecran-tuiles, #vue-repertoire, #vue-acces { flex: 1 1 auto; min-height: 0; }
  .controles {
    max-width: 960px;
    margin: 0 auto;
    padding: 14px 20px 0;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
  }
  .interrupteur {
    display: inline-flex;
    border: 1px solid #c9cdd6;
    border-radius: 10px;
    overflow: hidden;
    background: #fff;
    flex: none;
  }
  .interrupteur .segment {
    border: none;
    background: #fff;
    color: var(--texte);
    padding: 10px 16px;
    min-height: 44px;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
  }
  .interrupteur .segment[aria-pressed="true"] { background: var(--bleu-fonce); color: #fff; }
  #recherche {
    flex: 1;
    min-width: 180px;
    min-height: 44px;
    border: 1px solid #c9cdd6;
    border-radius: 10px;
    padding: 0 14px;
    font-size: 14px;
    color: var(--texte);
  }
  main { padding: 20px; max-width: 960px; margin: 0 auto; }
  .grille {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
  }
  .tuile {
    position: relative;
    display: block;
    text-decoration: none;
    color: #1D1D18;
    background: #fff;
    border-radius: 12px;
    border: 1px solid #e3e3de;
    border-left: 4px solid var(--c2, var(--bleu-moyen));
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    padding: 16px 52px 16px 16px;
    min-height: 44px;
    box-shadow: 0 1px 2px rgba(17, 40, 95, 0.08);
  }
  .tuile:hover { background: var(--halo, #F4F6FA); }
  .tuile .ligne { display: flex; align-items: center; gap: 10px; }
  .tuile .emoji {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px; height: 36px;
    border-radius: 50%;
    background: var(--c1, #e3e3de);
    font-size: 19px;
  }
  .tuile .nom { font-weight: 700; color: #1D1D18; }
  .tuile .aide { font-size: 13px; margin-top: 6px; color: #4a4a44; }
  .tuile .compteur {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    min-width: 0;
  }
  /* compteur.lignes (issue #464) : plusieurs lignes empilées sous le nom du
     module — la pastille reste unique, alignée sur la PREMIÈRE ligne, donc
     remontée en haut plutôt que centrée verticalement sur tout le bloc. */
  .tuile .compteur.multiligne { align-items: flex-start; }
  .tuile .compteur.multiligne .pastille { margin-top: 4px; }
  .tuile .compteur .pastille {
    flex: none;
    width: 10px;
    height: 10px;
    border-radius: 50%;
  }
  .tuile .compteur .texte-compteur {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    color: #1D1D18;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Compteur figé (ex. « AppSheet ») pour une tuile sans clé au Worker :
     gris, jamais coloré comme un vrai compteur, pas de pastille. */
  .tuile .compteur .texte-compteur-fixe { color: #8a8a82; }
  .tuile .compteur-lignes {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .tuile .ligne-compteur {
    font-size: 13px;
    color: #1D1D18;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Lignes au-delà de la 3e : cachées tant que la tuile n'est pas dépliée
     (bouton « +N autres », voir compteur-plus plus bas). */
  .tuile .ligne-compteur-cachee { display: none; }
  .tuile .compteur.deplie .ligne-compteur-cachee { display: block; }
  .tuile .compteur-plus {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    border: none;
    background: transparent;
    color: var(--bleu-clair);
    font-size: 12px;
    font-weight: 700;
    padding: 0;
    cursor: pointer;
  }

  /* ---------- TUILE GÉNÉRIQUE (issue #479) ----------
     lignes/pastilles/alerte de la tuile { lignes, pastilles, alerte, lien }
     publiée par le hub (hub/Tuiles.js) et transportée telle quelle par le
     Worker (worker/src/index.js). Remplace, pour les tuiles qui la publient,
     le rendu compteur.texte/lignes ci-dessus (`.compteur.multiligne` etc.,
     conservé pour le bandeau de relais et les reprises directes de
     `compteur`) — une seule tuile (`.compteur.tuile-generique`) empile
     lignes puis pastilles puis alerte, toujours dans cet ordre. */
  .tuile .compteur.tuile-generique { flex-direction: column; align-items: stretch; gap: 4px; }
  .tuile-lignes { display: flex; flex-direction: column; gap: 2px; }
  .tuile-ligne { display: flex; align-items: center; gap: 6px; min-width: 0; }
  .tuile-ligne-point { flex: none; width: 8px; height: 8px; border-radius: 50%; }
  .tuile-ligne-texte {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    color: #1D1D18;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .tuile-ligne-bouton, .tuile-alerte-bouton {
    flex: none;
    min-height: 28px;
    padding: 0 8px;
    border: none;
    border-radius: 6px;
    background: var(--bleu-fonce);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
  }
  /* Pastilles 2×2 (issue #479) : jusqu'à 4, deux colonnes toujours — avec 2
     ou 3 pastilles la grille reste sur 2 colonnes (une case incomplète),
     jamais un réalignement qui ferait bouger la mise en page selon le
     compte. */
  .tuile-pastilles {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px 8px;
    margin-top: 2px;
  }
  .tuile-pastille {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 32px;
    padding: 2px 0;
    border: none;
    background: transparent;
    text-align: left;
    cursor: default;
  }
  button.tuile-pastille { cursor: pointer; }
  .tuile-pastille-valeur { font-size: 14px; font-weight: 800; color: var(--bleu-fonce); line-height: 1.2; }
  .tuile-pastille-etiquette {
    font-size: 11px;
    color: #6b6b64;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Alerte (issue #479) : le bouton qui la règle est collé à côté d'elle,
     jamais ailleurs sur la tuile (règle EMV, voir CLAUDE.md). */
  .tuile-alerte {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 2px;
    padding-top: 4px;
    border-top: 1px solid #e3e3de;
  }
  .tuile-alerte-texte { flex: 1; min-width: 0; font-size: 12px; font-weight: 700; }
  .tuile-alerte-rouge .tuile-alerte-texte { color: var(--rouge, #C62828); }
  .tuile-alerte-ambre .tuile-alerte-texte { color: #E8A317; }
  .tuile-alerte-vert .tuile-alerte-texte { color: #5EB245; }
  .tuile-alerte-neutre .tuile-alerte-texte { color: #1D1D18; }

  .etat-pied {
    max-width: 960px;
    margin: 8px auto 0;
    padding: 0 20px;
    font-size: 12px;
    color: #6b6b64;
  }
  .etoile {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 44px;
    height: 44px;
    border: none;
    background: transparent;
    color: var(--bleu-fonce);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .vide {
    background: #fff;
    border: 1px dashed #c9cdd6;
    border-radius: 12px;
    padding: 20px;
    font-size: 14px;
    color: #4a4a44;
  }
  .vide strong { color: var(--bleu-fonce); }

  /* ---------- BANDEAU DE RELAIS (issue #444) ----------
     Une ligne, au-dessus de la grille : où en est la tournée de la semaine.
     Lu dans ETAT.modules.relais (Worker /etat — même route que les autres
     compteurs), jamais un calcul côté coquille. Couleurs identiques à
     COULEURS_COMPTEUR (script plus bas) pour rester cohérent avec les
     pastilles de compteur déjà affichées sur les tuiles. Caché tant
     qu'aucun compteur.texte n'est disponible — même convention [hidden] que
     le reste de la coquille, jamais une ligne vide affichée. */
  .relais-bandeau {
    max-width: 960px;
    margin: 14px auto 0;
    padding: 10px 14px;
    border-radius: 10px;
    color: #fff;
    font-size: 13px;
    font-weight: 700;
    line-height: 1.5;
  }
  @media (max-width: 700px) { .relais-bandeau { margin: 10px 12px 0; font-size: 12.5px; } }

  /* ---------- ONGLET « TOURNÉE » (issue #464) ----------
     Remplace la grille par les 5 étapes du relais, empilées 1->5, quand cet
     onglet est actif. Étapes ①②③ recalculées côté coquille à partir des
     compteurs déjà publics de checklist/comptabilisation/trailer-jack (même
     correspondance vert/orange/rouge -> fait/en_cours/bloqué que
     relaisEtapeModule_, hub/Relais.js) : le Worker n'expose pas
     relais.etapes (voir docs/connaissances/worker.md). ④/⑤ restent
     toujours « — »/« à venir », comme côté hub — jamais un état deviné. */
  .tournee { display: flex; flex-direction: column; gap: 10px; }
  .tournee-entete { margin: 0; font-weight: 800; color: var(--bleu-fonce); font-size: 15px; }
  .tournee-message { background: #fff; border: 1px dashed #c9cdd6; border-radius: 12px; padding: 16px; font-size: 14px; color: #4a4a44; }
  .tournee-liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
  .tournee-etape {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fff;
    border: 1px solid #e3e3de;
    border-radius: 12px;
    padding: 12px 14px;
    min-height: 44px;
    text-decoration: none;
    color: var(--texte);
  }
  a.tournee-etape:hover { background: #F4F6FA; }
  .tournee-etape.actif { background: #FFF6DE; border-color: var(--ambre); }
  .tournee-num { flex: none; width: 26px; font-size: 19px; font-weight: 800; color: var(--bleu-moyen); text-align: center; white-space: nowrap; }
  .tournee-corps { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .tournee-libelle { font-weight: 700; }
  .tournee-etat { font-size: 13px; color: #4a4a44; }
  .tournee-etape.inconnu .tournee-etat { color: #8a8a82; }
  .tournee-bouton {
    flex: none;
    min-height: 32px;
    padding: 0 12px;
    display: inline-flex;
    align-items: center;
    border-radius: 8px;
    background: var(--bleu-fonce);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
  }
  @media (max-width: 700px) {
    .tournee-etape { padding: 10px 12px; gap: 8px; }
    .tournee-num { width: 22px; font-size: 16px; }
  }

  /* Numéro de relais (issue #444) : pastille discrète sur l'icône des tuiles
     Checklist terrain/Comptabilisation des tournées/Livraisons (Trailer
     Jack) seulement — toujours affiché (l'ordre 1->3 est structurel, pas une
     donnée en direct). Le Worker ne sert que { module, calcule_le, etat,
     compteur } (issue #378) : la coquille ne peut donc pas savoir QUELLE
     étape est active comme le fait le hub (drive/hub-emv-vue.html, via
     relais() en Apps Script) — pas de surlignage de tuile ici tant que le
     Worker n'expose pas les étapes détaillées (demande séparée). */
  .tuile .emoji { position: relative; }
  .tuile .relais-num {
    position: absolute; top: -6px; left: -6px; z-index: 1;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--bleu-moyen); color: #fff;
    font-size: 10px; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  }
  .pied-installation {
    max-width: 960px;
    margin: 0 auto;
    padding: 0 20px 8px;
    font-size: 12px;
    color: #6b6b64;
  }
  .bouton-installer {
    min-height: 44px;
    padding: 8px 16px;
    border: 1px solid var(--bleu-fonce);
    border-radius: 8px;
    background: #fff;
    color: var(--bleu-fonce);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
  }

  /* Porte d'accès par passkey (issue #409). */
  .carte-porte {
    max-width: 420px;
    margin: 14px auto 0;
    padding: 16px;
    background: #fff;
    border: 1px solid #e3e3de;
    border-radius: 12px;
    text-align: center;
  }
  .carte-porte-titre { margin: 0 0 10px; font-weight: 800; color: var(--bleu-fonce); }
  .bouton-porte {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    width: 100%;
    padding: 0 16px;
    border: none;
    border-radius: 8px;
    background: var(--bleu-fonce);
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
  }
  .carte-porte .eawrap { justify-content: center; margin-top: 10px; }
  .carte-porte-aide { font-size: 12px; color: #6b6b64; margin: 8px 0 0; }
  .carte-porte-erreur { color: #C62828; font-size: 13px; margin: 8px 0 0; }
  .carte-porte-domaine { font-size: 13px; color: #6b6b64; margin: 4px 0 0; }

  /* Zone utilisateur (en-tête, en haut à droite) — issue #409. */
  .zone-utilisateur { margin-left: auto; position: relative; flex: none; }
  .bouton-utilisateur {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    min-height: 44px;
    padding: 4px 10px;
    border: none;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
    font-size: 13px;
    cursor: pointer;
  }
  .utilisateur-role { font-size: 11px; opacity: 0.85; }
  .menu-utilisateur {
    position: absolute;
    top: 48px;
    right: 0;
    background: #fff;
    border: 1px solid #e3e3de;
    border-radius: 10px;
    box-shadow: 0 4px 14px rgba(17, 40, 95, 0.18);
    min-width: 200px;
    overflow: hidden;
    z-index: 5;
  }
  .menu-utilisateur button {
    display: block;
    width: 100%;
    min-height: 44px;
    padding: 0 16px;
    border: none;
    background: #fff;
    color: var(--texte);
    font-size: 14px;
    text-align: left;
    cursor: pointer;
  }
  .menu-utilisateur button:hover { background: #F4F6FA; }

  /* Invitation (?inviter=) et vue Accès (?vue=acces) — issue #409. */
  .contenu-invitation { flex: 1 1 auto; display: flex; align-items: flex-start; justify-content: center; padding: 24px 16px; }
  .carte-invitation { max-width: 420px; width: 100%; background: #fff; border: 1px solid #e3e3de; border-radius: 12px; padding: 20px; }
  .carte-invitation label, .panneau-acces label, .etiquette-champ { display: block; font-size: 13px; font-weight: 700; margin: 12px 0 4px; color: var(--texte); }
  .carte-invitation input[type="text"], .panneau-acces input[type="text"], .panneau-acces select {
    width: 100%;
    min-height: 44px;
    border: 1px solid #c9cdd6;
    border-radius: 8px;
    padding: 0 12px;
    font-size: 14px;
  }
  .panneau-acces { max-width: 520px; margin: 0 auto 16px; padding: 16px; background: #fff; border: 1px solid #e3e3de; border-radius: 12px; }
  .titre-panneau-acces { font-size: 15px; margin: 16px 0 8px; color: var(--bleu-fonce); }
  .cases-modules { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0; }
  .case-module { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; background: #F4F6FA; border-radius: 8px; padding: 6px 10px; min-height: 32px; }
  .ligne-boutons-acces { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
  .bouton-secondaire-acces, .bouton-action-acces {
    min-height: 40px;
    padding: 0 14px;
    border: 1px solid var(--bleu-fonce);
    border-radius: 8px;
    background: #fff;
    color: var(--bleu-fonce);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
  }
  .carte-utilisateur .ligne-boutons-acces { margin-top: 10px; }

  /* Vue ?vue=repertoire (issue #405) : lecture native du répertoire clients
     via le Worker (AppSheet refuse l'affichage en iframe — issue #405),
     modification toujours par le bouton « Ouvrir dans AppSheet ↗ ». */
  .vue-cadre { display: flex; flex-direction: column; min-height: 0; }
  .barre-cadre {
    flex: none;
    display: flex;
    justify-content: flex-end;
    padding: 8px 12px;
    background: #fff;
    border-bottom: 1px solid #e3e3de;
  }
  .bouton-externe {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 14px;
    border: 1px solid var(--bleu-fonce);
    border-radius: 8px;
    color: var(--bleu-fonce);
    text-decoration: none;
    font-size: 13px;
    font-weight: 700;
    background: #fff;
    cursor: pointer;
  }
  .chargement-cadre { flex: none; display: flex; justify-content: center; padding: 16px; }

  .contenu-repertoire { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 12px 20px; overflow-y: auto; overflow-x: hidden; }
  .barre-recherche-repertoire { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; flex: none; }
  .barre-recherche-repertoire input[type="search"] { flex: 1; min-width: 200px; min-height: 44px; border: 1px solid #c9cdd6; border-radius: 10px; padding: 0 14px; font-size: 14px; }
  .compteur-repertoire { font-size: 13px; color: #6b6b64; white-space: nowrap; }

  /* Cellulaire (< 700px, règle générale issue #195) : cartes groupées par client. */
  .repertoire-cartes { display: none; }
  .groupe-client { padding: 10px 2px 4px; }
  .groupe-client-nom { font-weight: 800; color: var(--bleu-fonce); font-size: 15px; }
  .groupe-client-emplacement { font-size: 12px; color: #6b6b64; margin-top: 2px; }
  .groupe-client-jira { font-size: 12px; color: var(--bleu-clair); text-decoration: none; margin-top: 2px; display: inline-block; }
  .carte-contact { background: #fff; border: 1px solid #e3e3de; border-radius: 12px; padding: 12px; margin: 8px 0; }
  .carte-contact-nom { font-weight: 700; }
  .carte-contact-titre { font-size: 13px; color: #6b6b64; margin-top: 2px; }
  .bouton-carte-repertoire {
    display: block;
    width: 100%;
    min-height: 44px;
    line-height: 44px;
    text-align: center;
    margin-top: 8px;
    border: 1px solid var(--bleu-fonce);
    border-radius: 8px;
    color: var(--bleu-fonce);
    text-decoration: none;
    font-size: 14px;
    font-weight: 700;
  }

  /* Ordinateur (>= 700px) : tableau dense, triable, défilement propre au
     conteneur — jamais de défilement horizontal de la page (issue #195). */
  .repertoire-tableau-conteneur { display: block; overflow-x: auto; border: 1px solid #e3e3de; border-radius: 12px; }
  .repertoire-tableau { border-collapse: collapse; width: 100%; font-size: 13px; }
  .repertoire-tableau th, .repertoire-tableau td { padding: 8px 12px; border-bottom: 1px solid #e3e3de; text-align: left; white-space: nowrap; }
  .repertoire-tableau thead th { background: #F4F6FA; }
  .entete-tri { border: none; background: transparent; font-weight: 700; font-size: 13px; cursor: pointer; padding: 0; color: var(--texte); }
  .repertoire-tableau td a { color: var(--bleu-clair); }

  .pied-repertoire { flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 14px; padding: 10px 20px; font-size: 12px; color: #6b6b64; border-top: 1px solid #e3e3de; background: #fff; }
  .lien-discret { border: none; background: transparent; color: var(--bleu-clair); font-size: 12px; cursor: pointer; padding: 8px 0; min-height: 44px; }

  /* Autolaveuse EMV — même composant visuel que drive/emv-taches-vue.html et
     drive/emv-livraisons-vue.html (issue #429, remplace l'ancien indicateur à personnage des
     issues #375/#380) : jamais un sablier ni un rond qui tourne sans rapport avec un vrai
     traitement (règle permanente EMV — voir
     docs/connaissances/hub.md). web/index.html est une page statique Cloudflare Pages, sans
     Apps Script : elle ne peut pas charger drive/emv-activite-client.js.txt à l'exécution,
     donc ce bloc est une copie fixe du même dessin (CSS/SVG pur, aucune image ni bibliothèque). */
  .eawrap { display: flex; align-items: center; gap: 7px; padding: 8px 4px; min-height: 44px; }
  .eascene { position: relative; width: 40px; height: 16px; flex: none; overflow: hidden; }
  .eapiste { position: absolute; left: 0; right: 0; bottom: 2px; height: 2px; border-radius: 2px; background: #dfe4ee; }
  .eamachine {
    position: absolute; bottom: 0; left: 0; width: 18px; height: 12px; border-radius: 3px 6px 6px 3px;
    background: var(--bleu-fonce); animation: earoule 1.6s ease-in-out infinite;
  }
  .eamachine::before, .eamachine::after {
    content: ''; position: absolute; bottom: -2px; width: 4px; height: 4px; border-radius: 50%; background: #11285F;
  }
  .eamachine::before { left: 2px; }
  .eamachine::after { right: 2px; }
  .eapoussiere {
    position: absolute; bottom: 1px; width: 3px; height: 3px; border-radius: 50%; background: #8a94a6;
    animation: eaavale 1.6s ease-in-out infinite;
  }
  .eapoussiere:nth-child(3) { left: 22px; }
  .eapoussiere:nth-child(4) { left: 28px; animation-delay: .15s; }
  .eapoussiere:nth-child(5) { left: 34px; animation-delay: .3s; }
  @keyframes earoule { 0%, 100% { left: 0; } 50% { left: 20px; } }
  @keyframes eaavale { 0%, 15% { opacity: 1; } 45%, 100% { opacity: 0; } }
  @media (prefers-reduced-motion: reduce) { .eamachine, .eapoussiere { animation: none; } }

  /* Sous 700px (issue #195) : une colonne, cartes, cibles tactiles ≥ 44px. */
  @media (max-width: 700px) {
    main { padding: 12px; }
    .grille { grid-template-columns: 1fr; }
    header h1 { font-size: 19px; }
    .controles { flex-direction: column; align-items: stretch; padding: 12px 12px 0; }
    .interrupteur .segment { flex: 1; }
    #recherche { width: 100%; }
    .pied-installation, .etat-pied { padding-left: 12px; padding-right: 12px; }
    .contenu-repertoire { padding: 12px; }
    .barre-recherche-repertoire { flex-direction: column; align-items: stretch; }
    .repertoire-cartes { display: block; }
    .repertoire-tableau-conteneur { display: none; }
    .pied-repertoire { padding: 10px 12px; }
    .carte-porte { margin: 12px 12px 0; }
    .contenu-invitation { padding: 16px 12px; }
    .panneau-acces { margin: 0 0 16px; }
    .bouton-utilisateur { padding: 4px 8px; font-size: 12px; }
    .menu-utilisateur { min-width: 180px; }
  }
