
:root {
  color-scheme: light;
  --bg: #f2f2f7;
  --surface: #ffffff;
  --surface-sunken: #f7f7fa;
  --border: #e5e5ea;
  --border-strong: #c7c7cc;
  --text: #1c1c1e;
  --text-2: #3a3a3c;
  --muted: #6e6e73;

  --brand: #e8461e;
  --brand-hover: #c8380f;
  --brand-soft: #fdece7;
  --brand-border: #f6c4b5;

  --gold: #9a6b00;
  --gold-soft: #fbf3e0;
  --gold-border: #e8d5a6;
  --ok: #0c7a3e;      --ok-soft: #e7f4ec;
  --warn: #a86800;    --warn-soft: #fdf3e3;
  /* Vu, pas encore répondu : le bleu des messages lus. */
  --seen: #2463d6;    --seen-soft: #e8f0fd;
  --open: #7b3fe4;    --open-soft: #f1eafd;
  --crit: #c22828;    --crit-soft: #fdeceb;

  --r: 14px;
  --r-sm: 10px;
  --shadow: 0 1px 2px rgba(0,0,0,.04), 0 3px 12px rgba(0,0,0,.04);
  --pad: 16px;

  /* Matériau de la barre du haut : translucide, le contenu passe dessous. */
  --topbar-bg: rgba(255,255,255,.72);
  --topbar-edge: rgba(0,0,0,.08);
  /* Bord des cartes : à peine visible, c'est le fond qui les détache. */
  --card-edge: rgba(0,0,0,.05);
  /* Halo de saisie : la couleur de la marque, adoucie. */
  --focus-ring: color-mix(in srgb, var(--brand) 24%, transparent);
  /* Courbe d'arrivée sans rebond, proche d'un ressort amorti. */
  --ease: cubic-bezier(.22,.9,.32,1);
}
/* Le thème suit l'appareil par défaut. Un choix explicite le supplante dans
   les deux sens : sans le :not([data-theme="light"]), un système en sombre
   l'emporterait sur quelqu'un qui a demandé le clair. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #000000;
    --surface: #1c1c1e;
    --surface-sunken: #161618;
    --border: #2c2c2e;
    --border-strong: #48484a;
    --text: #f5f5f7;
    --text-2: #d1d1d6;
    --muted: #98989d;

    --brand: #ff6a3d;
    --brand-hover: #ff8055;
    --brand-soft: #33190f;
    --brand-border: #5c2c1a;

    --gold: #e0b84a;
    --gold-soft: #2a2415;
    --gold-border: #4a3f22;
    --ok: #4ec27d;      --ok-soft: #14291d;
    --warn: #e0a63a;    --warn-soft: #2b2312;
    --seen: #6b9bff;    --seen-soft: #16213a;
    --open: #b48cff;    --open-soft: #241a38;
    --crit: #f2706b;    --crit-soft: #2e1717;
    --shadow: none;
    --topbar-bg: rgba(22,22,24,.72);
    --topbar-edge: rgba(255,255,255,.08);
    --card-edge: rgba(255,255,255,.04);
  }
}
:root[data-theme="dark"] {
    color-scheme: dark;
    --bg: #000000;
    --surface: #1c1c1e;
    --surface-sunken: #161618;
    --border: #2c2c2e;
    --border-strong: #48484a;
    --text: #f5f5f7;
    --text-2: #d1d1d6;
    --muted: #98989d;

    --brand: #ff6a3d;
    --brand-hover: #ff8055;
    --brand-soft: #33190f;
    --brand-border: #5c2c1a;

    --gold: #e0b84a;
    --gold-soft: #2a2415;
    --gold-border: #4a3f22;
    --ok: #4ec27d;      --ok-soft: #14291d;
    --warn: #e0a63a;    --warn-soft: #2b2312;
    --seen: #6b9bff;    --seen-soft: #16213a;
    --open: #b48cff;    --open-soft: #241a38;
    --crit: #f2706b;    --crit-soft: #2e1717;
    --shadow: none;
    --topbar-bg: rgba(22,22,24,.72);
    --topbar-edge: rgba(255,255,255,.08);
    --card-edge: rgba(255,255,255,.04);
  }


* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/* ---------- Comportement d'application ----------
   Posée sur un écran d'accueil, la page doit se comporter comme une
   application, pas comme un site consulté dans un navigateur.

   « touch-action: manipulation » supprime le délai de trois cents
   millisecondes que les navigateurs mobiles s'accordent pour voir si un
   appui n'était pas le début d'un double-appui de zoom. C'est ce délai qui
   fait qu'un bouton « paraît » lent.

   Le reflet gris au doigt, la loupe de sélection et le menu du appui long
   sont des gestes de lecture : sur une feuille de route, ils ne font que
   gêner. La sélection reste permise là où l'on copie réellement quelque
   chose — une adresse, un numéro, un code. */
* { -webkit-tap-highlight-color: transparent; }
button, a, summary, label, input, select, .row-item { touch-action: manipulation; }
body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  /* Pas de rebond élastique en bout de liste, ni de « tirer pour recharger » :
     un livreur qui fait défiler ses arrêts ne doit pas recharger sa tournée. */
  overscroll-behavior-y: none;
  /* La barre d'accueil de l'iPhone mange le bas de l'écran : le dernier
     bouton d'une page ne doit pas se retrouver dessous. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter,
    "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  font-feature-settings: "cv05" 1, "tnum" 0;
}
a { color: var(--brand); text-underline-offset: 2px; }

/* Ce qu'on recopie vraiment reste sélectionnable, appui long compris. */
.stop-address, .access-link, .sms-preview, .client-name, .order-ref,
.money, code, input, textarea {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

/* ---------- En-tête ---------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  /* Sous l'encoche en mode application : sans cela, le logo passe dessous. */
  padding-top: env(safe-area-inset-top, 0px);
}
.topbar-inner {
  max-width: 960px; margin: 0 auto;
  padding: 14px var(--pad);
  display: flex; align-items: center; gap: 12px;
}
/* Le logo est tracé à la main, orange sur fond transparent : il tient sur les
   deux thèmes sans retouche. Sa hauteur est fixée, la largeur suit, pour que
   la barre ne saute pas pendant le chargement. */
.brand { display: inline-flex; align-items: center; text-decoration: none; flex: none; }
.brand img { height: 26px; width: auto; display: block; }
@media (max-width: 560px) { .brand img { height: 23px; } }
/* Le symbole seul, sur fond transparent, pour les barres trop chargées. Il
   ne s'affiche jamais en même temps que le mot, et se pose un peu plus haut
   que lui : sans le mot à côté, il paraîtrait perdu à taille égale.
   La règle porte « .brand » : sans lui, « .brand img » l'emportait et les
   deux images s'affichaient côte à côte. */
.brand .brand-mark { display: none; height: 30px; width: auto; }

/* ---------- Retour ----------
   L'application s'ouvre depuis l'écran d'accueil, en plein écran : pas de
   barre de navigateur, donc pas de bouton « précédent ». Chaque écran qui
   n'est pas un accueil porte le sien, discret, en tête de page. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  min-height: 36px;
  margin: -6px 0 6px -6px;
  padding: 0 10px 0 4px;
  border-radius: 10px;
  color: var(--text-2);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
}
.back-link svg { width: 20px; height: 20px; transform: rotate(90deg); }

/* ---------- Retours d'annulations, dans la réserve ---------- */
.returns-info { margin-top: 10px; border: 1px solid var(--border); border-radius: var(--r); }
.returns-info > summary { display: flex; align-items: center; gap: 8px; padding: 10px 12px;
  font-size: 14px; font-weight: 600; color: var(--text-2); cursor: pointer; list-style: none; }
.returns-info > summary::-webkit-details-marker { display: none; }
.returns-info > summary svg { width: 18px; height: 18px; color: var(--brand); flex: none; }
.returns-info[open] > summary { border-bottom: 1px solid var(--border); }
.returns-info > p { padding: 0 12px 10px; }
.js-return.is-gone { opacity: .45; }
.js-return.is-gone .qty { text-decoration: line-through; }

/* ---------- Invitation au tutoriel ---------- */
.tuto-invite { border-color: var(--brand-border); background: var(--brand-soft); }
.tuto-invite b { font-size: 17px; }
.tuto-invite p { margin: 4px 0 12px; color: var(--text-2); }
.back-link:focus-visible, .back-link:active { color: var(--text); background: var(--surface); }
@media (hover: hover) and (pointer: fine) { .back-link:hover { color: var(--text); background: var(--surface); } }
@media (max-width: 560px) { .brand .brand-mark { height: 28px; } }
.topbar h1 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -0.02em; }
.topbar .sub { color: var(--muted); font-size: 14px; }
/* La date reste visible sur téléphone, contrairement au sous-titre : c'est
   elle qui distingue la soirée d'hier de celle d'aujourd'hui. */
.topbar-date { color: var(--text-2); font-size: 14px; white-space: nowrap; }
@media (max-width: 400px) { .topbar-date { font-size: 13px; } }
.topbar-tag {
  margin-left: auto; color: var(--brand); font-weight: 700; font-size: 15px;
  font-variant-numeric: tabular-nums;
}
/* Les onglets défilent plutôt que de pousser la barre hors de l'écran :
   quatre d'entre eux ne tiennent pas sur un téléphone étroit, et une page
   qui glisse latéralement emporte aussi le bouton d'actualisation. */
nav {
  margin-left: auto; display: flex; gap: 2px;
  min-width: 0; overflow-x: auto; scrollbar-width: none;
}
nav::-webkit-scrollbar { display: none; }
nav a {
  padding: 8px 14px; border-radius: var(--r-sm); text-decoration: none;
  font-size: 15px; font-weight: 500; color: var(--text-2); white-space: nowrap;
}
nav a[aria-current] { background: var(--brand-soft); color: var(--brand); font-weight: 600; }

/* Sur un écran étroit, la navigation prime sur le sous-titre. */
@media (max-width: 460px) {
  .topbar-inner { padding: 12px 12px; gap: 8px; }
  .topbar .sub { display: none; }
  nav a { padding: 8px 9px; font-size: 14px; }
  /* Un logo en toutes lettres, quatre onglets et une date ne tiennent pas
     ensemble sur un téléphone. Seule la date cède là où la barre porte trois
     onglets ou plus : le livreur ouvre sa tournée depuis son espace, il sait
     quel jour on est. Le logo reste — c'est par lui qu'il revient chez lui, et
     une barre qui commence par un onglet ne ressemble plus à une application.
     Deux onglets, comme en gestion, laissent la place à tout. */
  .topbar-inner:has(nav a:nth-child(3)) .topbar-date { display: none; }
  :root { --pad: 14px; }
}

/* Sous 380 px, le mot cède quand même la place au symbole : quatre onglets
   coupés en deux seraient pires qu'un logo réduit. */
@media (max-width: 379px) {
  .topbar-inner:has(nav a:nth-child(3)) .brand .brand-word { display: none; }
  .topbar-inner:has(nav a:nth-child(3)) .brand .brand-mark { display: block; }
}

/* ---------- Structure ---------- */
main { max-width: 960px; margin: 0 auto; padding: var(--pad) var(--pad) 64px; }
.section-title {
  font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); margin: 24px 0 10px;
}
.section-title:first-child { margin-top: 0; }
.section-title .section-note { margin-left: 6px; }
.section-title .section-note::before { content: "· "; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  margin-bottom: 12px;
  overflow: hidden;
}
.card-body { padding: var(--pad); }
.card-head {
  padding: 14px var(--pad);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.card-head h2 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
.card-head > svg { flex: 0 0 auto; color: var(--muted); }
.card-head .meta { color: var(--muted); font-size: 14px; }
.card-foot {
  padding: 12px var(--pad); background: var(--surface-sunken);
  border-top: 1px solid var(--border);
}

/* ---------- Indicateurs ---------- */
/* Sur téléphone, les chiffres se rangent deux par deux : en trois colonnes,
   le quatrième laissait une case grise vide en bout de rangée. Un nombre
   impair fait occuper toute la largeur au dernier, pour la même raison. */
.stats { display: grid; gap: 1px; background: var(--border);
  grid-template-columns: repeat(2, 1fr); }
.stats > :last-child:nth-child(odd) { grid-column: 1 / -1; }
/* Trois chiffres tiennent sur une ligne : deux puis un seul en pleine
   largeur laissaient le troisième à part, comme oublié. */
.stats:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stats:has(> :nth-child(3):last-child) > :last-child { grid-column: auto; }
@media (max-width: 379px) {
  .stats:has(> :nth-child(3):last-child) .stat { padding-left: 10px; padding-right: 10px; }
}
@media (min-width: 620px) {
  .stats { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
  .stats > :last-child:nth-child(odd) { grid-column: auto; }
}
.stat { background: var(--surface); padding: 14px var(--pad); }
.stat b {
  display: block; font-weight: 700; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums; line-height: 1.15;
  /* Un montant ne doit jamais se couper entre le nombre et sa devise. */
  white-space: nowrap;
  font-size: clamp(19px, 5.4vw, 28px);
}
.stat span { font-size: 13px; color: var(--muted); }
.stat b.is-late { color: var(--warn); }

/* ---------- Étiquettes et bandeaux ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.03em;
  padding: 3px 9px; border-radius: var(--r-sm);
  background: var(--surface-sunken); color: var(--text-2);
  border: 1px solid var(--border);
  /* Un montant ne doit jamais se couper entre le nombre et sa devise. */
  white-space: nowrap;
}
.badge.brand { background: var(--brand-soft); color: var(--brand); border-color: var(--brand-border); }
.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.badge.crit { background: var(--crit-soft); color: var(--crit); border-color: transparent; }
/* Le petit format est un état du sorte : signalé sur la ligne, pas ailleurs. */
.badge.small-fmt {
  background: var(--gold); color: #fff; border-color: transparent;
  margin-left: 6px; font-size: 11px;
}

.banner {
  display: flex; gap: 10px; padding: 12px 14px; border-radius: var(--r);
  margin-bottom: 10px; font-size: 15px; border: 1px solid transparent;
}
.banner.info { background: var(--brand-soft); color: var(--text); border-color: var(--brand-border); }
.banner.warning { background: var(--warn-soft); color: var(--text); }
.banner.error { background: var(--crit-soft); color: var(--text); }
.banner b { font-weight: 700; }
.banner svg { flex: 0 0 auto; margin-top: 2px; }
.banner.warning svg { color: var(--warn); }
.banner.info svg { color: var(--brand); }
.badge svg { width: 13px; height: 13px; }

/* ---------- Boutons ---------- */
button, .btn {
  font: inherit; font-weight: 600; font-size: 15px;
  min-height: 44px; padding: 10px 16px;
  border-radius: var(--r-sm); border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--text);
  cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: background .12s ease, border-color .12s ease;
}
@media (hover: hover) and (pointer: fine) { button:hover, .btn:hover { background: var(--surface-sunken); } }
button:active:not(:disabled), .btn:active { background: var(--surface-sunken); }
/* Un bouton qui envoie : une petite roue à sa droite, et il ne se rappuie pas. */
button.is-sending { position: relative; opacity: .75; }
button.is-sending::after {
  content: ''; width: 14px; height: 14px; margin-left: 8px; flex: 0 0 auto;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: tg-tourne .7s linear infinite;
}
@keyframes tg-tourne { to { transform: rotate(360deg); } }
.banner.is-leaving { opacity: 0; transform: translateY(-6px); transition: opacity .35s ease, transform .35s ease; }
@media (prefers-reduced-motion: reduce) {
  button.is-sending::after { animation: none; }
  .banner.is-leaving { transform: none; }
}
button.primary, .btn.primary {
  background: var(--brand); border-color: var(--brand); color: #fff;
}
@media (hover: hover) and (pointer: fine) {
  button.primary:hover, .btn.primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
}
button.primary:active:not(:disabled), .btn.primary:active { background: var(--brand-hover); border-color: var(--brand-hover); }
button.quiet, .btn.quiet { border-color: transparent; background: transparent; color: var(--text-2); }
@media (hover: hover) and (pointer: fine) { button.quiet:hover, .btn.quiet:hover { background: var(--surface-sunken); } }
button.quiet:active:not(:disabled), .btn.quiet:active { background: var(--surface-sunken); }
button.danger, .btn.danger { color: var(--crit); border-color: var(--border-strong); }
@media (hover: hover) and (pointer: fine) { button.danger:hover { background: var(--crit-soft); } }
button.danger:active:not(:disabled), .btn.danger:active { background: var(--crit-soft); }
button:disabled { opacity: .45; cursor: not-allowed; }
/* Un bouton prend au moins la largeur de son libellé : il ne se coupe jamais
   sur deux ou trois lignes, et ce qui ne tient plus passe entier à la ligne
   suivante. Sur téléphone, la rangée se partage ensuite la place qui reste,
   si bien qu'un libellé long reste plus large qu'un court. (L'ancienne règle,
   « min(100%, max-content) », n'est pas valide : ignorée, elle laissait les
   boutons rétrécir jusqu'au mot le plus long.) */
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.btn-row > *, .btn-row > form > * { flex: 1 1 auto; min-width: -webkit-fit-content; min-width: fit-content; }
.btn-row > .icon-btn, .btn-row > form > .icon-btn { flex: 0 0 auto; min-width: 0; }
.btn-row form { display: contents; }
.btn-grow { width: 100%; }
/* Deux gestes qui s'opposent sur un même comptage tiennent côte à côte, même
   à 375 px : leur libellé passe sur deux lignes plutôt que de les empiler,
   l'un sous l'autre laissant croire que le second suit le premier. */
