/* Opinion Pro — feuille de style maison (aucune dépendance externe) */

:root {
  --primary: #4f46e5;
  --primary-dark: #4338ca;
  --primary-light: #eef2ff;
  --star: #f59e0b;
  --ink: #111827;
  --muted: #6b7280;
  --line: #e5e7eb;
  --bg: #f7f7fb;
  --surface: #ffffff;
  --success: #059669;
  --success-bg: #ecfdf5;
  --danger: #dc2626;
  --danger-bg: #fef2f2;
  --radius: 14px;
  --card-pad: 20px;
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 8px 24px rgba(16, 24, 40, .06);
}

* { box-sizing: border-box; }

/* `hidden` doit gagner : sinon un `display` pose par une classe (.btn) l'annule */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.55;
  /* un lien ou un mot tres long coupe plutot que d'elargir la page */
  overflow-wrap: break-word;
}

img { max-width: 100%; }

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

.container { max-width: 1080px; margin: 0 auto; padding: 0 20px; }
.narrow { max-width: 560px; }

/* ---------- Navigation ---------- */
.navbar {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.navbar .container {
  display: flex; align-items: center; gap: 18px;
  min-height: 62px; flex-wrap: wrap;
}
.brand {
  font-weight: 800; font-size: 1.15rem; color: var(--ink);
  display: flex; align-items: center; gap: 8px;
}
.brand:hover { text-decoration: none; }
.brand-icon { display: block; border-radius: 6px; }
.brand span { color: var(--primary); }
.nav-links { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.nav-links a { padding: 8px 12px; border-radius: 8px; color: var(--muted); font-weight: 500; }
.nav-links a:hover { background: var(--primary-light); color: var(--primary); text-decoration: none; }
/* un lien stylise en bouton garde le texte blanc du bouton */
.nav-links a.btn, .nav-links a.btn:hover { color: #fff; background: var(--primary); }
.nav-links a.btn:hover { background: var(--primary-dark); }
.nav-form { display: inline-flex; }

/* ---------- Titres & texte ---------- */
h1 { font-size: 1.9rem; margin: 0 0 6px; letter-spacing: -.02em; }
h2 { font-size: 1.3rem; margin: 0 0 14px; }
h3 { font-size: 1.05rem; margin: 0 0 8px; }
.page-head { margin: 28px 0 20px; display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.page-head .grow { flex: 1; min-width: 240px; }
.subtitle { color: var(--muted); margin: 0; }
.muted { color: var(--muted); }
/* `anywhere` et non `break-word` : seul le premier reduit vraiment la largeur
   minimale de l'element, donc empeche une URL sans espace d'elargir la page */
code { font-size: .9em; overflow-wrap: anywhere; }
.small { font-size: .875rem; }
.center { text-align: center; }

/* ---------- Icônes ----------
   Un seul jeu de traces (templates/includes/icones.html), pose une fois par page
   et appele par `{% icone 'nom' %}`. Deux proprietes font tout le travail :

   - `em` : l'icone suit la taille du texte qui l'entoure. La meme icone est donc
     plus petite dans une cellule de tableau que dans un bouton, sans qu'aucune
     regle n'ait a le prevoir.
   - `currentColor` : elle prend la couleur de ce texte. Un survol qui change la
     couleur d'un lien change celle de son icone, sans une ligne de plus.

   Elles remplacent les emoji, qui rendaient differemment sur chaque telephone et
   imposaient leurs propres couleurs au milieu de la palette du site. */
.ic {
  width: 1.08em; height: 1.08em; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  /* remonte l'icone sur la ligne de base : centree, elle parait tomber */
  vertical-align: -.17em;
}
.ic-lg { width: 1.45em; height: 1.45em; stroke-width: 1.8; }

/* Compteurs de reactions en ligne (includes/compteurs_reactions.html).
   `nowrap` sur chaque paire : un nombre ne doit jamais etre separe de son icone
   par un retour a la ligne, sinon on lit « 12 » seul en fin de ligne. */
.compteurs, .compteur { white-space: nowrap; }
.compteur .ic { color: var(--muted); }

/* ---------- Cartes ---------- */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--card-pad); box-shadow: var(--shadow);
}
/* l'empilement vertical est gere par .stack : pas de marge automatique entre
   cartes, sinon les cellules d'une .grid se retrouvent decalees entre elles */
.grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); align-items: start; }
.stack > * + * { margin-top: 16px; }

/* Deux colonnes (colonne laterale + contenu). Elles s'empilent des que la somme
   des largeurs minimales ne tient plus : sinon la page deborde horizontalement. */
.grid-split { grid-template-columns: minmax(260px, 1fr) minmax(320px, 1.4fr); }
.grid-split-lg { grid-template-columns: minmax(240px, 1fr) minmax(320px, 2fr); }
/* `minmax(0, 1fr)` et non `1fr` : sinon la colonne s'elargit jusqu'au contenu le
   moins compressible qu'elle abrite, et c'est toute la page qui deborde. */
@media (max-width: 760px) {
  .grid-split, .grid-split-lg { grid-template-columns: minmax(0, 1fr); }
}

/* Fiche profil. Les blocs sont des enfants directs de la grille — et non deux
   colonnes imbriquees — pour que l'ordre du HTML soit celui du mobile :
   note du mois, puis formulaire d'avis, puis partage, puis historique.
   Sur grand ecran, chacun est replace ici : le formulaire a droite, le reste
   a gauche, dans l'ordre habituel. */
.profil-grid {
  display: grid; gap: 16px; align-items: start;
  grid-template-columns: minmax(260px, 1fr) minmax(320px, 1.4fr);
}
.profil-grid > .bloc-note { grid-column: 1; grid-row: 1; }
.profil-grid > .bloc-partage { grid-column: 1; grid-row: 2; }
.profil-grid > .bloc-historique { grid-column: 1; grid-row: 3; }
.profil-grid > .bloc-avis { grid-column: 2; grid-row: 1 / -1; align-self: start; }

@media (max-width: 760px) {
  .profil-grid { grid-template-columns: minmax(0, 1fr); }
  /* Une seule colonne : on rend la main au flux du document. Il faut rappeler
     chaque classe — une media query n'ajoute aucune spécificité, et `> *` perdrait
     contre les `> .bloc-…` ci-dessus, laissant le formulaire dans une 2e colonne. */
  .profil-grid > .bloc-note,
  .profil-grid > .bloc-avis,
  .profil-grid > .bloc-partage,
  .profil-grid > .bloc-historique { grid-column: 1; grid-row: auto; }
}

