/* =========================================================
   Polices hébergées localement — Pharmacie des Battières
   Les fichiers sont servis depuis assets/fonts/ et non depuis
   un serveur tiers : aucune donnée de visiteur n'est transmise
   à l'extérieur, ce qui évite l'obligation de bandeau cookies.
   Voir docs/REGISTRE_COOKIES.md
   ========================================================= */

@font-face {
  font-family: 'Cinzel';
  src: url('/assets/fonts/cinzel-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cinzel';
  src: url('/assets/fonts/cinzel-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Jost';
  src: url('/assets/fonts/jost-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Jost';
  src: url('/assets/fonts/jost-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Marck Script';
  src: url('/assets/fonts/marck-script-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* =========================================================
   Pharmacie des Battières — feuille de style principale
   Charte : crème chaud dominant, anthracite en accent,
   bois et terracotta en touches chaleureuses.
   Typographie : Cinzel (enseigne) + Marck Script (signature)
   ========================================================= */

/* Palette — deux niveaux :
   les teintes « pures » servent aux usages décoratifs (bordures, fonds,
   liserés) ; les variantes suffixées servent au TEXTE et sont ajustées
   pour respecter le contraste WCAG AA (4.5:1) sur crème comme sur blanc.
   Enjeu concret : lisibilité pour une clientèle âgée. */
:root {
  --cream: #FAF3E7;
  --cream-deep: #F0E4CE;
  --wood: #C9A467;
  --terracotta: #B5652E;        /* décoratif : bordures, liserés */
  --terracotta-text: #A55C2A;   /* texte sur fond clair — conforme AA */
  --terracotta-bg: #B0622D;     /* fond portant du texte blanc — conforme AA */
  --ink: #26282B;
  --ink-soft: #5a5044;
  --ink-muted: #796E5D;         /* ajusté pour conformité AA */
  --ink-faint: #796E5C;         /* ajusté pour conformité AA */
  --border: #e8dcc8;
  --white: #ffffff;
  --radius: 10px;
  --radius-lg: 14px;
  /* Respiration verticale entre les blocs de contenu. Valeur UNIQUE pour que
     toutes les sections respirent pareil, plutôt que des retouches au cas par
     cas. 64px : nette montée depuis les 40px d'origine, jugés trop serrés,
     tout en restant cohérent avec le rythme existant (56px au-dessus du hero,
     48px avant le pied de page). Réduite sur mobile, où 64px imposerait un
     défilement inutile. */
  --section-gap: 64px;
  /* Respiration en FIN de page, sous le dernier bloc de contenu. 48px : c'est
     déjà l'écart que « .site-footer { margin-top: 48px } » impose entre le
     contenu et le pied de page sur le site public. On réemploie cette valeur
     plutôt que d'en inventer une, pour que la fin d'une page d'administration
     respire comme la fin d'une page publique. Réduite sur mobile, comme
     --section-gap. */
  --fin-de-page: 48px;
  /* Taille d'affichage des pictogrammes — DÉFINIE ICI, UNE SEULE FOIS. Ne
     jamais la refixer au cas par cas dans une page : c'est ce qui produit des
     séries de pictogrammes de tailles légèrement différentes, impossibles à
     rattraper ensuite. Les fichiers sont carrés (512x512), la valeur vaut donc
     largeur ET hauteur. */
  --picto-taille: 48px;
  --font-body: 'Jost', sans-serif;
  --font-display: 'Cinzel', serif;
  --font-signature: 'Marck Script', cursive;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* ...MAIS PAS SUR LES ÉCRANS D'ADMINISTRATION — 08/08/2026.
   Vérifié avant d'y toucher : c'est bien la ligne ci-dessus qui animait le
   retour après enregistrement. La redirection avec ancre dépose la page en
   haut, puis le navigateur descend EN GLISSANT jusqu'à l'opération traitée.
   La destination était juste, c'est le trajet qui gênait — et sur cinquante-six
   typages d'affilée, un trajet inutile répété est ce qui use.

   POURQUOI NE PAS SUPPRIMER LE LISSAGE PARTOUT : il sert. Relevé le
   08/08/2026, les seuls liens d'ancre internes du site sont les six entrées du
   sommaire de gammes.php ; l'administration, elle, n'en a aucun.

   ── UNE CLASSE EXPLICITE, ET NON UNE DÉDUCTION ───────────────────────────
   Première version : « html:has(> body:not(:has(> footer))) », qui déduisait
   « écran d'administration » de « la page n'a pas de pied de page ». Deux
   défauts, et le second a été mesuré dans un navigateur avant d'être cru :

     1. le critère n'est pas la DÉFINITION d'un écran d'administration, mais
        une propriété qu'ils partagent aujourd'hui. Un écran d'admin qui
        gagnerait un pied de page — ou une page publique qui en perdrait un —
        ferait mentir la règle, et personne ne ferait le lien ;
     2. surtout, CE SÉLECTEUR ÉTAIT INVALIDE : « :has() » ne peut pas être
        imbriqué dans « :has() ». Le navigateur rejetait la règle entière, elle
        n'a JAMAIS eu le moindre effet, et le commentaire qui l'accompagnait
        décrivait posément sa « dégradation acceptée ».
        La question se tranche en une ligne, dans la console du navigateur :
        CSS.supports('selector(html:has(> body:not(:has(> footer))))') → false.

   La classe coûte un attribut par écran et ne se trompe pas : elle survit à
   l'ajout d'un pied de page, n'exige aucun « :has() », et dit ce qu'elle est. */
html.admin { scroll-behavior: auto; }

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--cream);
  color: var(--ink);
  line-height: 1.6;
}

img { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }

.container {
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ---------- Typography helpers ---------- */

.brand {
  margin: 0;
  line-height: 1;
  white-space: nowrap;
}
.brand-main {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink);
}
.brand-script {
  font-family: var(--font-signature);
  color:var(--terracotta-text);
}

.eyebrow {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color:var(--terracotta-text);
  margin: 0 0 8px;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--ink);
  margin: 0 0 12px;
}
h1 { font-size: 28px; }
h2 { font-size: 21px; }
h3 { font-size: 16px; }

p { margin: 0 0 12px; color: var(--ink-soft); }

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 13px;
  padding: 11px 22px;
  border-radius: 6px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: opacity .15s ease;
}
.btn:hover { opacity: 0.85; }
.btn-primary { background: var(--ink); color: var(--cream); }
.btn-outline { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-accent { background: var(--terracotta-bg); color: #fff; }

/* ---------- Header / Nav ---------- */

/* Le fond crème du bandeau s'étend d'un bord à l'autre de l'écran ;
   seul le contenu (logo + menu) est contraint dans la zone centrée
   (.site-header-inner, voir includes/header.php). */
.site-header {
  border-bottom: 0.5px solid var(--border);
  background: var(--cream);
}
.site-header-inner {
  max-width: 1140px; /* aligné sur .container pour que le logo tombe sur le bord du contenu */
  margin: 0 auto;
  padding: 14px 24px; /* compense le logo agrandi : hauteur de bandeau raisonnable */
  display: flex;
  align-items: center; /* logo, liens, panier et bouton RDV sur la même ligne */
  justify-content: space-between;
  gap: 24px;
  position: relative;  /* ancre du menu déroulant mobile */
}
/* Compatibilité : pages (admin) qui utilisent encore <header class="site-header container"> */
.site-header.container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 24px;
}
.site-header .logo-group {
  display: flex;
  align-items: center;
  gap: 12px;
}
.site-header .logo-group img {
  /* Le logo est rendu en 90px mais ses marges négatives ramènent sa
     hauteur "de mise en page" à 70px : il occupe l'espace de respiration
     du padding vertical SANS agrandir le bandeau (hauteur inchangée).
     90 rendu dans un bandeau de ~99 => ~4px de marge en haut et en bas. */
  width: 90px;
  height: 90px;
  margin: -10px 0;
}
.site-header .brand-main { font-size: 22px; }
.site-header .brand-script { font-size: 24px; }

.main-nav {
  display: flex;
  align-items: center; /* corrige le décalage vertical panier / bouton RDV */
  /* 26 -> 16 -> 12px. Le second resserrement, le 06/08/2026, paie l'entrée
     « Dossiers » : le menu est passé de 638 à 707px, et la marge entre le nom
     de la pharmacie et le premier lien est tombée de ~45px à UN pixel.
     Un pixel n'est pas une marge : au survol, les libellés passent en graisse
     500 et s'élargissent d'environ 5,6px — le chevauchement serait revenu,
     et il ne se voit PAS comme un débordement de page (§12 d'ETAT_PROJET).
     12px sur six écarts rendent 24px, sans toucher ni à la taille de police
     ni aux libellés. Mesuré après coup : voir le commentaire du seuil burger. */
  gap: 12px;
  font-size: 15px;
  color: var(--ink-soft);
}
/* Chaque item tient sur UNE seule ligne : « Nos services », « Informations
   utiles » et le bouton « Prendre rendez-vous » se cassaient en deux, ce qui
   désalignait les libellés entre eux. */
.main-nav a { white-space: nowrap; }
.main-nav a.active,
.main-nav a:hover { color: var(--ink); font-weight: 500; }

.nav-toggle { display: none; }

/* Bascule hamburger : seuil relevé de 940 à 1140px, aligné sur la largeur
   maximale du conteneur.
   Mesuré dans le navigateur le 30/07/2026 : sans retour à la ligne, le bloc
   logo réclame 383px (image 88 + 12 + « PHARMACIE / des Battières » 283, que
   .brand empêche de se couper) et le menu 640px, soit 1047px de contenu —
   1095px avec les marges intérieures.
   Un seuil trop bas ne se voit PAS comme un débordement : le bloc logo, qui
   ne peut pas rétrécir, déborde alors sous le menu et le nom de la pharmacie
   chevauche « Nos services ». C'est ce qui se produisait à 1024px, où le
   chevauchement atteignait 64px.
   À 1140px, le conteneur est plafonné : la marge était d'environ 30px juste
   au-dessus du seuil et de 45px au-delà — de quoi absorber l'élargissement
   des libellés au survol (graisse 500, +5,6px) et les écarts de rendu.

   ── RELEVÉ DU 06/08/2026, APRÈS L'AJOUT DE « DOSSIERS » ──
   Le menu est passé de 638 à 707px. La marge est tombée à UN pixel, ce qui
   n'est pas une marge : un seul lien survolé coûte 6px et le nom de la
   pharmacie serait repassé sous le menu.
   L'écart entre liens a donc été resserré de 16 à 12px (six écarts, 24px
   rendus) : le menu redescend à 683px. LE SEUIL DE 1140 N'A PAS BOUGÉ, et
   c'est un constat mesuré, pas une reconduction par défaut :
       à 1154px et au-delà .... 24px au repos, 18px au pire survol
       à 1141px, juste au-dessus du seuil ... 11px au repos, 5px au survol
   Le pire survol est « Prendre rendez-vous » (+6px), et non un lien de menu.

   ⚠ LE BUDGET EST PRESQUE ÉPUISÉ. Il ne reste pas de place pour une huitième
   entrée. Or DEUX drapeaux en ajoutent TROIS à eux deux — relevé complété le
   07/08/2026, le troisième avait échappé au relevé du 06 :
       VENTE_EN_LIGNE_ENABLED .... « Nos gammes » ET « Panier », deux d'un coup
       PATIENT_SPACE_ENABLED ..... « Mon compte »
   (voir includes/header.php, où les trois sont conditionnés.)
   Pour 24px de marge au repos et 18px au pire survol, aucune des deux
   activations ne passe : la mise en page casse le jour même, et le symptôme
   ne sera pas un débordement mais le nom de la pharmacie repassant sous le
   menu — celui qu'on met le plus de temps à reconnaître.
   ⚠ LE CALCUL SE FAIT AVANT D'ACTIVER L'UN OU L'AUTRE, PAS APRÈS, et il est
   à faire DEUX FOIS : les deux drapeaux sont indépendants et peuvent être
   activés séparément ou ensemble. Les trois issues connues — remonter le
   seuil du burger, resserrer encore l'écart entre liens (il est déjà passé de
   16 à 12px), ou sortir une entrée du bandeau — se tranchent dans le lot
   d'activation. Rappelé au §1 de docs/PREREQUIS_VENTE_EN_LIGNE.md et en tête
   de docs/PREREQUIS_ESPACE_PATIENT.md, qui sont les documents qu'on lit avant
   de basculer un interrupteur ; les chiffres, eux, ne vivent qu'ICI. */
@media (max-width: 1140px) {
  .main-nav {
    display: none;
    position: absolute;
    top: 100%; /* juste sous le bandeau, quelle que soit sa hauteur */
    left: 0;
    right: 0;
    background: var(--cream);
    flex-direction: column;
    align-items: stretch;
    padding: 16px 24px;
    border-bottom: 0.5px solid var(--border);
    gap: 14px;
    z-index: 20;
  }
  .main-nav.open { display: flex; }
  .nav-toggle {
    display: block;
    background: none;
    border: none;
    font-size: 22px;
    color: var(--ink);
    cursor: pointer;
  }
  /* Logo et nom légèrement réduits sur petit écran — même principe :
     rendu 72px, mise en page 52px, bandeau inchangé */
  .site-header .logo-group img { width: 72px; height: 72px; margin: -10px 0; }
  .site-header .brand-main { font-size: 18px; }
  .site-header .brand-script { font-size: 19px; }
}

/* ---------- Hero ---------- */

.hero {
  text-align: center;
  /* Le bas du hero suit la même respiration que les autres sections, sinon
     l'écart hero → premier bloc resterait à 40px quand tous les autres
     passent à 64px. */
  padding: 56px 24px var(--section-gap);
}
.hero .brand-main { font-size: 32px; }
.hero .brand-script { font-size: 36px; }
.hero .tagline {
  font-size: 13px;
  color: var(--ink-faint);
  margin: 10px 0 24px;
}
.hero .actions { display: inline-flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* ---------- Callout blocks ---------- */

.callout {
  background: var(--cream-deep);
  border-left: 3px solid var(--terracotta);
  border-radius: 0 8px 8px 0;
  padding: 16px 20px;
  margin: 0 0 24px;
}
.callout p { margin: 0; color: var(--ink); font-size: 14px; }

.banner-dark {
  background: var(--ink);
  border-radius: var(--radius);
  padding: 22px 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin: 0 0 32px;
}
.banner-dark .label { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--wood); margin: 0 0 4px; }
.banner-dark .value { font-size: 18px; color: var(--cream); font-weight: 500; margin: 0; }

/* ---------- Cards ---------- */

.card {
  background: var(--white);
  border: 0.5px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}

.grid {
  display: grid;
  gap: 14px;
}
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-6 { grid-template-columns: repeat(6, 1fr); }

@media (max-width: 900px) {
  .grid-4, .grid-6 { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 600px) {
  .grid-2, .grid-3, .grid-4, .grid-6 { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Product cards ---------- */

.product-card { position: relative; }
.product-card .thumb {
  background: var(--cream-deep);
  border-radius: 6px;
  height: 80px;
  margin-bottom: 8px;
}
.product-card .name { font-size: 12px; color: var(--ink); margin: 0 0 2px; }
.product-card .brand-name { font-size: 12px; color: var(--ink-faint); margin: 0 0 6px; }
.product-card .price { font-size: 14px; font-weight: 500; color: var(--ink); margin: 0; }
.product-card .price .old { text-decoration: line-through; color: #b0a48f; font-weight: 400; font-size: 12px; margin-left: 6px; }
.product-card.promo { border: 2px solid var(--terracotta); }
.badge-promo {
  position: absolute; top: 8px; right: 8px;
  background: var(--terracotta-bg); color: #fff;
  font-size: 10px; padding: 2px 7px; border-radius: 5px;
}

/* ---------- Team ---------- */

.avatar {
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--cream-deep);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.avatar svg { width: 30px; height: 30px; }
/* Avatar en image (le dessin de Romy, 11/09/2026) : fond transparent, donc le
   même rond crème que les silhouettes ; le dessin y tient entier. */
.avatar.avatar-image { overflow: hidden; }
.avatar.avatar-image img { width: 44px; height: 44px; object-fit: contain; display: block; }
.team-member { display: flex; gap: 14px; align-items: flex-start; }
.team-member .name { font-size: 13px; font-weight: 500; color: var(--ink); margin: 0 0 2px; }
.team-member .role { font-size: 12px; color: var(--ink-muted); margin: 0 0 2px; }
.team-member .specialty { font-size: 12px; color:var(--terracotta-text); margin: 0; }
/* Présentation facultative sous la carte (11/09/2026) : une ou deux phrases,
   bornées en largeur pour qu'une carte seule ne s'étire pas sur toute la page. */
.team-member .bio { font-size: 12px; color: var(--ink-soft); margin: 6px 0 0; max-width: 46ch; line-height: 1.5; }

/* ---------- Footer ---------- */

.site-footer {
  background: var(--ink);
  color: var(--cream);
  padding: 40px 0 20px;
  margin-top: 48px;
}
.site-footer a { color: var(--cream); opacity: 0.85; }
.site-footer .cols {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  margin-bottom: 24px;
}
.site-footer h4 {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.05em;
  color: var(--wood);
  margin: 0 0 10px;
}
.site-footer p, .site-footer li { font-size: 13px; color: var(--cream); opacity: 0.85; margin: 0 0 6px; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer .bottom {
  border-top: 0.5px solid rgba(250,243,231,0.15);
  padding-top: 16px;
  font-size: 12px;
  opacity: 0.6;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
}
@media (max-width: 780px) {
  .site-footer .cols { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Services (pictogrammes vitrophanie) ---------- */

.service-card { display: flex; gap: 16px; align-items: flex-start; }
.service-card img { flex-shrink: 0; margin-top: 2px; }
.service-card h3 { margin-bottom: 2px; }
.service-card .service-sub {
  font-size: 12px;
  color: var(--terracotta-text);
  margin: 0 0 6px;
}
.service-mini { text-align: center; }
.service-mini img { margin: 0 auto; }

/* ---------- Animations ----------
   Niveau volontairement discret (clientèle âgée) :
   - apparition en fondu + court glissement au défilement (.reveal)
   - survol des cartes : légère élévation
   - effet plus marqué RÉSERVÉ au bandeau promotion (.banner-promo)
   Tout est désactivé si l'utilisateur a réglé son système en
   « réduire les animations » (prefers-reduced-motion). */

.reveal {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .45s ease, transform .45s ease;
}
.reveal.visible {
  opacity: 1;
  transform: none;
}
/* Sans JavaScript (ou si l'observer échoue), tout reste visible */
.no-js .reveal { opacity: 1; transform: none; }

.card {
  transition: transform .2s ease, box-shadow .2s ease;
}
a.card:hover, .product-card:hover, .service-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 14px rgba(38, 40, 43, 0.08);
}

/* Entrée plus ample avec léger rebond — uniquement le bandeau promo */
@keyframes bannerPop {
  0%   { opacity: 0; transform: translateY(22px) scale(0.97); }
  70%  { opacity: 1; transform: translateY(-3px) scale(1.005); }
  100% { opacity: 1; transform: none; }
}
.banner-promo.visible {
  animation: bannerPop .65s cubic-bezier(0.22, 0.9, 0.35, 1.15) both;
}
.banner-promo { opacity: 0; }
.no-js .banner-promo { opacity: 1; }

/* IMPÉRATIF accessibilité : aucun mouvement si le système le demande */
@media (prefers-reduced-motion: reduce) {
  .reveal, .banner-promo {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .card { transition: none; }
  a.card:hover, .product-card:hover, .service-card:hover {
    transform: none;
    box-shadow: none;
  }
  html { scroll-behavior: auto; }
}

/* ---------- Bandeau de consentement cookies ---------- */

.cookie-banner {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  background: var(--cream);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 20px rgba(38, 40, 43, 0.10);
  padding: 18px 0;
}
.cookie-banner[hidden] { display: none; }
.cookie-banner-inner {
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 24px;
}
.cookie-banner-text h2 { font-size: 15px; margin: 0 0 4px; }
.cookie-banner-text p { font-size: 13px; margin: 0 0 12px; }
.cookie-banner-text a { color: var(--terracotta-text); }

.cookie-choices { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.cookie-choices[hidden] { display: none; }
.cookie-choice { display: flex; align-items: flex-start; gap: 8px; font-size: 13px; color: var(--ink-soft); }
.cookie-choice input { margin-top: 3px; width: 16px; height: 16px; accent-color: var(--terracotta-bg); flex-shrink: 0; }

.cookie-actions { display: flex; flex-wrap: wrap; gap: 10px; }
/* Refuser et Accepter partagent EXACTEMENT le même style (btn-primary) :
   aucun n'est visuellement minoré. */
.cookie-btn { flex: 0 0 auto; }
.cookie-btn[hidden] { display: none; }

@media (max-width: 600px) {
  .cookie-actions { flex-direction: column; }
  .cookie-btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .cookie-banner { box-shadow: 0 -1px 0 var(--border); }
}

/* ---------- Promotions ---------- */

.promo-filtres { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.promo-filtres .btn { padding: 7px 14px; font-size: 13px; }

/* 3 colonnes, pour que la carte de la grille (~353 px) et celle du carrousel
   (~351 px) aient la MÊME largeur. C'était le défaut d'origine : les cartes du
   carrousel étaient visiblement plus étroites que celles du reste du site. */
.promo-grille {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
@media (max-width: 900px) { .promo-grille { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .promo-grille { grid-template-columns: 1fr; } }

.promo-card {
  background: var(--white);
  border: 0.5px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100%;
}
.promo-visuel {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--cream-deep);
}
/* SANS VISUEL, ET SUR TÉLÉPHONE SEULEMENT : le bandeau est aplati.
   Au format 4/3 il mesurait ~239 px sur une carte de 319 — près de la moitié
   de l'écran pour un bloc qui ne montre rien. Réduit à 3/1, il vaut ~106 px :
   assez pour tenir la place du futur visuel, plus assez pour l'occuper.
   La règle NE S'APPLIQUE QU'EN L'ABSENCE D'IMAGE (classe posée en PHP) : dès
   que LaboData fournira les visuels, le 4/3 revient de lui-même, sans qu'on
   ait à défaire quoi que ce soit. Sur grand écran, rien ne change — les cartes
   y sont côte à côte et le bandeau se lit comme une mise en page. */
@media (max-width: 560px) {
  .promo-visuel-attente { aspect-ratio: 3 / 1; }
}
.promo-visuel img { width: 100%; height: 100%; object-fit: cover; }

/* EMPLACEMENT NEUTRE quand aucun visuel n'est fourni.
   Un aplat uni, SANS TEXTE ni hachures — décidé le 05/08/2026.
   Il portait le nom de la marque en terracotta sur des bandes alternant deux
   crèmes : la paire de couleurs est notée AA dans la palette, mais les bandes
   HACHAIENT les lettres et le badge de remise par-dessus. Un problème
   d'accessibilité sur du site public, pour un bloc destiné à disparaître dès
   que LaboData fournira les visuels.
   La marque n'y manque pas : elle est juste en dessous, en tête du corps de la
   carte — ce qui règle du même coup le doublon. */
.promo-visuel-vide {
  width: 100%; height: 100%;
  background: var(--cream-deep);
  border-bottom: 0.5px solid var(--border);
}
.promo-badge {
  position: absolute; top: 10px; left: 10px;
  background: var(--terracotta-bg); color: #fff;
  font-size: 13px; font-weight: 500;
  padding: 4px 10px; border-radius: 6px;
}
.promo-corps { padding: 12px 14px; }
/* HIÉRARCHIE : la marque est ce que le patient reconnaît, le laboratoire
   rattache l'offre à un fabricant. Avant, les deux étaient à 13 px et c'est
   le laboratoire qui portait la police de titre et l'encre pleine : la
   lecture était inversée. */
.promo-marque {
  font-family: var(--font-display);
  font-size: 15px; letter-spacing: 0.04em;
  color: var(--ink); margin: 0 0 1px;
}
.promo-labo { font-size: 12px; color: var(--ink-soft); margin: 0 0 6px; }
.promo-meta {
  margin: 0 0 6px; display: flex; align-items: center;
  flex-wrap: wrap; gap: 6px 8px;
  font-size: 11px; color: var(--ink-faint);
}
/* NOWRAP sur les deux mentions : ce sont des flex-items, donc rétrécissables,
   et sans cela « Bébé & maman » se cassait sur trois lignes et « Jusqu'au
   31/08 » sur deux — une colonne de mots empilés. Chaque mention reste
   entière ; si les deux ne tiennent pas côte à côte, flex-wrap les met l'une
   sous l'autre, ce qui est lisible. */
.promo-cat, .promo-fin { white-space: nowrap; }
.promo-cat {
  background: var(--cream); border: 0.5px solid var(--border);
  border-radius: 999px; padding: 2px 8px; color: var(--ink-soft);
  /* ALIGNEMENT DU TEXTE, PAS DE LA PASTILLE. La pastille était bien calée à
     gauche, mais ses 8 px de marge intérieure (plus 0,5 px de filet) poussaient
     son texte vers la droite : il ne tombait pas sous « MUSTELA » ni sous
     « Expanscience ». On retire exactement cette valeur pour que la colonne de
     texte soit continue — c'est le texte qu'on lit, pas la boîte. */
  margin-left: -8.5px;
}
.promo-prix { margin: 0; display: flex; align-items: baseline; gap: 8px; }
.promo-prix-final { font-size: 18px; font-weight: 500; color: var(--ink); }
.promo-prix-barre { font-size: 13px; color: var(--ink-faint); text-decoration: line-through; }

.promo-mention {
  margin-top: 24px; font-size: 12px; color: var(--ink-faint);
  border-top: 0.5px solid var(--border); padding-top: 14px;
}

/* ---------- Carrousel (promotions accueil) ---------- */

/* LES FLÈCHES SONT HORS DU FLUX (position absolue).
   Dans le flux, elles coûtaient 96 px — 2 × 40 px plus les gouttières — et la
   piste était donc structurellement plus étroite que le conteneur : la carte
   du carrousel mesurait ~319 px quand la même carte, sur /promotions.php, en
   mesurait ~261 px puis ~353 px. Hors du flux, la piste occupe toute la
   largeur et la carte atteint ~351 px : les deux pages convergent.
   Elles se placent à -12 px, donc à cheval sur les 24 px de marge intérieure
   du conteneur : le débordement de page reste impossible. */
.carousel { position: relative; }
.carousel-piste {
  display: flex;
  flex-wrap: nowrap;
  gap: 16px;
  overflow-x: auto;
  /* MANDATORY et non « proximity » : avec proximity, le navigateur décide s'il
     cale ou non, et en pratique il ne calait pas — un glissement au doigt
     laissait la piste à cheval sur deux cartes. Le calage doit être une
     garantie, pas une suggestion. Chaque carte porte scroll-snap-align: start,
     donc le défilement s'arrête toujours sur un bord de carte. */
  scroll-snap-type: x mandatory;
  /* Le défilement animé est piloté en JS (requestAnimationFrame) pour un
     rendu fiable partout ; pas de scroll-behavior CSS qui entrerait en
     conflit avec l'animation JS. */
  padding: 4px;
  min-width: 0;
  /* masque la barre de défilement native, la navigation se fait aux flèches */
  scrollbar-width: none;
}
.carousel-piste::-webkit-scrollbar { display: none; }
/* 3 cartes visibles desktop (flex-basis fixe, pas de rétrécissement).
   La formule est relative à la PISTE : les flèches étant sorties du flux,
   celle-ci occupe désormais toute la largeur du conteneur, et la carte passe
   de ~319 px à ~351 px sans que ce calcul change. */
.carousel-item { flex: 0 0 calc((100% - 2 * 16px) / 3); scroll-snap-align: start; }
.carousel-piste:focus-visible { outline: 2px solid var(--terracotta-bg); outline-offset: 2px; }

@media (max-width: 900px) {
  .carousel-item { flex-basis: calc((100% - 16px) / 2); } /* 2 sur tablette */
}
@media (max-width: 560px) {
  /* LA CARTE SUIVANTE DÉPASSE, ET C'EST LA SEULE AFFORDANCE QUI RESTE.
     Les flèches sont masquées sous 560 px (elles couvraient un cinquième de la
     carte) : sans rien pour les remplacer, une carte occupait tout l'écran et
     RIEN NE DISAIT qu'il y en avait d'autres. Un visiteur ne fait pas glisser
     ce qu'il ne soupçonne pas.
     44 px retirés à la largeur, 16 px de gouttière : il reste 28 px de la carte
     suivante visibles au bord droit. C'est l'affordance la moins coûteuse —
     une ligne, aucun script, et elle se lit au doigt sans être expliquée. Des
     points de pagination auraient demandé du JavaScript à construire et à tenir
     à jour ; des flèches réduites n'ont nulle part où aller, la marge du
     conteneur ne faisant que 24 px. */
  .carousel-item { flex-basis: calc(100% - 44px); }
}

.carousel-fleche {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 40px; height: 40px; border-radius: 50%;
  border: 0.5px solid var(--border);
  background: var(--cream);
  color: var(--ink);
  font-size: 22px; line-height: 1;
  cursor: pointer;
  transition: opacity .15s ease, background .15s ease;
}
/* -12 px : la flèche mord sur les 24 px de marge intérieure du conteneur, donc
   elle ne recouvre la carte que sur 28 px et ne peut pas faire déborder la
   page. Fond opaque pour rester lisible par-dessus un visuel. */
.carousel-prev { left: -12px; }
.carousel-next { right: -12px; }
.carousel-fleche:hover { background: var(--cream-deep); }
.carousel-fleche:disabled { opacity: 0.3; cursor: default; }
.carousel-fleche:focus-visible { outline: 2px solid var(--terracotta-bg); outline-offset: 2px; }

/* SUR TÉLÉPHONE, LES FLÈCHES SONT MASQUÉES. Hors du flux, elles couvriraient
   56 px d'une carte qui n'en fait que ~319 — près d'un cinquième. La piste
   reste défilable au doigt (overflow-x) et au clavier (la piste porte
   tabindex="0"), donc aucune navigation n'est perdue. Elles restent dans le
   DOM : le script les interroge par sélecteur et n'a pas à savoir qu'elles
   sont masquées.
   Règle placée APRÈS les autres règles .carousel-fleche : à spécificité
   égale, c'est la dernière qui l'emporte. */
@media (max-width: 560px) {
  .carousel-fleche { display: none; }
}

/* ---------- Section spacing ---------- */

/* Une valeur UNIQUE gouverne l'espace entre tous les blocs de la page
   d'accueil (<section> n'est utilisé que là) : les deux manques signalés —
   entre les pictos « Nos gammes » et le titre « Nos services », puis entre le
   bouton Doctolib et « Votre officine » — relèvent du même réglage. */
section { padding: 0 0 var(--section-gap); }
/* IMPORTANT — les sections de l'accueil portent aussi la classe .container,
   dont « padding: 0 24px » l'emportait (spécificité 0-1-0 contre 0-0-1) : le
   padding bas de « section » n'était donc JAMAIS appliqué et les blocs se
   touchaient. C'est la cause réelle du manque de respiration constaté, et non
   la valeur elle-même. Ce sélecteur (0-1-1) rétablit l'espacement. */
section.container { padding-bottom: var(--section-gap); }
.section-title { text-align: center; margin-bottom: 24px; }

/* Sur mobile, 64px d'entre-deux allongeraient le défilement sans rien
   apporter : l'espace y est déjà rythmé par l'empilement des cartes. */
@media (max-width: 600px) {
  :root { --section-gap: 44px; --fin-de-page: 32px; }
}

/* ---------- Respiration en fin de page (pages sans pied de page) ----------
   Les pages d'administration n'ont PAS de pied de page : rien ne vient fermer
   le contenu. Or « .container { padding: 0 24px } » met le padding BAS à zéro
   par le raccourci, et les pages posent seulement un « padding-top » en style
   en ligne. Résultat : la dernière rangée de cartes touchait exactement le bord
   de la zone de défilement (mesuré : 0 px), et la page paraissait coupée.

   ⚠️ Ce n'est PAS le conflit de spécificité corrigé le 30/07/2026 sur la
   vitrine. Là-bas, « section { padding: 0 0 var(--section-gap) } » existait
   mais PERDAIT contre « .container » (0-0-1 contre 0-1-0). Ici, aucune règle de
   la feuille ne vise « main » : il n'y a rien à écraser, le padding bas n'a
   simplement jamais été posé. Même coupable — le raccourci de « .container » —
   mais deux mécanismes distincts.

   Le critère retenu est « la page n'a pas de pied de page », et non « c'est une
   page d'administration » : c'est l'absence de pied de page qui crée le besoin,
   et cela évite d'ajouter une classe au HTML. Les pages publiques, qui ont un
   pied de page, ne sont pas touchées — leur écart de 48px reste inchangé.

   Deux formulations du MÊME critère, à conserver ensemble :
     1. « :last-child » — support universel, y compris sur un vieux téléphone ;
     2. « :not(:has(> footer)) » — survivrait à l'ajout d'un <script> après
        </main>, qui ferait silencieusement tomber la première.
   Si un navigateur ancien ignore la seconde, la première fait le travail. */
main.container:last-child { padding-bottom: var(--fin-de-page); }
body:not(:has(> footer)) > main.container { padding-bottom: var(--fin-de-page); }

/* ---------- Pictogrammes ----------
   Série « picto-<sujet> » d'assets/img/, servie par la fonction picto() de
   includes/header.php. Taille imposée ici et nulle part ailleurs.
   Hauteur ET largeur sont fixées : la place est ainsi réservée avant le
   chargement de l'image, sans décalage de mise en page. */
.picto {
  width: var(--picto-taille);
  height: var(--picto-taille);
  display: block;
  /* Les dessins occupent 90 % de leur cadre : « contain » garantit qu'aucun
     ne sera rogné si un fichier s'écartait du gabarit carré. */
  object-fit: contain;
}
/* Centré dans les cartes, qui sont en texte centré. */
.card > .picto { margin: 0 auto 8px; }
/* En tête d'un dossier conseil : aligné à gauche comme le titre qui suit. */
.picto-dossier { margin: 0 0 10px; }
/* Bloc certification : pictogramme un peu plus présent, il porte le bloc. */
.picto-iso { width: 64px; height: 64px; flex-shrink: 0; }

/* ---------- Index des dossiers conseils ----------
   Six lignes « pictogramme + titre + accroche », et DEUX COLONNES QUI DOIVENT
   TENIR : tous les pictogrammes sur une verticale, tous les titres sur une
   autre. La longueur d'un titre ne doit déplacer personne.

   Ce que ça corrige, mesuré dans le navigateur le 07/08/2026 sur six dossiers :
   les pictogrammes s'étalaient sur 105px (x = 376 à 481) et les titres sur
   209px (x = 521 à 730), chaque ligne se centrant sur elle-même — un escalier.
   La cause n'était PAS dans cette page : c'est « .card > .picto { margin: 0
   auto } », écrit pour les cartes en texte centré de l'accueil. Sur un
   conteneur en flex, une marge « auto » absorbe l'espace libre des deux côtés :
   plus le texte est court, plus le pictogramme est repoussé vers la droite.
   La règle ci-dessous vient APRÈS celle-là dans la feuille, et c'est ce qui la
   neutralise — même spécificité, l'ordre tranche. */
.dossier-carte {
  display: flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  margin-bottom: 12px;
}
.dossier-carte > .picto {
  margin: 0;         /* ← annule le centrage hérité des cartes de l'accueil */
  flex-shrink: 0;    /* la colonne garde sa largeur quand l'accroche est longue */
}
/* Case vide de même largeur, quand un dossier n'a pas de pictogramme : elle ne
   se voit pas, elle tient la colonne. */
.picto-vide { display: block; }
.dossier-carte-texte { display: block; min-width: 0; }
.dossier-carte-titre {
  display: block;
  font-size: 15px; font-weight: 500; color: var(--ink);
  margin-bottom: 4px;
}
.dossier-carte-accroche { display: block; font-size: 13px; }

/* ---------- Articles des dossiers conseils ---------- */
.article-corps p { margin: 0 0 14px; }
.article-corps p:last-child { margin-bottom: 0; }
/* Titres de section du corps. Ils descendent d'un cran sous le <h1> du titre
   de l'article : la hiérarchie doit rester lisible pour les moteurs et les
   lecteurs d'écran, pas seulement à l'œil. */
.article-corps h2 {
  font-family: var(--font-display);
  font-size: 18px; font-weight: 500;
  color: var(--ink);
  margin: 26px 0 10px;
}
.article-corps h2:first-child { margin-top: 0; }
.article-corps ul { margin: 0 0 14px; padding-left: 20px; }
.article-corps li { margin: 0 0 6px; }
.article-corps strong { font-weight: 500; color: var(--ink); }
/* Filet de séparation, rendu depuis « --- » (07/08/2026). Sans cette règle il
   serait resté au style par défaut du navigateur — un liseré gris en relief,
   épais et daté, dans une page qui n'emploie que des traits de 0,5px. Le
   rendre n'était pas fini tant qu'il ne ressemblait pas au reste : il sert
   surtout à détacher le bloc « Sources » de la fin du texte. */
.article-corps hr {
  border: none;
  border-top: 0.5px solid var(--border);
  margin: 24px 0;
}
/* Mention légale : rendue par le site sur CHAQUE article, jamais recopiée dans
   le corps — voir article.php. Discrète, mais pas escamotée. */
.article-mention {
  margin: 22px 0 0;
  font-size: 12px; font-style: italic;
  color: var(--ink-soft);
}
/* Les deux dates, discrètes mais lisibles : sur un conseil de santé, la
   fraîcheur de l'information fait partie de l'information. */
.article-dates {
  margin: 18px 0 0;
  font-size: 12px;
  color: var(--ink-faint);
  padding-top: 10px;
  border-top: 0.5px solid var(--border);
}
.article-carte { display: block; text-decoration: none; margin-bottom: 10px; }
.article-carte-titre { font-size: 15px; font-weight: 500; color: var(--ink); margin: 0 0 4px; }
.article-carte-chapo { font-size: 13px; margin: 0; }
.article-carte-date { font-size: 11px; color: var(--ink-faint); margin: 6px 0 0; }

/* ---------- Grille de choix d'un pictogramme (administration) ----------
   De vrais boutons radio, SANS JavaScript : l'input précède son étiquette, et
   « input:checked + .picto-choix » marque la sélection. Le sélecteur frère est
   choisi plutôt que :has() parce qu'il fonctionne partout, sans exception.

   Les inputs sont masqués VISUELLEMENT mais restent focalisables au clavier :
   ni display:none ni visibility:hidden, qui les sortiraient de l'ordre de
   tabulation et rendraient le champ inutilisable sans souris. */
.picto-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 8px;
}
.picto-radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}
.picto-choix {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 10px 6px;
  min-height: 104px;
  border: 0.5px solid var(--border);
  border-radius: var(--radius);
  background: var(--white);
  cursor: pointer;
  text-align: center;
  transition: border-color .15s ease, background .15s ease;
}
.picto-choix:hover { border-color: var(--wood); background: var(--cream); }
.picto-choix-nom {
  font-size: 11px;
  line-height: 1.3;
  color: var(--ink-soft);
}
/* Vignette : plus grande que la taille courante, on choisit sur le dessin. */
.picto-vignette { width: 44px; height: 44px; }

/* Sélection — bordure franche et fond teinté. La couleur ne porte pas seule
   l'information : la bordure s'épaissit et le libellé passe en gras, pour que
   le choix reste lisible en cas de trouble de la vision des couleurs. */
.picto-radio:checked + .picto-choix {
  border: 2px solid var(--terracotta-bg);
  padding: 8.5px 4.5px;              /* compense les 1,5px de bordure gagnés */
  background: var(--cream-deep);
}
.picto-radio:checked + .picto-choix .picto-choix-nom {
  color: var(--ink);
  font-weight: 500;
}
/* Anneau de focus visible au clavier : sans lui, la navigation au clavier
   serait aveugle puisque l'input lui-même est masqué. */
.picto-radio:focus-visible + .picto-choix {
  outline: 2px solid var(--terracotta-text);
  outline-offset: 2px;
}
/* « Aucun pictogramme » : même gabarit que les autres, sans image. */
.picto-choix-aucun .picto-aucun-marque {
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px;
  color: var(--ink-faint);
  border: 1px dashed var(--border);
  border-radius: 50%;
}

@media (prefers-reduced-motion: reduce) {
  .picto-choix { transition: none; }
}

/* ---------- Services groupés par rendez-vous ----------
   Les groupes rythment la page : un intertitre, une phrase, puis la grille.
   L'écart entre groupes reprend --section-gap pour rester dans le rythme
   vertical du reste du site. */
.services-groupe { padding: 0 0 var(--section-gap); }
.services-groupe:last-of-type { padding-bottom: 0; }
.services-groupe-titre {
  font-family: var(--font-display);
  font-size: 17px;
  margin: 0 0 2px;
  padding-bottom: 6px;
  border-bottom: 0.5px solid var(--border);
}
.services-groupe-intro {
  font-size: 13px;
  color: var(--ink-faint);
  margin: 0 0 16px;
}

/* Survol : l'encre des SVG est FIXE (#3A3329 dans le fichier) — on ne peut pas
   la recolorer en CSS, et essayer via un filtre dénaturerait la teinte. On joue
   donc sur l'opacité et un léger relèvement, qui marchent identiquement pour
   les SVG et les PNG. */
a:hover > .picto,
a:hover .picto {
  opacity: 0.75;
  transform: translateY(-2px);
}
.picto { transition: opacity .2s ease, transform .2s ease; }

/* Même réserve que pour .reveal : qui demande moins d'animation n'en reçoit
   pas. Le retour d'information au survol subsiste par l'opacité, qui n'est pas
   un mouvement. */
@media (prefers-reduced-motion: reduce) {
  .picto { transition: none; }
  a:hover > .picto,
  a:hover .picto { transform: none; }
}
.section-title .eyebrow { }