.btn-split { flex-wrap: nowrap; }
.btn-split > * { flex: 1 1 auto; min-width: 0; }

/* ---------- Formulaires ---------- */
label.field { display: block; font-size: 14px; font-weight: 600; color: var(--text-2); margin-bottom: 6px; }
/* Tous les champs de saisie, sans exception : « tel » et « time » manquaient
   à cette liste et retombaient sur le rendu par défaut du navigateur, avec
   sa propre bordure, sa propre hauteur et sa propre police. */
input[type=search], input[type=number], input[type=text], input[type=password],
input[type=tel], input[type=time], input[type=email], input[type=date] {
  font: inherit; font-size: 16px; padding: 10px 12px;
  border-radius: var(--r-sm); min-height: 44px; width: 100%;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
  -webkit-appearance: none; appearance: none;
}
/* Le champ horaire garde son sélecteur natif mais aligne ses chiffres. */
input[type=time] { font-variant-numeric: tabular-nums; }
input[type=time]::-webkit-calendar-picker-indicator {
  opacity: .5; cursor: pointer; margin-left: auto;
}
input[type=tel] { font-variant-numeric: tabular-nums; letter-spacing: .02em; }
/* Les points de masquage sont serrés par défaut : on les espace pour qu'on
   compte ce qu'on a tapé d'un coup d'œil. */
input[type=password] { letter-spacing: .12em; }
input[type=password]::placeholder { letter-spacing: normal; }
input::placeholder { color: var(--muted); }
input:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px;
}
input[type=range] { width: 100%; height: 44px; accent-color: var(--brand); }
.stepper { display: flex; align-items: center; gap: 8px; }
.stepper button {
  width: 44px; min-width: 44px; padding: 0; font-size: 20px; font-weight: 700;
}
.stepper output {
  min-width: 2.5ch; text-align: center; font-size: 19px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}

details.quiet { margin-top: 4px; }
/* Dans une liste de compteurs, le volet d'ajout suit le même retrait que les
   lignes : à ras du bord, il flottait à gauche de tout le reste. */
details.quiet.inset > summary { padding-left: var(--pad); padding-right: var(--pad); }
details.quiet.inset { margin-top: 0; border-top: 1px solid var(--border); }
details.quiet summary svg { width: 16px; height: 16px; }
details.quiet summary {
  cursor: pointer; list-style: none; color: var(--text-2);
  font-size: 15px; font-weight: 600; padding: 12px 0; user-select: none;
  display: flex; align-items: center; gap: 8px; min-height: 44px;
}
details.quiet summary::-webkit-details-marker { display: none; }
details.quiet summary::after { content: "▾"; margin-left: auto; color: var(--muted); }
details.quiet[open] summary::after { content: "▴"; }

/* Une précision de contexte, sous un titre de section ou dans une carte :
   présente pour qui la cherche, effacée pour qui la connaît déjà. */
.section-note { color: var(--muted); font-weight: 500; text-transform: none; letter-spacing: 0; }
.hint { margin: 0; padding: 12px var(--pad); color: var(--text-2); font-size: 14px; }
/* Chiffres de fin de ligne : alignés à droite, la colonne se lit d'un coup
   d'œil quelle que soit la longueur des noms d'enseigne. */
.row-right { text-align: right; white-space: nowrap; }

/* Ce que le client a annoncé vouloir sortir, sur la feuille du livreur. */
.wish {
  display: inline-flex; align-items: center; gap: 7px; margin-left: auto;
  padding: 5px 10px; border-radius: 999px; font-size: 13px;
  background: var(--surface-sunken); color: var(--text-2); border: 1px solid var(--border);
}
.wish svg { width: 15px; height: 15px; }
.wish b { color: var(--text); }
/* Pas de réponse du client : ni carte ni espèces à préparer, et c'est une
   information en soi devant une porte. */
.wish.is-unset { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
/* Le client a ouvert son lien sans choisir : il l'a vu, comme un message lu.
   Bleu, entre l'orange du silence et le vert d'une réponse. */
.wish.is-seen { background: var(--open-soft); border-color: transparent; color: var(--open); }
/* Le client a choisi son règlement : confirmé, en bleu — le vert est réservé
   à ce qui est déjà payé. */
.wish.is-confirmed { background: var(--seen-soft); border-color: transparent; color: var(--seen); }
.client .js-pay.is-called { outline: 3px solid var(--brand); outline-offset: 4px; border-radius: var(--r-sm); transition: outline-color .3s; }

/* ---------- Fenêtre de confirmation (client) ---------- */
.confirm-pop {
  width: min(420px, calc(100vw - 32px)); max-height: calc(100dvh - 32px);
  border: 0; border-radius: 20px; padding: 0; background: var(--surface); color: var(--text);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .35); margin: auto;
}
.confirm-pop::backdrop { background: rgba(0, 0, 0, .5); }
.confirm-pop-body { display: grid; gap: 12px; padding: 24px 20px 16px; text-align: center; }
.confirm-pop-body h2 { margin: 0; font-size: 21px; }
.confirm-pop-body p { margin: 0; color: var(--text-2); line-height: 1.45; }
.confirm-pop-body p b { color: var(--text); }
.confirm-pop-icon {
  justify-self: center; width: 48px; height: 48px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--brand-soft); color: var(--brand);
}
.confirm-pop-icon svg { width: 26px; height: 26px; }
.confirm-pop-choice { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 4px 0; }
.confirm-pop-choice .is-wide { grid-column: 1 / -1; }
.confirm-pop-note { font-size: 14px; }
.confirm-pop-addr { display: flex; gap: 8px; align-items: flex-start; font-size: 14px; text-align: left; }
.confirm-pop-addr svg { flex: none; width: 16px; height: 16px; margin-top: 2px; color: var(--brand); }
/* L'adresse du client, à vérifier : en grand, le moyen de prévenir à côté. */
.client-address-main { display: flex; align-items: center; gap: 12px; padding: var(--pad); }
.client-address-icon { flex: none; display: inline-flex; width: 40px; height: 40px; border-radius: 50%;
  align-items: center; justify-content: center; background: var(--brand-soft); color: var(--brand); }
.client-address-icon svg { width: 20px; height: 20px; }
.client-address-main .grow { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.client-address-line { font-size: 17px; line-height: 1.3; }
.client-address-main .btn { flex: none; padding: 10px 14px; }
.client-address-check {
  display: flex; gap: 8px; align-items: flex-start; margin: 0; padding: 12px var(--pad);
  background: var(--warn-soft); color: var(--text); font-size: 14px; line-height: 1.4;
  border-top: 1px solid var(--border);
}
.client-address-check svg { flex: none; width: 16px; height: 16px; margin-top: 2px; color: var(--warn); }

/* ---------- Suivi client ---------- */
/* Une page ouverte d'une main, sur un trottoir ou depuis un canapé : peu
   d'éléments, gros, et l'heure de passage lisible sans lire. */
.client { max-width: 480px; margin: 0 auto; }
.client-hero { padding: 22px var(--pad); text-align: center; }
.client-eyebrow {
  display: block; color: var(--muted); font-size: 13px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 6px;
}
.client-slot { display: block; font-size: 34px; font-weight: 800; letter-spacing: -0.02em; }
/* L'heure prend la couleur de l'avertissement : elle tient déjà compte du
   retard, la teinte le dit sans une phrase de plus. */
.client-slot.is-late { color: var(--warn); }
.client-done {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 22px; font-weight: 700;
}
.client-done svg { width: 26px; height: 26px; color: var(--brand); }
.client-done-at { display: block; margin-top: 8px; color: var(--muted); font-size: 15px; }

/* Où se trouve le livreur. Le libellé annonce ce qu'on va lire, la ville
   porte le poids, et l'arrêt d'après reste en retrait : c'est une indication,
   pas une promesse. */
.client-where { padding: 16px var(--pad); border-top: 1px solid var(--border); text-align: center; }
.client-where-label {
  display: block; color: var(--muted); font-size: 13px; font-weight: 600; margin-bottom: 2px;
}
.client-city { display: block; font-size: 24px; font-weight: 700; letter-spacing: -0.01em; }
.client-then { display: block; margin-top: 6px; color: var(--muted); font-size: 14px; }
.client-then b { color: var(--brand); }

/* Le retard, annoncé plutôt que subi. Jaune : ce n'est pas une erreur, c'est
   une soirée qui déborde — et un client prévenu ne téléphone pas. */
.client-late {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px var(--pad); border-top: 1px solid var(--border);
  background: var(--warn-soft); color: var(--warn); font-size: 14px; line-height: 1.4;
}
.client-late svg { width: 18px; height: 18px; flex: none; margin-top: 1px; }
.client-late b { display: block; }
/* Le pendant vert du retard : le créneau a avancé. */
.client-early {
  display: flex; align-items: center; gap: 10px;
  padding: 12px var(--pad); border-top: 1px solid var(--border);
  background: var(--ok-soft); color: var(--ok); font-size: 14px; line-height: 1.4;
}
.client-early svg { width: 18px; height: 18px; flex: none; }
.client-slot.is-early { color: var(--ok); }

/* Le compte à rebours des arrêts : gris, puis orange, puis vert battant. La
   couleur dit s'il faut descendre, sans qu'on ait à relire le chiffre. */
.client-progress {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px var(--pad); border-top: 1px solid var(--border);
  color: var(--muted); font-size: 15px;
}
.client-progress b { color: var(--text-2); font-weight: 700; }
.client-progress.is-soon {
  background: var(--brand-soft); color: var(--brand); border-color: var(--brand-border);
}
.client-progress.is-soon b { color: var(--brand); }
.client-progress.is-now {
  background: var(--ok-soft); color: var(--ok); border-color: transparent; font-size: 17px;
}
.client-progress.is-now b { color: var(--ok); }
.pulse {
  width: 10px; height: 10px; border-radius: 50%; background: var(--ok); flex: none;
  box-shadow: 0 0 0 0 var(--ok); animation: pulse 1.6s ease-out infinite;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 55%, transparent); }
  70%  { box-shadow: 0 0 0 11px color-mix(in srgb, var(--ok) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 0%, transparent); }
}
/* Un point qui bat en continu devant quelqu'un que le mouvement gêne n'a plus
   rien d'utile : il reste vert, simplement immobile. */
@media (prefers-reduced-motion: reduce) {
  .pulse { animation: none; }
}

.client-driver {
  display: flex; align-items: center; gap: 12px;
  padding: 14px var(--pad); border-top: 1px solid var(--border);
}
.client-driver-icon {
  display: grid; place-items: center; width: 42px; height: 42px; flex: none;
  border-radius: 10px; background: var(--brand-soft); color: var(--brand);
}
.client-driver-icon svg { width: 22px; height: 22px; }
.client-driver .btn { flex: none; padding: 10px 14px; }
.nowrap { white-space: nowrap; }

/* Une ligne d'article du suivi client : la vignette donne le rythme, la
   quantité et la sorte se mettent à sa hauteur. Écrits au corps des autres
   listes, ils paraissaient perdus à côté d'un carré de 68 px. */
.client .row-item { padding-top: 10px; padding-bottom: 10px; }
.client .row-item .qty { font-size: 24px; min-width: 48px; }
.client .row-item .grow { font-size: 18px; }

/* Vignette produit : assez grande pour reconnaître le dessert, assez petite
   pour ne pas allonger la ligne. */
/* Sur la feuille du livreur, la vignette se cale sur la hauteur de la ligne :
   plus grande, elle allongerait chaque carte d'arrêt et repousserait les
   boutons d'action hors de l'écran. */
.thumb-sm { width: 52px; height: 52px; border-radius: 9px; }

.thumb {
  width: 68px; height: 68px; flex: none; border-radius: 10px;
  object-fit: cover; background: var(--surface-sunken); border: 1px solid var(--border);
}

/* Réglage d'écran, détaché de la commande : même carte, mais posée plus bas
   et sans le poids visuel du reste. */
.client-settings { margin-top: 20px; background: transparent; border-style: dashed; box-shadow: none; }

.client-total { display: flex; align-items: baseline; gap: 12px; font-size: 16px; }
.client-total b { font-size: 22px; }

/* Préférence de règlement : deux choix, jamais imposés, annulables d'un
   second appui sur celui déjà retenu. C'est aussi la confirmation de
   présence, d'où la place qu'elle prend, sous le créneau annoncé. */
.pay-form { padding: 14px; border-top: 1px solid var(--border); text-align: center; }
.pay-label { display: block; color: var(--text-2); font-size: 14px; margin-bottom: 8px; }
/* La question que pose le SMS : elle doit se lire avant les boutons. */
.pay-label.is-ask { font-size: 16px; line-height: 1.35; }
.pay-label.is-ask strong { color: var(--text); font-weight: 700; font-size: 17px; }
.pay-row { display: flex; gap: 8px; }
.pay-option {
  flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 10px 14px; border-radius: var(--r-sm);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text-2); font-size: 15px; font-weight: 600; cursor: pointer;
}
.pay-option svg { width: 19px; height: 19px; }
/* « Les deux » tient toute la largeur, sous les deux autres : c'est le cas
   rare, il se propose sans se disputer la place des cas courants. */
.pay-option.is-wide { width: 100%; margin-top: 8px; }
.pay-fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
/* Vente sur place : le prix de l'unité se corrige sous le nom de la sorte. */
.sell-price { display: flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 13px; color: var(--muted); }
.sell-price input { width: 72px; min-height: 36px; padding: 4px 8px; font-weight: 600; text-align: right; }
.sell-pay { display: flex; gap: 8px; }
.sell-choice {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  font-weight: 600; cursor: pointer;
}
.sell-choice input { position: absolute; opacity: 0; pointer-events: none; }
.sell-choice svg { width: 19px; height: 19px; }
.sell-choice:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); color: var(--brand); }
/* Payé en ligne : le choix reste lisible, mais éteint et sans prise. */
.pay-form.is-locked .pay-option { opacity: .45; pointer-events: none; }
.pay-form.is-locked .pay-label { color: var(--ok); font-weight: 600; }
/* La question du montant ne s'ouvre qu'une fois le partage choisi. */
.split-ask { margin-top: 10px; text-align: left; }
.split-ask .pay-label { margin-bottom: 6px; }
.split-row { display: flex; gap: 8px; }
.split-row input {
  flex: 1 1 auto; min-width: 0; min-height: 46px; padding: 10px 14px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); font: inherit; font-size: 16px;
}
.split-row input:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.split-row .btn { flex: 0 0 auto; }
@media (hover: hover) and (pointer: fine) { .pay-option:hover { background: var(--surface-sunken); } }
.pay-option:active { background: var(--surface-sunken); }
.pay-option.is-on {
  border-color: var(--brand); background: var(--brand-soft); color: var(--brand);
}

/* Recours après livraison. Rouge parce qu'il ne sert qu'en cas d'ennui, et
   discret le reste du temps : il n'apparaît qu'une fois la commande remise. */
.client-problem {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px var(--pad); border-top: 1px solid var(--border);
  background: var(--crit-soft);
}
.client-problem b { color: var(--crit); }
.client-problem .btn { flex: none; }
.client-problem .btn.danger { border-color: var(--crit); color: var(--crit); }

/* ---------- Écrans d'accès ---------- */
/* Une page qui ne demande qu'un mot de passe : centrée, étroite, sans rien
   autour qui puisse retenir le regard. */
