/* ============================================================================
   MyClickCollect — Design system de la web-app cliente
   ----------------------------------------------------------------------------
   Miroir web de l'app mobile : thème clair, vert profond, cartes arrondies.

   Ce fichier est INDÉPENDANT de css/style.css, qui habille le site vitrine B2B
   (fond sombre, glassmorphism). Les deux cohabitent tant que le site vitrine
   n'a pas migré sous /pro/ ; aucune page ne charge les deux.

   Mobile-first. La barre d'onglets basse devient une barre de navigation haute
   à partir de 1024px — une barre d'onglets flottante sur un écran large n'a
   aucun sens, et les règles d'adaptation Material demandent une navigation
   latérale ou haute au-delà de cette largeur.

   Contrastes vérifiés sur fond blanc (WCAG AA, 4.5:1 minimum pour le texte
   courant). Ne pas éclaircir --ink-500 ni --brand-600 sans revérifier.
   ============================================================================ */

/* ---------------------------------------------------------------- Tokens -- */

:root {
  color-scheme: light;

  /* Marque — vert profond relevé de la maquette.
     #33703B sur blanc = 5,9:1, donc le blanc passe en AA sur les boutons. */
  --brand-600: #33703b;
  --brand-700: #2a5d31;   /* pressé */
  --brand-500: #438a4d;
  --brand-100: #e2efe4;
  --brand-50:  #f1f7f1;

  /* Accents fonctionnels des tuiles d'accueil */
  --cream-50:  #fbeedd;
  --cream-700: #8a5a28;
  --sky-50:    #edf1f7;
  --sky-700:   #3a6690;
  --rose-50:   #fdf0ef;
  --rose-600:  #c04a48;
  --violet-50: #f3f0f9;
  --violet-600:#6d55b5;   /* sur blanc 5,7:1 */

  /* Sémantique d'état */
  --success-600: var(--brand-600);
  --success-50:  var(--brand-100);
  --danger-600:  #b3403d;
  --danger-50:   #fbeceb;
  --warning-700: #8a5a28;
  --warning-50:  var(--cream-50);

  /* Or des notes. L'ambre de maquette (#e0a32e) ne tenait que 2,1:1 sur
     --surface-2 : sous les 3:1 exigés d'un objet graphique porteur de sens
     (WCAG 1.4.11), et cinq étoiles de 14 px devenaient une trainée orange.
     Celui-ci fait 4,0:1 sur blanc et 3,7:1 sur --surface-2. */
  --star: #ad7407;

  /* Texte — ne jamais descendre sous --ink-500 pour du texte courant */
  --ink-900: #161c17;   /* titres              15,8:1 */
  --ink-700: #333b34;   /* corps               10,4:1 */
  --ink-500: #5c645d;   /* secondaire, méta     6,2:1 */
  --ink-400: #8a928b;   /* placeholder, désactivé — jamais porteur de sens */
  --on-brand: #ffffff;

  /* Surfaces */
  --surface:   #ffffff;
  --surface-2: #f6f8f5;
  --surface-3: #eef2ec;
  --border:    #e4e9e2;
  --border-strong: #ccd4ca;

  /* Crème des bandeaux illustrés — relevé au pixel dans img/bannier_site.png
     (#f8f2e6) et img/bannier_end.png (#f7f3ea, la carte arrondie). Les deux
     fichiers arrivent avec ce fond chaud : l'écran qui les porte le reprend,
     sinon le bord du PNG dessine une arête nette sur le gris-vert habituel. */
  --banner-cream: #f8f2e6;
  --banner-cream-line: #ece2cf;   /* même crème assombri — filets et bordures */
  --scrim: rgba(14, 20, 15, 0.5);   /* 50 % : isole le premier plan */

  /* Rayons */
  --r-xs: 8px;
  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-full: 999px;

  /* Ombres — échelle unique, ne pas inventer de valeurs intermédiaires */
  --sh-1: 0 1px 2px rgba(16, 24, 16, 0.06);
  --sh-2: 0 2px 8px rgba(16, 24, 16, 0.07);
  --sh-3: 0 8px 24px rgba(16, 24, 16, 0.10);
  --sh-4: 0 16px 40px rgba(16, 24, 16, 0.14);

  /* Espacement — rythme 4/8 */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-8: 32px;
  --s-10: 40px;
  --s-12: 48px;

  /* Typo */
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --fs-xs:  0.75rem;    /* 12px — libellés d'onglets et descriptions de tuiles
                           sur écran étroit ; jamais du texte courant */
  --fs-sm:  0.8125rem;  /* 13px — méta */
  --fs-base: 1rem;      /* 16px — corps. En dessous, iOS zoome les champs. */
  --fs-md:  1.0625rem;
  --fs-lg:  1.25rem;
  --fs-xl:  1.5rem;
  --fs-2xl: 1.75rem;

  /* Icônes */
  --icon-sm: 18px;
  --icon-md: 22px;
  --icon-lg: 26px;
  --icon-stroke: 2;

  /* Gabarits */
  --appbar-h: 56px;
  --tabbar-h: 68px;
  --tabbar-gap: 12px;        /* retrait de la barre flottante par rapport aux bords */
  --content-max: 640px;      /* mesure de lecture confortable en colonne */
  --content-max-wide: 1120px;

  /* Empilement — une échelle, pas des nombres au hasard */
  --z-sticky: 10;
  --z-tabbar: 40;
  --z-overlay: 100;
  --z-modal: 200;
  --z-toast: 300;

  /* Mouvement : 150–300 ms, sortie plus rapide que l'entrée */
  --t-in: 220ms cubic-bezier(0.2, 0, 0, 1);
  --t-out: 150ms cubic-bezier(0.4, 0, 1, 1);
}

/* ------------------------------------------------------------------ Base -- */

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

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

/* L'attribut `hidden` doit gagner, toujours.
   Le navigateur applique `[hidden] { display: none }` dans sa feuille par
   défaut — mais toute règle d'auteur la bat, quelle que soit la spécificité.
   Or beaucoup de nos composants imposent un display (`.stack`, `.empty`,
   `.meta`, les grilles…) : sans cette ligne, masquer un squelette ou un état
   vide en JavaScript ne produit tout simplement rien. C'est l'un des rares
   cas où `!important` est la bonne réponse et non un raccourci. */
[hidden] { display: none !important; }

body {
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--ink-700);
  background: var(--surface-2);
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