.profile-card { display: flex; flex-direction: column; height: 100%; }
@media (prefers-reduced-motion: no-preference) {
  /* Lège élévation au survol : incite au clic sans rien déplacer d'autre
     autour (transform, pas de changement de taille qui décalerait la grille). */
  .profile-card { transition: transform .18s ease, box-shadow .18s ease; }
  .profile-card:hover, .profile-card:focus-within {
    transform: translateY(-3px);
    box-shadow: 0 4px 8px rgba(16, 24, 40, .07), 0 16px 32px rgba(16, 24, 40, .10);
  }
}
.profile-card .avatar { margin-bottom: 12px; }
.profile-card .card-footer {
  margin-top: auto; padding-top: 14px; border-top: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}

/* Carte façon fil d'actualité : la photo occupe toute la largeur de la carte.
   Les marges négatives valent la même chose que le padding, d'où --card-pad. */
.feed-card { overflow: hidden; }
.feed-photo {
  display: block; margin: calc(var(--card-pad) * -1) calc(var(--card-pad) * -1) 14px;
  background: var(--primary-light); line-height: 0;
}
.feed-photo:hover { text-decoration: none; }
/* Format portrait (3:4) : c'est le cadrage d'une photo de personne. `aspect-ratio`
   plutôt qu'une hauteur fixe, pour que le cadre suive la largeur de la carte. */
.feed-photo img {
  display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover;
  transition: transform .25s;
}
.feed-photo:hover img { transform: scale(1.03); }
.feed-photo-empty {
  display: flex; align-items: center; justify-content: center;
  width: 100%; aspect-ratio: 3 / 4; font-size: 3.4rem; font-weight: 800;
  color: var(--primary); line-height: 1;
}
.feed-head h3 a { color: var(--ink); }
.feed-head h3 a:hover { color: var(--primary); }

/* Photo en grand sur la fiche du profil, en portrait elle aussi. Elle est bornée
   en largeur et centrée : en pleine largeur d'écran, un portrait 3:4 ferait une
   image démesurément haute qu'il faudrait faire défiler pour lire le nom. */
.hero-photo {
  display: block; margin: 16px auto 0; max-width: 340px;
  border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow); background: var(--primary-light); line-height: 0;
}
.hero-photo img { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }

.avatar {
  width: 64px; height: 64px; border-radius: 50%; object-fit: cover;
  background: var(--primary-light); color: var(--primary);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 1.4rem; overflow: hidden; flex: none;
}
.avatar-lg { width: 96px; height: 96px; font-size: 2rem; }

/* ---------- Étoiles ---------- */
.stars { color: var(--star); letter-spacing: 1px; white-space: nowrap; font-size: 1rem; }
.stars.lg { font-size: 1.5rem; }
.stars .empty { color: #d1d5db; }
.score { font-weight: 700; font-size: 1.05rem; }
.score-lg { font-size: 2.6rem; font-weight: 800; line-height: 1; letter-spacing: -.03em; }

/* ---------- Répartition ---------- */
.dist-row { display: flex; align-items: center; gap: 10px; font-size: .85rem; margin-bottom: 6px; }
.dist-row .label { width: 42px; color: var(--muted); white-space: nowrap; }
.dist-row .count { width: 32px; text-align: right; color: var(--muted); }
.bar { flex: 1; height: 8px; background: var(--line); border-radius: 99px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--star); border-radius: 99px; }

/* ---------- Statistiques ---------- */
.stat-row { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.stat {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px;
}
.stat .value { font-size: 1.7rem; font-weight: 800; letter-spacing: -.02em; }
.stat .label { color: var(--muted); font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; }

/* ---------- Chiffres cles (bandeau compact) ----------
   Variante resserree de .stat : trois vignettes qui tiennent cote a cote des
   320px, la ou .stat-row (minmax 150px) retomberait a deux colonnes et ferait
   passer la troisieme a la ligne toute seule. minmax(0, 1fr) et non 1fr :
   sinon la colonne s'elargit jusqu'a son contenu le moins compressible. */
/* max-width : sur grand ecran, trois vignettes etirees sur toute la largeur
   font beaucoup de vide autour d'un seul chiffre. Sans effet sous 560px. */
.chiffres-cles {
  display: grid; gap: 8px; grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: 560px;
}
.chiffres-cles .vignette {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 6px; text-align: center; min-width: 0;
}
.chiffres-cles .valeur { font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; }
.chiffres-cles .intitule {
  color: var(--muted); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .04em; overflow-wrap: anywhere;
}

/* ---------- Tuiles d'action ----------
   Deux colonnes sur telephone, une seule rangee des que la place le permet.
   `min(150px, 100%)` dans le minmax : sans le min(), une tuile plus large que
   l'ecran forcerait un debordement horizontal au lieu de se resserrer. */
.tuiles-actions {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
}
.tuile {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 6px; padding: 16px 10px; text-align: center; min-width: 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--ink); text-decoration: none;
  font-size: .9rem; font-weight: 600; line-height: 1.25;
}
.tuile:hover, .tuile:focus-visible { border-color: var(--primary); background: var(--primary-light); color: var(--primary); }
.tuile .ic { width: 1.6rem; height: 1.6rem; stroke-width: 1.7; vertical-align: 0; }
.tuile .detail { font-weight: 400; font-size: .78rem; color: var(--muted); }
.tuile:hover .detail, .tuile:focus-visible .detail { color: inherit; }