.gate { max-width: 400px; margin: 8vh auto 0; }
.gate-head { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.gate-head svg { width: 26px; height: 26px; color: var(--brand); flex: none; }
.gate .field { display: block; margin-bottom: 12px; }
.gate button { margin-top: 4px; }
/* Le code de tournée, tel qu'il s'affiche côté administration. */
.access-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.access-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 20px; font-weight: 700; letter-spacing: 0.22em;
  padding: 8px 14px; border-radius: var(--r-sm);
  background: var(--surface-sunken); border: 1px solid var(--border-strong);
}
.access-form { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.access-form /* Une soirée passée se déplie sur ses arrêts. */
.past-route > summary { cursor: pointer; list-style: none; }
.past-route > summary::-webkit-details-marker { display: none; }
/* Au doigt, il n'y a pas de survol : le navigateur laisse pourtant l'état
   « survolé » collé au dernier élément touché, et une tournée repliée restait
   orange. On ne colore donc au survol que là où un pointeur existe. */
@media (hover: hover) { .past-route > summary:hover { background: var(--brand-soft); } }
.past-route[open] > summary { background: var(--brand-soft); }
/* Carte d'une tournée : un SVG, aucune tuile, aucun appel réseau. */
/* Explication d'une annulation, côté client. */
.cancel-note { border-top: 1px solid var(--border); font-size: 16px; }

.route-map { margin: 0; padding: 0 var(--pad) var(--pad); }
.route-map { position: relative; }
/* La bulle d'un arrêt, posée sur la carte au-dessus de son numéro. */
.map-stop[data-map-stop] { cursor: pointer; }
.map-stop[data-map-stop]:focus { outline: none; }
.map-stop.is-picked circle:last-of-type { stroke: var(--text); stroke-width: 3.5; }
.map-pop {
  position: absolute; z-index: 30; width: min(280px, calc(100% - 24px));
  display: grid; gap: 4px; padding: 10px 12px 12px; font-size: 14px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: 14px; box-shadow: 0 12px 32px rgba(0, 0, 0, .28); text-align: left;
}
.map-pop[hidden] { display: none; }
.map-pop-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; color: var(--text-2); font-size: 13px; font-weight: 600; }
.map-pop-close { min-height: 0; width: 28px; height: 28px; padding: 0; border: 0; background: transparent; box-shadow: none; font-size: 20px; color: var(--muted); }
.map-pop-name { font-size: 16px; }
.map-pop-addr { color: var(--text-2); }
.map-pop-actions { display: flex; gap: 8px; margin-top: 6px; }
.map-pop-actions .btn { flex: 1; min-height: 38px; }
/* La liste des arrêts, sous la carte, dans le même ordre et les mêmes signes. */
.map-stops { list-style: none; margin: 12px 0 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; }
.map-stops > li { min-width: 0; }
.map-stops-row {
  width: 100%; display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 8px 4px; min-height: 0; border: 0; border-top: 1px solid var(--border);
  border-radius: 0; background: transparent; box-shadow: none; color: var(--text); font-weight: 400;
}
.map-stops-row .grow { min-width: 0; display: flex; flex-direction: column; }
.map-stops-row .grow b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.map-stops-row .grow .muted { font-size: 13px; }
.map-stops-row i {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-style: normal; font-size: 12px; font-weight: 800; box-sizing: border-box;
}
.map-stops-row i.is-todo { background: var(--surface); border: 2px solid var(--brand); color: var(--brand); }
.map-stops-row i.is-next { background: var(--brand); color: #fff; }
.map-stops-row i.is-done { background: var(--ok); color: #fff; }
.map-stops-row i.is-cancelled { background: var(--crit-soft); border: 1.5px dashed var(--crit); color: var(--crit); }
.map-stops-when { flex: 0 0 auto; font-weight: 700; font-variant-numeric: tabular-nums; font-size: 14px; }
.map-stops-when.muted { font-weight: 500; }
@media (hover: hover) and (pointer: fine) { .map-stops-row:hover { background: var(--surface-sunken); } }
.map-stops-row:active { background: var(--brand-soft); }
/* Livré ou annulé : la ligne s'estompe, ce qui reste à faire ressort. */
.map-stops-row.is-done .grow b, .map-stops-row.is-cancelled .grow b { color: var(--text-2); font-weight: 600; }
.map-stops-row.is-cancelled .grow b { text-decoration: line-through; text-decoration-color: var(--crit); }
/* Ville et pastille côte à côte ; la pastille passe dessous plutôt que de
   réduire la ville à trois lettres. */
.map-stops-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; min-width: 0; }
.map-stops-sub .muted { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Où en est le client : un point de couleur et un mot, sans fond ni bordure.
   Vert payé, bleu confirmé (reste à encaisser), violet lien ouvert, orange
   sans réponse — les mêmes couleurs que sur la carte d'arrêt. */
.map-chip {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 12.5px; font-weight: 500; line-height: 1.4; white-space: nowrap; color: var(--text-2);
}
.map-chip::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; box-sizing: border-box;
}
.map-chip.is-paid { color: var(--ok); }
.map-chip.is-confirmed { color: var(--seen); }
.map-chip.is-seen { color: var(--open); }
.map-chip.is-unset { color: var(--warn); }
.map-chip.is-failed, .map-chip.is-cancelled { color: var(--crit); }
.map-chip.is-done { color: var(--muted); }
.map-chip.is-cancelled { max-width: 14em; overflow: hidden; text-overflow: ellipsis; }
.map-stops-sub .map-chip + .muted, .map-stops-sub .muted + .map-chip { margin-left: 2px; }
/* Le bilan des arrêts restants : une ligne de texte, les mêmes points. */
.map-stops-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 12.5px;
}
.map-stops-summary .map-chip { font-size: 12.5px; }
.map-stops-summary + .map-stops { margin-top: 6px; }
.map-pop .map-chip { margin-left: 6px; vertical-align: -1px; }
/* Collée à la barre d'avancement, la carte semblait en faire partie. */
.progress + .route-map { padding-top: 14px; }
.route-map svg {
  /* Au-delà de cette largeur, les tuiles s'étirent et le fond devient flou :
     la carte se centre plutôt que de s'agrandir indéfiniment. */
  display: block; width: 100%; max-width: 520px; height: auto; margin: 0 auto;
  background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--r-sm);
}
/* Le tracé se détache du fond de rues par un liseré clair : sans lui, il se
   perd sur les routes de la carte, qui sont elles aussi colorées. */
.map-road-halo {
  fill: none; stroke: var(--surface); stroke-width: 7;
  stroke-linejoin: round; stroke-linecap: round; opacity: .9;
}
.map-road { fill: none; stroke: var(--brand); stroke-width: 3.5; stroke-linejoin: round; stroke-linecap: round; }
/* Le fond de rues est ramené au gris : les couleurs d'OpenStreetMap — parcs
   verts, zones bâties beiges, autoroutes jaunes — se disputent l'attention
   avec le tracé, qui est la seule chose à lire. Blanc en thème clair, gris
   sombre en thème sombre : un fond blanc éclatant au milieu d'une page noire
   éblouit dans une voiture, la nuit. */
.map-tiles { filter: grayscale(1) contrast(.72) brightness(1.12); }
:root[data-theme="dark"] .map-tiles { filter: grayscale(1) invert(1) contrast(.68) brightness(.82); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .map-tiles {
    filter: grayscale(1) invert(1) contrast(.68) brightness(.82);
  }
}
.map-road.is-rough { stroke-dasharray: 6 5; opacity: .5; }
/* Ce qui est déjà parcouru s'éteint : le regard tombe sur ce qui reste. */
.map-road-done {
  fill: none; stroke: var(--border-strong); stroke-width: 3.5;
  stroke-linejoin: round; stroke-linecap: round;
}
/* Le livreur sur son tracé. Le halo bat doucement : c'est un point vivant au
   milieu d'un dessin figé, et rien d'autre ne bouge sur cette carte. */