h1, h2, h3, h4 { color: var(--ink-900); line-height: 1.25; font-weight: 700; }
h1 { font-size: var(--fs-2xl); letter-spacing: -0.02em; }
h2 { font-size: var(--fs-xl); letter-spacing: -0.01em; }
h3 { font-size: var(--fs-md); }

a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
img { display: block; max-width: 100%; }

button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: none; cursor: pointer; }

/* Anneau de focus visible partout — jamais supprimé, seulement stylé. */
:focus-visible {
  outline: 3px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

::selection { background: var(--brand-100); color: var(--ink-900); }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Prix, quantités, compteurs : chiffres de largeur fixe, sinon la colonne
   de droite tressaute à chaque mise à jour. */
.num, .price, .rating-value { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- Icônes -- */

.icon {
  width: var(--icon-md);
  height: var(--icon-md);
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon--sm { width: var(--icon-sm); height: var(--icon-sm); }
.icon--lg { width: var(--icon-lg); height: var(--icon-lg); }

/* ---------------------------------------------------------- Structure ----- */

.app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.container {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--s-4);
}

/* Le contenu réserve la hauteur de la barre d'onglets + l'encoche basse,
   sinon la dernière carte de la liste passe dessous. */
.screen-body {
  flex: 1;
  padding-top: var(--s-4);
  /* La barre flotte : il faut lui réserver sa hauteur *et* son retrait bas,
     sinon la dernière carte de la liste passe dessous. */
  padding-bottom: calc(var(--tabbar-h) + var(--tabbar-gap)
                       + env(safe-area-inset-bottom, 0px) + var(--s-6));
}

/* Écrans sans barre d'onglets (connexion) : rien à réserver en bas. */
.screen-body--plain { padding-bottom: var(--s-10); }

.section { margin-bottom: var(--s-6); }

.section-title {
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink-900);
  margin-bottom: var(--s-3);
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
}

.divider { height: 1px; background: var(--border); border: 0; }

.stack   { display: flex; flex-direction: column; gap: var(--s-3); }
.stack-2 { display: flex; flex-direction: column; gap: var(--s-2); }
.row     { display: flex; align-items: center; gap: var(--s-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.grow { flex: 1; min-width: 0; }

.muted { color: var(--ink-500); font-size: var(--fs-sm); }

/* ------------------------------------------------------------- Barre haute */

.appbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  min-height: var(--appbar-h);
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  padding-top: calc(var(--s-2) + env(safe-area-inset-top, 0px));
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.appbar-title {
  flex: 1;
  text-align: center;
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink-900);
  /* Titre long : on tronque plutôt que de casser la hauteur de la barre.
     Le titre complet reste dans le <title> du document. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Contrepoids invisible : garde le titre optiquement centré quand il n'y a
   qu'un bouton retour à gauche. */
.appbar-spacer { width: 44px; flex: none; }

.appbar--brand { justify-content: space-between; }
.appbar--brand .appbar-title { text-align: left; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-weight: 800;
  font-size: var(--fs-md);
  color: var(--ink-900);
  letter-spacing: -0.01em;
}

.brand-mark {
  width: 30px; height: 30px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-xs);
  background: var(--brand-600);
  color: var(--on-brand);
}
.brand-mark .icon { width: 18px; height: 18px; }
.brand em { font-style: normal; color: var(--brand-600); }

/* --------------------------------------------------------------- Boutons -- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;               /* ≥ 44px : cible tactile */
  padding: 0 var(--s-5);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
  font-weight: 600;
  text-align: center;
  cursor: pointer;
  transition: background var(--t-out), color var(--t-out), border-color var(--t-out);
}

.btn--primary { background: var(--brand-600); color: var(--on-brand); }
.btn--primary:hover { background: var(--brand-700); }

.btn--secondary {
  background: var(--surface);
  color: var(--ink-900);
  border: 1px solid var(--border-strong);
}
.btn--secondary:hover { background: var(--surface-2); }

.btn--ghost { color: var(--brand-600); padding-inline: var(--s-3); }
.btn--ghost:hover { background: var(--brand-50); }

/* Action destructive : couleur sémantique + séparation visuelle. */
.btn--danger { background: var(--danger-50); color: var(--danger-600); }
.btn--danger:hover { background: #f6dcda; }

.btn--block { display: flex; width: 100%; }
/* 44px et pas moins : c'est le plancher tactile, y compris pour un bouton
   « petit ». Seule la typo et le calage horizontal se resserrent. */
.btn--sm { min-height: 44px; font-size: var(--fs-sm); padding-inline: var(--s-4); }

/* Retour visuel à l'appui : uniquement transform, aucun décalage de flux. */
.btn:active:not(:disabled) { transform: scale(0.98); }

.btn:disabled,
.btn[aria-disabled='true'] {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* Bouton icône seul — exige toujours un aria-label. */
.icon-btn {
  width: 44px; height: 44px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  color: var(--ink-700);
  transition: background var(--t-out), color var(--t-out);
}
.icon-btn:hover { background: var(--surface-3); }
.icon-btn--onbrand { color: var(--on-brand); }
.icon-btn--onbrand:hover { background: rgba(255, 255, 255, 0.18); }

/* Cœur favori : l'état actif ne se lit pas qu'à la couleur, l'icône se
   remplit — le seul rouge ne suffirait pas en vision daltonienne. */
.fav-btn { color: var(--ink-500); }
.fav-btn[aria-pressed='true'] { color: var(--rose-600); }
.fav-btn[aria-pressed='true'] .icon { fill: currentColor; }

/* ------------------------------------------------------------------ Cartes */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}

.card--flat { box-shadow: none; }
.card-body { padding: var(--s-4); }

/* Carte cliquable : élévation à l'appui, jamais de changement de taille de
   boîte qui décalerait les voisines. */
a.card, button.card {
  display: block;
  width: 100%;
  text-align: left;
  transition: box-shadow var(--t-out), border-color var(--t-out);
}
a.card:hover, button.card:hover { box-shadow: var(--sh-2); border-color: var(--border-strong); }
a.card:active, button.card:active { box-shadow: var(--sh-1); }

/* ========================================================================== */
/* Tuiles de navigation de l'accueil                                          */
/* -------------------------------------------------------------------------- */
/* Une pastille ronde en dégradé, un titre, une phrase, un chevron. Les quatre
   tuiles partagent le même corps de règle : seule la variante change, et elle
   ne pose que des couleurs. Ajouter une destination = une ligne de variante.

   La pastille porte une lueur de sa propre teinte plutôt qu'une ombre grise —
   c'est ce qui lui donne son relief. Elle est décorative : le titre porte le
   sens, l'icône est en aria-hidden, et le contraste du blanc sur le dégradé
   n'a donc pas à tenir les 4,5:1 d'un texte.                                 */

.nav-card,
.tile {
  --accent: var(--brand-600);          /* chevron, texte d'appui */
  --accent-bg: #f2f7ec;                /* fond de la tuile */
  --accent-a: #7cb463;                 /* haut du dégradé de pastille */
  --accent-b: #4a8c3e;                 /* bas du dégradé */
  --accent-glow: rgba(74, 140, 62, 0.45);
}

.tile--cream {
  --accent: var(--cream-700);
  --accent-bg: #fdf1e6;
  --accent-a: #f8ae6d; --accent-b: #e5843c;
  --accent-glow: rgba(229, 132, 60, 0.45);
}
.tile--sky {
  --accent: var(--sky-700);
  --accent-bg: #eff3f8;
  --accent-a: #93bdea; --accent-b: #5a8fc9;
  --accent-glow: rgba(90, 143, 201, 0.45);
}
.tile--violet {
  --accent: var(--violet-600);
  --accent-bg: var(--violet-50);
  --accent-a: #b79ee3; --accent-b: #8a6cc8;
  --accent-glow: rgba(138, 108, 200, 0.45);
}

/* Pastille ronde partagée par la grande carte et les tuiles */
.tile-badge {
  width: 44px; height: 44px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: linear-gradient(150deg, var(--accent-a), var(--accent-b));
  color: #fff;
  box-shadow: 0 6px 14px -6px var(--accent-glow),
              inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.tile-badge .icon { width: 22px; height: 22px; }
.tile-badge--lg { width: 56px; height: 56px; }
.tile-badge--lg .icon { width: 26px; height: 26px; }

/* Chevron d'appel : il reprend la couleur de la tuile, jamais le gris. */
.tile-go { color: var(--accent); flex: none; }

/* -- Grande carte pleine largeur (« Là ») -- */
.nav-card {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  border-radius: var(--r-lg);
  background: #fbfdf8;
  border: 1px solid rgba(255, 255, 255, 0.9);
  box-shadow: var(--sh-2);
  transition: box-shadow var(--t-out);
}
.nav-card:hover { box-shadow: var(--sh-3); }
/* Tout sauf le plan passe devant lui. La négation, plutôt qu'un `position`
   posé après, évite que l'ordre des règles décide du résultat. */
.nav-card > :not(.nav-card-map) { position: relative; }

.nav-card-text { min-width: 0; flex: 1; }
.nav-card h3 { font-size: var(--fs-lg); }
.nav-card p { font-size: var(--fs-sm); color: var(--ink-500); margin-top: var(--s-1); }

/* Plan stylisé en fond, côté chevron. Le masque le dissout vers la gauche :
   sans lui, son bord gauche ferait une arête au milieu de la carte. */
.nav-card-map {
  position: absolute;
  top: 0; bottom: 0; right: var(--s-6);
  width: 36%;
  max-width: 132px;
  pointer-events: none;
  opacity: 0.5;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 72%);
          mask-image: linear-gradient(to right, transparent, #000 72%);
}
.nav-card-map svg { width: 100%; height: 100%; }

/* -- Grille de tuiles -- */
/* `minmax(0, 1fr)` et non `1fr` : la valeur par défaut d'une piste est
   `minmax(auto, 1fr)`, donc une tuile refuse de descendre sous la largeur de
   son contenu et sort de sa colonne. Les deux colonnes sont alors inégales. */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3);
}

.tile {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  padding: var(--s-3);
  border-radius: var(--r-md);
  background: var(--accent-bg);
  border: 1px solid rgba(255, 255, 255, 0.85);
  box-shadow: var(--sh-1);
  transition: box-shadow var(--t-out);
}
.tile:hover { box-shadow: var(--sh-2); }

/* `min-width: 0` : sans lui, un mot long de la description refuse de se
   couper et pousse la tuile hors de sa colonne de grille. */
.tile-text { min-width: 0; flex: 1; }
.tile p { color: var(--ink-500); margin-top: var(--s-1); }

/* Le chevron des tuiles se range en bas à droite, sous la description. */
.tile .tile-go { display: block; margin-left: auto; margin-top: var(--s-2); }

/* Deux tuiles côte à côte ne laissent qu'une centaine de pixels au texte : le
   titre y tient en --fs-sm, pas en --fs-base — « Mes commandes » réclame
   106 px à 13 px, et une fois et quart plus à 16 px. C'est aussi la
   proportion de la maquette, où le titre fait environ la moitié de la
   largeur de sa tuile. */
.tile h3 { font-size: var(--fs-sm); }
.tile p { font-size: var(--fs-xs); }
.tile .tile-badge { width: 36px; height: 36px; }
.tile .tile-badge .icon { width: 18px; height: 18px; }

/* Dès qu'il y a la place, la pastille et la phrase reprennent leur taille. */
@media (min-width: 420px) {
  .tile { gap: var(--s-3); }
  .tile .tile-badge { width: 44px; height: 44px; }
  .tile .tile-badge .icon { width: 22px; height: 22px; }
  .tile p { font-size: var(--fs-sm); }
}

.tile-icon {
  width: 40px; height: 40px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
}
.tile-icon--rose { background: var(--rose-50); color: var(--rose-600); }
.tile-icon--sky  { background: var(--sky-50);  color: var(--sky-700); }
.tile-icon--brand{ background: var(--brand-100); color: var(--brand-600); }

/* Carte promo crème */
.promo-card {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4);
  border-radius: var(--r-md);
  background: var(--cream-50);
}
.promo-card h3 { font-size: var(--fs-base); color: var(--cream-700); }
.promo-card p { font-size: var(--fs-sm); color: var(--cream-700); opacity: 0.85; }