/* ---------- Badges ---------- */
.badge {
  display: inline-block; padding: 3px 10px; border-radius: 99px;
  font-size: .75rem; font-weight: 600; background: var(--primary-light); color: var(--primary);
}
.badge-muted { background: #f3f4f6; color: var(--muted); }
.badge-success { background: var(--success-bg); color: var(--success); }
.badge-danger { background: var(--danger-bg); color: var(--danger); }
.rank { font-weight: 800; color: var(--muted); width: 28px; display: inline-block; }
.rank-1 { color: var(--star); }

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 7px; justify-content: center;
  padding: 10px 18px; border-radius: 10px; border: 1px solid transparent;
  background: var(--primary); color: #fff; font-weight: 600; font-size: .93rem;
  cursor: pointer; font-family: inherit; transition: background .15s, box-shadow .15s;
}
.btn:hover { background: var(--primary-dark); text-decoration: none; color: #fff; }
.btn-outline { background: transparent; color: var(--primary); border-color: var(--line); }
.btn-outline:hover { background: var(--primary-light); color: var(--primary-dark); }
.btn-ghost { background: transparent; color: var(--muted); }
.btn-ghost:hover { background: #f3f4f6; color: var(--ink); }
.btn-danger { background: var(--danger); }
.btn-danger:hover { background: #b91c1c; }
.btn-sm { padding: 6px 12px; font-size: .82rem; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* ---------- Formulaires ---------- */
.form-control, select.form-control {
  width: 100%; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 10px; font-size: .95rem; font-family: inherit;
  background: var(--surface); color: var(--ink);
}
.form-control:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, .15);
}
.form-control.couleur { width: 56px; padding: 3px; }
.field { margin-bottom: 16px; }
.field > label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: 6px; }
.field .helptext { display: block; color: var(--muted); font-size: .82rem; margin-top: 4px; }
.field .errorlist { color: var(--danger); font-size: .85rem; margin: 6px 0 0; padding-left: 18px; }
/* `flex-start` et non `center` : une case dont le libellé tient sur deux lignes —
   celui de la politique de confidentialité — verrait sinon sa case flotter au
   milieu du texte. Le décalage de 2px la réaligne sur la première ligne. */
.form-check { display: flex; align-items: flex-start; gap: 8px; }
.form-check-input {
  width: 18px; height: 18px; accent-color: var(--primary);
  flex: none; margin-top: 2px;
}
.form-check label { font-weight: 400; }

/* Notation par étoiles cliquables (RadioSelect rend un <div> par choix) */
div.star-input { display: flex; gap: 8px; flex-wrap: wrap; padding: 0; margin: 0; }
div.star-input > div { flex: none; }
div.star-input label {
  display: block; padding: 8px 14px; border: 1px solid var(--line);
  border-radius: 10px; cursor: pointer; font-weight: 600; font-size: .95rem;
  background: var(--surface); transition: all .15s; white-space: nowrap;
}
div.star-input label:hover { border-color: var(--star); background: #fffbeb; }
div.star-input input { position: absolute; opacity: 0; width: 0; height: 0; }
div.star-input > div:has(input:checked) label {
  background: var(--star); border-color: var(--star); color: #fff;
  box-shadow: 0 2px 8px rgba(245, 158, 11, .35);
}
div.star-input > div:has(input:focus-visible) label { box-shadow: 0 0 0 3px rgba(79, 70, 229, .3); }

/* ---------- Notation en un clic (listes de profils) ---------- */
.quick-rate {
  margin-top: 12px; padding: 10px 12px;
  background: #fffbeb; border: 1px solid #fde68a; border-radius: 10px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
}
.quick-rate-label { font-size: .82rem; color: #92400e; }
.quick-stars { display: flex; gap: 2px; }
/* La marge interieure n'est pas decorative : c'est la surface qu'on touche.
   Sans elle (padding: 0 2px), le bouton mesurait 23x23 px — sous le minimum de
   24 px de WCAG 2.2 (SC 2.5.8), et tres loin des ~44 px confortables au pouce.
   Or c'est LE geste du produit : un client debout au comptoir, sur son
   telephone, qui met une note en un clic. Rater d'une etoile, c'est fausser
   la donnee qu'on est venu chercher.
   L'etoile garde sa taille visible (font-size inchange) ; seule la zone
   sensible grandit — 43 px de haut, ce que le pouce vise vraiment. */
.star-btn {
  border: none; background: none; padding: 10px 6px; cursor: pointer;
  font-size: 1.45rem; line-height: 1; color: #e5d5b0;
  font-family: inherit; transition: color .12s, transform .12s;
}
.star-btn.on { color: var(--star); }
/* survol : on remplit jusqu'a l'etoile pointee, et on efface l'ancienne note
   pour que l'apercu corresponde vraiment a ce qui sera enregistre */
.quick-stars:hover .star-btn.on { color: #e5d5b0; }
.quick-stars .star-btn:hover,
.quick-stars .star-btn:has(~ .star-btn:hover) { color: var(--star); }
.star-btn:hover { transform: scale(1.15); }
.star-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }

/* ---------- Barre de recherche ---------- */
.search-bar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.search-bar .form-control { flex: 1; min-width: 200px; }
/* pendant une requete, les resultats palissent legerement : on voit qu'ils bougent
   sans que la page ne saute (la hauteur ne change pas) */
[aria-busy="true"] { opacity: .55; transition: opacity .12s; }

/* ---------- Réactions « J'aime » / « J'adore » ---------- */
.reactions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
/* Boutons a l'icone seule : le libelle vit dans title/aria-label */
.react-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px; border-radius: 99px; border: 1px solid var(--line);
  background: var(--surface); color: var(--muted);
  font-family: inherit; font-size: 1.05rem; line-height: 1.2; cursor: pointer;
  transition: border-color .15s, background .15s, color .15s, transform .12s;
}
.react-btn:hover { border-color: var(--primary); background: var(--primary-light); transform: translateY(-1px); }
.react-btn.on { border-color: var(--primary); background: var(--primary-light); }
/* La reaction choisie se remplit. Un emoji ne pouvait dire que « pose » ou
   « pas pose » ; un trace, lui, se remplit — c'est le meme geste que le coeur
   qu'on colore, et ca se lit sans avoir a comparer deux bordures. */
.react-btn .ic { color: var(--muted); }
.react-btn.on .ic { color: var(--primary); fill: currentColor; fill-opacity: .16; }
.react-down.on .ic { color: var(--danger); }
.react-btn .count {
  font-size: .82rem; font-weight: 700; min-width: 12px; text-align: center;
  font-family: system-ui, sans-serif;
}
.react-btn.on .count { color: var(--primary-dark); }
/* le pouce vers le bas ne se pare pas des couleurs de marque quand il est actif */
.react-down:hover, .react-down.on { border-color: #fca5a5; background: var(--danger-bg); }
.react-down.on .count { color: var(--danger); }
.react-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ---------- Volets repliés (<details> natifs, sans JavaScript) ----------
   Les boutons sont côte à côte ; celui qu'on ouvre prend toute la ligne grâce à
   `flex-basis: 100%`, et son panneau s'affiche donc en pleine largeur. */
.volets { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }
.volet { flex: 0 0 auto; }
.volet[open] { flex: 1 1 100%; }
/* un `display` autre que `list-item` suffit à retirer le triangle, mais
   Safari a besoin qu'on vise aussi son pseudo-élément */
.volet > summary { cursor: pointer; list-style: none; }
.volet > summary::-webkit-details-marker { display: none; }
.volet > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.volet-corps { margin-top: 10px; }

/* ---------- Partage & QR codes ---------- */
.share-card .qr { width: 170px; height: 170px; display: block; margin: 0 auto; }
.share-card input { text-align: center; font-size: .8rem; }

/* Le lien et son bouton « Copier » côte à côte ; le bouton passe dessous quand
   c'est trop étroit. `min-width: 0` sur le champ, sinon l'URL empêche le flex
   de le rétrécir et la page déborde. */
.copie { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.copie > .form-control { flex: 1 1 180px; min-width: 0; }
.copie > .btn { flex: 0 0 auto; white-space: nowrap; }
.share-card .copie { justify-content: center; }

/* ---------- Texte long dépliable (includes/texte_long.html) ---------- */
.texte-long > summary { display: block; cursor: pointer; list-style: none; }
.texte-long > summary::-webkit-details-marker { display: none; }
/* ouvert, l'aperçu ferait doublon avec le texte entier */
.texte-long[open] > summary .apercu { display: none; }
.texte-long .bascule { display: block; margin-top: 4px; font-size: .85rem;
  font-weight: 600; color: var(--primary); }
.texte-long .bascule::after { content: 'Lire la suite ▾'; }
.texte-long[open] .bascule::after { content: 'Réduire ▴'; }
.texte-long > summary:hover .bascule { text-decoration: underline; }
.texte-long > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.texte-entier { overflow-wrap: anywhere; }

/* ---------- Pagination ---------- */
.pagination {
  display: flex; gap: 12px; align-items: center; justify-content: center;
  flex-wrap: wrap; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line);
}
.pagination [aria-disabled] { opacity: .45; cursor: default; }

/* La carte du QR de la boutique : le texte à gauche, le code à droite */
.qr-boutique { display: flex; gap: 20px; align-items: center; margin-top: 20px; }
.qr-boutique .grow { flex: 1; min-width: 0; }
.qr-boutique .qr { width: 150px; height: 150px; flex: none; }

/* ---------- Apercus des imprimes (shops/partials/apercus_imprimes.html) ----------
   Les images sont des SVG servis par une vue : elles portent donc leurs
   proportions natives (54 x 86 mm pour la carte, A4 pour l'affiche), et se
   redimensionnent sans que la page saute au chargement. On les brident en
   largeur plutot qu'en hauteur : une carte-badge etalee sur toute la colonne
   ne ressemblerait plus a une carte. */
.apercus {
  display: grid; gap: 16px; margin-top: 12px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  align-items: start; justify-items: center;
}
.apercu { margin: 0; text-align: center; max-width: 100%; }
.apercu-cadre {
  display: block; background: var(--bg); border: 1px solid var(--line);
  border-radius: 10px; padding: 14px;
}
.apercu-cadre img {
  display: block; width: 100%; height: auto; margin: 0 auto;
  /* l'ombre detache l'imprime de son cadre : sans elle, une carte a fond blanc
     (styles spotlight et duotone) se confondrait avec le gris de la page */
  box-shadow: 0 2px 6px rgba(16, 24, 40, .14), 0 12px 28px rgba(16, 24, 40, .10);
}
.apercu-carte img { max-width: 170px; border-radius: 6px; }
.apercu-affiche img { max-width: 200px; border-radius: 3px; }
.apercu-legende { font-size: .76rem; color: var(--muted); margin-top: 8px; }

/* Selecteur du profil montre sur la carte. Il coiffe l'apercu plutot que de le
   suivre : on choisit d'abord qui, on regarde ensuite. Le bouton n'existe que
   pour la version sans JavaScript — apercu-carte.js le masque et se contente
   de changer l'adresse de l'image. */
.apercu-choix {
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  justify-content: center; margin-bottom: 10px;
}
.apercu-choix label { font-size: .76rem; color: var(--muted); }
.apercu-choix .form-control { width: auto; max-width: 100%; padding: 4px 8px; font-size: .82rem; }

/* La colonne d'apercu suit la saisie : sur grand ecran elle reste a l'ecran
   pendant qu'on descend dans les reglages. En dessous, les deux colonnes sont
   deja empilees (.grid-split) et un element colle generait plus qu'il n'aide. */
@media (min-width: 761px) {
  .apercu-colonne { position: sticky; top: 16px; }
}

.qr-sheet {
  display: grid; gap: 16px; margin-top: 20px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.qr-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px; text-align: center; box-shadow: var(--shadow);
}
.qr-card .qr { width: 100%; max-width: 180px; height: auto; margin: 10px auto 0; display: block; }
.qr-shop { font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.qr-name { font-size: 1.15rem; font-weight: 800; letter-spacing: -.01em; margin-top: 2px; }
.qr-role { color: var(--muted); font-size: .85rem; }
.qr-call { margin-top: 8px; font-size: .85rem; font-weight: 600; color: var(--primary); }

/* ---------- Supervision : graphiques ----------
   Une seule teinte pour toutes les marques de données : ces graphiques disent une
   grandeur (« combien »), pas une identité. Une palette catégorielle ici ne ferait
   que redire la hauteur des barres avec une deuxième couleur.
   Traits fins, grille en filet, remplissages saturés reserves aux petites marques. */
.etiquette { text-transform: uppercase; letter-spacing: .05em; }

.filtre-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 14px var(--card-pad);
}
.filtre-bar label { font-weight: 600; font-size: .9rem; }
.filtre-bar .form-control { width: auto; flex: 1 1 180px; }

/* Colonnes : une par mois, hauteur proportionnelle au nombre d'avis */
.chart-cols { display: flex; align-items: flex-end; gap: 6px; margin-top: 18px; }
.chart-col {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  align-items: center; gap: 6px;
}
.chart-valeur { font-size: .78rem; font-weight: 700; font-variant-numeric: tabular-nums; }
/* les mois ordinaires s'effacent : seuls le sommet et le mois affiché sont annoncés */
.chart-valeur.discret { color: var(--muted); font-weight: 500; }
.chart-fut {
  display: flex; align-items: flex-end; justify-content: center;
  width: 100%; height: 140px;
}
/* pas de `min-height` : un mois sans aucun avis ne doit pas laisser un talon
   qui se lirait comme « presque rien » plutôt que comme « rien » */
.chart-fut > i {
  display: block; width: 100%; max-width: 34px;
  background: var(--primary); border-radius: 4px 4px 0 0;
}
.chart-col.courant .chart-fut > i { background: var(--primary-dark); }
.chart-axe {
  font-size: .72rem; color: var(--muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.chart-col.courant .chart-axe { color: var(--ink); font-weight: 700; }
.chart-axe-seul { margin-top: 6px; align-items: flex-start; }

/* Courbe : échelle fixe 0→5, une seule série, donc pas de légende à porter */
.chart-line { display: flex; gap: 10px; margin-top: 18px; }
.chart-line svg { flex: 1; min-width: 0; height: 120px; }
.chart-echelle {
  display: flex; flex-direction: column; justify-content: space-between;
  font-size: .72rem; color: var(--muted); height: 120px; flex: none;
  font-variant-numeric: tabular-nums;
}
.chart-line .grille { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.chart-line .aire { fill: var(--primary); opacity: .1; }
.chart-line .trait {
  fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke;
}
/* anneau de la couleur du fond plutôt qu'un contour : les points restent lisibles
   là où ils chevauchent le trait */
.chart-line .point { fill: var(--primary); stroke: var(--surface); stroke-width: 2; opacity: .55; }
.chart-line .point.vif { opacity: 1; r: 5; }

/* Classement : la barre se lit sur l'échelle de notation (0→5), pas sur le maximum */
.rank-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; }
.rank-row + .rank-row { border-top: 1px solid var(--line); }
.rank-corps { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
/* `.bar` porte `flex: 1` pour la répartition des étoiles ; dans cette colonne
   flex, ce `flex-basis: 0` écraserait sa hauteur — on le neutralise */
.rank-corps .bar { flex: none; width: 100%; height: 6px; margin-top: 3px; }
.rank-corps .bar > i { background: var(--primary); }
.rank-chiffres {
  flex: none; text-align: right; display: flex; flex-direction: column; line-height: 1.25;
}

.signaux { list-style: none; margin: 0; padding: 0; }
.signaux li {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 0; font-size: .9rem;
}
.signaux li + li { border-top: 1px solid var(--line); }
.signaux .badge { flex: none; min-width: 34px; text-align: center; font-variant-numeric: tabular-nums; }

details.card > summary { cursor: pointer; }

/* ---------- Avis ---------- */
.review { padding: 16px 0; border-bottom: 1px solid var(--line); }
.review:last-child { border-bottom: none; }
.review-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.review-author { font-weight: 600; }
.review p { margin: 6px 0 0; }
.review.hidden-review { opacity: .55; }

/* ---------- Tableaux ---------- */
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: .92rem; }
th, td { padding: 12px 10px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: middle; }
th { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); font-weight: 700; }
tbody tr:hover { background: #fafafe; }
td.num, th.num { text-align: right; }

/* Cellule « profil » d'un tableau : pastille + nom. Trois precautions, sans
   lesquelles le nom deborde sa colonne et vient se coller au role de la colonne
   voisine — deja serre a 390px, franchement superpose a 320px avec un nom un
   peu long :
   - la pastille ne doit PAS retrecir (flex-shrink par defaut vaut 1), sinon le
     cercle s'ovalise et le nom gagne quand meme trop peu de place ;
   - un enfant de flex a besoin de min-width: 0 pour pouvoir se compresser,
     sinon il impose la largeur de son plus long mot et deborde ;
   - la colonne garde une largeur minimale : le tableau s'elargit alors et
     defile dans son .table-wrap, ce qui est le comportement voulu sur mobile. */
.cellule-profil { display: flex; align-items: center; gap: 10px; min-width: 0; }
.cellule-profil .avatar { flex: 0 0 auto; width: 36px; height: 36px; font-size: .9rem; }
.cellule-profil strong { min-width: 0; overflow-wrap: anywhere; }
th.col-profil, td.col-profil { min-width: 150px; }

/* ---------- Ligne de tableau cliquable, sans JavaScript ----------
   Le HTML n'autorise pas d'envelopper un <tr> dans un <a>, et un `onclick`
   sur la ligne demanderait du script — le projet s'en passe. Chaque cellule
   d'information recoit donc son propre lien de niveau bloc vers la meme page.

   Le point qui compte : la marge interieure DESCEND de la cellule vers le
   lien. En la laissant sur le <td>, les 10px du bord de chaque cellule
   resteraient une bande morte, et la ligne ne serait cliquable qu'au milieu —
   exactement le defaut qu'on cherche a corriger. */
td.cellule-lien { padding: 0; }
.lien-cellule { display: block; padding: 12px 10px; color: inherit; text-decoration: none; }
.lien-cellule:hover { text-decoration: none; }

/* ---------- Messages ---------- */
.messages { margin: 18px 0 0; padding: 0; list-style: none; }
.messages li {
  padding: 12px 16px; border-radius: 10px; margin-bottom: 10px;
  background: var(--primary-light); color: var(--primary-dark); font-size: .92rem;
}
.messages li.success { background: var(--success-bg); color: var(--success); }
.messages li.error { background: var(--danger-bg); color: var(--danger); }

/* ---------- État vide ---------- */
.empty { text-align: center; padding: 48px 20px; color: var(--muted); }
/* l'icone d'un etat vide est seule et en grand : trait affine pour qu'elle reste
   un decor, et non un pictogramme qui crie plus fort que la phrase */
.empty .ic {
  display: block; margin: 0 auto 12px; vertical-align: 0;
  width: 2.4rem; height: 2.4rem; stroke-width: 1.4; opacity: .55;
}

/* ---------- Bannière reset mensuel ---------- */
.notice {
  background: linear-gradient(135deg, #eef2ff, #f5f3ff);
  border: 1px solid #ddd6fe; border-radius: var(--radius);
  padding: 14px 18px; font-size: .9rem; color: #4338ca;
  display: flex; gap: 10px; align-items: center;
}
/* sans `min-width: 0`, un enfant de flex refuse de descendre sous sa largeur minimale */
.notice > * { min-width: 0; }
/* une suppression définitive ne se signale pas dans la même couleur qu'une info */
.notice-danger {
  background: #fef2f2; border-color: #fecaca; color: #991b1b;
  align-items: flex-start;
}
.notice-danger a { color: inherit; text-decoration: underline; }
/* ---------- Contact & horaires d'une boutique ---------- */
/* Deux colonnes qui s'empilent d'elles-mêmes : `auto-fit` + `minmax` évitent
   d'écrire une media query, et un seul bloc renseigné occupe toute la largeur. */
.contact-boutique {
  display: grid; gap: 16px 32px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.contact-bloc h3 { margin-bottom: 6px; font-size: .95rem; }
.contact-ligne { margin: 0 0 4px; font-size: .92rem; }

/* Un bouton de formulaire qui doit se lire comme un lien : le geste est
   secondaire, mais il modifie des données — donc un POST, donc un <button>. */
.lien-bouton {
  background: none; border: 0; padding: 0; font: inherit; font-size: .85rem;
  color: var(--primary); cursor: pointer; text-align: left;
}
.lien-bouton:hover { text-decoration: underline; }
details > summary.lien-bouton { list-style: none; }
details > summary.lien-bouton::-webkit-details-marker { display: none; }

.liste-suppression { margin: 0; padding-left: 20px; font-size: .92rem; }
.liste-suppression li { margin-bottom: 4px; }

/* ---------- Catalogue d'une boutique ---------- */
.catalogue-grille { display: flex; flex-direction: column; }
.catalogue-article {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 0; border-bottom: 1px solid var(--line);
}
.catalogue-article:last-child { border-bottom: 0; }
.catalogue-article .grow { flex: 1; min-width: 0; }
.catalogue-entete { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.catalogue-prix { font-weight: 700; white-space: nowrap; flex: none; }
.catalogue-indisponible { opacity: .55; }

/* ---------- Pied de page ---------- */
footer {
  margin-top: 56px; padding: 28px 0; border-top: 1px solid var(--line);
  color: var(--muted); font-size: .87rem; background: var(--surface);
}
footer a { color: var(--muted); text-decoration: underline; }
footer a:hover { color: var(--primary); }

/* ---------- Page légale ---------- */
/* Une page de texte se lit sur une colonne étroite : au-delà d'environ 70
   caractères, l'œil perd le début de la ligne suivante. */
.legal { max-width: 760px; font-size: .9rem; }
.legal h2 { font-size: 1.05rem; margin: 26px 0 8px; }
.legal h2:first-of-type { margin-top: 0; }
.legal h3 { font-size: .95rem; margin: 18px 0 6px; color: var(--primary-dark); }
.legal p { margin: 0 0 12px; }
.legal ul { margin: 0 0 12px; padding-left: 22px; }
.legal li { margin-bottom: 6px; }
.legal table { font-size: .85rem; }

/* ---------- Page d'accueil (templates/pages/accueil.html) ----------
   Aucune couleur ni police nouvelle : la page reprend les jetons du site et ses
   composants. Ce qui est ajoute ici, et seulement cela : une echelle de titre
   plus grande, un rythme de sections, et une revelation au defilement.

   Pas de bande pleine largeur : elle demanderait `margin-left: calc(50% - 50vw)`,
   or `vw` compte la barre de defilement — la page deborderait horizontalement de
   quelques pixels sur tout ecran qui en affiche une. Les sections sont donc des
   blocs arrondis, ce qui est de toute facon le langage du site. */

/* system-ui, mais tres grand et tres serre : l'effet vient du corps et du
   cranage, pas d'une police tierce — donc zero octet a telecharger, ce qui
   compte sur un reseau irregulier. */
.titre-geant {
  font-size: clamp(2.3rem, 5.6vw, 4.2rem);
  line-height: 1.02; letter-spacing: -.035em; font-weight: 800; margin: 0 0 20px;
}
.titre-section {
  font-size: clamp(1.7rem, 3.4vw, 2.6rem);
  line-height: 1.08; letter-spacing: -.03em; font-weight: 800; margin: 0 0 14px;
}
.chapo { font-size: clamp(1rem, 1.5vw, 1.15rem); color: var(--muted); max-width: 52ch; }
.surtitre {
  font-size: .76rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .14em; color: var(--primary); margin-bottom: 14px;
}
.sous-actions { margin-top: 20px; font-size: .87rem; color: var(--muted); }

.accueil-heros { padding: clamp(28px, 4vw, 56px) 0 clamp(40px, 5vw, 72px); }
.accueil-grille {
  display: grid; gap: clamp(28px, 4vw, 52px); align-items: center;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
}
.accueil-visuel { position: relative; }
/* halo de marque derriere la carte ; `inset` sans valeur horizontale negative,
   sinon il elargit le document (meme piege que le 100vw ci-dessus) */
.accueil-visuel::before {
  content: ""; position: absolute; inset: -10% 0;
  background: radial-gradient(circle at 50% 45%, var(--primary-light), transparent 70%);
  z-index: 0;
}
.accueil-visuel > * { position: relative; z-index: 1; margin: 0 auto; }
/* z-index explicite (et non juste `position: relative`) : sans lui, cette
   colonne n'ouvre pas de contexte d'empilement propre et la lanière de la
   carte (voir `.squelette-cordon`), qui doit passer DERRIÈRE ce texte,
   passerait dessus à la place — `.accueil-visuel` n'a pas de z-index, donc
   tout son sous-arbre peint au niveau normal, sous n'importe quel z-index
   explicite ici. */
.accueil-texte-hero { position: relative; z-index: 2; }

/* Squelette de chargement : la silhouette de la carte imprimable
   (`carte_pdf.py`, style spotlight) — logo en haut à gauche, avatar en
   médaillon, nom, rôle, notes, QR en bas — plutôt qu'un faux profil rempli.
   Portrait, format ISO/CEI 7810 ID-1 (53,98 x 85,60 mm) : la largeur se
   calcule depuis une hauteur cible, pas l'inverse, sinon la carte devient
   plus haute que la colonne de texte en face sur les grands écrans. Le
   reflet se déplace en continu ; la carte respire de haut en bas pour
   signaler que c'est vivant, pas figé. */
.squelette-carte {
  aspect-ratio: 53.98 / 85.6;
  width: min(230px, 62vw);
  display: flex; flex-direction: column; align-items: center;
  padding: 22px 18px; gap: 12px;
}
.squelette { border-radius: 8px; background: var(--line); }
.squelette-entete-badge { display: flex; align-items: center; gap: 8px; align-self: flex-start; }
.squelette-logo { width: 26px; height: 26px; border-radius: 50%; flex: none; }
.squelette-logo-nom { width: 60px; height: 8px; }
.squelette-avatar { width: 42%; aspect-ratio: 1; border-radius: 50%; margin-top: 6px; }
.squelette-ligne-nom { width: 68%; height: 14px; margin-top: 4px; }
.squelette-ligne-role { width: 42%; height: 9px; }
/* « un peu plus grosses que l'actuelle » : 1.05rem → 1.5rem. */
.squelette-etoiles { display: inline-flex; gap: 5px; font-size: 1.5rem; line-height: 1; margin-top: 2px; }
.squelette-etoile { color: var(--line); }
.squelette-etoile::before { content: "★"; }
.squelette-qr { width: 34%; aspect-ratio: 1; border-radius: 8px; flex: none; margin-top: auto; }

/* La lanière : un cordon qui vient s'accrocher en haut de la carte, comme un
   vrai badge porté au cou — plutôt qu'une carte qui flotte sans attache.
   Positionné en `absolute` par rapport à `.squelette-carte` (déjà
   `position: relative` via `.accueil-visuel > *`), il en sort largement par
   le haut sans être rogné (aucun ancêtre ne pose `overflow: hidden`), assez
   loin pour passer sous le titre de la colonne de texte — voir le commentaire
   sur `.accueil-texte-hero` pour l'empilement. Se déplace avec la carte
   puisqu'il en est un enfant : pas d'animation séparée à synchroniser. */
.squelette-cordon {
  position: absolute; left: -492px; top: -43px;
  width: 615px; height: 165px; z-index: 0;
  fill: none; stroke: var(--line); stroke-width: 6; stroke-linecap: round;
  overflow: visible;
}
.squelette-clip {
  position: absolute; z-index: 1; top: -6px; left: 50%; transform: translateX(-50%);
  width: 22px; height: 13px; border-radius: 4px;
}

.accueil-bande {
  padding: clamp(44px, 6vw, 80px) 0;
  border-radius: var(--radius); margin-bottom: 8px;
}
.accueil-bande-claire, .accueil-bande-teintee, .accueil-bande-marque {
  padding-left: clamp(18px, 3vw, 40px); padding-right: clamp(18px, 3vw, 40px);
}
.accueil-bande-claire { background: var(--surface); border: 1px solid var(--line); }
.accueil-bande-teintee { background: var(--primary-light); }
.accueil-bande-marque { background: var(--primary); color: #fff; }
.accueil-bande-marque .titre-section { color: #fff; }
.accueil-bande-marque .chapo { color: rgba(255, 255, 255, .82); }
.accueil-bande-marque .btn { background: #fff; color: var(--primary); border-color: #fff; }
.accueil-bande-marque .btn:hover { background: var(--primary-light); color: var(--primary-dark); }

.accueil-arguments-bande { padding-top: 20px; padding-bottom: 20px; }
.accueil-arguments {
  display: flex; gap: 12px 26px; flex-wrap: wrap; justify-content: center;
  font-size: .92rem; font-weight: 600; color: var(--muted);
}
.accueil-arguments span { display: inline-flex; align-items: center; gap: 8px; }

.accueil-intro { max-width: 60ch; margin-bottom: 36px; }

.accueil-principes { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.accueil-principe { padding: clamp(24px, 3.2vw, 36px); }
.accueil-principe h3 { font-size: clamp(1.2rem, 2vw, 1.55rem); letter-spacing: -.02em; margin-bottom: 12px; }
.accueil-principe-ic, .accueil-atout-ic, .accueil-etape-num {
  border-radius: 16px; background: var(--primary-light); color: var(--primary);
  display: grid; place-items: center; flex: none;
}
.accueil-principe-ic { width: 54px; height: 54px; margin-bottom: 20px; }
.accueil-principe-ic .ic { width: 1.6rem; height: 1.6rem; vertical-align: 0; }
.accueil-etape-num {
  width: 38px; height: 38px; border-radius: 12px; margin-bottom: 16px;
  font-weight: 800; font-size: 1.05rem;
}

.accueil-atouts { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.accueil-atout {
  display: flex; gap: 14px; align-items: flex-start;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px;
}
.accueil-atout-ic { width: 40px; height: 40px; border-radius: 12px; }
.accueil-atout-ic .ic { width: 1.25rem; height: 1.25rem; vertical-align: 0; }
.accueil-atout b { display: block; margin-bottom: 3px; }
/* `span.muted` et non `span` seul : un `span` nu matchait aussi
   `.accueil-atout-ic` (c'est également un span) et écrasait son
   `display: grid` — l'icône se retrouvait collée en haut à gauche de son
   médaillon au lieu d'être centrée. */
.accueil-atout span.muted { display: block; }

/* Mouvement — natif, sans une ligne de JavaScript.
   L'etat par defaut est VISIBLE : l'animation ne s'ajoute que la ou le
   navigateur sait la piloter au defilement. Ailleurs, la page se lit
   normalement. C'est l'amelioration progressive, comme le reste du site. */
@keyframes accueil-revele { from { opacity: 0; transform: translateY(22px); } }
@keyframes accueil-entre { from { opacity: 0; transform: translateY(16px); } }
@keyframes squelette-reflet { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@keyframes squelette-respire { 0%, 100% { transform: translateY(-13px); } 50% { transform: translateY(13px); } }
/* chaque etoile s'allume a son tour et le reste jusqu'a la fin du cycle : vu
   ensemble, les cinq creent un remplissage progressif de 1 a 5, jamais figé. */
@keyframes squelette-etoile-remplit {
  0%, 6% { color: var(--line); }
  12%, 88% { color: var(--muted); }
  94%, 100% { color: var(--line); }
}

@media (prefers-reduced-motion: no-preference) {
  .entre { animation: accueil-entre .7s cubic-bezier(.2, .7, .2, 1) both; }
  .entre-2 { animation-delay: .09s; }
  .entre-3 { animation-delay: .18s; }
  .entre-4 { animation-delay: .27s; }
  @supports (animation-timeline: view()) {
    .revele {
      animation: accueil-revele linear both;
      animation-timeline: view(); animation-range: entry 2% cover 26%;
    }
  }

  .squelette {
    background-image: linear-gradient(100deg, var(--line) 35%, var(--surface) 50%, var(--line) 65%);
    background-size: 250% 100%;
    animation: squelette-reflet 1.8s ease-in-out infinite;
  }
  .squelette-carte { animation: squelette-respire 3.4s ease-in-out infinite; }
  .squelette-etoile { animation: squelette-etoile-remplit 3s ease-in-out infinite; }
  .squelette-etoile:nth-child(2) { animation-delay: .25s; }
  .squelette-etoile:nth-child(3) { animation-delay: .5s; }
  .squelette-etoile:nth-child(4) { animation-delay: .75s; }
  .squelette-etoile:nth-child(5) { animation-delay: 1s; }
}

@media (max-width: 860px) {
  /* Une seule colonne. Pas de `order` : la promesse doit se lire avant le
     visuel — montrer le produit a quelqu'un qui ne sait pas encore ce qu'il
     regarde ne lui apprend rien. */
  .accueil-grille { grid-template-columns: minmax(0, 1fr); }
  .accueil-visuel::before { display: none; }

  /* La carte et le texte sont empilés, plus côte à côte : la lanière n'a
     plus rien à croiser. Repli plus simple — une petite anse au-dessus de
     la carte — plutôt que le long cordon pensé pour la mise en page bureau. */
  .squelette-cordon, .squelette-clip { display: none; }
  .squelette-carte::before {
    content: ""; position: absolute; top: -20px; left: 50%; transform: translateX(-50%);
    width: 32px; height: 20px; border: 5px solid var(--line); border-bottom: none;
    border-radius: 16px 16px 0 0;
  }
}

/* ---------- Mobile ----------
   Tout s'empile a la verticale : rien ne doit deborder sur le cote, la page ne
   defile que vers le bas. Ce qui reste large par nature (tableaux, planche de QR)
   defile a l'interieur de son propre cadre. */
@media (max-width: 640px) {
  h1 { font-size: 1.5rem; }
  h2 { font-size: 1.15rem; }
  :root { --card-pad: 16px; }
  .container { padding: 0 14px; }

  /* Navigation : la marque sur sa ligne, puis les liens qui se partagent
     toute la largeur a parts egales — une seule rangee, pas de retour a la ligne */
  .navbar .container {
    flex-direction: column; align-items: stretch;
    gap: 8px; padding: 10px 14px; min-height: 0;
  }
  .brand { justify-content: center; font-size: 1.05rem; }
  .nav-links {
    width: 100%; margin-left: 0; gap: 6px;
    flex-wrap: nowrap; align-items: stretch;
  }
  /* `flex: 1 1 0` : les items se partagent la largeur a parts strictement egales,
     quelle que soit la longueur de leur libelle */
  .nav-links > *, .nav-form { flex: 1 1 0; min-width: 0; }
  .nav-links a, .nav-form .btn {
    display: flex; width: 100%; height: 100%;
    align-items: center; justify-content: center; text-align: center;
    padding: 8px 4px; font-size: .8rem; line-height: 1.2;
    /* un libelle trop long passe a la ligne DANS son propre bouton plutot que
       d'etre tronque ou de renvoyer l'item suivant a la ligne suivante */
    white-space: normal;
  }
  .nav-court { display: none; } /* « ma boutique », « (demo) » : superflu sur mobile */

  /* Fiche profil : photo, note, partage, formulaire, avis — les uns sous les autres */
  .page-head { flex-direction: column; align-items: stretch; gap: 12px; }
  .page-head .avatar { align-self: center; }
  /* le portrait garde son ratio ; on borne seulement sa largeur */
  .hero-photo { max-width: 260px; }
  .share-card .qr { width: 100%; max-width: 200px; height: auto; }

  /* Les rangees d'actions restent lisibles plutot que de se tasser */
  .btn-row { width: 100%; }
  .btn-row > .btn { flex: 1 1 auto; }
  .search-bar .btn { flex: 1 1 auto; }
  /* `auto-fit, minmax(130px, ...)` retombait a une seule colonne sous 360px
     (3 x 130px ne tient pas), et le libelle en majuscules ("COMMENTAIRES")
     se coupait au milieu du mot dans les colonnes de 2 devenues trop
     etroites. Deux colonnes fixes, quel que soit le nombre de vignettes :
     jamais de colonne unique, jamais de mot coupe — un total impair laisse
     la derniere vignette seule sur sa ligne, ce qui reste lisible. */
  .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .stat { padding: 10px 8px; min-width: 0; }
  .stat .value { font-size: 1.3rem; }
  .stat .label { font-size: .66rem; letter-spacing: 0; }

  /* Atouts de l'accueil : `auto-fit, minmax(270px, ...)` ne tient pas a deux
     sur un telephone, donc retombait a une seule carte par ligne — beaucoup
     de defilement pour cinq lignes. Deux colonnes fixes, icone au-dessus du
     texte plutot qu'a cote (une carte deux fois plus etroite n'a plus la
     place d'un intitule en une ligne a cote de l'icone). */
  .accueil-atouts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .accueil-atout {
    flex-direction: column; align-items: center; text-align: center;
    padding: 14px 10px; gap: 8px;
  }
  .accueil-atout b { font-size: .86rem; }
  .accueil-atout span.muted { font-size: .76rem; }
  .quick-rate { justify-content: center; }
  .reactions { justify-content: space-between; }
  .qr-sheet { grid-template-columns: 1fr; }
  /* le QR passe au-dessus du texte, et les volets prennent toute la largeur */
  .qr-boutique { flex-direction: column-reverse; text-align: center; }
  .qr-boutique .btn-row { justify-content: center; }
  .volet, .volet > summary { width: 100%; }

  /* Un tableau est large par nature : plutot que de tasser ses colonnes, on le
     laisse defiler horizontalement DANS son cadre (.table-wrap). La page, elle,
     ne defile que vers le bas. */
  /* les graphiques se resserrent au lieu de forcer un defilement lateral */
  .chart-cols { gap: 3px; }
  .chart-fut { height: 110px; }
  .chart-valeur { font-size: .68rem; }
  .chart-axe { font-size: .62rem; }
  .filtre-bar .form-control, .filtre-bar .btn { flex: 1 1 100%; width: 100%; }

  .table-wrap { -webkit-overflow-scrolling: touch; }
  .table-wrap table { min-width: 460px; }
  th, td { padding: 10px 8px; }
  /* la cellule cliquable garde une marge nulle : c'est son lien qui la porte,
     sinon la ligne cesse d'etre cliquable sur ses bords (voir .lien-cellule) */
  td.cellule-lien { padding: 0; }
  .lien-cellule { padding: 10px 8px; }
}

/* ---------- Impression (planche de QR codes) ---------- */
@media print {
  .navbar, footer, .no-print, .messages { display: none !important; }
  body { background: #fff; }
  .container { max-width: none; padding: 0; }
  .qr-sheet { grid-template-columns: repeat(2, 1fr); gap: 10mm; }
  .qr-card { break-inside: avoid; box-shadow: none; }
}