.map-me-dot { fill: #2f6fed; stroke: var(--surface); stroke-width: 2.5; }
.map-me-halo { fill: #2f6fed; opacity: .25; animation: tg-pouls 2s ease-in-out infinite; }
@keyframes tg-pouls {
  0%, 100% { opacity: .25; }
  50% { opacity: .08; }
}
@media (prefers-reduced-motion: reduce) { .map-me-halo, .map-stop .map-halo { animation: none; } }
.map-stop circle { fill: var(--surface); stroke: var(--brand); stroke-width: 2.5; }
.map-stop text { fill: var(--brand); font-size: 12px; font-weight: 800; text-anchor: middle; }
/* Quatre états, quatre lectures : à livrer (blanc cerclé d'orange), en cours
   (orange plein, halo), livré (vert, coche), annulé (rouge pâle, croix). La
   couleur seule ne porte jamais le sens : la forme du signe le redit. */
.map-stop.is-next circle { fill: var(--brand); stroke: var(--surface); stroke-width: 3; }
.map-stop.is-next text { fill: #fff; }
.map-stop .map-halo { fill: var(--brand); stroke: none; opacity: .28; animation: tg-pouls 2s ease-in-out infinite; }
.map-stop.is-done circle { fill: var(--ok); stroke: var(--surface); }
.map-stop.is-done text { fill: #fff; font-size: 13px; }
.map-stop.is-cancelled circle { fill: var(--crit-soft); stroke: var(--crit); stroke-dasharray: 3 2; }
.map-stop.is-cancelled text { fill: var(--crit); font-size: 14px; }
.map-legend {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 14px;
  margin-top: 8px; font-size: 13px; color: var(--text-2);
}
.map-legend span { display: inline-flex; align-items: center; gap: 6px; }
.map-legend i {
  width: 14px; height: 14px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-style: normal; font-size: 9px; font-weight: 800; color: #fff; box-sizing: border-box;
}
.map-legend .is-todo { background: var(--surface); border: 2px solid var(--brand); }
.map-legend .is-next { background: var(--brand); }
.map-legend .is-done { background: var(--ok); }
.map-legend .is-cancelled { background: var(--crit-soft); border: 1.5px dashed var(--crit); color: var(--crit); }
/* Le suivi client : un trait plus épais, des repères plus gros. On la regarde
   à bout de bras, une main sur la porte, pas assis à une table. */
.is-approach .map-road { stroke-width: 6; }
.is-approach .map-road-halo { stroke-width: 10; }
.map-home circle { fill: var(--brand); stroke: var(--surface); stroke-width: 3; }
.map-home-roof { fill: none; stroke: #fff; stroke-width: 1.8; stroke-linejoin: round; }

.map-depot rect { fill: var(--text); stroke: var(--surface); stroke-width: 2.5; }
.map-depot text { fill: var(--surface); font-size: 12px; font-weight: 700; text-anchor: middle; }
.route-map figcaption { margin-top: 8px; font-size: 13px; color: var(--muted); text-align: center; }
/* La mention d'OpenStreetMap est due, pas informative : elle se fait discrète
   sans disparaître. */
.map-credit { display: block; margin-top: 2px; font-size: 11px; opacity: .7; }
/* Le délai avant le reste : c'est la seule chose qu'on cherche sur cette
   carte, et elle doit se lire sans la chercher. */
.map-eta { color: var(--text); font-size: 16px; }

.past-route .rows { padding-left: 12px; border-left: 2px solid var(--border); margin-left: 14px; }

.code-input { max-width: 200px; }

/* Un code dicté au téléphone se relit caractère par caractère. */
.code-input {
  text-transform: uppercase; letter-spacing: 0.22em;
  font-size: 20px; font-weight: 700; text-align: center;
}

/* ---------- Listes ---------- */
.rows > * + * { border-top: 1px solid var(--border); }
.row-item { display: flex; gap: 12px; align-items: center; padding: 13px var(--pad); }
/* Utilitaire global : les compteurs des lignes de réglage doivent s'aligner
   sur la même verticale quelle que soit la longueur du nom de sorte. */
.grow { flex: 1 1 auto; min-width: 0; }
.num-chip {
  flex: 0 0 32px; height: 32px; border-radius: var(--r-sm);
  background: var(--surface-sunken); border: 1px solid var(--border);
  color: var(--text-2); display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 15px; font-variant-numeric: tabular-nums;
}
.money { font-variant-numeric: tabular-nums; font-weight: 700; }

/* ---------- Quantité contre numéro d'arrêt ----------
   Les deux sont des nombres à quelques centimètres l'un de l'autre. On les
   sépare par la forme et la couleur : la quantité est colorée et suivie d'un
   « × », le numéro d'arrêt reste une petite pastille grise. */
.qty {
  flex: 0 0 auto; min-width: 44px;
  display: inline-flex; align-items: baseline; justify-content: flex-end; gap: 1px;
  color: var(--brand); font-weight: 800; font-size: 19px;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
.qty.big { font-size: 26px; min-width: 52px; }
.qty .times { font-size: 0.62em; font-weight: 700; opacity: .75; }
/* Ce qui est libre de partir à la remise porte la couleur des « en plus ». */
.qty.is-extra { color: var(--gold); }
.badge.gold { background: var(--gold-soft); color: var(--gold); border-color: var(--gold-border); }
/* Les deux parts d'un règlement partagé, côte à côte sur la carte repliée. */
.badge-pair { display: inline-flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
.stop-num {
  flex: 0 0 26px; height: 26px; border-radius: 999px;
  background: var(--surface-sunken); border: 1px solid var(--border);
  color: var(--muted); display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 13px; font-variant-numeric: tabular-nums;
}
.stop-label {
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); display: flex; align-items: center; gap: 8px; margin-bottom: 2px;
}
.stop-card.is-next { border-color: var(--brand); border-width: 2px; }
.eta {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text-2);
}
.pay-line { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding-bottom: 4px; }
.addr-line { padding-top: 0; }
.adjust-note {
  display: block; font-size: 13px; font-weight: 600; color: var(--warn);
  letter-spacing: 0; text-transform: none;
}
.note-line { display: flex; gap: 8px; align-items: flex-start; margin-top: 8px; color: var(--text-2); }
.note-line svg { flex: 0 0 auto; margin-top: 3px; }
/* La note du labo sur un client connu : elle doit se voir avant de sonner. */
.note-line.client-note {
  background: var(--brand-soft); color: var(--text); border-radius: 10px;
  padding: 8px 10px; font-weight: 600;
}
.client-badges { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
/* ---------- Mode planification ---------- */
.plan-open { margin-bottom: 12px; }
.planner-map .route-map { padding-top: var(--pad); }
.planner-summary {
  position: sticky; top: calc(env(safe-area-inset-top, 0px) + 60px); z-index: 15;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px;
  padding: 10px 14px; margin: 0 0 4px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--border-strong); box-shadow: var(--shadow);
  font-size: 14px; transition: opacity .15s;
}
.planner-summary b { font-size: 18px; font-variant-numeric: tabular-nums; }
.planner.is-loading .planner-summary, .planner.is-loading .js-plan-map { opacity: .55; }
.is-later { color: var(--warn); font-weight: 600; }
.is-earlier { color: var(--ok); font-weight: 600; }
.planner-list { list-style: none; margin: 0 0 12px; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
.planner-row {
  display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 2px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: background .3s, border-color .3s;
}
.planner-row.is-focus, .planner-row.is-moved { border-color: var(--brand); background: var(--brand-soft); }
.planner-row .grip { flex: 0 0 auto; width: 32px; }
.planner-num {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 12px; font-weight: 800;
  background: var(--surface); border: 2px solid var(--brand); color: var(--brand); box-sizing: border-box;
}
.planner-who { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.planner-who b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.planner-who .muted { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.planner-time { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; font-variant-numeric: tabular-nums; }
.planner-time b { font-size: 16px; }
.planner-time small { font-size: 12px; white-space: nowrap; color: var(--muted); text-decoration: line-through; }
.planner-arrows { flex: 0 0 auto; display: flex; flex-direction: column; }
.planner-arrows button {
  width: 32px; height: 22px; min-height: 0; padding: 0; border: 0; background: transparent;
  box-shadow: none; color: var(--muted); display: inline-flex; align-items: center; justify-content: center;
}
.planner-arrows button:first-child svg { transform: rotate(180deg); }
.planner-arrows button:active { color: var(--brand); }
.planner-bar {
  position: sticky; bottom: 0; z-index: 15; display: flex; gap: 8px; align-items: center;
  padding: 10px 0 calc(10px + env(safe-area-inset-bottom, 0px)); background: var(--bg);
}
.planner-bar .primary { flex: 1; }
.planner-bar button { min-width: 0; }
@media (max-width: 359px) {
  .planner-bar { flex-wrap: wrap; }
  .planner-bar .primary { flex: 1 1 100%; order: -1; }
  .planner-bar .quiet { flex: 1; }
}
.planner-list [data-sort-item].is-dragging { transform: scale(1.03); }

/* ---------- Module Commandes ---------- */
.order-card > summary { list-style: none; cursor: pointer; }
.order-card > summary::-webkit-details-marker { display: none; }
.order-sum { display: flex; gap: 12px; align-items: flex-start; padding: 12px var(--pad); }
.order-sum-main { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: 2px 8px; align-items: baseline; }
.order-sum-meta { flex-basis: 100%; font-size: 13px; }
.order-sum-side { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; }
.order-card[open] .order-sum { border-bottom: 1px solid var(--border); }
.order-detail { display: grid; gap: 8px; }
.order-detail a[href^="tel:"] { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; }
.order-items .row-item { padding-left: 0; padding-right: 0; }
/* Un montant changé à la main : doré, et la mention de celui d'origine. */
.amount-changed { display: inline-flex; flex-direction: column; gap: 2px; }
.amount-changed .money { color: var(--gold); }
.amount-changed-note { font-size: 13px; font-weight: 600; color: var(--gold); }

/* ---------- Carte d'arrêt : en-tête stable ---------- */
/* Poignée, repli et trois points vivent ensemble en haut à droite, et n'en
   bougent jamais : un nom long passe à la ligne au lieu de les pousser. Le
   menu déborde de la carte au lieu d'y être rogné. */
.stop-card { overflow: visible; }
.stop-head { flex-wrap: nowrap; align-items: flex-start; }
.card-tools { display: flex; align-items: center; gap: 0; flex: 0 0 auto; margin: -6px -10px 0 0; }
.card-tools .stop-menu { margin-left: 0; }
.stop-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin-top: 6px; }
.stop-meta .badge { font-size: 12px; }
.rating-badge svg { width: 12px; height: 12px; fill: var(--gold); }
.grip, .fold-toggle {
  width: 36px; height: 36px; padding: 0; border: 0; background: transparent; box-shadow: none;
  color: var(--muted); display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); min-height: 0;
}
.grip { cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.grip:active { cursor: grabbing; background: var(--brand-soft); color: var(--brand); }
.fold-toggle svg { transition: transform .18s ease-out; }
.fold-toggle:active { background: var(--brand-soft); color: var(--brand); }
.fold-city { display: none; color: var(--text-2); font-size: 15px; margin-top: 2px; }

/* Repliée : le nom, la commande et la ville. Pendant un glisser, toutes les
   cartes le sont, pour voir la liste entière d'un coup. */
.stop-card.is-folded > :not(.card-head),
.sort-list.is-sorting .stop-card > :not(.card-head) { display: none; }
.stop-card.is-folded .card-head,
.sort-list.is-sorting .stop-card .card-head { border-bottom: 0; }
.stop-card.is-folded .stop-label, .stop-card.is-folded .stop-meta,
.stop-card.is-folded .modified-note, .stop-card.is-folded .added-note,
.sort-list.is-sorting .stop-meta, .sort-list.is-sorting .modified-note,
.sort-list.is-sorting .added-note { display: none; }
.stop-card.is-folded .fold-city, .sort-list.is-sorting .fold-city { display: block; }
.stop-card.is-folded .client-name, .sort-list.is-sorting .client-name { font-size: 18px; }
.stop-card.is-folded .fold-toggle svg { transform: rotate(-90deg); }
.sort-list.is-sorting .insert-gap, .sort-list.is-sorting #infos,
.sort-list.is-sorting .fold-toggle, .sort-list.is-sorting .stop-menu { display: none; }
.sort-list.is-sorting .stop-card { margin-bottom: 8px; }
[data-sort-item].is-dragging {
  box-shadow: 0 18px 40px rgba(0, 0, 0, .28); transform: scale(1.02);
  border-color: var(--brand); opacity: .97; pointer-events: none;
}
.sort-rows [data-sort-item].is-dragging { background: var(--surface); border-radius: var(--r-sm); }
.sort-rows .sort-placeholder { margin: 0; border-radius: var(--r-sm); }
.sort-rows .grip { margin: -6px -4px 0 -8px; align-self: flex-start; }
.tracking-eval { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-top: 2px; font-size: 14px; }
.sort-placeholder {
  border: 2px dashed var(--brand-border); border-radius: var(--r);
  background: var(--brand-soft); margin-bottom: 8px;
}
.sort-list.is-saving { opacity: .55; pointer-events: none; }
body.is-sorting { user-select: none; -webkit-user-select: none; overscroll-behavior: none; }
@media (prefers-reduced-motion: reduce) { [data-sort-item].is-dragging { transform: none; } }

/* ---------- Étoiles ---------- */
.stars { display: inline-flex; align-items: center; gap: 2px; }
.star { position: relative; display: inline-flex; width: 22px; height: 22px; }
.star svg { width: 22px; height: 22px; fill: var(--border-strong); }
.star-fill { position: absolute; inset: 0 auto 0 0; overflow: hidden; }
.star-fill svg { fill: var(--gold); }
.stars-value { margin-left: 6px; font-variant-numeric: tabular-nums; }
.stars.is-small .star, .stars.is-small .star svg { width: 14px; height: 14px; }
.stars.is-small .stars-value { font-size: 13px; margin-left: 4px; }
.stars.is-input { gap: 4px; padding: 4px 0; cursor: pointer; touch-action: none; user-select: none; -webkit-user-select: none; }
.stars.is-input .star, .stars.is-input .star svg { width: 34px; height: 34px; }
.stars.is-input:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; border-radius: 6px; }
.stars-input { display: inline-flex; align-items: center; gap: 8px; }
.stars-input .stars-value { font-size: 18px; min-width: 2.2em; }
.rate-block {
  display: grid; gap: 8px; margin: 14px 0; padding: 12px; border-radius: var(--r-sm);
  background: var(--surface-sunken); border: 1px solid var(--border);
}
.rate-title { font-weight: 600; font-size: 15px; }
.rate-block textarea { min-height: 0; }
.closed-eval { margin-top: 10px; display: grid; gap: 6px; }
.closed-review { margin: 0; color: var(--text-2); }
.eval-edit > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
  color: var(--brand); font-weight: 600; font-size: 14px; padding: 6px 0;
}
.eval-edit > summary::-webkit-details-marker { display: none; }
.eval-edit > summary svg { width: 15px; height: 15px; fill: currentColor; }
.client-eval { display: grid; gap: 4px; margin-bottom: 12px; }
.client-eval p { margin: 0; }
.visit-rating { display: block; margin-top: 2px; }
.visit-review { display: block; color: var(--text-2); font-style: italic; }
.client-visit .grow { min-width: 0; }

/* La note persistante du client, modifiable d'un toucher. */
.client-note-edit > summary { list-style: none; cursor: pointer; }
.client-note-edit > summary::-webkit-details-marker { display: none; }
.client-note-edit .client-note svg:last-child { width: 15px; height: 15px; opacity: .6; margin-top: 4px; }
.client-note-edit[open] .client-note { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.client-note-edit .client-note-form { margin-top: 8px; }
.client-note-add {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 8px;
  font-size: 14px; font-weight: 600; color: var(--muted);
}
.client-note-add svg { width: 14px; height: 14px; }

/* ---------- Hors ligne ---------- */
.offline-bar {
  position: fixed; z-index: 60; left: 12px; right: 12px;
  top: calc(env(safe-area-inset-top, 0px) + 8px);
  background: var(--warn); color: #fff; font-weight: 600; font-size: 14px;
  border-radius: 12px; padding: 10px 14px; text-align: center;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
}
.offline-bar[hidden] { display: none; }
.stop-card.is-queued { opacity: .6; }
.stop-card.is-queued .card-foot { pointer-events: none; }
.queued-note {
  margin: 0 var(--pad) 8px; padding: 8px 10px; border-radius: 10px;
  background: var(--warn-soft); color: var(--warn); font-weight: 600; font-size: 14px;
}

/* ---------- Fiche client ---------- */
.client-file { border-top: 1px solid var(--border); }
.section-label {
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-2); margin-bottom: 8px;
}
.client-note-form { display: grid; gap: 8px; }
.client-note-form button { justify-self: start; }
.client-visits { display: grid; gap: 2px; }
.client-visit { display: flex; gap: 10px; font-size: 14px; padding: 6px 0; border-top: 1px solid var(--border); }
.client-visit > span:first-child { min-width: 58px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.client-visit.is-cancelled .grow { color: var(--warn); }

/* ---------- Compteurs ---------- */
.stepper-row {
  display: flex; gap: 12px; align-items: center;
  padding: 12px 0; border-top: 1px solid var(--border);
}
/* Hors d'un conteneur déjà rembourré, les lignes de réglage doivent porter
   leur propre marge : sinon le bouton « + » se fait rogner par la carte. */
.stepper-list > .stepper-row { padding-left: var(--pad); padding-right: var(--pad); }
.stepper-list > .stepper-row:first-child { border-top: 0; }
.stepper { display: flex; align-items: center; gap: 0; flex: 0 0 auto; }
.stepper button {
  width: 44px; min-width: 44px; padding: 0; border-radius: 0;
  border-right-width: 0; color: var(--text-2);
}
.stepper button:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.stepper button:last-child {
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  border-right-width: 1px; border-left-width: 0;
}
.stepper input[type=number] {
  width: 56px; min-width: 56px; text-align: center; border-radius: 0;
  font-weight: 700; font-size: 17px; font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.stepper input[type=number]::-webkit-outer-spin-button,
.stepper input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper.at-zero input { color: var(--muted); }
/* Les petits pots forment un encadré doré, séparé de la commande en grand
   format. Empilés sous elle sans cadre, les deux listes de compteurs se
   confondaient et une quantité se saisissait dans le mauvais format. */
.small-block {
  margin: 0 var(--pad) 4px; border: 1px solid var(--gold);
  border-radius: 10px; overflow: hidden; background: var(--gold-soft);
}
.small-block[hidden] { display: none; }
.small-block .stepper-list > .stepper-row { border-top-color: var(--gold-border); }
.sub-head.gold {
  background: transparent; color: var(--gold);
  border-color: transparent; border-bottom: 1px solid var(--gold-border);
}
.stepper.is-small input { color: var(--gold); }
.stepper.is-small button { color: var(--gold); }
.editor-sep {
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--brand);
}


/* ---------- Numéro de commande ----------
   Seul repère commun avec Shopify et le client au téléphone : il accompagne
   toujours le nom, et reste lisible quand la carte est réduite. */
.order-ref {
  color: var(--brand); font-weight: 700; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.client-name { margin: 0; font-size: 21px; font-weight: 700; letter-spacing: -0.01em; }
/* Signalé sans cadre : c'est une nuance sur la commande, pas une alerte. */
.modified-note { color: var(--brand); font-size: 13px; font-weight: 600; margin-top: 2px; }

/* ---------- Lignes d'articles corrigées ----------
   L'or marque ce qui ne figurait pas sur la commande d'origine, le barré ce
   que le client a refusé. Deux écarts opposés, deux signaux distincts. */
.item-added { color: var(--gold); }
.item-added .qty { color: var(--gold); }
.item-removed { color: var(--muted); }
.item-removed .qty { color: var(--muted); }
.item-removed .adjust-note { color: var(--muted); }
.struck { text-decoration: line-through; }

/* ---------- Volets dépliables ---------- */
details.panel > summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: 10px;
  padding: 14px var(--pad); min-height: 52px; font-size: 16px;
}
details.panel > summary::-webkit-details-marker { display: none; }
details.panel > summary::after {
  content: "▾"; margin-left: auto; color: var(--muted); font-size: 13px;
}
details.panel[open] > summary::after { content: "▴"; }
details.panel > summary > svg { flex: 0 0 auto; color: var(--muted); }
@media (hover: hover) and (pointer: fine) { details.panel > summary:hover { background: var(--surface-sunken); } }
details.panel > summary:active { background: var(--surface-sunken); }
.panel-body { padding: 0 var(--pad) var(--pad); }
/* Libellé et compte sur deux lignes : à côté d'un badge, la ligne unique se
   cassait au milieu du nombre. */
.panel-title { display: flex; flex-direction: column; line-height: 1.25; }
.panel-title small { color: var(--muted); font-size: 14px; font-weight: 500; }
.sub-head {
  display: flex; align-items: center; gap: 8px;
  padding: 11px var(--pad); background: var(--brand-soft); color: var(--brand);
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  border-top: 1px solid var(--brand-border); border-bottom: 1px solid var(--brand-border);
}
.sub-head svg { width: 15px; height: 15px; }
.sub-head-ref { font-weight: 700; letter-spacing: 0.02em; opacity: .8; }

/* Un résumé de volet qui doit se lire et se toucher comme un bouton. */
details.action { margin-top: 8px; }

/* La commande du soir occupe le haut de la fiche commerçant : c'est le geste
   quotidien, la fiche elle-même ne se touche presque jamais. Le fond creusé
   et le trait de séparation évitent qu'on la confonde avec l'habituelle qui
   la suit. */
details.tonight { border-bottom: 1px solid var(--border); background: var(--surface-sunken); }
details.tonight > summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: 10px;
  padding: 13px var(--pad); min-height: 48px; font-size: 15px;
}
details.tonight > summary::-webkit-details-marker { display: none; }
details.tonight > summary::after { content: "▾"; margin-left: auto; color: var(--muted); }
details.tonight[open] > summary::after { content: "▴"; }
@media (hover: hover) and (pointer: fine) { details.tonight > summary:hover { background: var(--surface); } }
details.tonight > summary:active { background: var(--surface); }
details.tonight > summary > svg { color: var(--text-2); flex: none; }
details.tonight > form { background: var(--surface); border-top: 1px solid var(--border); }
summary.as-button {
  border-radius: var(--r-sm); border: 1px solid var(--border-strong);
  justify-content: center; font-weight: 600; min-height: 44px; padding: 10px 16px;
}
summary.as-button::after { display: none; }
summary.as-button.primary {
  background: var(--brand); border-color: var(--brand); color: #fff;
}
summary.as-button.primary > svg { color: #fff; }
@media (hover: hover) and (pointer: fine) { summary.as-button.primary:hover { background: var(--brand-hover); } }
summary.as-button.primary:active { background: var(--brand-hover); }
summary.as-button.danger { color: var(--crit); }
summary.as-button.danger > svg { color: var(--crit); }
@media (hover: hover) and (pointer: fine) { summary.as-button.danger:hover { background: var(--crit-soft); } }
summary.as-button.danger:active { background: var(--crit-soft); }
details.action[open] > summary.as-button { border-radius: var(--r-sm) var(--r-sm) 0 0; }
details.action[open] .panel-body {
  border: 1px solid var(--border); border-top: 0;
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  padding: 14px; background: var(--surface);
}
button.big, .btn.big, summary.as-button.big { min-height: 52px; font-size: 17px; }

/* ---------- Fenêtre modale ----------
   Les volets dépliés poussaient le reste de la carte hors de l'écran au pire
   moment : le livreur est à la remise, devant le client, il ne doit pas avoir à chercher. */
dialog {
  border: 0; border-radius: var(--r); padding: 0; width: min(420px, calc(100vw - 32px));
  background: var(--surface); color: var(--text); box-shadow: 0 12px 40px rgba(0,0,0,.28);
}
dialog::backdrop { background: rgba(0,0,0,.45); }
dialog .card-head { border-bottom: 1px solid var(--border); }
dialog .card-head h2 { min-width: 0; }
dialog .card-body { padding: var(--pad); }
.dialog-amount {
  font-size: 32px; font-weight: 800; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums; white-space: nowrap; text-align: center;
  padding: 6px 0 16px;
}
/* Ce qui a déjà été réglé se dit sous la somme restante, sans quoi le
   livreur croirait à une erreur de montant. */
.dialog-note {
  margin: -10px 0 14px; text-align: center; font-size: 14px; color: var(--text-2);
}
.dialog-close {
  margin-left: auto; border: 0; background: transparent; color: var(--muted);
  min-height: 32px; padding: 0 6px; font-size: 20px; line-height: 1;
  display: inline-flex; align-items: center; text-decoration: none;
}

/* ---------- Champ de recherche ----------
   L'indice énumère cinq critères : à la taille du texte courant, il se fait
   couper au milieu et n'annonce plus rien. */
.input-icon { position: relative; display: block; }
.input-icon svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  color: var(--muted); pointer-events: none;
}
/* Plus précis que la densité des champs sur ordinateur, qui ramenait la
   marge sous l'icône. */
.input-icon input, .input-icon input[type] { padding-left: 40px; }
.input-icon input::placeholder { font-size: 13px; }

/* ---------- Photos des sortes ---------- */
.photo-card .card-head h2 { font-size: 19px; }
.photo { display: block; width: 100%; height: auto; background: var(--surface-sunken); }
.photo-card.is-absent { opacity: .55; }
.photo-card.is-absent .photo { filter: grayscale(1); }

/* Un résultat est un bouton : il conduit à sa carte, donc il doit s'annoncer
   comme touchable et offrir une cible au pouce. */
.finder-hit {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  width: 100%; min-height: 52px; padding: 11px 12px; margin: 0;
  border: 0; border-top: 1px solid var(--border); border-radius: 0;
  background: transparent; color: var(--text); text-align: left;
  font: inherit; cursor: pointer;
}
.finder-hit:focus-visible, .finder-hit:active { background: var(--surface-sunken); }
@media (hover: hover) and (pointer: fine) { .finder-hit:hover { background: var(--surface-sunken); } }
.finder-hit .muted { font-size: 14px; }

/* La carte atteinte se signale un instant : après un défilement, l'œil a
   besoin qu'on lui dise où il vient d'arriver. */
.is-found { animation: found 1.6s ease-out; }
@keyframes found {
  0%, 40% { box-shadow: 0 0 0 3px var(--brand); }
  100% { box-shadow: 0 0 0 3px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .is-found { animation: none; box-shadow: 0 0 0 3px var(--brand); }
}
.finder-hit:first-child { border-top: 0; }
/* La recherche en tête de feuille, comme une vraie barre de recherche : un
   champ arrondi, et des résultats qui s'ouvrent par-dessus la page. */
/* Au-dessus des cartes qui suivent, mais sous la barre du haut (20) et ses
   menus : la liste des comptes, les trois points d'un arrêt passent devant. */
.finder-bar { position: relative; z-index: 10; margin: 0 0 14px; }
.finder-bar input[type="search"] { min-height: 46px; background: var(--surface); }
.finder-results {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .14); padding: 2px 0;
  max-height: min(60vh, 440px); overflow-y: auto; overscroll-behavior: contain;
}
.finder-results:empty { display: none; }
.finder-results > .muted { display: block; padding: 14px 16px; }
.finder-results .finder-hit { padding-left: 16px; padding-right: 16px; }
/* En mode filtre, la liste se réduit sous la barre ; le compte dit combien. */
.is-filtered { display: none !important; }
.finder-count { padding: 6px 2px 0; font-size: 13px; color: var(--muted); }
.finder-count:empty { display: none; }
/* Un passage annoncé après l'heure promise au client n'est pas un détail
   d'affichage : c'est un engagement rompu, il doit se voir. */
.eta.is-late { color: var(--warn); }
/* Onglet Liens : une ligne par client, le bouton d'envoi au bout du pouce. */
.links-cta { margin: 0 0 12px; }
.links-left { margin: 0; font-size: 19px; font-weight: 700; }
.links-list { padding: 4px 0; }
.msg-row { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-top: 1px solid var(--border); }
.msg-row:first-child { border-top: 0; }
.msg-who { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.msg-who b { font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-who .muted { font-size: 13px; display: flex; flex-wrap: wrap; align-items: center; gap: 0 8px; }
.msg-row .btn { flex: 0 0 auto; white-space: nowrap; }
.msg-send .ico-done, .msg-send.is-sent .ico-todo { display: none; }
.msg-send.is-sent .ico-done { display: inline-flex; }
.msg-send.is-sent { background: var(--ok-soft); color: var(--ok); border-color: transparent; }

/* ---------- Grille des modules ----------
   Sur ordinateur, l'exploitation se pilote d'un coup d'œil : chaque processus
   a sa tuile, avec son état courant lisible sans entrer dedans. */
/* Un état vide qui porte une action : le texte explique, le bouton agit, et
   les deux se séparent quand la ligne devient trop courte. */
.empty-action {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 16px var(--pad); color: var(--muted);
}
.empty-action > span { flex: 1 1 200px; }
.empty-action .btn { flex: none; }

.modules { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 620px) { .modules { grid-template-columns: repeat(2, 1fr); } }
/* Trois par ligne : cinq modules tiennent alors en deux rangées pleines,
   sans tuile orpheline en bout de grille. */
@media (min-width: 900px) { .modules { grid-template-columns: repeat(3, 1fr); } }
.module {
  display: flex; flex-direction: column; gap: 6px;
  padding: 18px; text-decoration: none; color: var(--text);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow);
  transition: border-color .12s ease, transform .12s ease;
}
@media (hover: hover) and (pointer: fine) { .module:hover { border-color: var(--brand); transform: translateY(-1px); } }
.module:active { border-color: var(--brand); }
.module-icon { color: var(--brand); }
.module-icon svg { width: 24px; height: 24px; }
.module-title { font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
.module-value {
  margin-top: 6px; font-size: 15px; font-weight: 700; color: var(--brand);
  font-variant-numeric: tabular-nums;
}
.module.is-urgent { border-color: var(--warn); }
.module.is-urgent .module-value { color: var(--warn); }

/* Sur téléphone, une tuile par module remplissait un écran pour six lignes de
   texte : on retrouve une liste, comme celle des arrêts d'une tournée. Icône,
   nom, état, chevron — assez pour choisir sans lire, assez court pour tout
   voir d'un coup. */
@media (max-width: 619px) {
  .modules { gap: 0; background: var(--border); border-radius: var(--r); overflow: hidden;
    box-shadow: var(--shadow); border: 1px solid var(--border); }
  .module {
    display: grid; grid-template-columns: 40px minmax(0, 1fr) auto 12px;
    align-items: center; column-gap: 12px; row-gap: 0;
    padding: 14px var(--pad); min-height: 66px;
    border: 0; border-radius: 0; box-shadow: none;
    margin-bottom: 1px;
  }
  .module:last-child { margin-bottom: 0; }
  .module:active { transform: none; background: var(--surface-sunken); }
  .module-icon {
    grid-row: 1; display: grid; place-items: center; width: 40px; height: 40px;
    border-radius: 10px; background: var(--brand-soft);
  }
  .module-icon svg { width: 21px; height: 21px; }
  .module-title { grid-row: 1; grid-column: 2; font-size: 16px; }
  /* La description explique ce qu'on sait déjà après deux soirées. */
  .module-value { grid-row: 1; grid-column: 3; margin: 0; font-size: 14px; white-space: nowrap; }
  /* Chaque pièce a sa colonne : sans valeur à droite (Habitudes, Gestion), le
     chevron glissait dans la colonne de la valeur et se décalait des autres. */
  .module::after {
    content: ""; grid-row: 1; grid-column: 4; width: 8px; height: 8px;
    border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
    transform: rotate(-45deg); justify-self: end;
  }
  .module.is-urgent { border: 0; background: var(--warn-soft); }
  .module.is-urgent .module-icon { background: var(--surface); color: var(--warn); }
}

/* ---------- Formulaires de configuration ---------- */
.form-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.form-grid .wide { grid-column: 1 / -1; }
label.field input, label.field select { margin-top: 4px; }
/* Ajout d'un arrêt : l'adresse tient la ligne, le nom et le téléphone se
   partagent la suivante quand la largeur le permet. Une grille plutôt que des
   blocs empilés, sinon le panneau s'étire sur un écran de téléphone. */
.add-stop-fields {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.add-stop-fields .wide { grid-column: 1 / -1; }
.add-stop-fields label.field { margin-bottom: 0; }
/* L'heure de départ et son bouton sur une seule ligne : c'est un réglage,
   pas un formulaire. */
.schedule-set { display: flex; align-items: flex-end; gap: 8px; }
.schedule-set label.field { margin-bottom: 0; min-width: 0; }
/* La précision passe sous le libellé : collée à sa suite, elle s'y soudait. */
.schedule-set label.field small { display: block; font-weight: 500; color: var(--muted); }
.schedule-set button { flex: none; }
select {
  font: inherit; font-size: 16px; padding: 10px 12px; width: 100%;
  border-radius: var(--r-sm); min-height: 44px;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
}
.check-field, .check-inline {
  display: flex; align-items: center; gap: 8px;
  font-weight: 500; color: var(--text-2);
}
.check-inline { font-size: 13px; margin-top: 2px; }
.check-field input[type=checkbox], .check-inline input[type=checkbox] {
  width: 20px; height: 20px; min-height: 0; accent-color: var(--brand); margin: 0;
}
.card.is-off, details.panel.is-off { opacity: .6; }
.rows > details.panel > summary { padding: 10px var(--pad); }
/* Vingt-neuf cases à cocher en colonne se parcourent mal : en grille, la
   liste tient d'un coup d'œil. */
.flavor-grid {
  display: grid; gap: 8px 16px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
/* Un livreur parti est la norme : le signaler par un bandeau reviendrait à ne
   plus rien signaler. Une pastille discrète suffit, et c'est l'exception —
   encore au labo — qui porte une étiquette. */
.dot-live {
  width: 10px; height: 10px; border-radius: 50%; background: var(--ok);
  box-shadow: 0 0 0 3px var(--ok-soft); flex: 0 0 auto;
}
/* La part traitée d'une tournée. Le fond doit se voir sur une carte blanche,
   sinon une tournée à peine entamée ressemble à une barre absente. */
.live {
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  color: var(--muted); font-size: 13px;
}

.progress { height: 6px; background: var(--border); }
.progress > span { display: block; height: 100%; background: var(--brand); transition: width .3s ease; }
/* L'avancement en toutes lettres, sur la largeur de la carte, sous la barre. */
.progress-count {
  padding: 12px var(--pad) 0; font-size: 19px; font-weight: 600; letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums; color: var(--text-2);
}
.progress-count b { font-size: 26px; font-weight: 800; color: var(--text); }
.progress-count span { font-size: 15px; font-weight: 500; color: var(--warn); }
.progress-count + .route-map { padding-top: 12px; }
.stat b.is-cancel { color: var(--crit); }
/* État des services : une pastille verte ou orange devant chaque service. */
.health-row { align-items: center; }
.health-dot { width: 10px; height: 10px; border-radius: 999px; flex: none; }
.health-dot.is-ok { background: var(--ok); box-shadow: 0 0 0 4px var(--ok-soft); }
.health-dot.is-off { background: var(--warn); box-shadow: 0 0 0 4px var(--warn-soft); }
.health-row form { flex: none; }
.client-announcement { margin-bottom: 12px; }
/* Changer l'adresse du départ : replié tant qu'on part du labo. */
.start-change > summary { display: flex; align-items: center; gap: 10px; cursor: pointer; list-style: none; font-weight: 600; }
.start-change > summary::-webkit-details-marker { display: none; }
.start-change > summary svg { width: 18px; height: 18px; color: var(--brand); }
/* La barre d'adresse du départ, l'épingle à droite pour prendre sa position. */
.start-search { position: relative; display: flex; align-items: center; margin-top: 6px; }
.start-search input { flex: 1 1 auto; min-width: 0; padding-right: 52px; }
.start-pin {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; min-height: 40px; padding: 0; border: 0; border-radius: 999px;
  display: grid; place-items: center; background: transparent; color: var(--brand);
}
.start-pin:active { background: var(--brand-soft); }
.start-pin svg { width: 22px; height: 22px; }
.start-pin.is-busy svg { animation: tg-pouls 1s ease-in-out infinite; }
.start-status { min-height: 18px; margin-top: 4px; font-size: 13px; color: var(--muted); }
.map-depot.is-start rect { fill: var(--brand); }
.map-depot.is-start text { fill: #fff; }
.shopify-sync { font-size: 13px; color: var(--muted); }
.shopify-sync.is-done { color: var(--ok); }
.shopify-sync.is-failed { color: var(--warn); }
/* L'ordre d'une tournée : l'écart avec le calcul et les gestes d'ensemble. */
.order-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.order-bar form { display: contents; }
.order-bar button { min-height: 36px; padding: 6px 12px; font-size: 14px; }
.order-bar button svg { width: 16px; height: 16px; }
.order-delta {
  display: inline-flex; align-items: center; min-height: 30px; padding: 4px 12px;
  border-radius: 999px; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
  background: var(--surface-sunken); color: var(--text-2);
}
.order-delta.is-worse { background: var(--warn-soft); color: var(--warn); }
.order-delta.is-better { background: var(--ok-soft); color: var(--ok); }
.order-controls { display: grid; gap: 4px; padding-bottom: 6px; margin-bottom: 6px; border-bottom: 1px solid var(--border); }
.order-controls .quiet { width: 100%; justify-content: flex-start; }
.order-to { display: flex; gap: 6px; }
.order-to select { flex: 1 1 auto; min-height: 40px; }
/* L'historique : les faits d'une soirée en une ligne qui revient à la ligne. */
.history-facts { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 14px; color: var(--text-2); }
.history-facts b { color: var(--text); font-variant-numeric: tabular-nums; }
.history-alert { color: var(--warn); display: inline-flex; align-items: center; gap: 4px; font-weight: 600; }
.history-alert svg { width: 15px; height: 15px; }
.assign-row { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.assign-row .grow { flex: 1 1 200px; }

/* ---------- Retour à l'arrêt en cours ----------
   Flottant en bas à droite, au-dessus de la zone du pouce. Il s'efface dès
   que la carte concernée est à l'écran : affiché en permanence, il
   masquerait un bouton d'action au moment où le livreur en a besoin. */
.jump-current {
  position: fixed; right: 14px; z-index: 30;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  display: grid; place-items: center;
  width: 46px; height: 46px; padding: 0;
  border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); text-decoration: none;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .25);
}
/* Le symbole est orange sur transparent : il se détache aussi bien du blanc
   que du gris sombre, là où un aplat de marque l'aurait avalé. */
.jump-current img { width: 26px; height: 26px; display: block; }
@media (hover: hover) and (pointer: fine) { .jump-current:hover { background: var(--surface-sunken); } }
.jump-current:active { background: var(--surface-sunken); }
.jump-current[hidden] { display: none; }

/* ---------- Carte réduite ---------- */
/* Les deux moyens de règlement se valent : même poids visuel, côte à côte.
   Le display:block annule le display:contents hérité de .btn-row, qui
   escamotait le formulaire et privait la ligne de ses colonnes. */
.pay-choice > form { display: block; flex: 1 1 0; min-width: 0; }
.pay-choice button { width: 100%; }

.card.closed > details > summary { padding: 16px var(--pad); }
.closed-title { font-size: 18px; font-weight: 700; }
/* L'orange est un signal d'attention : réservé aux commandes annulées, il
   perdrait tout sens s'il marquait aussi les livraisons réussies. */
.card.closed.is-cancelled .closed-title,
.card.closed.is-cancelled .closed-title .order-ref { color: var(--brand); }
.closed-title .order-ref { color: var(--muted); }
.muted { color: var(--muted); }
.empty { color: var(--muted); text-align: center; padding: 40px 20px; }
.scroll { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 15px; }
th, td { text-align: left; padding: 9px 4px; border-bottom: 1px solid var(--border); }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.04em; }
td.n, th.n { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Densité sur poste de travail ----------
   Le mobile impose 44 px de cible tactile ; à la souris, cette générosité
   devient de l'espace perdu et oblige à faire défiler pour lire une liste
   qu'un écran d'ordinateur pourrait tenir d'un bloc. */
@media (min-width: 900px) and (pointer: fine) {
  :root { --pad: 14px; }
  body { font-size: 15px; }
  button, .btn { min-height: 34px; padding: 6px 12px; font-size: 14px; }
  button.big, .btn.big, summary.as-button.big { min-height: 40px; font-size: 15px; }
  input[type=search], input[type=number], input[type=text], input[type=tel],
  input[type=time], input[type=email], input[type=date], select {
    min-height: 34px; padding: 6px 10px; font-size: 14px;
  }
  .card-head { padding: 10px var(--pad); }
  .card-head h2 { font-size: 15px; }
  .row-item { padding: 9px var(--pad); }
  .stepper-row { padding: 8px 0; }
  .stepper button { width: 34px; min-width: 34px; }
  .stepper input[type=number] { width: 48px; min-width: 48px; font-size: 15px; }
  .stat { padding: 10px var(--pad); }
  .stat b { font-size: 22px; }
  .section-title { margin: 18px 0 8px; }
  details.panel > summary { padding: 10px var(--pad); min-height: 40px; font-size: 15px; }
  .module { padding: 14px; }
  .module-title { font-size: 16px; }
  main { padding: var(--pad) var(--pad) 40px; max-width: 1120px; }
}

/* ---------- Saisie au doigt ----------
   iOS zoome sur tout champ dont le texte fait moins de 16 px, et ne dézoome
   pas au départ du champ : la page reste agrandie et décalée. On corrige la
   cause, le corps du champ, plutôt que d'interdire le zoom à tout le monde.
   Seulement au doigt : la souris garde la densité du poste de travail. Les
   champs déjà plus grands (compteurs, codes) gardent leur taille, leurs
   règles étant plus précises. */
@media (pointer: coarse) {
  input, select, textarea { font-size: 16px; }
  .input-icon input::placeholder { font-size: 16px; }
}

/* ---------- Sélecteur segmenté ----------
   Deux préréglages exclusifs ne méritent pas deux boutons pleine largeur :
   c'est un choix, pas deux actions. */
.seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--r-sm); overflow: hidden; }
.seg button {
  border: 0; border-radius: 0; min-height: 32px; padding: 5px 14px;
  font-size: 14px; background: var(--surface); color: var(--text-2);
}
.seg button + button { border-left: 1px solid var(--border); }
@media (hover: hover) and (pointer: fine) { .seg button:hover { background: var(--brand-soft); color: var(--brand); } }
.seg button:active { background: var(--brand-soft); color: var(--brand); }
.seg button.is-on { background: var(--brand); color: #fff; }
@media (hover: hover) and (pointer: fine) { .seg button.is-on:hover { background: var(--brand-hover); color: #fff; } }
.seg button.is-on:active { background: var(--brand-hover); color: #fff; }
/* Choix segmenté à boutons radio : un réglage à valeur, coché d'un doigt. */
.segset { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--r-sm); overflow: hidden; margin-top: 6px; }
.segset label { display: inline-flex; align-items: center; padding: 9px 14px; font-size: 14px; cursor: pointer; color: var(--text-muted); background: var(--surface); user-select: none; }
.segset label + label { border-left: 1px solid var(--border); }
.segset input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
@media (hover: hover) and (pointer: fine) { .segset label:hover { background: var(--brand-soft); color: var(--brand); } }
.segset label:active { background: var(--brand-soft); color: var(--brand); }
.segset label:has(input:checked) { background: var(--brand); color: #fff; }
@media (hover: hover) and (pointer: fine) { .segset label:has(input:checked):hover { background: var(--brand-hover); color: #fff; } }
.segset label:has(input:checked):active { background: var(--brand-hover); color: #fff; }
.segset label:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: -2px; }

/* Menu d'un arrêt : trois points effacés, panneau au clic. Rien d'affiché
   tant qu'on ne le demande pas — la liste doit rester lisible d'un coup d'œil. */
.stop-menu { position: relative; margin-left: 6px; }
.stop-menu > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: var(--r-sm); color: var(--text-muted);
}
.stop-menu > summary::-webkit-details-marker { display: none; }
@media (hover: hover) { .stop-menu > summary:hover { background: var(--brand-soft); color: var(--brand); } }
.stop-menu[open] > summary { background: var(--brand-soft); color: var(--brand); }
.stop-menu-panel {
  position: absolute; right: 0; top: 36px; z-index: 20; min-width: 168px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); box-shadow: 0 8px 24px rgba(0,0,0,.18); padding: 6px;
}
.stop-menu-panel button, .stop-menu-panel .btn {
  width: 100%; justify-content: flex-start; white-space: nowrap;
}
/* Le menu se pose au-dessus de la carte, pas dedans : sans quoi il ouvrirait
   le pli du haut de fiche et pousserait tout le reste vers le bas. */
.stop-card .card-head { position: relative; }

/* Le « + » entre deux arrêts : une ligne discrète, qui ne prend la place
   d'aucune carte et ne se voit que si on la cherche. Cible tactile de 44 px
   quand même — on la vise d'un pouce, en voiture, à l'arrêt. */
.insert-gap {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px; margin: 2px 0; border-radius: var(--r-sm);
  color: var(--muted); text-decoration: none;
  border: 1px dashed var(--border); background: transparent;
}
.insert-gap:focus-visible {
  color: var(--brand); border-color: var(--brand-border); background: var(--brand-soft);
}
@media (hover: hover) {
  .insert-gap:hover { color: var(--brand); border-color: var(--brand-border); background: var(--brand-soft); }
}
/* Au doigt il n'y a pas de survol : le libellé reste lisible d'emblée. */

/* Case à cocher en pleine ligne, cliquable partout, pas seulement sur le carré. */
.check-row { display: flex; align-items: center; gap: 10px; min-height: 44px; cursor: pointer; }
.check-row input { width: 20px; height: 20px; accent-color: var(--brand); }

/* Écran des messages : le gabarit se saisit au clavier, l'aperçu se lit. */
textarea {
  width: 100%; font: inherit; font-size: 15px; line-height: 1.5; padding: 10px 12px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); resize: vertical; min-height: 88px;
}
textarea:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* L'aperçu ressemble à ce que le client verra : une bulle, pas un champ. */
.sms-preview {
  margin: 6px 0 0; padding: 12px 14px; border-radius: 14px;
  background: var(--brand-soft); color: var(--text);
  font-size: 15px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
}
.var-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
  padding: 2px 6px; border-radius: 4px; background: var(--brand-soft); color: var(--brand);
  white-space: nowrap;
}
.badge.warn { background: var(--warn-soft); color: var(--text); }

/* Sur la feuille du livreur, deux signaux distincts d'un coup d'œil. */
.wish.is-there { color: var(--ok); }
.wish.is-away { color: #9b2c2c; font-weight: 700; }

/* Règlement d'avance : une proposition, pas une injonction. */
.pay-now { padding: 0 14px 14px; text-align: center; }
.pay-now .pay-label { display: block; margin-bottom: 8px; }
.pay-now-error { margin: 0 0 8px; font-size: 14px; color: #9b2c2c; }
.paid-note {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 0 14px 14px; padding: 12px; border-radius: var(--r-sm);
  background: var(--ok-soft, #e7f5ec); color: var(--ok); font-size: 15px;
}
/* Sur la feuille du livreur, « payé » doit être impossible à manquer : c'est
   sur cette mention qu'il laissera partir un client sans encaisser. */
.paid-flag {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px; font-size: 13px; font-weight: 700;
  background: var(--ok-soft, #e7f5ec); color: var(--ok);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.paid-flag svg { width: 14px; height: 14px; }
.paid-flag.is-failed { background: var(--warn-soft); color: #9b5d09; }
/* Ce qui reste à encaisser, en orange sous le vert de ce qui est réglé. */
.paid-flag.is-due { background: var(--warn-soft); color: var(--warn); }
/* La mention se range à droite du montant, sur la même ligne : une carte de
   livraison doit tenir sur un écran, et chaque étage gagné compte. */
.pay-line { gap: 8px; }
.pay-line .money { margin-right: auto; }
/* Quand il reste à encaisser, « manque » se pose sous « payé », aligné sur
   lui : les deux sommes se lisent l'une au-dessus de l'autre, comme un
   compte, plutôt que de part et d'autre de la carte. */
.pay-line:has(.is-due) .paid-flag { margin-left: auto; }
.pay-line:has(.is-due) .is-due { margin-top: 6px; }

/* « Commande rajoutée » : une mention, pas une alerte. L'or dit déjà « hors
   commande d'origine » ailleurs dans la feuille. */
.added-note { margin-top: 4px; font-size: 13px; font-weight: 600; color: var(--gold, #9a7b16); }
/* Une ligne de liste qui conduit ailleurs. */
.link-row { text-decoration: none; color: inherit; }
@media (hover: hover) { .link-row:hover { background: var(--brand-soft); } }

/* Ce qui coïncide, dans un résultat de recherche. */
.finder-hit mark {
  background: var(--brand-soft); color: var(--brand);
  font-weight: 700; border-radius: 2px; padding: 0;
}
.finder-tel { display: block; font-variant-numeric: tabular-nums; letter-spacing: .02em; }

/* Saisie d'un tarif, alignée à droite comme un montant. */
.price-input {
  width: 96px; text-align: right; font: inherit; padding: 8px 10px;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text); font-variant-numeric: tabular-nums;
}
.small-line { border-top: 1px solid var(--border); }

/* Une tournée se déplie : repliée, elle tient en une ligne d'état ; dépliée,
   elle donne tout. La liste de la soirée redevient lisible d'un coup d'œil. */
details.card > summary.card-head { cursor: pointer; list-style: none; align-items: flex-start; }
details.card > summary.card-head::-webkit-details-marker { display: none; }
details.card > summary.card-head::after {
  content: ''; width: 9px; height: 9px; margin-left: 4px; flex: none; align-self: center;
  border-right: 2px solid var(--text-muted); border-bottom: 2px solid var(--text-muted);
  transform: rotate(45deg); transition: transform .15s ease;
}
details.card[open] > summary.card-head::after { transform: rotate(-135deg); }
@media (hover: hover) { details.card > summary.card-head:hover { background: var(--brand-soft); } }

/* Accès personnel d'un livreur : un lien qu'on lit pour le vérifier, pas
   qu'on tape. */
.access-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Un réglage : sa valeur et son unité, alignées à droite de l'explication. */
.tuning-input { display: flex; align-items: center; gap: 6px; flex: none; }
.tuning-input .price-input { width: 88px; }

/* Saisie d'un code d'accès : court, en capitales, chiffres alignés. */
.code-input {
  width: 116px; font: inherit; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  padding: 8px 10px; border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text);
}

.access-link {
  flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
  padding: 6px 8px; border-radius: var(--r-sm); background: var(--surface-alt, var(--bg));
  border: 1px solid var(--border);
}

/* Le choix de thème : une ligne de réglage, pas un bouton d'action. Il ne se
   touche qu'une fois, et ne doit pas concurrencer ce qui l'entoure. */
.theme-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px var(--pad); color: var(--text-2); font-size: 15px;
}

/* ---------- Lignes de configuration ----------
   Une fiche se lit en une ligne et s'ouvre pour être modifiée : dérouler
   quinze formulaires complets pour en corriger un est un mauvais échange. */
.cfg-row { display: flex; align-items: center; gap: 12px; width: 100%; min-width: 0; }
.cfg-row .cfg-main { flex: 1 1 auto; min-width: 0; }
.cfg-row .cfg-name { font-weight: 600; }
.cfg-row .cfg-meta {
  color: var(--muted); font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rank { color: var(--muted); font-variant-numeric: tabular-nums; font-weight: 700;
  flex: 0 0 auto; min-width: 1.5ch; }


/* ---------- Module Clients ---------- */
.client-row { align-items: center; }
.client-row-name { display: flex; align-items: center; gap: 8px; min-width: 0; }
.client-row-name b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.client-row-note { display: inline-flex; color: var(--muted); }
.client-row-note svg { width: 14px; height: 14px; }
.client-row-meta {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  font-size: 13px; color: var(--text-2); text-align: right;
}
.rating-inline { display: inline-flex; align-items: center; gap: 3px; font-weight: 600; color: var(--text); }
.rating-inline svg { width: 12px; height: 12px; fill: var(--gold); stroke: var(--gold); }
.client-file.card-body { border-top: 0; }
/* Tous / À risque : deux liens sobres sous la recherche. */
.seg-links { display: flex; gap: 18px; margin: 10px 2px 12px; font-size: 14px; }
.seg-links a { color: var(--text-2); text-decoration: none; padding-bottom: 3px; border-bottom: 2px solid transparent; }
.seg-links a.is-on { color: var(--text); font-weight: 600; border-bottom-color: var(--brand); }

/* Le nom d'un client mène à sa fiche, sans changer d'allure. */
.plain-link { color: inherit; text-decoration: none; }
@media (hover: hover) and (pointer: fine) { .plain-link:hover { text-decoration: underline; } }

/* ---------- Numéros du client ---------- */
.phone-row.is-main { background: var(--surface-sunken); }
.phone-swap { margin: 4px 0 0; }
.phone-swap button {
  min-height: 0; padding: 4px 0; gap: 6px; font-size: 14px; font-weight: 600;
  border: 0; background: transparent; box-shadow: none; color: var(--brand);
}
.phone-swap svg { width: 14px; height: 14px; }

/* ---------- Correction d'une commande ---------- */
.order-edit { margin-top: 10px; border-top: 1px solid var(--border); padding-top: 8px; }
.order-edit > summary { display: flex; align-items: center; gap: 8px; cursor: pointer; font-weight: 600; color: var(--brand); padding: 6px 0; list-style: none; }
.order-edit > summary::-webkit-details-marker { display: none; }
.order-edit > summary svg { width: 16px; height: 16px; }
.order-edit form { margin-top: 6px; }

/* ---------- Administration de la boutique ---------- */
.shop-admin-thumb { flex: none; width: 44px; height: 44px; border-radius: 10px; object-fit: cover; background: var(--surface-sunken); }
.shop-admin-preview { display: block; max-width: 220px; max-height: 160px; border-radius: 12px; margin-top: 10px; object-fit: cover; }
.shop-admin-preview[hidden] { display: none; }
/* Les onglets : une ligne de pastilles qui défile au doigt, jamais coupée */
.shop-tabs { display: flex; gap: 6px; overflow-x: auto; margin: 6px 0 14px; padding: 4px 0; scrollbar-width: none; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; }
.shop-tabs::-webkit-scrollbar { display: none; }
.shop-tabs a { flex: none; scroll-snap-align: start; display: inline-flex; align-items: center; min-height: 38px; padding: 0 14px; border-radius: 19px; background: var(--surface); border: 1px solid var(--border); color: var(--text-2); font-size: 14px; font-weight: 600; text-decoration: none; white-space: nowrap; }
.shop-state .card-body { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; }
.shop-state form { flex: 1 1 160px; }
.shop-state form button { width: 100%; }
.shop-quick { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.shop-quick .btn { flex: 1 1 auto; min-width: -webkit-fit-content; min-width: fit-content; justify-content: center; white-space: nowrap; }
@media (min-width: 1024px) { body.has-side .shop-quick .btn { flex: 0 1 auto; } }
.shop-link { display: flex; align-items: center; gap: 12px; padding: 14px var(--pad); color: var(--text); text-decoration: none; }
.shop-link svg { width: 18px; height: 18px; transform: rotate(-90deg); color: var(--text-2); }
.shop-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.shop-line > .grow { flex: 1 1 200px; }
.shop-line > button, .shop-line > .btn, .shop-line > form { flex: 1 1 160px; }
.shop-line > form > button { width: 100%; }
.shop-stock { padding: 4px 0; }
.shop-stock-row { display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 4px var(--pad); border-top: 1px solid var(--border); }
.shop-stock-row:first-child { border-top: 0; }
.shop-stock-row input { width: 88px; text-align: center; font-weight: 700; }
.shop-stock-row b { min-width: 40px; text-align: right; }
.shop-tabs a.is-on { background: var(--brand); border-color: var(--brand); color: #fff; }
/* Les Tiramigo : une grille de cartes, photo carrée, prix et stock */
.shop-products { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-bottom: 16px; }
.shop-product { display: flex; flex-direction: column; gap: 4px; padding: 8px 8px 12px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border); color: var(--text); text-decoration: none; min-width: 0; }
.shop-product:active { transform: scale(.98); }
.shop-product.is-hidden { opacity: .6; }
.shop-product-photo { position: relative; display: flex; align-items: center; justify-content: center; aspect-ratio: 1; border-radius: 10px; overflow: hidden; background: var(--surface-sunken); color: var(--text-3, var(--text-2)); margin-bottom: 4px; }
.shop-product-photo img { width: 100%; height: 100%; object-fit: cover; }
.shop-product-photo svg { width: 28px; height: 28px; }
.shop-product-photo .badge { position: absolute; top: 6px; left: 6px; }
.shop-product-photo .badge + .badge { left: auto; right: 6px; }
.shop-product b { font-size: 15px; line-height: 1.2; overflow-wrap: anywhere; }
.shop-product small { font-size: 13px; color: var(--text-2); }
.shop-product .is-low { color: var(--warn); font-weight: 700; }
.shop-product .is-out { color: var(--crit); font-weight: 700; }
.shop-product.is-new { align-items: center; justify-content: center; gap: 10px; min-height: 200px; border-style: dashed; color: var(--brand); text-align: center; }
.shop-product.is-new span { display: flex; width: 48px; height: 48px; border-radius: 50%; align-items: center; justify-content: center; background: var(--brand-soft); }
.shop-product.is-new svg { width: 24px; height: 24px; }
/* La fiche d'un Tiramigo : la photo à gauche (en haut sur téléphone), les champs à droite */
.shop-back { display: inline-flex; align-items: center; gap: 4px; min-height: 44px; color: var(--text-2); text-decoration: none; font-weight: 600; }
.shop-back svg { width: 18px; height: 18px; transform: rotate(90deg); }
.shop-edit-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 12px; margin-bottom: 6px; }
.shop-delete { justify-self: start; color: var(--crit) !important; }
.shop-edit { display: grid; gap: 14px; align-items: start; }
@media (min-width: 760px) { .shop-edit { grid-template-columns: 260px minmax(0, 1fr); } .shop-edit-photo { position: sticky; top: 76px; } }
.shop-edit-photo { display: grid; gap: 10px; }
.shop-photo-drop { position: relative; display: flex; align-items: flex-end; justify-content: center; aspect-ratio: 1; border-radius: 18px; overflow: hidden; background: var(--surface-sunken); border: 2px dashed var(--border); cursor: pointer; }
.shop-photo-drop img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.shop-photo-drop img[hidden] { display: none; }
.shop-photo-drop input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.shop-photo-hint { position: relative; display: inline-flex; align-items: center; gap: 6px; margin: 12px; padding: 8px 14px; border-radius: 18px; background: rgba(0, 0, 0, .65); color: #fff; font-size: 14px; font-weight: 600; pointer-events: none; }
.shop-photo-hint svg { width: 16px; height: 16px; }
.shop-photos { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.shop-photo-mini { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; aspect-ratio: 1; border-radius: 10px; overflow: hidden; background: var(--surface-sunken); border: 1px solid var(--border); font-size: 11px; color: var(--text-2); cursor: pointer; }
.shop-photo-mini img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.shop-photo-mini input[type=checkbox] { position: absolute; top: 4px; right: 4px; z-index: 1; width: 18px; height: 18px; }
.shop-photo-mini span { position: absolute; bottom: 0; left: 0; right: 0; padding: 2px; background: rgba(0, 0, 0, .55); color: #fff; text-align: center; }
.shop-photo-mini:has(input:checked) img { opacity: .35; }
.shop-photo-mini.is-add { border-style: dashed; color: var(--brand); }
.shop-photo-mini.is-add span { position: static; background: none; color: inherit; }
.shop-photo-mini.is-add svg { width: 20px; height: 20px; }
.shop-photo-mini.is-add input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.shop-edit-fields { display: grid; gap: 12px; min-width: 0; }
.shop-edit-fields .card { margin: 0; }
.shop-readonly { display: flex; align-items: center; min-height: 44px; font-weight: 500; color: var(--text-2); }
.shop-edit-actions, .shop-save-bar { position: sticky; bottom: calc(8px + env(safe-area-inset-bottom, 0px)); z-index: 5; display: flex; flex-wrap: wrap; gap: 8px; padding: 10px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border); box-shadow: 0 8px 24px rgba(0, 0, 0, .18); }
.shop-edit-actions > *, .shop-save-bar > * { flex: 1 1 0; min-width: min(100%, max-content); }
.shop-edit-actions { flex-wrap: nowrap; }
/* Le contenu : un bloc repliable par partie de la boutique, des tableaux à lignes */
.shop-content { display: grid; gap: 10px; }
.shop-block { margin: 0; }
.shop-block > summary { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; padding: 14px var(--pad); cursor: pointer; list-style: none; }
.shop-block > summary::-webkit-details-marker { display: none; }
.shop-block > summary small { color: var(--text-2); font-size: 13px; }
.shop-block > summary::after { content: ""; margin-left: auto; width: 8px; height: 8px; border-right: 2px solid var(--text-2); border-bottom: 2px solid var(--text-2); transform: rotate(45deg); align-self: center; }
.shop-block[open] > summary::after { transform: rotate(-135deg); }
.shop-block > .card-body { border-top: 1px solid var(--border); display: grid; gap: 4px; }
.shop-rows { display: grid; gap: 8px; margin: 6px 0 10px; }
.shop-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 44px; gap: 8px; align-items: start; }
.shop-row input, .shop-row textarea { width: 100%; min-width: 0; }
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--text-2); cursor: pointer; }
.icon-btn svg { width: 16px; height: 16px; }
.js-rows > .js-row-add { justify-self: start; }
@media (max-width: 600px) {
  .shop-row { grid-template-columns: minmax(0, 1fr) 44px; padding: 10px; border-radius: 12px; background: var(--surface-sunken); }
  .shop-row > :first-child { grid-column: 1; }
  .shop-row > :nth-child(2) { grid-column: 1; grid-row: 2; }
  .shop-row > .js-row-remove { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
}

/* ---------- Commandes du site (administration) ---------- */
.so-actions { margin: 4px 0 12px; }
.so-day { display: flex; align-items: center; justify-content: center; gap: 18px; margin: 14px 0 8px; font-size: 15px; }
.so-day b { min-width: 14ch; text-align: center; text-transform: capitalize; }
.so-day a { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 50%; font-size: 24px; text-decoration: none; color: var(--brand); background: var(--surface); border: 1px solid var(--border); }
a.so-row { color: inherit; text-decoration: none; }
.so-row-main { min-width: 0; }
.so-row-main small { display: block; margin-top: 2px; }
.so-row-side { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; flex: none; }
.so-row.is-off { opacity: .6; }
@media (hover: hover) and (pointer: fine) { a.so-row:hover { background: var(--surface-sunken); } }
a.so-row:active { background: var(--surface-sunken); }
.so-qty small { display: block; margin-top: 2px; }
.so-ship { display: grid; gap: 0; margin-bottom: 6px; }
.so-total { margin-top: 10px; border: 1px solid var(--border); border-radius: 12px; }
.so-total .row-item { padding: 9px 12px; }
.js-so-address { position: relative; }
.so-suggest { position: absolute; left: 0; right: 0; top: 100%; z-index: 5; display: grid; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 8px 24px rgba(0, 0, 0, .12); overflow: hidden; }
.so-suggest[hidden] { display: none; }
.so-suggest button { text-align: left; border: 0; border-radius: 0; background: none; padding: 11px 12px; font-weight: 500; min-height: 44px; }
.so-suggest button + button { border-top: 1px solid var(--border); }
@media (hover: hover) and (pointer: fine) { .so-suggest button:hover { background: var(--surface-sunken); } }

/* ---------- Villes livrées : la carte, comme celle du thème Shopify ---------- */
.js-cities [hidden] { display: none !important; }
.city-tabs { display: flex; gap: 6px; overflow-x: auto; padding: 10px var(--pad); border-bottom: 1px solid var(--border); scrollbar-width: none; }
.city-tabs button { flex: none; min-height: 36px; padding: 0 12px; border-radius: 18px; font-size: 13px; font-weight: 600; background: var(--surface-sunken); border: 1px solid transparent; color: var(--text-2); white-space: nowrap; }
.city-tabs button.is-on { background: #ea5330; color: #fff; border-color: #ea5330; }
.city-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; padding: 10px var(--pad) 8px; font-size: 15px; }
.city-pending { padding: 0 var(--pad) 10px; font-size: 14px; display: grid; gap: 4px; }
.city-pending[hidden], .city-pending [hidden] { display: none; }
.city-plus { color: #ea5330; }
.city-minus { color: var(--text-2); }
.city-map { position: relative; width: 100%; aspect-ratio: 16 / 10; max-height: min(78vh, 760px); background: #e4dad1; overflow: hidden; border-top: 1px solid var(--border); }
@media (max-width: 600px) { .city-map { aspect-ratio: auto; height: 68vh; height: 68svh; min-height: 380px; max-height: none; } }
/* Zoom : boutons plus et moins dans le coin de la carte */
.city-zoom { position: absolute; right: 10px; bottom: 10px; display: grid; gap: 6px; z-index: 2; }
.city-zoom button { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 12px; border: 1px solid rgba(53, 33, 28, .2); background: #fff; color: #35211c; box-shadow: 0 2px 8px rgba(0, 0, 0, .15); cursor: pointer; padding: 0; }
.city-zoom svg { width: 20px; height: 20px; }
.city-communes path.is-flash { stroke: #35211c; stroke-width: 3px; }
/* La recherche de ville, au-dessus de la carte */
.city-search { position: relative; margin: 0 0 10px; }
.city-search > svg { position: absolute; left: 14px; top: 50%; width: 18px; height: 18px; transform: translateY(-50%); color: var(--text-2); pointer-events: none; }
.city-search input { width: 100%; min-height: 48px; padding: 0 14px 0 42px; border-radius: 14px; font-size: 16px; }
.city-results { position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 10; list-style: none; margin: 0; padding: 6px; border-radius: 14px; background: var(--surface); border: 1px solid var(--border); box-shadow: 0 12px 30px rgba(0, 0, 0, .3); }
.city-results button { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 52px; padding: 0 12px; border: 0; border-radius: 10px; background: none; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.city-results button:hover, .city-results button:focus-visible { background: var(--surface-sunken); outline: none; }
.city-results .grow { display: grid; gap: 1px; min-width: 0; }
.city-results small { color: var(--text-2); font-size: 12px; font-weight: 600; }
.city-results b { min-width: 76px; text-align: center; padding: 8px 10px; border-radius: 10px; font-size: 13px; background: #ea5330; color: #fff; }
.city-results button.is-on b { background: var(--surface-sunken); color: var(--text-2); border: 1px solid var(--border); }
.city-results-empty { padding: 12px; color: var(--text-2); }
.city-map svg { display: block; width: 100%; height: 100%; touch-action: none; user-select: none; -webkit-user-select: none; }
.city-communes path, .city-arr path { fill: #f7efe7; stroke: #dfcab7; stroke-width: .6px; stroke-linejoin: round; cursor: pointer; transition: fill .15s ease, opacity .25s ease; }
.city-communes path[data-code="75056"] { fill: #e3cdb8; stroke: #fff; stroke-width: 1.6px; }
.city-communes path.is-on, .city-arr path.is-on { fill: #ea5330; stroke: rgba(255, 255, 255, .8); }
.city-communes path.is-part { fill: #f19a80; }
.city-communes path.is-changed, .city-arr path.is-changed { stroke: #35211c; stroke-width: 1.4px; }
.city-communes path.is-away { fill: #ebe3dc; stroke: #d8cbbf; cursor: zoom-in; }
/* Les villes livrées des autres départements : orange pâle, pour se situer sans détourner l'œil. */
.city-communes path.is-on.is-away { fill: #f2bfae; stroke: rgba(255, 255, 255, .8); }
@media (hover: hover) and (pointer: fine) {
  .city-communes path:not(.is-away):hover, .city-arr path:hover { fill: #f2d9c6; }
  .city-communes path.is-on:not(.is-away):hover, .city-arr path.is-on:hover { fill: #c9401f; }
  .city-communes path.is-away:hover { fill: #e3d6ca; }
  .city-communes path.is-on.is-away:hover { fill: #eba48e; }
}
.city-arr { display: none; }
.is-paris .city-arr { display: inline; }
.is-paris .city-communes path[data-code="75056"] { display: none; }
.city-lim-blanc, .city-lim, .city-dep-on { fill: none; pointer-events: none; stroke-linejoin: round; stroke-linecap: round; }
.city-lim-blanc { stroke: #fff; stroke-width: 4px; }
.city-lim { stroke: #b99b82; stroke-width: 1.3px; }
.city-dep-on { stroke: #35211c; stroke-width: 2.6px; }
.city-marks { font-family: Poppins, "Arial Black", system-ui, sans-serif; font-weight: 900; }
.city-marks text { fill: #35211c; pointer-events: none; }
.city-paris-nom { letter-spacing: .06em; }
.city-badge { cursor: pointer; }
.city-badge circle { fill: #fff; stroke: #35211c; }
.city-badge.is-on circle { fill: #35211c; stroke: #fff; }
.city-badge.is-on text { fill: #fff; }
@media (hover: hover) and (pointer: fine) { .city-badge:hover circle { fill: #ea5330; stroke: #fff; } .city-badge:hover text { fill: #fff; } }
.city-tip { position: absolute; left: 0; top: 0; pointer-events: none; background: #35211c; color: #fff; font-size: 13px; font-weight: 600; padding: 6px 10px; border-radius: 8px; max-width: calc(100% - 12px); text-wrap: balance; box-shadow: 0 4px 14px rgba(0, 0, 0, .2); }
.city-tip[hidden] { display: none; }
/* Des villes changées pas encore enregistrées tiennent l'application hors des rechargements (data-busy), sans griser l'écran. */
.js-cities[data-busy] { opacity: 1; }
.city-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; margin: 0; color: #8a7a70; }

/* ---------- Commerçants : une ligne par commerçant ---------- */
.pro-row-head { display: flex; align-items: center; gap: 12px; padding: 14px var(--pad) 2px; }
.pro-row-name { min-width: 0; font-size: 16px; line-height: 1.25; overflow-wrap: anywhere; }
.pro-row-head > .badge { flex: none; }
/* La ligne d'adresse prend toute la largeur : l'étiquette d'état ne l'écrase plus. */
.pro-row-meta { display: flex; flex-wrap: wrap; gap: 2px 10px; padding: 0 var(--pad) 10px; font-size: 13px; }
.pro-row-meta > * + *::before { content: "·"; margin-right: 10px; color: var(--muted); }
/* Le montant toujours au même endroit, en haut à droite de la commande. */
.pro-row-order { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 6px 12px; padding: 0 var(--pad) 12px; font-size: 14px; }
.pro-row-order .money { white-space: nowrap; }
.pro-row-stock { grid-column: 1 / -1; justify-self: start; }
.pro-row-note { margin: 0; padding: 0 var(--pad) 12px; font-size: 13px; color: var(--text-2); }
.pro-row-actions { padding: 0 var(--pad) 14px; }
/* Des boutons de même largeur, sur une ligne quand il y a la place, empilés sinon :
   un libellé ne se coupe jamais en deux. */
.pro-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.pro-actions form { display: contents; }
.pro-actions button { flex: 1 1 auto; min-width: -webkit-fit-content; min-width: fit-content; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; white-space: nowrap; padding: 0 14px; }
@media (min-width: 1024px) { body.has-side .pro-actions button { flex: 0 1 auto; } }
.pro-actions button > span { overflow: hidden; text-overflow: ellipsis; }
.pro-actions button > svg { flex: none; width: 18px; height: 18px; }
.sub-head.plain { background: transparent; color: var(--text-2); border-color: transparent; border-bottom: 1px solid var(--border); }
.pro-start { padding: 12px var(--pad) 4px; }
.pro-start > span { display: block; font-size: 13px; font-weight: 600; color: var(--text-2); margin-bottom: 8px; }

/* ---------- Comptes ---------- */
.account-secret { border: 2px solid var(--brand); }
.account-password { font: 800 26px/1.2 ui-monospace, Menlo, monospace; letter-spacing: .06em; margin: 6px 0; user-select: all; }
.account-dot { flex: none; width: 34px; height: 34px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--brand-soft); color: var(--brand); font-weight: 800; }
.account-dot.is-off { background: var(--surface-sunken); color: var(--muted); }

/* ---------- Client à risque ---------- */
.risk-line { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--warn); margin-top: 2px; }
.risk-line svg { width: 14px; height: 14px; flex: 0 0 auto; }

/* ---------- Relance des sans réponse ---------- */
.remind-row .grow { min-width: 0; }
.remind-state { font-size: 13px; }
.remind-row.is-done { opacity: .55; }
.remind-row.is-done .remind-state { color: var(--ok); }

/* ---------- Comptabilité ---------- */
.acc-presets { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 14px; }
.acc-presets a { color: var(--text-2); text-decoration: none; padding: 4px 0; border-bottom: 2px solid transparent; }
.acc-presets a.is-on { color: var(--text); font-weight: 600; border-bottom-color: var(--brand); }
.acc-range { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-top: 12px; }
.acc-range .field { flex: 1 1 140px; margin: 0; }
.acc-range button { flex: 0 0 auto; }
.acc-delta { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }
.acc-delta.is-up { color: var(--ok); }
.acc-delta.is-down { color: var(--crit); }
.acc-margin { background: var(--surface-sunken); }
.acc-margin b { font-size: 17px; }
.acc-bar-row { gap: 10px; }
.acc-bar-label { flex: 0 0 38%; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.acc-bar { flex: 1 1 auto; height: 8px; border-radius: 4px; background: var(--surface-sunken); overflow: hidden; }
.acc-bar > span { display: block; height: 100%; background: var(--brand); border-radius: 4px; }
.acc-bar-value { flex: 0 0 auto; min-width: 2.5em; text-align: right; font-variant-numeric: tabular-nums; }
/* ---------- Finitions ----------
   Une couche posée par-dessus le reste, dans l'esprit des applications
   d'Apple : une réponse au doigt immédiate, des matériaux plutôt que des
   aplats, des mouvements qui arrivent sans rebond et repartent par où ils
   sont venus. Elle ne change aucune mise en page. */

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-optical-sizing: auto;
}
/* Un saut vers un arrêt ne s'arrête plus sous la barre collante. */
html { scroll-padding-top: calc(76px + env(safe-area-inset-top, 0px)); scroll-behavior: smooth; }

/* La barre du haut devient un matériau : translucide, floutée, le contenu
   défile dessous au lieu d'être coupé net par un aplat. */
.topbar {
  background: var(--topbar-bg);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom-color: var(--topbar-edge);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar { background: var(--surface); }
}

.card { border-color: var(--card-edge); }
.badge { border-radius: 999px; }

/* Titres : resserrés à mesure qu'ils grandissent, comme le veut la typo
   d'Apple ; les petites capitales d'en-tête, à peine espacées. */
.card-head h2 { letter-spacing: -0.015em; }
.section-title { letter-spacing: 0.04em; font-weight: 600; }

/* La réponse au doigt. Le bouton s'enfonce dès l'appui, pas au relâcher :
   sur une feuille de route, un bouton qui ne réagit pas tout de suite est un
   bouton qu'on appuie deux fois. */
button, .btn, summary.as-button {
  transition: background-color .15s var(--ease), border-color .15s var(--ease),
    color .15s var(--ease), transform .18s var(--ease), box-shadow .15s var(--ease);
}
button:active:not(:disabled), .btn:active, summary.as-button:active {
  transform: scale(.97);
  transition-duration: 60ms;
}
.link-row:active, .row-item.link-row:active { background: var(--surface-sunken); }
nav a { transition: background-color .15s var(--ease), color .15s var(--ease); }

/* Champs : un halo plutôt qu'un liseré, et la bordure prend la couleur. */
input[type=search], input[type=number], input[type=text], input[type=password],
input[type=tel], input[type=time], input[type=email], input[type=date] {
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
input:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--focus-ring);
}
button:focus-visible, a:focus-visible, summary:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px;
}

/* Fenêtres : elles arrivent comme une matière, voile flouté derrière. */
dialog {
  border-radius: 20px;
  box-shadow: 0 24px 64px rgba(0,0,0,.24), 0 2px 8px rgba(0,0,0,.08);
}
dialog::backdrop {
  background: rgba(0,0,0,.32);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
dialog[open] { animation: tg-monte .32s var(--ease) both; }
dialog[open]::backdrop { animation: tg-voile .24s ease both; }
@keyframes tg-monte { from { opacity: 0; transform: translateY(14px) scale(.98); } }
@keyframes tg-voile { from { opacity: 0; } }

/* Sur téléphone, une feuille qui monte du bas : les boutons de la livraison
   tombent sous le pouce, à la remise, la main libre tenant le bac. */
@media (max-width: 560px) {
  dialog {
    width: 100%; max-width: 100%;
    margin: auto 0 0;
    max-height: 92dvh; overflow-y: auto;
    border-radius: 22px 22px 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  dialog[open] { animation: tg-feuille .36s var(--ease) both; }
  @keyframes tg-feuille { from { transform: translateY(100%); } }
}

/* ---------- Bouton d'actualisation ----------
   Discret mais toujours là, au bout de la barre : un rond sans fond, de la
   couleur du texte secondaire, qui se teinte à l'appui et tourne pendant
   que la page revient. */
.topbar-refresh {
  flex: none; width: 36px; min-width: 36px; height: 36px; min-height: 36px;
  padding: 0; margin-left: 4px; border: 0; border-radius: 999px;
  background: transparent; color: var(--muted);
  display: inline-grid; place-items: center;
}
.topbar-inner:not(:has(nav, .topbar-tag)) .topbar-refresh { margin-left: auto; }
.topbar-refresh svg { width: 19px; height: 19px; }
@media (hover: hover) and (pointer: fine) {
  .topbar-refresh:hover { background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text-2); }
}
.topbar-refresh:active { background: color-mix(in srgb, var(--text) 10%, transparent); }
.topbar-refresh.is-spinning svg { animation: tg-tour .8s linear infinite; }

/* ---------- Pastille de compte ----------
   Une lettre dans un rond, à gauche de l'actualisation : la gestion en
   orange, chaque livreur en gris. Un appui déplie la liste des comptes. */
.account-menu { position: relative; flex: none; margin-left: 4px; }
.topbar-inner:not(:has(nav, .topbar-tag)) .account-menu { margin-left: auto; }
.topbar-inner:not(:has(nav, .topbar-tag)) .account-menu + .topbar-refresh { margin-left: 4px; }
.account-menu > summary {
  list-style: none; cursor: pointer; width: 36px; height: 36px;
  display: grid; place-items: center; border-radius: 999px;
  -webkit-user-select: none; user-select: none;
}
.account-menu > summary::-webkit-details-marker { display: none; }
.account-menu > summary:active .account-chip { transform: scale(.92); }
.account-chip {
  width: 28px; height: 28px; border-radius: 999px; flex: none;
  display: inline-grid; place-items: center;
  font-size: 13px; font-weight: 700; letter-spacing: 0;
  background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text);
  transition: transform 120ms ease-out;
}
.account-chip.is-lab { background: var(--brand-soft); color: var(--brand); }
.account-chip.is-large { width: 48px; height: 48px; font-size: 20px; }
.account-chip.has-photo { overflow: hidden; background: var(--surface-sunken); }
.account-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }
.account-menu[open] > summary .account-chip { box-shadow: 0 0 0 2px var(--brand); }
.account-panel {
  position: absolute; right: 0; top: 44px; z-index: 60; min-width: 220px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r); box-shadow: 0 12px 32px rgba(0, 0, 0, .2); padding: 6px;
}
.account-panel-title {
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); padding: 8px 10px 4px;
}
.account-item {
  display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 8px 10px;
  border-radius: 10px; color: var(--text); text-decoration: none; font-weight: 600;
}
.account-item:active { background: color-mix(in srgb, var(--text) 8%, transparent); }
@media (hover: hover) and (pointer: fine) {
  .account-item:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
}
.account-item.is-current { color: var(--brand); }
.account-manage {
  display: block; margin-top: 4px; padding: 10px; border-top: 1px solid var(--border);
  font-size: 14px; color: var(--text-2); text-decoration: none;
}
.account-manage:active { color: var(--brand); }
.account-editor { margin: 4px 0 0; padding-top: 4px; border-top: 1px solid var(--border); }
.account-editor button {
  width: 100%; justify-content: flex-start; border: 0; background: transparent; box-shadow: none;
  min-height: 40px; padding: 8px 10px; font-size: 15px; color: var(--text-2);
}
/* Fiche d'un gérant : la photo se change d'un appui, l'appareil en pastille. */
.manager-head { align-items: center; gap: 14px; }
.manager-photo { position: relative; flex: none; cursor: pointer; border-radius: 999px; }
.manager-photo .account-chip.is-large { width: 56px; height: 56px; font-size: 22px; }
.manager-photo-edit {
  position: absolute; right: -2px; bottom: -2px; width: 24px; height: 24px; border-radius: 999px;
  display: grid; place-items: center; background: var(--brand); color: #fff;
  border: 2px solid var(--surface);
}
.manager-photo-edit svg { width: 13px; height: 13px; }
.manager-photo input { position: absolute; inset: 0; opacity: 0; width: 100%; cursor: pointer; }
.manager-photo.is-busy { opacity: .5; pointer-events: none; }
.vehicle-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
/* Le modèle sur sa ligne, la plaque et les boutons dessous : côte à côte, la
   plaque se coupait au milieu sur un téléphone. */
.vehicle-row input[name="name"] { flex: 1 1 100%; min-width: 0; }
.vehicle-row input[name="plate"] { flex: 1 1 120px; min-width: 0; text-transform: uppercase; }
@media (min-width: 620px) { .vehicle-row input[name="name"] { flex: 2 1 0; } }
.vehicle-row button { flex: none; }
.vehicle-row button svg { width: 18px; height: 18px; }
.account-intro h2 { font-size: 28px; letter-spacing: -0.02em; margin: 8px 0 6px; }
.account-intro p { margin: 0; }
.account-list { display: grid; gap: 12px; margin-top: 20px; }
.account-tile {
  display: flex; align-items: center; gap: 4px; min-height: 80px; padding: 0 8px 0 0;
  background: var(--surface); border: 1px solid var(--card-edge, var(--border));
  border-radius: var(--r); color: var(--text);
  transition: transform 120ms ease-out, border-color 120ms ease-out;
}
.account-go {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 14px; padding: 16px;
  color: inherit; text-decoration: none;
}
.account-tile:has(.account-go:active) { transform: scale(.98); border-color: var(--brand); }
.account-photo {
  flex: none; width: 44px; height: 44px; border-radius: 999px; display: grid; place-items: center;
  color: var(--muted); cursor: pointer; position: relative;
}
.account-photo:active { background: var(--surface-sunken); }
.account-photo svg { width: 20px; height: 20px; }
.account-photo input { position: absolute; inset: 0; opacity: 0; width: 100%; cursor: pointer; }
.account-tile.is-busy { opacity: .5; pointer-events: none; }
.account-hint { text-align: center; margin-top: 16px; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.account-tile.is-current { border-color: var(--brand); }
.account-name { display: block; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; }
.account-role { display: block; font-size: 14px; }
@keyframes tg-tour { to { transform: rotate(360deg); } }

/* ---------- Entre deux pages ----------
   La page suivante se fait attendre : on le montre tout de suite, par
   paliers. Une barre file dès l'appui ; après un instant, la page se voile ;
   si ça dure, ses cartes prennent le reflet d'un squelette qui charge. */
.nav-progress {
  position: fixed; left: 0; right: 0; top: env(safe-area-inset-top, 0px);
  height: 3px; z-index: 100; pointer-events: none;
  background: var(--brand); border-radius: 0 3px 3px 0;
  transform-origin: 0 50%; transform: scaleX(0); opacity: 0;
}
.is-leaving .nav-progress {
  opacity: 1;
  animation: tg-progression 9s cubic-bezier(.08,.7,.15,1) forwards;
}
@keyframes tg-progression {
  0% { transform: scaleX(0); }
  8% { transform: scaleX(.4); }
  100% { transform: scaleX(.94); }
}
[data-busy] { opacity: .6; }
.is-waiting main { opacity: .6; pointer-events: none; transition: opacity .25s var(--ease); }
.is-slow main .card { position: relative; }
.is-slow main .card::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 30%,
    color-mix(in srgb, var(--surface) 75%, transparent) 50%, transparent 70%);
  background-size: 220% 100%;
  animation: tg-reflet 1.15s linear infinite;
}
@keyframes tg-reflet { from { background-position: 160% 0; } to { background-position: -60% 0; } }

/* D'une page à l'autre, un fondu bref plutôt qu'une coupure blanche ; la
   barre du haut ne bouge pas, seul le contenu change. Les navigateurs qui ne
   savent pas le faire passent simplement d'une page à l'autre, comme avant. */
@view-transition { navigation: auto; }
.topbar { view-transition-name: tg-barre; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .16s; }

/* Moins de mouvement demandé : les fondus restent, les glissements partent. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .is-leaving .nav-progress { animation: none; transform: scaleX(.6); }
  .is-slow main .card::after { animation: none; display: none; }
  .topbar-refresh.is-spinning svg { animation: none; }
  @view-transition { navigation: none; }
  button:active:not(:disabled), .btn:active, summary.as-button:active { transform: none; }
  dialog[open] { animation: tg-voile .2s ease both; }
}
/* Moins de transparence demandée : des matériaux pleins. */
@media (prefers-reduced-transparency: reduce) {
  .topbar { background: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
  dialog::backdrop { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(0,0,0,.5); }
}
/* Plus de contraste demandé : des bords nets, des aplats francs. */
@media (prefers-contrast: more) {
  :root { --card-edge: var(--border-strong); --topbar-edge: var(--border-strong); }
  .topbar { background: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ================================================================== *
 * Ordinateur
 *
 * La gestion s'ouvre aussi sur un ordinateur, et ne doit pas y être un
 * téléphone étiré : menu latéral permanent, toute la largeur, boutons et
 * textes plus grands, et des dispositions qui profitent de la place — la
 * carte à côté du déroulé, les tournées et les commandes en grille, les
 * réglages sur une colonne avec un sommaire.
 * ================================================================== */
.side-nav { display: none; }
/* Titre de page et sommaire : réservés à l'ordinateur, le téléphone garde
   sa barre du haut. */
.page-head, .page-toc { display: none; }
@media (min-width: 1024px) {
  body.has-side { --side: 248px; }
  body.has-side .side-nav {
    display: flex; flex-direction: column; gap: 4px;
    position: fixed; z-index: 15; left: 0; bottom: 0; top: 0;
    width: var(--side); padding: calc(env(safe-area-inset-top, 0px) + 96px) 14px 20px; overflow-y: auto;
    background: var(--surface); border-right: 1px solid var(--border);
  }
  .side-link {
    display: flex; align-items: center; gap: 12px; padding: 11px 14px;
    border-radius: 12px; text-decoration: none; color: var(--text-2);
    font-size: 16px; font-weight: 600; transition: background .15s ease, color .15s ease;
  }
  .side-link svg { width: 21px; height: 21px; flex: 0 0 auto; }
  .side-link[aria-current] { background: var(--brand-soft); color: var(--brand); }
  @media (hover: hover) and (pointer: fine) {
    .side-link:not([aria-current]):hover { background: var(--surface-sunken); color: var(--text); }
  }
  body.has-side .topbar-inner { max-width: none; padding-left: 24px; padding-right: 28px; }
  /* Le menu latéral remplace les onglets du haut. */
  body.has-side .topbar nav { display: none; }
  /* Le compte et l'actualisation, à droite de la barre. */
  body.has-side .account-menu { margin-left: auto; }
  body.has-side .topbar-refresh { margin-left: auto; }
  body.has-side .account-menu + .topbar-refresh { margin-left: 6px; }
  body.has-side .side-editor { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--border); }
  body.has-side .side-editor button { width: 100%; justify-content: flex-start; gap: 12px; padding: 11px 14px;
    border: 0; background: transparent; color: var(--text-2); font-size: 15px; }
  body.has-side main {
    max-width: 1360px; margin: 0 0 0 var(--side);
    padding: 28px clamp(28px, 3vw, 56px) 80px;
  }
  body.has-side main.desk-cols { max-width: none; }
  body.has-side .back-link { display: none; }
  body.has-side .page-head {
    display: block; margin: 0 0 22px; font-size: 30px; font-weight: 800; letter-spacing: -0.025em;
    line-height: 1.15;
  }
  /* Plus grand : on vise avec une souris, mais on lit de plus loin. */
  body.has-side { font-size: 16px; }
  body.has-side .card-head h2 { font-size: 19px; }
  /* Sur grand écran, un bouton garde la largeur de son libellé : étiré sur
     toute la largeur d'une carte, il paraissait démesuré à côté de son texte. */
  body.has-side .btn-row > *, body.has-side .btn-row > form > * { flex: 0 1 auto; }
  body.has-side .btn-grow { width: auto; }
  body.has-side .card-foot > button, body.has-side .card-foot > .btn,
  body.has-side .card-foot > form > button, body.has-side .card-body > form > button.primary,
  body.has-side .btn-row > button, body.has-side .btn-row > .btn, body.has-side .btn-row > form > button,
  body.has-side main > .btn.btn-grow {
    min-height: 46px; padding: 11px 22px; font-size: 15.5px;
  }
  body.has-side main > a.btn.btn-grow, body.has-side main > .btn.btn-grow { display: inline-flex; }
  body.has-side .section-title { font-size: 13.5px; margin-top: 30px; }
  body.has-side .stat b { font-size: 28px; }
  body.has-side .stat span { font-size: 14px; }
  body.has-side input, body.has-side select, body.has-side textarea { font-size: 16px; }

  /* Accueil : la grille des modules double le menu latéral, elle s'efface. */
  body.has-side .modules, body.has-side .modules-title { display: none; }
  /* Les chiffres sur une ligne ; six se rangent trois par trois tant que
     l'écran ne les tient pas tous, plutôt que cinq et un orphelin. */
  body.has-side .stats { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  body.has-side .stats > :last-child:nth-child(odd) { grid-column: auto; }
  body.has-side .stats:has(> :nth-child(6):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* Pages de réglages et de fiches : une seule colonne de lecture, à largeur
     confortable, et un sommaire collant à droite. Des colonnes « en
     cascade », aux cartes de hauteurs inégales, faisaient un patchwork. */
  body.has-side main.desk-cols { max-width: 920px; }
  main.desk-cols > .section-title { margin-top: 34px; }
  main.desk-cols > .page-head + .section-title,
  main.desk-cols > .page-head + .back-link + .section-title { margin-top: 0; }
  body.has-side main > .page-head + .back-link + .section-title,
  body.has-side main > .page-head + .section-title { margin-top: 0; }

  /* Stock : les sortes en grille, nom et compteur côte à côte. */
  .stock-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); column-gap: 28px; }
  .stock-grid > details, .stock-grid > .empty { grid-column: 1 / -1; }
  .stock-grid > .stepper-row { padding-left: var(--pad); padding-right: var(--pad); }

  /* Génération : les tournées côte à côte ; commandes : en grille. */
  .routes-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); gap: 16px; align-items: start; }
  .routes-grid > .card { margin-bottom: 0; }
  .orders-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); gap: 12px; align-items: start; }
  .orders-grid > .card { margin-bottom: 0; }

  /* Accueil : la carte de la tournée à droite des chiffres, plutôt que
     centrée seule au milieu d'une bande vide. */
  body.has-side .dash-route {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 560px);
    grid-template-areas: "head head" "stats map" "bar map" "count map" "foot map";
    grid-template-rows: auto auto auto 1fr auto;
  }
  body.has-side .dash-route > .card-head { grid-area: head; }
  body.has-side .dash-route > .stats { grid-area: stats; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.has-side .dash-route > .progress { grid-area: bar; }
  body.has-side .dash-route > .progress-count { grid-area: count; padding-bottom: var(--pad); }
  body.has-side .dash-route > .route-map { grid-area: map; padding: var(--pad); border-left: 1px solid var(--border); }
  body.has-side .dash-route > .card-foot { grid-area: foot; }

  /* Suivi : la carte reste sous les yeux pendant qu'on lit le déroulé. */
  .desk-split { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 24px; align-items: start; }
  .desk-split-side { position: sticky; top: 96px; }
  .desk-split-main > .section-title:first-child { margin-top: 0; }
}
@media (min-width: 1280px) {
  body.has-side main.desk-cols {
    max-width: 1220px; display: grid; grid-template-columns: minmax(0, 880px) 220px;
    column-gap: 56px; align-content: start;
  }
  main.desk-cols > * { grid-column: 1; min-width: 0; }
  body.has-side main.desk-cols > .page-toc {
    display: flex; flex-direction: column; gap: 2px; grid-column: 2; grid-row: 1 / span 400;
    position: sticky; top: 96px; align-self: start; padding-left: 16px; border-left: 1px solid var(--border);
  }
  .page-toc-title { font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
  .page-toc a { padding: 6px 0; font-size: 14px; color: var(--text-2); text-decoration: none; line-height: 1.3; }
  .page-toc a.is-here { color: var(--brand); font-weight: 600; }
  @media (hover: hover) and (pointer: fine) { .page-toc a:hover { color: var(--text); } }
}
@media (min-width: 1500px) {
  body.has-side .stats:has(> :nth-child(6):last-child) { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

@media print {
  .topbar, nav, button, .noprint, .side-nav { display: none !important; }
  body { background: #fff; color: #000; font-size: 12pt; }
  body.has-side main { margin: 0; }
  .card { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
}