/* ----------------------------------------------------------- Champs & form */

.field { display: flex; flex-direction: column; gap: var(--s-2); }

/* Libellé toujours visible : un placeholder disparaît à la saisie et laisse
   l'utilisateur sans repère. */
.field-label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-700);
}
.field-label .req { color: var(--danger-600); margin-left: 2px; }

.input {
  width: 100%;
  min-height: 48px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  font-size: var(--fs-base);   /* 16px : empêche le zoom auto d'iOS */
  transition: border-color var(--t-out), box-shadow var(--t-out);
}
.input::placeholder { color: var(--ink-400); }
.input:focus {
  outline: none;
  border-color: var(--brand-600);
  box-shadow: 0 0 0 3px var(--brand-100);
}
.input[aria-invalid='true'] { border-color: var(--danger-600); }
.input:disabled { background: var(--surface-2); color: var(--ink-400); cursor: not-allowed; }
/* Lecture seule ≠ désactivé : reste lisible et sélectionnable. */
.input[readonly] { background: var(--surface-2); color: var(--ink-700); }

.input-group { position: relative; }
.input-group .input { padding-right: 52px; }
.input-group .icon-btn { position: absolute; right: 2px; top: 50%; transform: translateY(-50%); }

.field-hint { font-size: var(--fs-sm); color: var(--ink-500); }

/* Case à cocher : toute la ligne est cliquable, le libellé compris — viser un
   carré de 18px au pouce est une cible trop petite sur mobile. */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  font-size: var(--fs-sm);
  color: var(--ink-700);
  cursor: pointer;
}
.check input[type='checkbox'] {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  accent-color: var(--brand-600);
  cursor: pointer;
}

/* Erreur : sous le champ concerné, avec icône — pas seulement en rouge. */
.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  font-size: var(--fs-sm);
  color: var(--danger-600);
}
.field-error .icon { width: 16px; height: 16px; margin-top: 2px; }

.form-actions { display: flex; flex-direction: column; gap: var(--s-3); margin-top: var(--s-5); }

.form-sep {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--ink-500);
  font-size: var(--fs-sm);
}
.form-sep::before,
.form-sep::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* Barre de recherche */
.searchbar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 48px;
  padding: 0 var(--s-4);
  border-radius: var(--r-full);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  color: var(--ink-500);
}
.searchbar:focus-within { border-color: var(--brand-600); box-shadow: 0 0 0 3px var(--brand-100); }
/* Le champ occupe toute la hauteur de la barre : sans ça, seule une bande de
   24px au centre est réellement cliquable, et le reste de la barre ne donne
   pas le focus. */
.searchbar input {
  flex: 1;
  align-self: stretch;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font-size: var(--fs-base);
  color: var(--ink-900);
}

/* ---------------------------------------------------------------- Pilules -- */

/* Défilement horizontal volontaire et borné à la bande de filtres : le corps
   de page, lui, ne défile jamais horizontalement. */
.chips {
  display: flex;
  gap: var(--s-2);
  overflow-x: auto;
  padding-block: var(--s-1);
  scrollbar-width: none;
}
.chips::-webkit-scrollbar { display: none; }

.chip {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  padding: 0 var(--s-4);
  border-radius: var(--r-full);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--ink-700);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--t-out), border-color var(--t-out), color var(--t-out);
}
.chip:hover { background: var(--surface-2); }
.chip[aria-pressed='true'] {
  background: var(--brand-600);
  border-color: var(--brand-600);
  color: var(--on-brand);
}

/* ---------------------------------------------------------------- Onglets -- */

.tabs {
  display: flex;
  gap: var(--s-5);
  border-bottom: 1px solid var(--border);
}

.tab {
  position: relative;
  min-height: 48px;
  padding: 0 var(--s-1);
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink-500);
}
.tab[aria-selected='true'] { color: var(--brand-600); }
.tab[aria-selected='true']::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  bottom: -1px;
  height: 2px;
  background: var(--brand-600);
  border-radius: 2px 2px 0 0;
}

/* ---------------------------------------------------------- Fiche boutique */

.shop-card { display: flex; gap: var(--s-3); padding: var(--s-3); }

.thumb {
  width: 64px; height: 64px;
  flex: none;
  border-radius: var(--r-sm);
  object-fit: cover;
  background: var(--surface-3);
}
.thumb--lg { width: 80px; height: 80px; }
.thumb--sm { width: 44px; height: 44px; border-radius: var(--r-xs); }

/* Repli quand le commerçant n'a pas de logo, ou que l'image casse : une
   initiale plutôt qu'un rectangle vide ou une icône d'image brisée. */
.thumb-fallback {
  display: grid;
  place-items: center;
  background: var(--brand-100);
  color: var(--brand-700);
  font-weight: 700;
  font-size: var(--fs-lg);
  object-fit: initial;
}
.thumb-fallback.thumb--sm { font-size: var(--fs-base); }

.shop-name {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--ink-900);
}
.shop-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.verified { color: var(--brand-600); }
.verified .icon { width: 16px; height: 16px; }

.meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-2);
  font-size: var(--fs-sm);
  color: var(--ink-500);
  margin-top: 2px;
}
.meta .sep { color: var(--border-strong); }

.rating { display: inline-flex; align-items: center; gap: var(--s-1); color: var(--ink-700); }
/* 16 px et pas de contour : l'étoile du sprite est une forme pleine, et un
   stroke de 2 unités à cette taille arrondit les cinq pointes jusqu'à fermer
   la silhouette. Pleine ou vide, jamais une nuance d'opacité — un demi-ton
   d'or ne se distingue pas du fond. */
.rating .icon { width: 16px; height: 16px; fill: var(--star); stroke: none; }
.rating .icon--empty { fill: none; stroke: var(--ink-400); stroke-width: 2; }

/* Ouvert / fermé : point coloré + libellé, jamais le point seul. */
.status { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--fs-sm); font-weight: 600; }
.status::before { content: ''; width: 7px; height: 7px; border-radius: var(--r-full); background: currentColor; }
.status--open   { color: var(--success-600); }
.status--closed { color: var(--ink-500); }

/* ------------------------------------------------------------------ Listes */

.list { display: flex; flex-direction: column; }

.list-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  min-height: 64px;
  padding: var(--s-3) var(--s-4);
  text-align: left;
  border-bottom: 1px solid var(--border);
  transition: background var(--t-out);
}
.list-row:last-child { border-bottom: none; }
.list-row:hover { background: var(--surface-2); }

.row-title { font-weight: 600; color: var(--ink-900); }
.row-sub { font-size: var(--fs-sm); color: var(--ink-500); }
.chevron { color: var(--ink-400); }

/* -------------------------------------------------------------- Commandes */

/* Badge d'état : couleur + mot. La couleur seule ne porte jamais le sens. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 3px var(--s-3);
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.01em;
}
.badge--done      { background: var(--success-50); color: var(--brand-700); }
.badge--pending   { background: var(--warning-50); color: var(--warning-700); }
.badge--cancelled { background: var(--danger-50);  color: var(--danger-600); }
.badge--info      { background: var(--sky-50);     color: var(--sky-700); }

/* Bandeau d'état en tête du détail de commande */
.order-hero {
  position: relative;
  overflow: hidden;
  padding: var(--s-5);
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, var(--brand-600), var(--brand-500));
  color: var(--on-brand);
}
.order-hero .badge {
  background: rgba(255, 255, 255, 0.2);
  color: var(--on-brand);
  font-size: var(--fs-sm);
}
.order-hero p { color: rgba(255, 255, 255, 0.9); font-size: var(--fs-sm); margin-top: var(--s-2); }

.summary { display: flex; flex-direction: column; }

.summary-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--border);
}
.summary-row:last-child { border-bottom: none; }

.summary-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-top: var(--s-4);
  font-size: var(--fs-lg);
  font-weight: 800;
  color: var(--ink-900);
}

.qty { font-size: var(--fs-sm); color: var(--ink-500); }
.price { font-weight: 600; color: var(--ink-900); white-space: nowrap; }

/* --------------------------------------------------------------- Filtres -- */

.filter-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-3);
}

.filter-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-1);
  border-radius: var(--r-sm);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-700);
  text-align: center;
}
.filter-tile .tile-icon { width: 44px; height: 44px; border-radius: var(--r-full); }
.filter-tile[aria-pressed='true'] { color: var(--brand-700); }
.filter-tile[aria-pressed='true'] .tile-icon {
  background: var(--brand-600);
  color: var(--on-brand);
}

/* Ligne de choix — la zone cliquable couvre toute la ligne, pas juste le rond */
.radio-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  width: 100%;
  min-height: 48px;
  padding: var(--s-2) 0;
  text-align: left;
  font-weight: 500;
  color: var(--ink-700);
}
.radio-mark {
  width: 22px; height: 22px;
  flex: none;
  border-radius: var(--r-full);
  border: 2px solid var(--border-strong);
  display: grid;
  place-items: center;
}
.radio-row[aria-checked='true'] { color: var(--ink-900); font-weight: 600; }
.radio-row[aria-checked='true'] .radio-mark { border-color: var(--brand-600); }
.radio-row[aria-checked='true'] .radio-mark::after {
  content: '';
  width: 11px; height: 11px;
  border-radius: var(--r-full);
  background: var(--brand-600);
}

/* Curseur de distance */
.range {
  width: 100%;
  height: 44px;                /* cible tactile, même si la piste est fine */
  background: none;
  -webkit-appearance: none;
  appearance: none;
}
.range::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: var(--r-full);
  background: var(--surface-3);
}
.range::-moz-range-track {
  height: 4px;
  border-radius: var(--r-full);
  background: var(--surface-3);
}
.range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px; height: 22px;
  margin-top: -9px;
  border-radius: var(--r-full);
  background: var(--brand-600);
  border: 3px solid var(--surface);
  box-shadow: var(--sh-2);
}
.range::-moz-range-thumb {
  width: 22px; height: 22px;
  border-radius: var(--r-full);
  background: var(--brand-600);
  border: 3px solid var(--surface);
  box-shadow: var(--sh-2);
}

/* -------------------------------------------------------- Barre d'onglets */

/* 5 entrées maximum, icône + libellé. Une navigation en icônes seules se
   devine mal et fait chuter la découvrabilité. */
/* Barre flottante : détachée des bords, arrondie, posée sur le contenu par son
   ombre plutôt que séparée de lui par un filet. L'encoche basse est absorbée
   par le décalage `bottom`, pas par la hauteur — la barre garde donc la même
   taille sur un téléphone à encoche et sur un autre. */
.tabbar {
  position: fixed;
  inset-inline: var(--tabbar-gap);
  bottom: calc(var(--tabbar-gap) + env(safe-area-inset-bottom, 0px));
  /* Bornée et centrée : étirée sur toute une tablette, elle disperserait cinq
     onglets sur 900 px et le pouce n'atteindrait plus rien. */
  max-width: 460px;
  margin-inline: auto;
  z-index: var(--z-tabbar);
  display: flex;
  align-items: flex-start;
  justify-content: space-around;
  /* Pas d'espace entre les onglets : la largeur va aux libellés, qui en
     manquent. Ils restent séparés par leur propre centrage. */
  gap: 0;
  height: var(--tabbar-h);
  background: var(--surface);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3);
}

.tabbar-item {
  flex: 1;
  min-width: 0;
  min-height: var(--tabbar-h);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: var(--s-1) 0;
  /* 12px et non 13 : à cinq entrées sur un écran de 375 px, chaque onglet ne
     dispose que d'environ 70 px et « Mes favoris » se coupe dès 13 px. */
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-500);
  transition: color var(--t-out);
}

/* Le trait sous l'onglet actif. Il est posé sur *tous* les onglets, incolore
   ailleurs : sinon l'apparition du trait décalerait le libellé vers le haut
   à chaque changement d'onglet. */
.tabbar-item::after {
  content: '';
  width: 22px;
  height: 3px;
  border-radius: var(--r-full);
  background: transparent;
}
.tabbar-item span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Destination réservée aux comptes, visiteur non connecté. Grisée et inerte :
   elle dit ce qu'apporte un compte au lieu de mener à un écran vide. */
.tabbar-item[aria-disabled='true'],
.tile[aria-disabled='true'] {
  color: var(--ink-400);
  cursor: default;
}
.tile[aria-disabled='true'] { box-shadow: none; opacity: 0.72; }
.tile[aria-disabled='true']:hover { box-shadow: none; }
.tile[aria-disabled='true'] h3 { color: var(--ink-500); }
/* Le chevron perd sa couleur d'accent : coloré, il continuerait d'appeler. */
.tile[aria-disabled='true'] .tile-go { color: var(--ink-400); }

.tabbar-item[aria-current='page'] { color: var(--brand-600); }
.tabbar-item[aria-current='page'] .icon { stroke-width: 2.4; }
.tabbar-item[aria-current='page']::after { background: var(--brand-600); }

/* Bouton scan central. Il déborde du haut de la barre d'un quart de son
   diamètre : posé plus haut il se détacherait de la navigation, posé dedans
   il cesserait d'être le geste principal. Pas d'anneau blanc — sur une barre
   flottante il n'a rien à découper, c'est son ombre qui le décolle. */
.tabbar-fab {
  flex: none;
  width: 58px; height: 58px;
  margin-top: -14px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--brand-600);
  color: var(--on-brand);
  box-shadow: 0 10px 20px -8px rgba(26, 62, 31, 0.55);
  transition: background var(--t-out);
}
.tabbar-fab:hover { background: var(--brand-700); }
.tabbar-fab:active { transform: scale(0.96); }

/* ---------------------------------------------------------- QR de retrait */

.qr-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  text-align: center;
}

/* Fond blanc et marge imposés : un QR posé sur une surface teintée ou collé
   au bord perd son contraste et sa zone de silence, et les lecteurs décrochent. */
.qr-image {
  width: 220px;
  max-width: 100%;
  height: auto;
  padding: var(--s-3);
  border-radius: var(--r-md);
  background: #fff;
  border: 1px solid var(--border);
}

/* --------------------------------------------------- Bandeaux illustrés -- */

/* Les deux PNG de l'accueil sont livrés avec leur propre fond crème. Plutôt
   que de les poser sur le gris-vert de la page — ce qui laisserait une arête
   au millimètre près du bord du fichier — l'écran entier passe au crème du
   PNG. Les cartes blanches et le vert de marque tiennent le contraste dessus
   (blanc sur #f8f2e6 reste lisible, --ink-500 y garde 5,9:1). */
.screen-body--banners {
  background: var(--banner-cream);
  padding-top: 0;   /* le bandeau haut commence au pixel zéro de l'écran */
}

.page-banner {
  /* Le fond derrière l'image, pour les largeurs où elle est bornée. */
  background: var(--banner-cream);
}

/* `<picture>` est en ligne par défaut : sans ça, l'image blocée à l'intérieur
   n'a plus de bloc conteneur à sa mesure et le centrage `auto` ne prend pas. */
.page-banner picture { display: block; }

.page-banner img {
  /* Bornée en largeur : au-delà, un bandeau de 2600 px s'étire sur toute la
     fenêtre et le sujet devient minuscule au centre. */
  width: 100%;
  max-width: var(--content-max-wide);
  height: auto;
  margin-inline: auto;
}

.page-banner--top { margin-bottom: var(--s-5); }

/* Bandeau de pied : le PNG est transparent autour d'une carte crème arrondie.
   Il se cadre donc sur la mesure du contenu, comme une carte de plus. */
.page-banner--end {
  background: none;
  margin-top: var(--s-8);
  padding-inline: var(--s-4);
}
.page-banner--end img { max-width: var(--content-max); }

/* Le filet du pied de page suit le crème, pas le gris-vert. */
.screen-body--banners .app-footer { border-top-color: var(--banner-cream-line); }

/* ------------------------------------------------------------ Pied de page */

/* Se place à la fin du contenu défilant. La réserve de `.screen-body` pour la
   barre d'onglets s'applique déjà, il n'y a donc rien à ajouter en bas. */
.app-footer {
  margin-top: var(--s-8);
  padding-top: var(--s-5);
  border-top: 1px solid var(--border);
}

.app-footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s-2);
  margin-bottom: var(--s-2);
}

/* Le rembourrage vertical porte la cible tactile à 44px sans écarter
   visuellement les liens : sur mobile ils sont serrés et se ratent sinon. */
.app-footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--s-1);
  font-size: var(--fs-sm);
  color: var(--ink-500);
}
.app-footer-links a:hover { color: var(--brand-600); text-decoration: underline; }

.app-footer-legal {
  font-size: var(--fs-sm);
  color: var(--ink-400);
  line-height: 1.6;
}

.app-footer-pro {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--brand-600);
}
.app-footer-pro:hover { text-decoration: underline; }

/* ------------------------------------------------------------ Texte long -- */

/* Pages légales : mesure de lecture bornée, hiérarchie lisible. */
.prose { max-width: 68ch; }
.prose h2 {
  font-size: var(--fs-lg);
  margin-top: var(--s-8);
  margin-bottom: var(--s-3);
}
.prose h3 {
  font-size: var(--fs-base);
  margin-top: var(--s-5);
  margin-bottom: var(--s-2);
}
.prose p, .prose li { color: var(--ink-700); line-height: 1.7; }
.prose p { margin-bottom: var(--s-3); }
.prose ul { margin: 0 0 var(--s-3) var(--s-5); list-style: disc; }
.prose li { margin-bottom: var(--s-2); }
.prose a { color: var(--brand-600); text-decoration: underline; }
.prose strong { color: var(--ink-900); }

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--s-4);
  font-size: var(--fs-sm);
}
.prose th, .prose td {
  text-align: left;
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.prose th { color: var(--ink-900); font-weight: 700; }

/* Un tableau ne doit jamais élargir la page : il défile dans sa propre boîte. */
.table-scroll { overflow-x: auto; }

/* Trou à combler avant mise en ligne — volontairement voyant, pour qu'aucune
   mention manquante ne parte en production sans se faire remarquer. */
.todo {
  display: inline-block;
  padding: 2px var(--s-2);
  border-radius: var(--r-xs);
  background: var(--cream-50);
  border: 1px dashed var(--cream-700);
  color: var(--cream-700);
  font-size: var(--fs-sm);
  font-weight: 600;
}

/* --------------------------------------------------------- États vides ---- */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-3);
  padding: var(--s-10) var(--s-5);
}
.empty-icon {
  width: 64px; height: 64px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--surface-3);
  color: var(--ink-500);
}
.empty h3 { font-size: var(--fs-md); }
.empty p { color: var(--ink-500); font-size: var(--fs-sm); max-width: 34ch; }

/* Squelettes de chargement — préférés au spinner au-delà de ~1 s, et surtout
   ils réservent la place, donc aucun saut de mise en page à l'arrivée. */
.skeleton {
  border-radius: var(--r-xs);
  background: linear-gradient(90deg, var(--surface-3) 25%, #f4f7f3 37%, var(--surface-3) 63%);
  background-size: 400% 100%;
  animation: skeleton 1.4s ease infinite;
}
.skeleton--text { height: 12px; }
.skeleton--title { height: 16px; width: 60%; }
.skeleton--thumb { width: 64px; height: 64px; border-radius: var(--r-sm); }

@keyframes skeleton {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}

/* -------------------------------------------------------------------- Carte */
/* Fond de carte glissant de l'écran Là — cf. js/map.js. Les tuiles et
   les repères sont positionnés par transform depuis le JavaScript ; tout ce
   qui suit ne fait que les habiller. Ne pas donner de `transform` à .map-pin
   ni à .map-tile ici : il serait écrasé à chaque rendu. */

.map {
  position: relative;
  overflow: hidden;
  height: clamp(220px, 40dvh, 380px);
  border-radius: var(--r-lg);
  background: var(--surface-3);
  box-shadow: var(--sh-1);
  /* Le glissement pilote la carte : sans ça, un doigt sur la carte fait
     défiler la page derrière elle. */
  touch-action: none;
  cursor: grab;
  user-select: none;
}
.map.is-dragging { cursor: grabbing; }

.map-plane,
.map-pins { position: absolute; inset: 0; }
.map-pins { pointer-events: none; }

.map-tile {
  position: absolute;
  top: 0; left: 0;
  width: 256px; height: 256px;
  max-width: none;          /* la règle globale img{max-width:100%} écraserait */
  will-change: transform;
}

/* Disque du rayon de recherche. Sa taille et sa position viennent du
   JavaScript — comme les tuiles et les repères, ne pas lui donner de
   `transform` ici. Le remplissage reste très pâle : il couvre parfois tout
   l'écran, et doit rester lisible par-dessus. */
.map-radius {
  position: absolute;
  top: 0; left: 0;
  border-radius: var(--r-full);
  border: 2px solid rgba(51, 112, 59, 0.5);
  background: rgba(51, 112, 59, 0.09);
  pointer-events: none;
  will-change: transform;
}

/* Sur la vue aérienne, un vert pâle sur des toits sombres disparaît : le trait
   passe au blanc, qui tient sur n'importe quelle photo. */
.map--satellite .map-radius {
  border-color: rgba(255, 255, 255, 0.85);
  background: rgba(255, 255, 255, 0.14);
}

.map-pin {
  position: absolute;
  top: 0; left: 0;
  /* La pastille pointe le lieu par sa pointe basse, d'où le décalage. */
  margin: -42px 0 0 -16px;
  width: 32px; height: 32px;
  display: grid;
  place-items: center;
  pointer-events: auto;
  border-radius: var(--r-full);
  border: 2px solid var(--surface);
  background: var(--brand-600);
  color: var(--on-brand);
  font-size: var(--fs-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--sh-2);
}
.map-pin::after {
  content: '';
  position: absolute;
  bottom: -8px;
  border: 5px solid transparent;
  border-top-color: var(--surface);
}
/* Sélection signalée par un anneau, jamais par un `transform` — celui-ci
   porte la position du repère et serait écrasé au rendu suivant. */
.map-pin.is-selected {
  background: var(--ink-900);
  box-shadow: 0 0 0 4px var(--brand-100), var(--sh-3);
  z-index: 2;
}

.map-pin--me {
  margin: -7px 0 0 -7px;
  width: 14px; height: 14px;
  border: 2px solid var(--surface);
  background: var(--sky-700);
  box-shadow: 0 0 0 6px rgba(58, 102, 144, 0.18);
}
.map-pin--me::after { content: none; }

.map-controls {
  position: absolute;
  top: var(--s-3);
  right: var(--s-3);
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow: hidden;
  border-radius: var(--r-xs);
  box-shadow: var(--sh-2);
}
.map-control {
  width: 36px; height: 36px;
  background: var(--surface);
  color: var(--ink-900);
  font-size: var(--fs-lg);
  line-height: 1;
}
.map-control:hover { background: var(--surface-2); }

/* Bascule plan / satellite. En bas à gauche : les commandes de zoom tiennent
   le haut à droite et l'attribution le bas à droite. */
.map-layer-btn {
  position: absolute;
  left: var(--s-3);
  bottom: var(--s-3);
  min-height: 36px;
  padding: 0 var(--s-3);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink-900);
  font-size: var(--fs-sm);
  font-weight: 600;
  box-shadow: var(--sh-2);
}
.map-layer-btn:hover { background: var(--surface-2); }

.map-attribution {
  position: absolute;
  right: 0; bottom: 0;
  padding: 2px var(--s-2);
  border-top-left-radius: var(--r-xs);
  background: rgba(255, 255, 255, 0.82);
  color: var(--ink-500);
  font-size: var(--fs-xs);
}
.map-attribution:hover { text-decoration: underline; }

/* Sur la vue aérienne, le fond est sombre et bavard : les repères ont besoin
   d'un contour plus net, et l'attribution d'un fond plus franc pour rester
   lisible au-dessus des toits. */
.map--satellite .map-pin { border-width: 3px; }
.map--satellite .map-pin--me { box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.45); }
.map--satellite .map-attribution { background: rgba(255, 255, 255, 0.92); }

/* ----------------------------------------------------------- Fiche boutique */

/* Photo de devanture en tête de fiche. Rapport imposé côté serveur (3:2) —
   `aspect-ratio` ici ne fait que réserver la place avant le chargement, pour
   que le contenu ne saute pas sous le doigt. */
.cover {
  width: 100%;
  /* `height: auto` n'est pas décoratif : la balise porte width="1200"
     height="800" pour réserver sa place avant le chargement, et ces attributs
     s'appliquent comme une hauteur fixe de 800 px tant qu'aucune règle
     d'auteur ne la remplace. Sans cette ligne, la photo occupait presque tout
     l'écran et `aspect-ratio` était ignoré — une hauteur explicite le neutralise.
     Plafonnée ensuite au quart de la hauteur : une devanture sert à reconnaître
     l'endroit, pas à repousser sous la ligne de flottaison ce qu'on vient
     chercher — l'adresse, les horaires, le bouton commander. `object-fit: cover`
     recadre au lieu de déformer. */
  height: auto;
  aspect-ratio: 3 / 2;
  max-height: 25dvh;
  object-fit: cover;
  border-radius: var(--r-lg);
  background: var(--surface-3);
}

.rating-hero {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
}
.rating-hero .score {
  font-size: var(--fs-2xl);
  font-weight: 800;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}

/* Répartition des notes — cinq barres, une par niveau d'étoiles. */
.breakdown { display: flex; flex-direction: column; gap: var(--s-2); }
.breakdown-row { display: flex; align-items: center; gap: var(--s-3); }
.breakdown-row .level { width: 2.5rem; font-size: var(--fs-sm); color: var(--ink-500); }
.breakdown-row .count { width: 2rem; text-align: right; font-size: var(--fs-sm); color: var(--ink-500); }
.bar {
  flex: 1;
  height: 8px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  overflow: hidden;
}
.bar > span { display: block; height: 100%; background: var(--star); }

.review { padding: var(--s-4) 0; border-bottom: 1px solid var(--border); }
.review:last-child { border-bottom: none; }
.review-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); }
.review-comment { color: var(--ink-700); margin-top: var(--s-2); }

/* Réponse du commerçant : rattachée à l'avis, jamais à sa place. */
.review-reply {
  margin-top: var(--s-3);
  padding-left: var(--s-3);
  border-left: 3px solid var(--brand-100);
}
.review-reply .who { font-size: var(--fs-sm); font-weight: 600; color: var(--ink-500); }

/* Étoiles cliquables du formulaire d'avis. */
.stars { display: flex; gap: var(--s-1); }
.star-btn {
  padding: var(--s-1);
  color: var(--border-strong);
  line-height: 0;
}
.star-btn .icon { width: 32px; height: 32px; }
.star-btn[aria-checked='true'],
.star-btn.is-lit { color: var(--star); }
.star-btn.is-lit .icon { fill: currentColor; }

/* ------------------------------------------------------------------ Scanner */

.scanner {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 4;
  max-height: 60dvh;
  margin-inline: auto;
  border-radius: var(--r-lg);
  background: #101410;
  display: grid;
  place-items: center;
}
.scanner video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Viseur : un cadre clair sur un voile sombre, pour dire où viser sans
   masquer ce que la caméra voit. */
.scanner-frame {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.scanner-frame::before {
  content: '';
  width: min(62%, 240px);
  aspect-ratio: 1;
  border: 3px solid rgba(255, 255, 255, 0.9);
  border-radius: var(--r-md);
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.38);
}
.scanner-hint {
  position: absolute;
  inset-inline: 0;
  bottom: var(--s-4);
  text-align: center;
  color: #fff;
  font-size: var(--fs-sm);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}

/* Rang de la boutique dans la liste — même numéro que sa pastille sur la
   carte. C'est le seul lien visuel entre les deux. */
.rank {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 22px; height: 22px;
  border-radius: var(--r-full);
  background: var(--brand-100);
  color: var(--brand-700);
  font-size: var(--fs-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Fiche désignée depuis la carte : l'anneau dure le temps qu'on la retrouve. */
.card.is-selected {
  box-shadow: 0 0 0 2px var(--brand-600), var(--sh-2);
}

/* Bandeau d'état au-dessus de la liste : position utilisée, rayon, repli. */
.result-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin: var(--s-4) 0 var(--s-3);
}

/* ------------------------------------------------------------ Superposition */

.sheet-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: var(--scrim);
  animation: fade var(--t-in);
}

.sheet {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: var(--z-modal);
  max-height: 88dvh;
  overflow-y: auto;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--sh-4);
  animation: sheet-in var(--t-in);
}

.sheet-handle {
  width: 40px; height: 4px;
  margin: var(--s-3) auto var(--s-1);
  border-radius: var(--r-full);
  background: var(--border-strong);
}

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes sheet-in {
  from { transform: translateY(16px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* Barre d'action collée en bas d'une feuille ou d'un formulaire long */
.action-bar {
  position: sticky;
  bottom: 0;
  padding: var(--s-4);
  padding-bottom: calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* ==========================================================================
   Écrans larges — la barre d'onglets basse devient une navigation haute.
   ========================================================================== */

@media (min-width: 1024px) {
  :root { --tabbar-h: 64px; }

  .screen-body { padding-bottom: var(--s-12); }

  .container { max-width: var(--content-max-wide); padding-inline: var(--s-6); }
  .container--narrow { max-width: var(--content-max); }

  /* Le bandeau de pied suit la mesure du contenu, qui s'élargit ici. */
  .page-banner--end { padding-inline: var(--s-6); }
  .page-banner--end img { max-width: var(--content-max-wide); }

  /* La barre reste en dernier dans le DOM — l'ordre de lecture et l'ordre de
     tabulation suivent l'ordre visuel sur mobile, où elle est en bas. Un
     `sticky` ne pourrait donc pas remonter en tête de page : elle est fixée,
     et c'est `.app` qui lui réserve sa bande. */
  .app { padding-top: var(--tabbar-h); }

  .appbar { top: var(--tabbar-h); }

  /* La barre remonte en tête et redevient pleine largeur : elle n'est plus
     une barre d'onglets flottante mais le bandeau de navigation du site. */
  .tabbar {
    position: fixed;
    top: 0;
    bottom: auto;
    inset-inline: 0;
    max-width: none;      /* la borne de la barre flottante ne vaut plus ici */
    margin-inline: 0;
    height: var(--tabbar-h);
    padding: 0 var(--s-6);
    justify-content: center;
    gap: var(--s-2);
    align-items: center;
    border-radius: 0;
    box-shadow: none;
    border-bottom: 1px solid var(--border);
  }

  /* L'onglet actif se signale par sa pastille, plus par un trait. */
  .tabbar-item::after { display: none; }

  .tabbar-item {
    flex: none;
    flex-direction: row;
    gap: var(--s-2);
    min-height: 44px;
    padding: 0 var(--s-4);
    border-radius: var(--r-full);
    font-size: var(--fs-sm);
  }
  .tabbar-item:hover { background: var(--surface-2); }
  .tabbar-item[aria-current='page'] { background: var(--brand-50); }

  /* Le scan redevient un bouton normal : plus de FAB flottant hors contexte. */
  .tabbar-fab {
    width: auto; height: 44px;
    margin-top: 0;
    padding: 0 var(--s-4);
    gap: var(--s-2);
    display: inline-flex;
    align-items: center;
    border-radius: var(--r-full);
    border: none;
    box-shadow: none;
    font-size: var(--fs-sm);
    font-weight: 600;
  }

  .tile-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .filter-grid { grid-template-columns: repeat(8, 1fr); }

  /* Les listes de boutiques et de commandes passent en grille. */
  .grid-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: var(--s-4);
  }

  /* Une feuille de filtres devient une boîte de dialogue centrée. */
  .sheet {
    inset: 50% auto auto 50%;
    transform: translate(-50%, -50%);
    /* 100% et non 100vw : `vw` inclut la barre de défilement, ce qui ferait
       déborder la boîte de la largeur de celle-ci sur une fenêtre étroite. */
    width: min(560px, calc(100% - 2 * var(--s-8)));
    max-height: 84dvh;
    border-radius: var(--r-lg);
  }
  .sheet-handle { display: none; }
  @keyframes sheet-in {
    from { transform: translate(-50%, -46%); opacity: 0; }
    to   { transform: translate(-50%, -50%); opacity: 1; }
  }
}

/* ==========================================================================
   Mouvement réduit — on garde les changements d'état, on retire le mouvement.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .btn:active:not(:disabled),
  .tabbar-fab:active { transform: none; }
  .skeleton { animation: none; }
}

/* Impression : la navigation n'a aucun sens sur papier. */
@media print {
  .tabbar, .appbar, .action-bar { display: none !important; }
  body { background: #fff; }
}
