/* ==========================================================================
   components.css — la bibliothèque
   Construite section par section, en contexte réel. Ordre : nav, hero.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Boutons
   Pilule pleine : c'est le geste de la planche retenue. Le bleu ne se pose
   que sur l'action — jamais sur une décoration.
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 14px 26px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-full);
  font-size: var(--text-body);
  font-weight: var(--w-medium);
  line-height: var(--lh-heading);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--color-cta); color: #fff; }
.btn--primary:hover { background: var(--color-cta-hover); color: #fff; }

.btn--ghost {
  background: var(--color-surface);
  color: var(--color-ink);
  border-color: var(--color-border);
}
.btn--ghost:hover { background: var(--color-surface-2); color: var(--color-ink); }

/* Le secondaire posé sur une surface déjà blanche : --ghost y disparaîtrait,
   son fond étant celui de la carte. Ici c'est le trait qui porte le bouton —
   et un trait qui porte un bouton doit se voir : --border-strong, pas --border. */
.btn--outline {
  background: transparent;
  color: var(--color-ink);
  border-color: var(--color-border-strong);
  border-width: 1px;
}
.btn--outline:hover { background: var(--color-surface-2); border-color: var(--color-ink-soft); color: var(--color-ink); }

.btn--sm { padding: 10px 19px; font-size: var(--text-small); letter-spacing: var(--track-body); }
.btn svg { flex: none; }

/* --------------------------------------------------------------------------
   Hero — la carte photo arrondie, la nav posée dessus, le titre en bas
   -------------------------------------------------------------------------- */
/* Le pourtour du hero est blanc, comme la section qui le suit : les deux se
   fondent et le haut de page ne se découpe plus en bandes. */
.hero-frame { background: var(--color-surface); }

.hero {
  position: relative;
  margin: 8px 8px 0;
  border-radius: 24px;
  overflow: hidden;
  isolation: isolate;
  min-height: 660px;
  display: flex;
  flex-direction: column;
  background: var(--color-dark);
}

.hero-media { position: absolute; inset: 0; z-index: -2; }
.hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 32%; }

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(180deg,
      rgba(20, 24, 36, .55) 0%,
      rgba(20, 24, 36, .18) 15%,
      transparent 28%),
    linear-gradient(0deg,
      rgba(20, 24, 36, .88) 0%,
      rgba(20, 24, 36, .74) 20%,
      rgba(20, 24, 36, .42) 42%,
      rgba(20, 24, 36, .10) 62%,
      transparent 78%);
}

/* ---- Photos claires : le voile ne suffit plus ---- */
/* Le dégradé par défaut est réglé pour une photo sombre en haut et en bas.
   Sur une salle de soins, murs blancs et plafond éclairé, la nav tombait à
   3,30:1 et le fil d'Ariane à 4,20:1, tous deux sous le seuil AA. Ce n'est
   pas une correction cosmétique : c'est du texte illisible.
   À poser sur .hero quand la photo est claire, et à retirer le jour où elle
   ne l'est plus. */
.hero--bright::after {
  background:
    linear-gradient(180deg,
      rgba(20, 24, 36, .72) 0%,
      rgba(20, 24, 36, .42) 13%,
      transparent 34%),
    linear-gradient(0deg,
      rgba(20, 24, 36, .92) 0%,
      rgba(20, 24, 36, .84) 22%,
      rgba(20, 24, 36, .58) 46%,
      rgba(20, 24, 36, .24) 66%,
      transparent 84%);
}

/* ---- La nav, posée sur la photo ---- */
/* Posé au-dessus du hero, mais hors de lui : à l'intérieur, le hero est un
   contexte d'empilement isolé et aucun z-index n'aurait pu franchir la
   frontière. C'est ce qui la faisait passer sous les sections suivantes. */
.site-header {
  position: absolute;
  top: 8px; left: 8px; right: 8px;
  z-index: var(--z-nav);
}

/* Une fois le hero passé, elle devient claire : elle appartient alors à la
   page, plus à la photo. Tout ce qui était blanc redevient encre. */
.site-header.is-stuck {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-nav);
  transition: top 300ms var(--ease-out);
}

/* Le fond flouté est porté par un calque, jamais par l'en-tête lui-même.
   backdrop-filter crée un référentiel pour les descendants en position fixe,
   au même titre que transform : posé sur l'en-tête, il enfermait le tiroir de
   menu dans les 58 px de la barre. */
.site-header.is-stuck::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, #FFFFFF 86%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--color-border);
}

/* On descend : l'en-tête s'efface et rend ses pixels à la page.
   On remonte : il revient. Personne ne cherche le menu en descendant.

   L'escamotage passe par « top » et non par « transform » : un élément
   transformé devient le référentiel de ses descendants en position fixe, et
   le tiroir de menu vit à l'intérieur de cet en-tête. Avec un transform, il
   se calait sur la barre au lieu de l'écran et se retrouvait réduit à sa
   hauteur. */
/* Pages intérieures : barre pleine, pas de flou. Voir page.js. */
.site-header.is-solid::before {
  background: var(--color-surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.site-header.is-stuck.is-hidden { top: -90px; }
.site-header.is-stuck .nav { height: 66px; }
@media (max-width: 768px) { .site-header.is-stuck .nav { height: 58px; } }
.site-header.is-stuck .nav-links a { color: var(--color-ink-soft); }
.site-header.is-stuck .nav-links a:hover,
.site-header.is-stuck .nav-links a[aria-current="page"] { color: var(--color-ink); }
.site-header.is-stuck .btn--glass {
  background: transparent;
  color: var(--color-ink-soft);
  border-color: var(--color-border);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.site-header.is-stuck .btn--glass:hover { background: var(--color-surface-2); color: var(--color-ink); }
.site-header.is-stuck .btn--light { background: var(--color-cta); color: #fff; }
.site-header.is-stuck .btn--light:hover { background: var(--color-cta-hover); color: #fff; }
.site-header.is-stuck .nav-toggle {
  background: var(--color-surface);
  border-color: var(--color-border);
  color: var(--color-ink);
}

/* Le logo change de version en même temps que le fond. */
.brand-logo--dark { display: none; }
.site-header.is-stuck .brand-logo--light { display: none; }
.site-header.is-stuck .brand-logo--dark { display: block; }

@media (prefers-reduced-motion: reduce) { .site-header.is-stuck { transition: none; } }
/* Tiroir ouvert : la barre ne bouge plus, le corps ne défile pas. */
.menu-open .site-header.is-stuck { top: 0; }

.nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-8);
  height: 82px;
  padding-inline: var(--space-8);
}

.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: #fff; flex: none; }
.brand-logo { width: auto; height: 38px; flex: none; }
.brand-text { font-size: var(--text-body); font-weight: var(--w-medium); letter-spacing: var(--track-heading); line-height: 1.05; }
.brand-text span { display: block; font-size: var(--text-micro); font-weight: var(--w-regular); opacity: .7; letter-spacing: var(--track-label); text-transform: uppercase; margin-top: 1px; }

.nav-panel { display: contents; }
.nav-panel-cta, .nav-only-mobile, .drawer-head, .drawer-actions, .drawer-foot,
.nav-overlay, .drawer-accordion, .drawer-sub { display: none; }
.nav-links { display: flex; gap: var(--space-6); margin: 0 0 0 auto; padding: 0; list-style: none; }
.nav-links li { margin: 0; }
.nav-links a {
  display: block;
  padding: 6px 2px;
  color: rgba(255, 255, 255, .82);
  font-size: var(--text-small);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-body);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-links a:hover, .nav-links a[aria-current="page"] { color: #fff; }

.nav-end { display: flex; align-items: center; gap: var(--space-3); flex: none; margin-left: var(--space-6); }

.btn--glass {
  background: rgba(255, 255, 255, .14);
  color: #fff;
  border-color: rgba(255, 255, 255, .28);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.btn--glass:hover { background: rgba(255, 255, 255, .24); color: #fff; }

.btn--light { background: #fff; color: var(--color-ink); }
.btn--light:hover { background: var(--color-surface-2); color: var(--color-ink); }

.nav-toggle { display: none; }

/* ---- Le bloc titre, en bas à gauche ---- */
.hero-inner {
  margin-top: auto;
  padding: var(--space-16) var(--space-8) var(--space-24);
  position: relative;
  z-index: 1;
}
.hero-copy { max-width: 42rem; }

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: var(--space-6);
  padding: 7px 15px 7px 11px;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, .14);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: #fff;
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: .01em;
}
/* La pastille garde exactement le même dessin : seule la couleur du point
   change avec l'état réel du cabinet. */
.hero-badge .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #4ADE80;
  box-shadow: 0 0 0 3px rgba(74, 222, 128, .25);
  transition: background-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.hero-badge.is-open  .dot { background: #4ADE80; box-shadow: 0 0 0 3px rgba(74, 222, 128, .25); }
.hero-badge.is-soon  .dot { background: #FBBF24; box-shadow: 0 0 0 3px rgba(251, 191, 36, .28); }
.hero-badge.is-closed .dot { background: rgba(255, 255, 255, .5); box-shadow: 0 0 0 3px rgba(255, 255, 255, .16); }

/* La serif 400 vaut ici comme ailleurs (D-040). Deux réglages sont propres au
   hero, parce que le titre y est blanc sur une photo et non encre sur du fond :
   l'approche reste un peu plus serrée que --track-display, et l'ombre portée est
   légèrement renforcée. Une serif en 400 a des pleins et des déliés bien plus
   fins qu'une sans en 800 : sur une zone claire de la photo, les déliés
   disparaissaient. */
.hero h1 {
  margin-bottom: var(--space-5);
  color: #fff;
  font-family: var(--font-display);
  font-size: var(--text-display);
  font-weight: var(--w-regular);
  line-height: var(--lh-display);
  letter-spacing: -0.022em;
  text-wrap: pretty;
  hyphens: manual;
  text-shadow: 0 1px 2px rgba(11, 30, 91, .34), 0 2px 28px rgba(11, 30, 91, .40);
}
/* ⚠️ `font-weight` était à --w-bold ici. Le woff2 de titrage n'est servi QU'EN
   400 (fonts.css : figer les axes est ce qui le fait passer de 126 à 21 Ko), donc
   demander 700 ne donnait pas une italique grasse mais une FAUSSE graisse
   synthétisée par le navigateur, qui épaissit au hasard et bave sur la photo.
   L'italique se distingue par sa pente et par sa couleur, pas par son poids. */
.hero h1 em {
  font-style: italic;
  font-weight: inherit;
  letter-spacing: -0.012em;
  color: var(--color-on-dark);
}

.hero .lede {
  margin-bottom: 0;
  max-width: 35rem;
  color: rgba(255, 255, 255, .88);
  font-size: var(--text-body);
  font-weight: var(--w-regular);
  line-height: var(--lh-small);
  letter-spacing: 0;
  text-wrap: pretty;
  text-shadow: 0 1px 18px rgba(20, 24, 36, .4);
}

/* ---- Le fil d'Ariane posé sur la photo ---- */
/* Même composant que les pages intérieures, encre inversée. Sur une page
   intérieure à hero, il prend la place qu'occupe la pastille d'horaires sur
   l'accueil : les horaires ne disent rien d'une page d'équipe, le chemin si. */
/* Le fil est plus haut que le titre : il tombe dans la bande où les deux
   dégradés du hero sont au plus faible. L'encre y est donc franche, pas
   estompée — mesuré à 3,26:1 en blanc 72 %, sous le seuil. */
.hero .fil {
  color: rgba(255, 255, 255, .92);
  text-shadow: 0 1px 3px rgba(20, 24, 36, .55), 0 1px 16px rgba(20, 24, 36, .5);
}
.hero .fil a { color: rgba(255, 255, 255, .92); }
.hero .fil a:hover { color: #fff; }
.hero .fil svg { color: rgba(255, 255, 255, .6); }
.hero .fil [aria-current="page"] { color: #fff; }

/* --------------------------------------------------------------------------
   Barre d'action flottante — chevauche le bas du hero
   -------------------------------------------------------------------------- */
.action-bar {
  position: relative;
  z-index: 3;
  margin: -46px auto 0;
  max-width: 1000px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 14px 14px 14px var(--space-6);
  background: var(--color-surface);
  /* Elle flotte désormais sur du blanc : sans ombre profonde elle disparaît.
     C'est l'élévation, pas le contraste de fond, qui la fait exister. */
  border: 1px solid var(--color-border);
  border-radius: 20px;
  box-shadow: var(--shadow-deep);
}
.action-item { flex: 1 1 0; display: flex; align-items: center; gap: 12px; padding: 4px 8px; min-width: 0; }
/* Sans ce min-width sur le conteneur intérieur, l'ellipsis ne s'applique pas
   et le texte sort de la carte. Il faut le poser sur chaque parent en flex. */
.action-item > span { min-width: 0; flex: 1 1 auto; }
/* Les horaires sont la valeur la plus longue : on leur donne un peu plus de
   place plutôt que de les tronquer. */
.action-item:first-child { flex: 1.35 1 0; }
.action-item + .action-item { border-left: 1px solid var(--color-border); padding-left: var(--space-5); }
.action-item svg { flex: none; color: var(--color-ink-soft); }
/* Le bloc qui tourne : le texte se relève en fondu, rien ne clignote et la
   largeur est figée par la grille, donc aucune ligne ne bouge. */
[data-trust] .lbl, [data-trust] .val, [data-trust] svg {
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
[data-trust].is-out .lbl,
[data-trust].is-out .val,
[data-trust].is-out svg { opacity: 0; transform: translateY(-5px); }

/* Le filet qui avance : il annonce qu'autre chose arrive, sinon le changement
   surprend au lieu d'être attendu. */
[data-trust] { position: relative; overflow: hidden; }
[data-trust].is-ticking::after {
  content: "";
  position: absolute;
  left: var(--space-5); right: 8px; bottom: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--color-blue-200);
  transform-origin: left;
  animation: trust-progress 2600ms linear infinite;
}
@keyframes trust-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) { [data-trust].is-ticking::after { display: none; } }
.action-item .lbl { display: block; margin-bottom: 1px; font-size: var(--text-micro); color: var(--color-ink-soft); font-weight: var(--w-medium); letter-spacing: var(--track-label); text-transform: uppercase; }
.action-item .val { display: block; font-size: var(--text-small); color: var(--color-ink); font-weight: var(--w-medium); letter-spacing: var(--track-heading); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.action-item a.val { text-decoration: none; }

.action-go {
  flex: none;
  width: 56px; height: 56px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-cta);
  color: #fff;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.action-go:hover { background: var(--color-cta-hover); color: #fff; transform: scale(1.04); }

/* ---- 1024 ---------------------------------------------------------------- */
@media (max-width: 1024px) {
  .nav { padding-inline: var(--space-6); gap: var(--space-5); }
  .nav-links { gap: var(--space-5); }
  .hero { min-height: 580px; }
  .hero-inner { padding: var(--space-12) var(--space-6) var(--space-20); }
  .action-bar { max-width: none; margin-inline: var(--space-6); }
  .action-item .val { font-size: var(--text-small); }
}

/* ---- 768 : la nav bascule, la barre s'empile ----------------------------- */
@media (max-width: 768px) {
  /* Le hero occupe 80 % de l'écran : à 520 px fixes, le texte était tassé
     contre la barre flottante. 100svh tient compte de la barre d'adresse du
     navigateur, contrairement à 100vh. */
  .hero { margin: 6px 6px 0; border-radius: 18px; min-height: 92svh; }
  @supports not (height: 1svh) { .hero { min-height: 88vh; } }

  .site-header { top: 6px; left: 6px; right: 6px; }
  .nav { height: 68px; padding-inline: var(--space-4); gap: var(--space-3); }
  .brand-logo { height: 30px; }
  .nav-end { margin-left: auto; }
  .nav-end .btn { display: none; }
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; padding: 0;
    border: 1.5px solid rgba(255, 255, 255, .3);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, .14);
    color: #fff;
    backdrop-filter: blur(8px);
    cursor: pointer;
  }

  /* ---- le tiroir latéral ---- */
  .nav-overlay {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 190;
    background: rgba(20, 24, 36, .45);
    opacity: 0;
    visibility: hidden;
    transition: opacity 320ms var(--ease-out), visibility 320ms;
    -webkit-tap-highlight-color: transparent;
  }
  .menu-open .nav-overlay { opacity: 1; visibility: visible; }
  .menu-open { overflow: hidden; }

  .nav-panel {
    position: fixed;
    top: 0; right: 0; bottom: 0;
    left: auto;
    z-index: 200;
    display: flex;
    flex-direction: column;
    width: min(340px, 86vw);
    padding: 0;
    background: var(--color-surface);
    border: 0;
    border-radius: 0;
    box-shadow: -8px 0 44px -8px rgba(20, 24, 36, .22);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* La courbe des tiroirs iOS : départ franc, arrivée douce. */
    transform: translateX(100%);
    visibility: visible;
    opacity: 1;
    transition: transform 420ms cubic-bezier(.32, .72, 0, 1);
  }
  .menu-open .nav-panel { transform: none; }

  .drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: none;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-border);
  }
  .drawer-logo { height: 26px; width: auto; }
  .drawer-close {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-md);
    background: none;
    color: var(--color-ink-soft);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
  }
  .drawer-close:active, .drawer-close:hover { background: var(--color-surface-2); color: var(--color-ink); }

  .nav-links { display: flex; flex-direction: column; flex: 0 0 auto; margin: 0; padding: var(--space-2) 0; }
  .nav-links li {
    margin: 0;
    opacity: 0;
    transform: translateX(18px);
    transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out);
  }
  .menu-open .nav-links li { opacity: 1; transform: none; }
  .menu-open .nav-links li:nth-child(1) { transition-delay: 90ms; }
  .menu-open .nav-links li:nth-child(2) { transition-delay: 140ms; }
  .menu-open .nav-links li:nth-child(3) { transition-delay: 190ms; }
  .menu-open .nav-links li:nth-child(4) { transition-delay: 240ms; }
  .menu-open .nav-links li:nth-child(5) { transition-delay: 290ms; }

  .nav-links a {
    display: flex;
    align-items: center;
    min-height: 46px;
    padding: var(--space-2) var(--space-5);
    color: var(--color-ink);
    font-size: var(--text-small);
    font-weight: var(--w-regular);
    letter-spacing: var(--track-body);
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
  }
  .nav-links a:active { background: var(--color-surface-2); }
  .nav-links a[aria-current="page"] {
    color: var(--color-cta);
    background: var(--color-surface-3);
    box-shadow: inset -3px 0 0 var(--color-cta);
  }
  .nav-only-mobile { display: list-item; }

  /* ---- le sous-menu ---- */
  .has-sub .sub-parent-link { display: none; }
  .drawer-accordion {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    width: 100%;
    min-height: 46px;
    padding: var(--space-2) var(--space-5);
    border: 0;
    background: none;
    color: var(--color-ink);
    font-family: inherit;
    font-size: var(--text-small);
    font-weight: var(--w-regular);
    letter-spacing: var(--track-body);
    text-align: left;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .drawer-accordion svg { flex: none; color: var(--color-ink-soft); transition: transform var(--dur-base) var(--ease-out); }
  .drawer-accordion[aria-expanded="true"] { color: var(--color-cta); }
  .drawer-accordion[aria-expanded="true"] svg { transform: rotate(180deg); color: var(--color-cta); }

  /* grid-template-rows 0fr vers 1fr : le seul dépliage qui s'anime sans
     connaître la hauteur du contenu à l'avance. */
  .drawer-sub {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows var(--dur-base) var(--ease-out);
  }
  .drawer-accordion[aria-expanded="true"] + .drawer-sub { grid-template-rows: 1fr; }
  .drawer-sub-inner { overflow: hidden; }
  .drawer-sub a {
    display: flex;
    align-items: center;
    min-height: 42px;
    padding: var(--space-2) var(--space-5) var(--space-2) var(--space-8);
    color: var(--color-ink-soft);
    font-size: var(--text-small);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
  }
  .drawer-sub a:active { background: var(--color-surface-2); color: var(--color-ink); }
  .drawer-sub a:last-child { color: var(--color-cta); font-weight: var(--w-medium); }
  .nav-only-mobile a { color: var(--color-danger); }

  .drawer-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: none;
    margin-top: auto;
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-border);
  }
  .drawer-actions .btn { width: 100%; justify-content: center; padding: 12px 18px; font-size: var(--text-small); }

  .drawer-foot {
    flex: none;
    padding: var(--space-4) var(--space-5) var(--space-5);
    border-top: 1px solid var(--color-border);
    background: var(--color-surface-2);
  }
  .drawer-info {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin: 0 0 var(--space-3);
    color: var(--color-ink-soft);
    font-size: var(--text-micro);
    line-height: var(--lh-small);
  }
  .drawer-info svg { flex: none; margin-top: 2px; color: var(--color-blue-400); }

  .drawer-social { display: flex; gap: var(--space-2); margin-top: var(--space-4); }
  .drawer-social a {
    width: 38px; height: 38px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-ink-soft);
    -webkit-tap-highlight-color: transparent;
    transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
  }
  .drawer-social a:active, .drawer-social a:hover { color: var(--color-cta); border-color: var(--color-blue-200); }

  .menu-open .drawer-actions,
  .menu-open .drawer-foot { animation: drawer-in 320ms var(--ease-out) both; }
  .menu-open .drawer-actions { animation-delay: 300ms; }
  .menu-open .drawer-foot { animation-delay: 340ms; }
  @keyframes drawer-in { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }

  /* ---- le bouton, qui devient une croix ---- */
  .nav-toggle .bars { display: block; position: relative; width: 18px; height: 12px; }
  .nav-toggle .bars i {
    position: absolute;
    left: 0;
    width: 100%; height: 1.7px;
    border-radius: 2px;
    background: currentColor;
    transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
  }
  .nav-toggle .bars i:nth-child(1) { top: 0; }
  .nav-toggle .bars i:nth-child(2) { top: 5px; }
  .nav-toggle .bars i:nth-child(3) { top: 10px; }
  .menu-open .nav-toggle .bars i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
  .menu-open .nav-toggle .bars i:nth-child(2) { opacity: 0; }
  .menu-open .nav-toggle .bars i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

  @media (prefers-reduced-motion: reduce) {
    .nav-panel, .nav-links li, .nav-overlay { transition: none; }
    .menu-open .drawer-actions, .menu-open .drawer-foot { animation: none; }
  }

  .hero-media img { object-position: 50% 26%; }
  .hero-inner { padding: var(--space-12) var(--space-5) var(--space-16); }
  .hero .lede { max-width: 30rem; }
  .hero-badge { margin-bottom: var(--space-5); }
  .hero h1 { margin-bottom: var(--space-4); }
  .action-bar {
    flex-wrap: wrap;
    margin: -30px var(--space-3) 0;
    padding: var(--space-4);
    border-radius: 18px;
  }
  .action-item { flex: 1 1 100%; gap: 10px; padding: 4px 0; }
  [data-trust].is-ticking::after { left: 0; right: 0; }
  .action-item + .action-item {
    border-left: 0; border-top: 1px solid var(--color-border);
    padding-left: 8px; padding-top: var(--space-3); margin-top: var(--space-3);
  }
  .action-go { width: 100%; height: 52px; border-radius: var(--radius-full); margin-top: var(--space-4); gap: 10px; font-weight: var(--w-medium); }
}

/* ---- 480 ----------------------------------------------------------------- */
@media (max-width: 480px) {
    .hero h1 br { display: none; }
}

/* --------------------------------------------------------------------------
   Sections courantes
   -------------------------------------------------------------------------- */
/* Le rythme vertical n'est plus uniforme : une page dont toutes les sections
   ont le même rembourrage défile comme un tableur. */
.section { padding: var(--space-24) 0; }
.section--air { padding: calc(var(--space-24) + var(--space-8)) 0; }
.section--dense { padding: var(--space-20) 0; }
.section--tight { padding: var(--space-20) 0; }

/* Bande blanche pleine largeur : c'est elle qui donne le rythme quand on
   descend la page — gris, blanc, gris. */
.section--alt { background: var(--color-surface); border-block: 1px solid var(--color-border); }
/* Le hero et la section suivante sont tous deux blancs : le filet les
   séparait alors qu'on veut une seule surface continue. */
.hero-frame + .section--alt { border-top: 0; }

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-16);
  align-items: start;
}
.split-copy .eyebrow {
  display: block;
  margin-bottom: var(--space-3);
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.split-copy h2 { margin-bottom: var(--space-4); font-size: var(--text-title); }
.split-copy p { color: var(--color-ink-soft); font-size: var(--text-lead); line-height: var(--lh-body); max-width: 560px; }
.split-copy p + p { margin-top: var(--space-4); }
.split-copy .lede { color: var(--color-ink); font-weight: var(--w-medium); max-width: none; }

/* --------------------------------------------------------------------------
   Cartes chiffres
   -------------------------------------------------------------------------- */
.stats { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
.stats--trio { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 768px) { .stats--trio { grid-template-columns: 1fr; } }

.stat {
  padding: var(--space-6) var(--space-6) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
/* Le chiffre passe à la serif (D-040), comme les chiffres d'Ibn Sina. C'est le
   seul endroit hors h1/h2 où elle descend, et il se justifie : un chiffre isolé
   et grand est un titre, pas du texte courant.
   ⚠️ Elle ne descend PAS dans les tableaux de prix, qui restent en sans : eux
   ont des colonnes à aligner, un chiffre isolé n'en a aucune.
   Le risque qu'on croyait courir ici a été MESURÉ, et il n'existe pas : le woff2
   de titrage est un sous-ensemble, donc rien ne garantissait a priori qu'il eût
   gardé ses chiffres tabulaires. Vérification faite dans le navigateur, « 111 »
   et « 000 » rendent 317,406 px tous les deux, avec ET sans `tabular-nums` :
   les chiffres de Source Serif 4 sont tabulaires par construction. Les six
   `.stat b.num` d'/invisalign-prix-maroc/ ne peuvent donc pas se désaligner. */
.stat b {
  display: block;
  font-family: var(--font-display);
  font-size: var(--text-title);
  font-weight: var(--w-regular);
  line-height: 1;
  letter-spacing: var(--track-title);
  color: var(--color-ink);
}
.stat b .unit { font-size: var(--text-heading); font-weight: var(--w-regular); letter-spacing: var(--track-title); }
/* Enfant direct, et pas « .stat span » : l'unité vit dans un span imbriqué, que
   le sélecteur large passait en display:block. « > 95 % » se cassait alors en
   deux lignes sans qu'aucune largeur soit en cause. */
.stat > span { display: block; margin-top: 10px; font-size: var(--text-small); color: var(--color-ink-soft); line-height: var(--lh-small); }

.link-more {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: var(--space-6);
  color: var(--color-link);
  font-size: var(--text-body);
  font-weight: var(--w-medium);
  text-decoration: none;
}
.link-more:hover { gap: 11px; }
.link-more svg { transition: transform var(--dur-fast) var(--ease-out); }

@media (max-width: 1024px) {
  .section { padding: var(--space-20) 0; }
  .split { gap: var(--space-10); }
}
@media (max-width: 768px) {
  .section { padding: var(--space-16) 0; }
  .split { grid-template-columns: 1fr; gap: var(--space-8); }
  .stat { padding: var(--space-5); }
}
@media (max-width: 480px) {
  .stats { grid-template-columns: 1fr 1fr; gap: var(--space-3); }
  /* La règle .stats ci-dessus vient après le passage à une colonne de
     .stats--trio (ligne 703), à spécificité égale, et l'annulait donc sous
     480 px : trois cartes repassaient en deux colonnes et la troisième restait
     seule sur sa ligne. Mesuré à 375 px le 2026-09-14 sur /invisalign-prix-maroc/,
     première page du site à employer .stats--trio. */
  .stats--trio { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Split texte / média — 40 / 60
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   .study — un chiffre d'étude, sa phrase et sa source
   -------------------------------------------------------------------------- */
/* Une donnée publiée ne se pose pas comme une statistique de brochure : le
   chiffre porte, et la source le tient. Le liseré bleu à gauche la distingue
   d'une carte ordinaire sans ajouter d'ombre, D-016. La source n'est pas
   décorative, elle est la condition pour que le chiffre soit publiable. */
.study {
  margin: 0;
  padding: var(--space-8);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-cta);
  border-radius: var(--radius-lg);
}
.study-label {
  display: block;
  margin-bottom: var(--space-4);
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.study b {
  display: block;
  font-size: var(--text-display);
  font-weight: var(--w-display);
  line-height: 1.05;
  letter-spacing: var(--track-display);
}
.study p {
  margin: var(--space-3) 0 0;
  font-size: var(--text-lead);
  line-height: var(--lh-lead);
}
.study cite {
  display: block;
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  font-style: normal;
  line-height: var(--lh-small);
}

/* --------------------------------------------------------------------------
   .slot — emplacement média en attente
   -------------------------------------------------------------------------- */
/* Le cabinet livrera des photos et des vidéos plus tard. En attendant, un cadre
   qui dit ce qu'il attend vaut mieux qu'une image empruntée : il se voit, il ne
   peut pas passer pour du contenu, et il se remplace par une <img> ou une
   <video> sans toucher à la mise en page.
   ⛔ Aucun .slot ne doit atteindre la production. Voir docs/STATUS.md. */
.slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-8);
  aspect-ratio: 4 / 3;
  border: 2px dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
  background: var(--color-surface-2);
  color: var(--color-ink-soft);
  text-align: center;
}
.slot svg { color: var(--color-border-strong); }
.slot-tag {
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.slot-what {
  max-width: 34ch;
  font-size: var(--text-small);
  line-height: var(--lh-small);
}
.slot--video    { aspect-ratio: 16 / 9; }
.slot--portrait { aspect-ratio: 4 / 5; }
.slot--square   { aspect-ratio: 1 / 1; }

/* --------------------------------------------------------------------------
   Carrousel
   -------------------------------------------------------------------------- */
.carousel {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface-2);
  box-shadow: var(--shadow-lift);
}
.carousel-track { position: relative; aspect-ratio: 1 / 1; }

.carousel-slide {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-slow) var(--ease-out), visibility var(--dur-slow);
}
.carousel-slide.is-active { opacity: 1; visibility: visible; }
.carousel-slide img { width: 100%; height: 100%; object-fit: cover; }

/* Monogramme : aucun portrait n'existe pour ce praticien dans la bibliothèque.
   Mettre le visage d'un autre sous son nom serait une usurpation, et une carte
   vide serait un trou. Deux initiales tiennent la place, et le jour de la
   séance photo elles cèdent la place à une <img> sans rien changer d'autre. */
.carousel-mono {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: var(--color-surface-3);
  color: var(--color-cta);
  font-size: clamp(2.75rem, 7vw, 4.5rem);
  font-weight: var(--w-bold);
  letter-spacing: .03em;
}

.carousel-slide figcaption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: var(--space-10) var(--space-5) var(--space-4);
  color: #fff;
  font-size: var(--text-small);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-body);
  background: linear-gradient(0deg, rgba(20, 24, 36, .82) 0%, rgba(20, 24, 36, .45) 45%, transparent 100%);
}

.carousel-nav {
  position: absolute;
  top: 50%;
  margin-top: -19px;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .35);
  border-radius: 50%;
  background: rgba(20, 24, 36, .38);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.carousel:hover .carousel-nav,
.carousel:focus-within .carousel-nav { opacity: 1; }
.carousel-nav:hover { background: rgba(20, 24, 36, .62); }
.carousel-prev { left: var(--space-4); }
.carousel-next { right: var(--space-4); }

.carousel-dots {
  position: absolute;
  right: var(--space-5); bottom: var(--space-5);
  display: flex;
  gap: 7px;
}
.carousel-dots button {
  width: 7px; height: 7px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, .45);
  cursor: pointer;
  transition: width var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
}
.carousel-dots button[aria-selected="true"] { width: 22px; background: #fff; }

@media (max-width: 1024px) {
}
@media (max-width: 768px) {
  /* Les flèches deviennent la commande du doigt, et les pastilles redeviennent
     ce qu'elles sont : un témoin de position. Sept pixels espacés de sept ne
     peuvent pas porter une zone de 44 px sans que celle du voisin la recouvre.

     Deux choses à faire en même temps, et la seconde est la moins évidente :
     sur un écran tactile il n'y a pas de survol, donc `opacity: 0` jusqu'au
     survol laisserait des flèches démasquées et pourtant invisibles. */
  .carousel-nav {
    display: inline-flex;
    opacity: 1;
    width: 44px; height: 44px;
    margin-top: -22px;
  }
  /* Le clavier garde son accès : `pointer-events` ne concerne que le pointeur.
     Seul le doigt est renvoyé vers la flèche. */
  .carousel-dots button { pointer-events: none; }
  .carousel-dots { right: var(--space-4); bottom: var(--space-4); }
  .carousel-slide figcaption { padding: var(--space-8) var(--space-4) var(--space-3); }
}

/* --------------------------------------------------------------------------
   Rail des spécialités — images portrait, texte dessous, défilement horizontal
   Pas de cartes, pas d'ombres : l'image porte tout.
   -------------------------------------------------------------------------- */
.rail-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-6);
  margin-bottom: var(--space-12);
}
.rail-head .section-head { margin-bottom: 0; max-width: none; }
.rail-head .section-head h2 { margin-bottom: 0; }
.rail-head .rail-all {
  color: var(--color-ink-soft);
  font-size: var(--text-body);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.rail-head .rail-all:hover { color: var(--color-ink); }

.rail-arrows { display: flex; gap: var(--space-2); }
.rail-arrows button {
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--color-ink);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.rail-arrows button:hover { background: var(--color-ground); }
.rail-arrows button[disabled] { opacity: .28; cursor: default; }
.rail-arrows button[disabled]:hover { background: transparent; }

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 3 * var(--space-3)) / 4);
  gap: var(--space-3);
  overflow-x: auto;
  /* auto sur un axe rend l'autre axe scrollable aussi : sans ce hidden, la
     moindre transformation d'un enfant décale le rail verticalement. */
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.rail::-webkit-scrollbar { display: none; }

.rail-item { scroll-snap-align: start; text-decoration: none; color: inherit; }
.rail-item:hover { color: inherit; }

.rail-shot {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--color-surface-2);
}
.rail-shot img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 26%;
  transition: transform var(--dur-slow) var(--ease-out);
}
.rail-item:hover .rail-shot img { transform: scale(1.035); }

.rail-tag {
  position: absolute;
  left: 12px; bottom: 12px;
  padding: 6px 12px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  color: var(--color-ink);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-body);
}

.rail-item h3 {
  margin: var(--space-4) 0 var(--space-2);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
  line-height: var(--lh-heading);
}
/* Pas de soulignement au survol : dans une rangée serrée, le trait ajoute une
   ligne visuelle sous chaque titre et alourdit la grille. Le zoom de l'image
   suffit à signaler que la carte est cliquable. */
.rail-item h3 { text-decoration: none; }
.rail-item:hover h3 { text-decoration: none; color: var(--color-cta); }
.rail-item p { margin: 0; color: var(--color-ink-soft); font-size: var(--text-small); line-height: var(--lh-small); }
.rail-item p + p { margin-top: 2px; }

@media (max-width: 1024px) {
  .rail { grid-auto-columns: calc((100% - 2 * var(--space-3)) / 3); }
}
@media (max-width: 768px) {
  .rail { grid-auto-columns: calc((100% - var(--space-3)) / 2); }
  .rail-arrows { display: none; }
  .rail-arrows { display: none; }
}
@media (max-width: 480px) {
  .rail { grid-auto-columns: 74%; }
}

/* --------------------------------------------------------------------------
   Apparition au défilement
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-enter) var(--ease-out),
              transform var(--dur-enter) var(--ease-out);
  will-change: opacity, transform;
  /* 🔴 FILET, posé le 2026-09-21 (audit avant bascule).
     De 28 à 60 blocs par page sont en `opacity: 0` tant que `reveal.js` ne s'est pas
     exécuté, tableaux de prix compris, 60 sur la page d'urgence. Sans filet, un
     `reveal.js` qui ne se charge pas rend la page BLANCHE, et rien dans les en-têtes
     ne le signale. Ce n'est pas une hypothèse : le 2026-09-21, `tracking.js` répondait
     404 sur le serveur de test après un déploiement incomplet.
     Ce filet fait apparaître le contenu au bout de 2 s SI le script n'a pas pris la
     main. Il ne coûte rien dans le cas normal, où `reveal.js` pose `data-reveal` dès
     sa première ligne et annule l'animation ci-dessous. */
  animation: reveal-secours .01s linear 2s forwards;
}
@keyframes reveal-secours { to { opacity: 1; transform: none; } }
/* Le script a pris la main : le filet se retire, l'apparition au défilement reprend. */
html[data-reveal] .reveal { animation: none; }
.reveal.is-in { opacity: 1; transform: none; animation: none; }

/* Si l'utilisateur a demandé moins d'animation, tout est visible d'emblée. */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* --------------------------------------------------------------------------
   Réalisations — la carte, avec le comparateur dedans
   Une image, une poignée. Sous le capot c'est un input range : souris,
   tactile, clavier et lecteur d'écran fonctionnent sans code en plus.
   -------------------------------------------------------------------------- */
.ba-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }
/* Deux cas seulement : la grille de trois laisse alors une colonne vide à droite
   sur ordinateur, mesuré le 2026-09-14 sur /invisalign-prix-maroc/. Variante
   additive : les pages à trois cartes ne bougent pas, et les deux requêtes média
   ci-dessous la reprennent puisqu'elles ciblent .ba-grid et viennent après. */
.ba-grid--duo { grid-template-columns: repeat(2, 1fr); }

.ba-card {
  margin: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.ba-card:hover {
  transform: translateY(-5px);
  box-shadow: var(--shadow-deep);
  border-color: var(--color-blue-200);
}

/* ---- le comparateur ---- */
.compare {
  --pos: 50%;
  position: relative;
  aspect-ratio: 1000 / 505;
  overflow: hidden;
  background: var(--color-surface-2);
  touch-action: pan-y;
  user-select: none;
}
.compare img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  pointer-events: none;
}
/* L'avant est découpé à la position de la poignée : les deux photos restent
   alignées au pixel, quelle que soit la position. */
.compare-before { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }

.compare-line {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--pos);
  width: 2px;
  margin-left: -1px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(20, 24, 36, .18);
  pointer-events: none;
}
.compare-grip {
  position: absolute;
  top: 50%; left: var(--pos);
  width: 38px; height: 38px;
  margin: -19px 0 0 -19px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: #fff;
  color: var(--color-ink);
  box-shadow: 0 2px 10px rgba(20, 24, 36, .3);
  pointer-events: none;
  transition: transform var(--dur-fast) var(--ease-out);
}
.ba-card:hover .compare-grip { transform: scale(1.07); }

.compare-range {
  position: absolute;
  inset: 0;
  z-index: 4;
  width: 100%; height: 100%;
  margin: 0; padding: 0;
  opacity: 0;
  cursor: ew-resize;
  background: transparent;
  -webkit-appearance: none;
          appearance: none;
}
.compare-range::-webkit-slider-thumb { -webkit-appearance: none; width: 2px; height: 100%; }
.compare-range::-moz-range-thumb { width: 2px; height: 100%; border: 0; border-radius: 0; }
.compare-range:focus-visible ~ .compare-grip { box-shadow: 0 0 0 4px rgba(91, 124, 250, .5); }

/* ---- les étiquettes : blanc et noir ---- */
.ba-label {
  position: absolute;
  top: var(--space-3);
  z-index: 3;
  padding: 3px 9px;
  border-radius: var(--radius-sm);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  line-height: var(--lh-small);
  pointer-events: none;
}
.ba-label--avant { left: var(--space-3); background: #fff; color: var(--color-ink); box-shadow: 0 1px 3px rgba(20, 24, 36, .2); }
.ba-label--apres { right: var(--space-3); background: var(--color-dark); color: #fff; }

/* ---- le pied de carte ---- */
.ba-info { padding: var(--space-6); }
.ba-info h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
}
.ba-info p { margin: 0; color: var(--color-ink-soft); font-size: var(--text-small); line-height: var(--lh-small); }

@media (max-width: 1024px) { .ba-grid { grid-template-columns: 1fr 1fr; gap: var(--space-5); } }
@media (max-width: 700px)  { .ba-grid { grid-template-columns: 1fr; } }


/* --------------------------------------------------------------------------
   En-tête de section — gabarit repris du site Ibn Sina
   ⚠️ 2026-09-05 : ce bloc existait EN DOUBLE, et `.reveal` en TRIPLE. Les deux
   copies n'étaient pas identiques — la première portait les règles `p + p`,
   la seconde `--solo` — donc chacune servait à quelque chose et la duplication
   était invisible au rendu. Elle a coûté une session en 17 : c'est la dernière
   définition qui gagne, et une règle ajoutée dans la première copie n'avait
   aucun effet. Fusionné, vérifié à styles calculés identiques sur 7 pages.
   Ne pas recréer un second bloc `.section-head` plus bas dans ce fichier.
   Sur-titre 12 px très espacé · titre 36 px · chapô 18 px en interligne large.
   Bloc de 640 px, chapô de 560 px, 48 px avant le contenu.
   -------------------------------------------------------------------------- */
.section-head { max-width: 640px; margin-bottom: var(--space-12); }
.section-head .eyebrow {
  display: block;
  margin-bottom: var(--space-3);
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* Le bloc-tête peut porter plus d'un paragraphe : `p { margin: 0 }` les colle
   sinon, et une liste intercalée ne rétablit pas l'écart. */
.section-head p + p,
.duo-copy p + p { margin-top: var(--space-4); }
.section-head .ticks + p,
.duo-copy .ticks + p { margin-top: var(--space-5); }
.section-head p {
  margin: 0;
  max-width: 560px;
  color: var(--color-ink-soft);
  font-size: var(--text-lead);
  line-height: var(--lh-body);
}
/* Un titre centré qui court sur 1 180 px oblige l'œil à un long retour à la
   ligne. On le ramène à une mesure de lecture. */
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center h2 { max-width: 28ch; margin-inline: auto; }   /* 20ch en sans — D-040 */
.section-head--center p { margin-inline: auto; }
/* Quand le bloc EST la section et que rien ne le suit, la marge basse qui
   annonce le contenu n'a plus rien à annoncer. */
.section-head--solo { margin-bottom: 0; }

/* Pied de section : un seul pas suivant, centré. */
.section-foot { display: flex; justify-content: center; margin-top: var(--space-12); }

@media (max-width: 768px) {
  .section-head { margin-bottom: var(--space-10); }
  .section-foot { margin-top: var(--space-10); }
  .section-foot .btn { width: 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Bande sombre — le fondateur
   Deuxième point d'ancrage de la page après le hero.
   -------------------------------------------------------------------------- */
.section--dark { background: var(--color-dark); color: #fff; }

/* Essai fond clair pour la section du fondateur : la carte se distingue alors
   par son filet et son ombre, pas par le contraste du fond. */
.section--black { background: var(--color-surface); }
.section--black .profile { box-shadow: var(--shadow-deep); }

/* La section prend tout l'écran : la carte a alors la place de porter le
   parcours complet plutôt qu'un résumé. */
.section--full {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: var(--space-16);
}
.section--full > .container { width: 100%; }
.section--dark h2 { color: #fff; }
.section--dark .eyebrow { color: var(--color-on-dark); }

.founder { display: grid; grid-template-columns: 380px 1fr; gap: var(--space-16); align-items: center; }

.founder-portrait {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: #1a1e2b;
}
.founder-portrait img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%; }

.founder-copy h2 { margin-bottom: var(--space-4); font-size: var(--text-title); }
.founder-copy p {
  margin: 0 0 var(--space-4);
  max-width: 54ch;
  color: rgba(255, 255, 255, .82);
  font-size: var(--text-strong);
  line-height: var(--lh-body);
}
.founder-copy p:last-of-type { margin-bottom: 0; }
.founder-copy b { color: #fff; font-weight: var(--w-medium); }

/* La ligne de preuves — quatre faits, pas une phrase */
.founder-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin: var(--space-8) 0;
  padding: var(--space-6) 0;
  border-block: 1px solid rgba(255, 255, 255, .14);
}
.founder-facts div { min-width: 0; }
.founder-facts b {
  display: block;
  font-size: var(--text-heading);
  font-weight: var(--w-display);
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: #fff;
}
.founder-facts span {
  display: block;
  margin-top: 5px;
  font-size: var(--text-small);
  color: rgba(255, 255, 255, .62);
  line-height: var(--lh-small);
}

.link-more--dark { color: var(--color-on-dark); }
.link-more--dark:hover { color: #fff; }

@media (max-width: 1024px) {
  .founder { grid-template-columns: 300px 1fr; gap: var(--space-10); }
  .founder-facts { gap: var(--space-6); }
}
@media (max-width: 768px) {
  .founder { grid-template-columns: 1fr; gap: var(--space-8); }
  .founder-portrait { max-width: 280px; }
  .founder-facts { gap: var(--space-5) var(--space-8); margin: var(--space-6) 0; }
  .founder-facts b { font-size: var(--text-heading); }
}

/* --------------------------------------------------------------------------
   Carte profil — image à gauche, tout le reste à droite
   L'image est posée à l'intérieur de la carte, pas collée à ses bords :
   c'est ce retrait qui fait la différence entre une carte et une vignette.
   -------------------------------------------------------------------------- */
.section--dark { background: var(--color-dark); color: #fff; }

/* Essai fond clair pour la section du fondateur : la carte se distingue alors
   par son filet et son ombre, pas par le contraste du fond. */
.section--black { background: var(--color-surface); }
.section--black .profile { box-shadow: var(--shadow-deep); }

/* La carte du fondateur porte l'ombre la plus profonde de la page. */
.profile {
  display: grid;
  grid-template-columns: 44% 1fr;
  gap: var(--space-10);
  align-items: stretch;
  padding: 8px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-deep);
}

.profile-media {
  position: relative;
  min-height: 560px;
  overflow: hidden;
  /* Le rayon intérieur vaut celui de la carte moins son retrait : c'est à
     cette condition seule que les deux courbes restent parallèles. */
  border-radius: calc(var(--radius-lg) - 8px);
  background: var(--color-surface-2);
}
.profile-media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; }

.profile-chips {
  position: absolute;
  top: var(--space-4); left: var(--space-4); right: var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.chip-glass {
  padding: 6px 13px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  color: var(--color-ink);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-body);
  white-space: nowrap;
}
.profile-rating {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 13px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  color: var(--color-ink);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  white-space: nowrap;
}
.profile-rating svg { color: #E8A317; }

.profile-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-8) var(--space-8) var(--space-8) var(--space-2);
}

.profile-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}
.profile-body .eyebrow {
  display: block;
  margin-bottom: 0;
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.profile-title { margin-bottom: var(--space-4); }
/* h3 admis : sur une page intérieure le h2 porte la question de la section,
   et le nom du praticien devient un cran plus bas. Le style ne change pas. */
.profile-title h2,
.profile-title h3 { margin: 0; font-size: var(--text-title); letter-spacing: -0.03em; }
.profile-head .pill-outline { margin-left: auto; }
.pill-outline {
  margin-left: auto;
  flex: none;
  padding: 6px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  color: var(--color-ink-soft);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  white-space: nowrap;
}

.profile-meta {
  margin: 0 0 var(--space-4);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  font-weight: var(--w-medium);
}
.profile-desc {
  margin: 0;
  /* 52ch la laissait s'élargir de 560 à 609 px en passant de 1280 à 1024,
     quand la grille lui rend de la place : la prose grandissait pendant que
     l'écran rétrécissait. 48ch la tient sous sa largeur de 1280. */
  max-width: 48ch;
  color: var(--color-ink-soft);
  font-size: var(--text-body);
  line-height: var(--lh-body);
}
.profile-desc b { color: var(--color-ink); font-weight: var(--w-medium); }
.profile-desc + .profile-desc { margin-top: var(--space-3); }

/* Quatre faits en cartes discrètes. La deuxième tourne : le Dr Kandil a cinq
   titres, les aligner tous ferait une liste que personne ne lit. */
.profile-facts {
  display: grid;
  /* Colonnes inégales : la carte qui tourne prend la place de la dernière,
     qui n'a que deux mots. « Chirurgie parodontale » tient alors sur une
     seule ligne, et plus rien ne peut grandir. */
  grid-template-columns: 1.05fr 1.5fr 1fr 0.75fr;
  gap: var(--space-3);
  margin: var(--space-6) 0;
}

/* Sans la carte qui tourne, la répartition de l'accueil ne vaut plus : la
   dernière colonne à 0,75fr coupait « 10 ans » en deux lignes. Ici quatre
   tuiles fixes, et le chiffre ne se casse jamais. */
.profile-facts--fixed { grid-template-columns: 1fr 1.45fr 1.05fr 0.95fr; }
.profile-facts--fixed .fact b { white-space: nowrap; }
.fact {
  /* Hauteur réservée pour deux lignes de libellé. La carte qui tourne passe de
     « Implantologie » à « Chirurgie parodontale » : sans cette réserve, la
     grille grandit d'une ligne toutes les trois secondes et toute la page
     sous elle se décale. */
  min-height: 6.25rem;
  padding: var(--space-4);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.fact b {
  display: block;
  font-size: var(--text-heading);
  font-weight: var(--w-display);
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--color-ink);
}
.fact span {
  display: block;
  margin-top: 6px;
  font-size: var(--text-micro);
  color: var(--color-ink-soft);
  line-height: var(--lh-heading);
}

/* La carte qui tourne : rien ne clignote, le texte se relève en fondu. */
.fact--cycle .fact-slot,
.pill-cycle .fact-slot {
  display: block;
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.fact--cycle.is-out .fact-slot,
.pill-cycle.is-out .fact-slot { opacity: 0; transform: translateY(-5px); }

/* La pilule garde une largeur stable : sans ça, le bord droit sautille à
   chaque changement de mot. */
.pill-cycle { overflow: hidden; min-width: 11rem; text-align: center; }

.fact--cycle { position: relative; overflow: hidden; }
.fact--cycle::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--color-blue-200);
  transform-origin: left;
  animation: fact-progress 3000ms linear infinite;
}
@keyframes fact-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (prefers-reduced-motion: reduce) {
  .fact--cycle::after { display: none; }
  .fact--cycle .fact-slot { transition: none; }
}

.profile-foot {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-8);
}
.profile-figure b {
  display: block;
  font-size: var(--text-heading);
  font-weight: var(--w-display);
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--color-ink);
}
.profile-figure span { display: block; margin-top: 2px; font-size: var(--text-small); color: var(--color-ink-soft); }

.profile-foot .btn { margin-left: 0; }

@media (max-width: 1024px) {
  .section--full { min-height: 0; display: block; padding-block: var(--space-20); }
  .profile { grid-template-columns: 1fr; gap: var(--space-2); }
  .profile-media { min-height: 0; aspect-ratio: 16 / 9; }
  .profile-body { padding: var(--space-6) var(--space-5) var(--space-5); }
}
@media (max-width: 768px) {
  /* La carte du fondateur prend presque toute la largeur : sur un écran de
     390 px, la gouttière du conteneur lui coûtait 40 px de contenu. */
  .section--full > .container { padding-inline: 10px; }
}
@media (max-width: 560px) {
  .section--full > .container { padding-inline: 8px; }
  .profile { padding: 6px; }
  .profile-media { min-height: 0; aspect-ratio: 4 / 3; border-radius: calc(var(--radius-lg) - 6px); }
  .profile-facts { grid-template-columns: 1fr 1fr; gap: var(--space-3); }
  .profile-foot { flex-wrap: wrap; gap: var(--space-4); }
  .profile-foot .btn { width: 100%; justify-content: center; }
}

/* Réseaux, à côté du bouton */
.profile-social { display: flex; gap: var(--space-2); margin-left: auto; }
.profile-social a {
  width: 42px; height: 42px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  color: var(--color-ink-soft);
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.profile-social a:hover {
  color: var(--color-cta);
  border-color: var(--color-blue-200);
  background: var(--color-surface-3);
}
@media (max-width: 560px) {
  .profile-foot { flex-direction: column; align-items: stretch; }
  .profile-social { margin-left: 0; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Cartes équipe, en vertical
   Même vocabulaire que la carte du fondateur, en plus petit : c'est lui qui
   reste le point fort de la page.
   -------------------------------------------------------------------------- */
.team-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }

.team-card {
  margin: 0;
  padding: 8px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.team-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-deep); }

.team-media {
  position: relative;
  aspect-ratio: 16 / 10;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--color-surface-3);
}
.team-media img { width: 100%; height: 100%; object-fit: cover; }

/* Emplacement en attendant la séance photo */
.team-initials {
  font-size: clamp(2.5rem, 2rem + 2vw, 3.5rem);
  font-weight: var(--w-display);
  letter-spacing: -0.04em;
  color: var(--color-blue-300);
  line-height: 1;
}

.team-chips {
  position: absolute;
  top: var(--space-3); left: var(--space-3); right: var(--space-3);
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.team-chips .chip-glass { font-size: var(--text-micro); padding: 5px 12px; }

.team-body { padding: var(--space-5); }

.team-name {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}
/* Un seul élément crie par carte : c'est le nom. */
.team-name h3 {
  margin: 0;
  font-size: var(--text-heading);
  font-weight: var(--w-bold);
  color: var(--color-ink);
  letter-spacing: var(--track-title);
}
/* La pastille de rôle situe, elle n'appelle pas : trois axes plus bas que le
   nom, en taille, en graisse et en contraste. */
.team-name .pill-outline {
  margin-left: auto;
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  color: var(--color-ink-soft);
  padding: 4px 11px;
  border-color: var(--color-border);
}

/* Colonne étroite : le corps descend d'un cran en taille et en interligne,
   sinon il produit des lignes de six mots séparées par du vide. */
.team-desc {
  margin: 0;
  max-width: none;
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  font-weight: var(--w-regular);
  line-height: var(--lh-small);
}

.team-note {
  margin-top: var(--space-6);
  color: var(--color-ink-soft);
  font-size: var(--text-body);
  text-align: center;
}

@media (max-width: 700px) {
  .team-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Plateau technique
   Trois blocs puis deux, pour que la dernière rangée occupe toute la largeur.
   -------------------------------------------------------------------------- */
.kit-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--space-4); }
.kit { grid-column: span 2; }
.kit:nth-last-child(-n+2) { grid-column: span 3; }
/* Trois cartes : la règle ci-dessus est réglée pour quatre (2+2+3+3) et fait
   déborder un trio. Ici, trois colonnes égales. */
.kit-grid--trio .kit,
.kit-grid--trio .kit:nth-last-child(-n+2) { grid-column: span 2; }

/* Empilée, pour vivre dans la demi-colonne d'un .duo : côte à côte dans 560 px
   les deux cartes cassent leur titre sur trois lignes. Centrée en hauteur face
   au texte, sinon le blanc s'accumule sous elle. */
.kit-grid--stack { grid-template-columns: 1fr; }
.kit-grid--stack .kit,
.kit-grid--stack .kit:nth-last-child(-n+2) { grid-column: 1; }
.duo > .kit-grid--stack { align-self: center; }

.kit {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
/* ---- La carte-lien : /services/ route vers les huit disciplines ---------- */
/* Toute la carte est cliquable, pas seulement un « en savoir plus » : sur un
   téléphone c'est une cible de 100 px de haut au lieu de 20. */
a.kit {
  text-decoration: none;
  color: inherit;
  transition: border-color var(--ease-out, .2s), background var(--ease-out, .2s);
}
a.kit:hover,
a.kit:focus-visible {
  border-color: var(--color-accent);
  background: var(--color-surface);
}
a.kit h3 { color: var(--color-ink); }

.kit-price {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 650;
  color: var(--color-accent);
}

.kit-icon {
  flex: none;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md);
  background: var(--color-surface-3);
  color: var(--color-accent);
}
.kit h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
  line-height: var(--lh-heading);
}
.kit p { margin: 0; color: var(--color-ink-soft); font-size: var(--text-small); line-height: var(--lh-small); }

.section-note {
  /* La mesure globale posée sur les paragraphes bridait ce bloc et le laissait
     calé à gauche : le texte était centré, pas le bloc. 62ch rendait 91
     caractères par ligne à 14 px, mesurés sur 25 pages. */
  max-width: 51ch;
  margin: var(--space-8) auto 0;
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  text-align: center;
}

/* Deux écarts manquaient, trouvés sur /orthodontie/ le 2026-08-26.

   1. .section-note n'a pas de marge basse, parce qu'elle avait toujours fermé
      une section. Dès qu'un bloc la suit, il se colle dessous : 0 px mesuré
      entre la note et les deux cartes-liens des gouttières.

   2. Sa marge haute de 32 px est calée sur du texte. Après un tableau, une
      frise ou une image, 32 px collent la note à ce qu'elle commente, et elle
      se lit comme une légende. .section-head pose déjà 48 sous son chapô :
      on aligne, la page n'a plus que deux écarts au lieu de trois. */
.section-note + .cv,
.section-note + .duo,
.section-note + .tbl-wrap,
.section-note + .kit-grid,
.section-note + .faq,
.section-note + .steps,
.section-note + .cases,
.section-note + .stats,
.section-note + .profile,
.section-note + .rail,
.section-note + .practical,
.section-note + .ba-grid,
.section-note + .kit-grid--trio,
.section-note + .slot { margin-top: var(--space-12); }
/* La liste ci-dessus ne prévoyait pas le cas le plus simple : un paragraphe.
   Sans cette règle la note centrée et le paragraphe suivant se touchaient.
   Trouvé sur la page détartrage le 2026-09-14. */
.section-note + p,
.section-note + h3 { margin-top: var(--space-10); }

/* Un sous-titre entre deux blocs de prose. Le h3 nu est calibré comme un titre
   de carte, 17 px et margin 0 : posé dans une .container il se lit comme un
   paragraphe en gras et colle à ses voisins. Trouvé sur la page détartrage le
   2026-09-14, où deux h3 portaient toute une sous-section. */
.sub-head {
  margin: var(--space-12) 0 var(--space-4);
  font-size: var(--text-heading);
}
.sub-head:first-child { margin-top: 0; }

/* La carte du praticien respire quand un bloc la suit. Elle porte une ombre
   profonde et un fond propre : collée au bloc suivant, les deux se lisent comme
   une seule pièce. Trouvé sur la page prix des implants le 2026-09-09, où le
   .profile du Dr Kandil précède les trois praticiens. */
.profile + .duo,
.profile + .section-note,
.profile + .stats,
.profile + .tbl-wrap,
.profile + .cv,
.profile + .faq { margin-top: var(--space-16); }

.cv + .section-note,
.duo + .section-note,
.tbl-wrap + .section-note,
.kit-grid + .section-note,
.faq + .section-note,
.steps + .section-note,
.cases + .section-note,
.stats + .section-note,
.profile + .section-note,
.rail + .section-note,
.practical + .section-note,
.ba-grid + .section-note,
.slot + .section-note { margin-top: var(--space-12); }

/* ── .cite ───────────────────────────────────────────────────────────────────
   Le bloc de mise en exergue. Créé le 2026-09-15 pour
   /composite-dentaire-prix-maroc/, à la demande du client.

   .section-note met une aparté en retrait : plus petite, plus pâle, centrée.
   .cite fait l'inverse, elle arrête la lecture sur une phrase qui compte.
   Les deux cohabitent sur une même page : la note pour l'administratif, la
   citation pour ce que le lecteur doit emporter.

   Pas de guillemets décoratifs, et c'est délibéré : ces phrases ne sont dites
   par personne. Un glyphe « ferait passer une affirmation du cabinet pour une
   parole rapportée, ce que le registre médical de voix-kandil interdit. Le
   filet d'accent fait le même travail typographique sans mentir sur la source.

   Le filet vit en ::before plutôt qu'en border-left : un border sur un bloc à
   coins arrondis se coupe en biseau dans les angles, et à 3 px cela se voit.

   ── La couleur, arrêtée le 2026-09-15 après deux essais ──
   1. --color-surface-3 : le bloc portait le même bleu pâle que les cartes de
      chiffres, les étapes et le calculateur. Il ne se distinguait de rien.
   2. --color-dark : visible, mais quatre pavés d'encre de nuit dans une page
      de prix pèsent lourd, et la teinte n'appartenait à rien d'autre.

   3. La règle retenue, demandée par le client : la citation prend le fond de
      l'autre section. Le site n'alterne que deux surfaces, --color-ground pour
      les sections nues et --color-surface pour les .section--alt ; la citation
      porte celle qu'elle n'a pas sous elle. Blanc sur le bleu de page, bleu de
      page sur le blanc. Aucune couleur nouvelle, et l'inversion suffit à la
      détacher sans bord ni ombre : c'est un aplat, pas une carte.

   Contrastes. Encre 16,9:1 sur blanc, 14,5:1 sur le bleu de page. Liens
   5,71:1 et 4,88:1. Le filet, élément non textuel, 3,68:1 et 3,14:1, au-dessus
   du seuil de 3:1 de WCAG 1.4.11. */
/* Calée à gauche et non centrée, contrairement à .section-note. La note est une
   légende : texte centré dans un bloc centré, elle se lit comme un aparté posé
   à côté. La citation est dans le flux : son filet doit tomber sur le bord
   gauche des paragraphes, sinon le bloc flotte à 320 px de la colonne de texte
   (mesuré à 1280 px le 2026-09-15) et le filet ne marque plus rien.
   Même mesure que le corps, --measure-body, pour que les bords droits
   s'alignent aussi. */
.cite {
  position: relative;
  max-width: var(--measure-body);
  margin: var(--space-8) 0;
  padding: var(--space-6) var(--space-8);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

/* L'inversion. Une section nue montre --color-ground, une .section--alt pose du
   blanc par-dessus : la citation prend l'autre des deux, dans les deux sens. */
.section--alt .cite { background: var(--color-ground); }

.cite::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--color-accent);
}

.cite p {
  margin: 0;
  color: var(--color-ink);
  font-size: var(--text-lead);
  font-weight: var(--w-regular);
  line-height: 1.55;
  text-wrap: pretty;
}

.cite p + p { margin-top: var(--space-4); }

.cite a { color: var(--color-blue-600); }

@media (max-width: 480px) {
  .cite { padding: var(--space-5) var(--space-6); }
  .cite p { font-size: var(--text-body); }
}

/* Mêmes règles d'écart que .section-note : le bloc n'a pas à connaître ce qui
   le précède, mais les composants qui ne portent pas de marge basse, si. */
.cv + .cite,
.duo + .cite,
.tbl-wrap + .cite,
.kit-grid + .cite,
.faq + .cite,
.steps + .cite,
.cases + .cite,
.stats + .cite,
.ticks + .cite,
.profile + .cite,
.rail + .cite,
.practical + .cite,
.ba-grid + .cite,
.slot + .cite { margin-top: var(--space-12); }

/* Un .slot posé sous un bloc de contenu suit la même règle : il n'a pas de
   marge propre, et il se collait au .duo qui le précède. */
.duo + .slot,
.cv + .slot,
.tbl-wrap + .slot,
.kit-grid + .slot,
.cases + .slot,
.profile + .slot,
.duo + .kit-grid,
.cv + .kit-grid,
.tbl-wrap + .kit-grid,
.cases + .kit-grid,
/* .stats n'avait jamais précédé une grille de cartes : les deux blocs se
   collaient, mesuré à 0 px le 2026-09-14 sur /invisalign-prix-maroc/, où les
   trois montants du remboursement passent en .stats au-dessus de deux .kit.
   Même défaut et même correctif que .tbl-wrap le 2026-09-10. */
.stats + .kit-grid,
.profile + .kit-grid { margin-top: var(--space-12); }

/* Un tableau n'a jamais eu de marge basse, parce qu'il avait toujours fermé sa
   section ou précédé une .section-note, qui apporte la sienne. Dès qu'un second
   tableau ou un .duo le suit, les deux se collent : mesuré à 0 px sur
   /remboursement-de-limplant-dentaire-au-maroc/ le 2026-09-10, deux fois dans la
   même section. Le .section-head y pose déjà 48 px avant le premier tableau, et
   l'écart disparaissait après. Même valeur que les règles ci-dessus.
   L'inverse manquait aussi : un tableau posé sous un .duo s'y collait, mesuré
   à 0 px le même jour dans la section mutuelles de la même page. */
/* Et les deux cas les plus simples manquaient encore à cette liste : un
   paragraphe ou un sous-titre posé sous un tableau. Mesuré à 0 px sur la page
   détartrage le 2026-09-14, dans la section « détartrage ou blanchiment » : le
   paragraphe touchait le bord bas du tableau. 40 px et non 48, parce qu'un bloc
   de texte suit un tableau de plus près qu'un composant. */
.tbl-wrap + p { margin-top: var(--space-10); }

/* ══ L'échelle verticale ═════════════════════════════════════════════════════
   Écrite le 2026-09-15 après avoir mesuré les 53 écarts de
   /composite-dentaire-prix-maroc/ dans Chrome, et non lu la cascade. Trois
   incohérences en sont sorties, toutes réparées ici.

   Le principe : l'écart dit ce qui appartient à quoi. Un texte serré sous un
   composant en fait partie ; un composant écarté ouvre autre chose.

   | 16 px | paragraphe → paragraphe · sous-titre → son contenu    | base.css |
   | 20 px | paragraphe → la liste qu'il annonce                   | .ticks   |
   | 24 à 32 px | composant → son lien de sortie                   | .link-more |
   | 32 px | texte → aparté : la note appartient au paragraphe     | .section-note, .cite |
   | 40 px | composant → bloc de texte                             | ici     |
   | 48 px | composant → composant · texte → composant · TOUT → sous-titre | ici |

   Les trois défauts mesurés le 2026-09-15 :

   1. .facture → p valait 0 px. Le calculateur est un bloc positionné sans
      marge basse, comme .stats et .tbl-wrap avant lui. Même famille de trou.
   2. .cite → .link-more valait 72 px. .link-more est en inline-flex : sa marge
      ne fusionne pas avec celle du bloc au-dessus, les deux s'additionnent et
      l'interligne s'y ajoute. Remise à zéro, l'écart retombe dans l'échelle.
   3. .sub-head prenait 40 px après un tableau, une citation ou une note, et
      48 px partout ailleurs. Un sous-titre n'est pas un bloc de texte : il
      ouvre une idée, il prend toujours le plus grand écart. Il sort donc de la
      règle des 40 px et rejoint celle des 48.                                 */

/* 40 px — un bloc de texte suit un composant de plus près qu'un autre composant. */
.stats + p,
.cite + p,
.facture + p,
.steps + p { margin-top: var(--space-10); }

/* 48 px — un sous-titre ouvre une idée, quel que soit ce qui le précède. */
.tbl-wrap + .sub-head,
.stats + .sub-head,
.cite + .sub-head,
.steps + .sub-head,
.ticks + .sub-head,
.points + .sub-head,
.facture + .sub-head,
.section-note + .sub-head { margin-top: var(--space-12); }

/* 48 px — composant sous composant. Trouvé en mesurant les pages voisines avec
   la même sonde : sur /bridge-dentaire-au-maroc/, les étapes de la pose
   touchaient le bloc du déroulé, 0 px. La règle est générale, elle ne vaut pas
   que pour la page qui l'a fait écrire. */
.duo + .steps,
.duo + .stats,
.duo + .facture,
.cv + .steps,
.steps + .steps,
.stats + .steps,
.tbl-wrap + .facture,
.steps + .facture,
.facture + .tbl-wrap,
.facture + .stats,
.facture + .steps { margin-top: var(--space-12); }

/* Le lien de sortie appartient au bloc qu'il prolonge. Sa marge ne fusionne pas
   (inline-flex), donc elle s'ajoutait à celle du bloc et l'interligne par-dessus :
   72 px mesurés sous une .cite. On la reprend à zéro et on laisse la marge basse
   du composant faire l'écart, ce qui donne 32 px sous une .cite, 24 sous un
   paragraphe. */
.cite + .link-more,
.stats + .link-more,
.steps + .link-more,
.facture + .link-more { margin-top: 0; }

.duo + .tbl-wrap,
.cv + .tbl-wrap,
.faq + .tbl-wrap,
.stats + .tbl-wrap,
.cases + .tbl-wrap,
.tbl-wrap + .tbl-wrap,
.tbl-wrap + .section-head,
.tbl-wrap + .duo,
.tbl-wrap + .cv,
.tbl-wrap + .faq,
.tbl-wrap + .stats,
.tbl-wrap + .cases,
.tbl-wrap + .ba-grid,
.tbl-wrap + .profile,
.tbl-wrap + .steps { margin-top: var(--space-12); }

/* Sous 768, .section-head descend à 40 : ces écarts suivent, sinon la note
   respire plus que le chapô sur l'écran où la page se lit vraiment. */
@media (max-width: 768px) {
  .section-note + .cv,
  .section-note + .duo,
  .section-note + .tbl-wrap,
  .section-note + .kit-grid,
  .section-note + .faq,
  .section-note + .steps,
  .section-note + .cases,
  .section-note + .stats,
  .section-note + .profile,
  .section-note + .rail,
  .section-note + .practical,
  .section-note + .ba-grid,
  .section-note + .kit-grid--trio,
  .cv + .section-note,
  .duo + .section-note,
  .tbl-wrap + .section-note,
  .kit-grid + .section-note,
  .faq + .section-note,
  .steps + .section-note,
  .cases + .section-note,
  .stats + .section-note,
  .profile + .section-note,
  .rail + .section-note,
  .practical + .section-note,
  .ba-grid + .section-note,
  .slot + .section-note,
  .section-note + .slot,
  .duo + .slot,
  .cv + .slot,
  .tbl-wrap + .slot,
  .kit-grid + .slot,
  .cases + .slot,
  .profile + .slot,
  .duo + .kit-grid,
  .cv + .kit-grid,
  .tbl-wrap + .kit-grid,
  .cases + .kit-grid,
  .stats + .kit-grid,
  .profile + .kit-grid,
  .duo + .tbl-wrap,
  .cv + .tbl-wrap,
  .faq + .tbl-wrap,
  .stats + .tbl-wrap,
  .cases + .tbl-wrap,
  .tbl-wrap + .tbl-wrap,
  .tbl-wrap + .duo,
  .tbl-wrap + .cv,
  .tbl-wrap + .faq,
  .tbl-wrap + .stats,
  .tbl-wrap + .cases,
  .tbl-wrap + .ba-grid,
  .tbl-wrap + .profile,
  .tbl-wrap + .steps { margin-top: var(--space-10); }
}

@media (max-width: 1024px) {
  .kit-grid { grid-template-columns: repeat(2, 1fr); }
  .kit, .kit:nth-last-child(-n+2) { grid-column: span 1; }
}
@media (max-width: 620px) {
  .kit-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Avis patients
   Un témoignage en grand à gauche, quatre cartes courtes à droite.
   Les attributs data- sont les points d'accroche du futur flux Google :
   assets/js/avis.js lira /assets/data/avis.json et les remplira.
   -------------------------------------------------------------------------- */
.avis-score {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  max-width: 520px;
  margin: 0 auto var(--space-10);
  padding: var(--space-5) var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.avis-note { font-size: clamp(2rem, 1.7rem + 1.3vw, 2.5rem); font-weight: var(--w-display); letter-spacing: -0.04em; line-height: 1; color: var(--color-ink); }
.avis-stars { display: flex; gap: 2px; color: #E8A317; }
.avis-count { margin: 6px 0 0; color: var(--color-ink-soft); font-size: var(--text-small); }
.avis-score .link-more { margin: 0 0 0 auto; font-size: var(--text-small); }

.avis-layout { display: grid; grid-template-columns: 350px 1fr; gap: var(--space-5); }
.avis-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }

/* ---- le témoignage en grand ---- */
.avis-featured {
  position: relative;
  margin: 0;
  /* Pas de rapport imposé : la carte s'étire sur toute la hauteur des deux
     rangées de droite. */
  height: 100%;
  min-height: 420px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--color-surface-2);
  box-shadow: var(--shadow-card);
}
.avis-featured img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 24%; }
.avis-featured::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg,
    rgba(20, 24, 36, .88) 0%,
    rgba(20, 24, 36, .62) 26%,
    rgba(20, 24, 36, .18) 48%,
    transparent 68%);
}
.avis-featured figcaption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  padding: var(--space-6);
  color: #fff;
}
.avis-featured .avis-stars { margin-bottom: var(--space-4); }
.avis-featured blockquote {
  margin: 0 0 var(--space-5);
  font-size: var(--text-lead);
  line-height: var(--lh-small);
  color: #fff;
  text-shadow: 0 1px 16px rgba(20, 24, 36, .5);
}
/* Le guillemet décoratif, discret, derrière la signature */
.avis-mark {
  position: absolute;
  right: var(--space-5); bottom: var(--space-4);
  font-size: 6rem;
  line-height: .6;
  font-weight: var(--w-display);
  color: rgba(255, 255, 255, .16);
  pointer-events: none;
}
.avis-featured .avis-by { border-top-color: rgba(255, 255, 255, .22); }
.avis-featured .avis-by b { color: #fff; }
.avis-featured .avis-by span { color: rgba(255, 255, 255, .7); }
.avis-featured .avis-avatar { background: rgba(255, 255, 255, .18); color: #fff; }

/* ---- les cartes courtes ---- */
.avis-card {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.avis-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-deep); }
.avis-card .avis-stars { margin-bottom: var(--space-4); }
.avis-card blockquote {
  margin: 0 0 var(--space-5);
  color: var(--color-ink);
  font-size: var(--text-strong);
  font-weight: var(--w-regular);
  line-height: var(--lh-lead);
}

.avis-by {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.avis-avatar {
  flex: none;
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-surface-3);
  color: var(--color-blue-600);
  font-size: var(--text-small);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
}
.avis-by b { display: block; font-size: var(--text-small); font-weight: var(--w-medium); color: var(--color-ink-soft); }
.avis-by span { display: block; font-size: var(--text-micro); color: var(--color-ink-soft); }

@media (max-width: 1024px) {
  .avis-layout { grid-template-columns: 300px 1fr; }
}
@media (max-width: 820px) {
  .avis-layout { grid-template-columns: 1fr; }
  .avis-featured { height: auto; aspect-ratio: 16 / 10; }
}
@media (max-width: 560px) {
  .avis-grid { grid-template-columns: 1fr; }
  .avis-featured { height: auto; aspect-ratio: 4 / 5; }
  .avis-score { flex-wrap: wrap; gap: var(--space-3); }
  .avis-score .link-more { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   Première visite — trois étapes numérotées
   -------------------------------------------------------------------------- */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-5); }

.step {
  position: relative;
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  margin-bottom: var(--space-4);
  border-radius: 50%;
  background: var(--color-surface-3);
  color: var(--color-blue-600);
  font-size: var(--text-small);
  font-weight: var(--w-medium);
  font-variant-numeric: tabular-nums;
}
.step h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
}
.step p { margin: 0; color: var(--color-ink-soft); font-size: var(--text-small); line-height: var(--lh-lead); }

/* La promesse qui porte la section */
.pledge {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-5);
  padding: var(--space-6);
  background: var(--color-dark);
  border-radius: var(--radius-lg);
  color: #fff;
}
.pledge-icon {
  flex: none;
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, .12);
  color: var(--color-on-dark);
}
.pledge b { display: block; font-size: var(--text-lead); font-weight: var(--w-medium); letter-spacing: var(--track-heading); }
.pledge span { display: block; margin-top: 3px; color: rgba(255, 255, 255, .78); font-size: var(--text-small); }
.pledge .btn { margin-left: auto; flex: none; }

@media (max-width: 900px) { .steps { grid-template-columns: 1fr; } }
@media (max-width: 620px) {
  .pledge { flex-wrap: wrap; gap: var(--space-4); }
  .pledge .btn { margin-left: 0; width: 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Venir au cabinet — moitié sombre, moitié carte, pleine largeur
   -------------------------------------------------------------------------- */
.visite { display: grid; grid-template-columns: 1fr 1fr; min-height: 520px; }

.visite-info {
  display: flex;
  align-items: center;
  padding: var(--space-16) var(--space-10);
  background: var(--color-dark);
  color: #fff;
}
.visite-inner { width: 100%; max-width: 470px; margin-left: auto; padding-right: var(--space-8); }
.visite-inner .eyebrow { display: block; margin-bottom: var(--space-3); color: var(--color-on-dark); font-size: var(--text-micro); font-weight: var(--w-medium); letter-spacing: var(--track-label); text-transform: uppercase; }
.visite-inner h2 { margin-bottom: var(--space-6); color: #fff; font-size: var(--text-title); }

/* Une ligne de prose au-dessus des coordonnées : elle dit qui vient d'où, ce que
   la liste d'adresse ne dit pas. Même couleur que .visite-line pour qu'elle se
   lise comme une introduction et non comme une quatrième coordonnée. */
.visite-intro {
  margin: calc(var(--space-6) * -1) 0 var(--space-6);
  font-size: var(--text-small);
  line-height: var(--lh-body);
  color: rgba(255, 255, 255, .76);
}

.visite-lines { display: flex; flex-direction: column; gap: var(--space-4); margin-bottom: var(--space-6); }
.visite-line { display: flex; align-items: flex-start; gap: var(--space-3); font-size: var(--text-small); line-height: var(--lh-body); color: rgba(255, 255, 255, .76); }
.visite-line svg { flex: none; margin-top: 3px; color: var(--color-on-dark); }
.visite-line a { color: #fff; text-decoration: none; }
.visite-line a:hover { color: var(--color-on-dark); }
.visite-line b { color: #fff; font-weight: var(--w-medium); }

/* Le repère qui sert vraiment : personne ne trouve Derb Riad du premier coup. */
.visite-tip {
  margin-bottom: var(--space-6);
  padding: var(--space-4) var(--space-5);
  border-left: 2px solid var(--color-on-dark);
  background: rgba(255, 255, 255, .06);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: var(--text-small);
  line-height: var(--lh-lead);
  color: rgba(255, 255, 255, .82);
}
.visite-tip b { color: #fff; font-weight: var(--w-medium); }

/* Le bord monte de .3 à .40 (D-040). Ce bouton n'a AUCUN fond : son contour est
   la seule chose qui dise où l'on peut cliquer, donc WCAG 1.4.11 demande 3:1.
   Il ne les tenait pas, et pas seulement depuis le marine : sur l'ancienne bande
   #181D31 il donnait déjà 2,70:1. La marine #0B1E5B est un peu plus claire
   (luminance 0,0176 contre 0,0129), ce qui l'avait fait tomber à 2,58:1.
   .40 donne 3,56:1. Mesuré, pas estimé. */
.btn--onDark { background: transparent; color: #fff; border-color: rgba(255, 255, 255, .40); }
.btn--onDark:hover { background: rgba(255, 255, 255, .1); color: #fff; border-color: rgba(255, 255, 255, .55); }

.visite-map { position: relative; min-height: 320px; background: var(--color-surface-2); }
.visite-map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

@media (max-width: 900px) {
  .visite { grid-template-columns: 1fr; min-height: 0; }
  .visite-info { padding: var(--space-12) var(--gutter); }
  .visite-inner { max-width: none; margin-left: 0; padding-right: 0; }
  .visite-map { min-height: 300px; }
}

/* --------------------------------------------------------------------------
   Bande de conversion — le dernier appel avant le pied de page
   Le sous-titre est à 88 % de blanc : à 80 % il tombe sous 4,5:1 sur ce bleu.
   -------------------------------------------------------------------------- */
/* 🔵 La bande ne porte plus le MÊME marine que le pied de page (D-040).
   Les deux étaient à `--color-cta`, donc au même #0B1E5B, donc collés : deux
   blocs sombres de 300 px empilés sans couture, que l'œil lit comme un seul
   pavé et dans lequel le dernier appel à l'action se noyait. La bande monte
   d'un cran, à `--color-navy-700` #12306F. Mesuré : 1,242:1 contre le pied de
   page, là où c'était 1,000:1, c'est-à-dire rien. Tout ce qui se pose dessus
   reste largement au-dessus des seuils : blanc plein 12,51:1, chapô à 88 %
   10,02:1, note à 74 % 7,54:1, bord du bouton fantôme 3,76:1 pour 3 exigés. */
.cta-band { background: var(--color-navy-700); padding: var(--space-20) 0; }

/* 🔴 LA MISE EN PAGE ÉTAIT CASSÉE, et elle l'était AVANT le changement de peau.
   `.cta-band-actions` portait `flex: none` — donc « ne rétrécis jamais » — et
   `horaires.js` lui ajoute une troisième enfant, la note « Nous sommes fermés ».
   Deux boutons et une note sur une seule ligne qui refuse de plier font 887 px
   sur un conteneur de 1 180 : il restait **221 px** au titre.
   Mesuré sur les 18 pages qui portent la bande : **11 titres sur 4 lignes ou
   plus, jusqu'à 7 sur /orthodontie/**, dans une colonne de 221 px, à côté d'un
   vide de plusieurs centaines de pixels. Identique avant la serif — celle-ci a
   seulement ajouté une ligne sur quatre pages, elle n'a pas créé le défaut.
   Ce qui le corrige : le bloc de texte RÉCLAME sa place au lieu de la céder, la
   note descend sous les boutons, et le tout se replie quand il n'y a plus la
   place au lieu d'écraser la colonne de gauche. */
.cta-band-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8) var(--space-10);
}
/* 26rem de base : en dessous, mieux vaut passer les boutons à la ligne que de
   continuer à comprimer le titre. */
.cta-band-inner > :not(.cta-band-actions) { flex: 1 1 26rem; min-width: 0; }
.cta-band h2 {
  margin-bottom: var(--space-3);
  max-width: 30ch;   /* 22ch en sans — le ch a rétréci avec la serif, D-040 */
  color: #fff;
  font-size: var(--text-title);
}
.cta-band p {
  margin: 0;
  max-width: 44ch;
  color: rgba(255, 255, 255, .88);
  font-size: var(--text-strong);
  line-height: var(--lh-lead);
}

/* Un lien dans le chapô de la bande : l'encre par défaut est le bleu du bouton,
   c'est-à-dire la couleur du fond. Il devient blanc souligné. */
/* Une seconde ligne dans la bande, pour les renseignements pratiques : adresse
   et horaires. Elle ne doit pas peser autant que la phrase qui la précède, et
   .cta-band p porte margin 0, donc les deux paragraphes se toucheraient. Écrite
   pour la page détartrage le 2026-09-14 : le geste au-dessus, les
   renseignements en dessous. */
.cta-band .cta-band-note {
  margin-top: var(--space-4);
  max-width: 54ch;
  font-size: var(--text-small);
  color: rgba(255, 255, 255, .74);
}

.cta-band p a { color: #fff; text-decoration-color: rgba(255, 255, 255, .5); }
.cta-band p a:hover { text-decoration-color: #fff; }

/* `flex: none` est remplacé par `0 1 auto` : le groupe garde sa taille tant
   qu'il y a la place, et accepte de plier quand il n'y en a plus.
   La note injectée par `horaires.js` prend une ligne à elle, sous les boutons,
   alignée à droite comme eux. Sans ça elle s'ajoute au bout de la rangée et
   c'est ELLE qui volait 240 px au titre. */
/* ⚠️ `max-width` n'est pas un réglage cosmétique ici, c'est LUI qui corrige.
   Faire passer la note à la ligne ne suffisait pas : pour calculer la largeur
   « max-content » d'un conteneur flex, le navigateur additionne TOUS les enfants
   sur une seule ligne, `flex-wrap` ou non. Le groupe réclamait donc toujours
   887 px (493 de boutons + 381 de note), ne tenait pas à côté du texte, et les
   deux blocs se retrouvaient l'un SOUS l'autre — le titre pleine largeur, les
   boutons collés à gauche en dessous. Plafonner le groupe casse ce calcul.
   34rem laisse les deux boutons sur une ligne (493 px mesurés) et donne le
   reste au texte. `min()` protège le mobile, où 34rem dépasserait l'écran. */
.cta-band-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-3);
  flex: 0 1 auto;
  max-width: min(100%, 34rem);
}
.cta-band-actions .ferme-note {
  flex: 1 0 100%;
  margin: 0;
  max-width: none;
  text-align: right;
}
.btn--white { background: #fff; color: var(--color-cta); }
.btn--white:hover { background: var(--color-surface-2); color: var(--color-cta-hover); }
.btn--outline-light { background: transparent; color: #fff; border-color: rgba(255, 255, 255, .45); }
.btn--outline-light:hover { background: rgba(255, 255, 255, .12); color: #fff; border-color: rgba(255, 255, 255, .7); }

@media (max-width: 1024px) {
  .cta-band-inner { flex-direction: column; align-items: flex-start; gap: var(--space-8); }
  .cta-band h2 { max-width: none; }
  /* 🔴 `flex: 1 1 26rem` DOIT être annulé ici, et c'est le seul endroit qui
     compte. `flex-basis` se mesure sur l'axe PRINCIPAL : en `row` il vaut une
     largeur, en `column` il vaut une HAUTEUR. Le conteneur bascule en colonne à
     partir d'ici, donc les 26rem écrits pour donner sa largeur au titre lui
     imposaient 416 px de haut pour 210 px de contenu — 206 px de vide au milieu
     de la bande, sur mobile seulement, là où le desktop était juste.
     Mesuré : bloc de texte à 416 px, exactement 26rem, h2 89 + marge 12 + p 109. */
  .cta-band-inner > :not(.cta-band-actions) { flex: 0 1 auto; }
  /* Empilé, tout le bloc est calé à gauche : la note doit suivre. Alignée à
     droite elle restait suspendue sous des boutons pleine largeur, seule
     ligne du site à fuir vers la droite. */
  .cta-band-actions { max-width: none; justify-content: flex-start; }
  .cta-band-actions .ferme-note { text-align: left; }
}
@media (max-width: 560px) {
  .cta-band { padding: var(--space-16) 0; }
  .cta-band-actions { flex-direction: column; width: 100%; }
  .cta-band-actions .btn { width: 100%; justify-content: center; }
  /* Même piège que ci-dessus, un cran plus bas : le groupe d'actions passe en
     colonne ici, donc le `flex: 1 0 100%` qui met la note sur sa propre LIGNE
     en disposition horizontale lui demanderait 100 % de la HAUTEUR du groupe.
     En colonne elle est déjà seule sur sa ligne : elle n'a plus besoin de rien. */
  .cta-band-actions .ferme-note { flex: 0 0 auto; }
}

/* ---- La clôture en panneau fendu (4.1.3) ----
   Deux moitiés dans un seul cadre très arrondi : la sombre porte la promesse,
   la claire porte le formulaire. La page ne finit plus sur un aplat mais sur
   un objet, et l'objet dit qu'il reste une chose à faire.
   Le mouvement tombe sous `prefers-reduced-motion`, les jetons de durée
   passant à zéro. */
/* Même correction que `.cta-band`, et pour la même raison (D-040) : cette
   variante est la clôture de 14 pages — toutes les pages de prix — et sa moitié
   sombre était sur `--color-dark`, c'est-à-dire exactement le marine du pied de
   page. Vérifié au rendu : la colonne sombre coulait dans le pied sans la
   moindre couture, sur toute la hauteur. Elle monte au même `--color-navy-700`.
   Tout ce qui se pose dessus reste au-dessus des seuils : blanc 12,51:1,
   chapô 10,02:1, note 6,32:1, bord du bouton fantôme 3,76:1. Le filigrane
   « Kandil » est à 1,13:1, et c'est voulu : c'est un décor, pas un texte. */
.cta-band--split { padding: 0; background: var(--color-navy-700); }

.split-panel { display: grid; grid-template-columns: 1fr 1fr; }

/* Pleine largeur, mais le texte reste aligné sur la grille du site : chaque
   moitié serre son contenu contre l'axe central, sur la demi-largeur du
   conteneur. Les deux colonnes se lisent donc comme la suite de la page et non
   comme un encart posé dessus. */
.split-inner {
  position: relative;
  width: 100%;
  max-width: calc(var(--container) / 2);
  padding-inline: var(--gutter);
}

/* La moitié sombre */
.split-dark {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;
  padding-block: var(--space-12);
  /* ⚠️ C'est CETTE ligne qui peint la moitié sombre, pas celle de
     `.cta-band--split` : le fond du parent était recouvert et le changer seul
     n'avait rien changé à l'écran. Vu en lisant les styles calculés, pas en
     regardant la page, où les deux marines sont trop proches pour qu'on
     distingue lequel gagne. Les deux sont désormais sur `--color-navy-700`. */
  background: var(--color-navy-700);
}
.split-dark h2 {
  position: relative;
  margin: 0 0 var(--space-4);
  max-width: 19ch;   /* 14ch en sans — le ch a rétréci avec la serif, D-040 */
  color: #fff;
  font-size: var(--text-display);
  line-height: 1.08;
}
.split-dark p {
  position: relative;
  margin: 0;
  max-width: 42ch;
  color: rgba(255, 255, 255, .74);
  font-size: var(--text-body);
  line-height: var(--lh-lead);
}
/* Dans le panneau fendu tout est calé à gauche : la note doit l'être aussi.
   Elle héritait du `flex-end` et du `text-align: right` écrits pour la bande
   pleine largeur, où les boutons SONT à droite. Ici ils sont à gauche, et la
   note partait seule vers la droite sous eux. */
.split-dark .cta-band-actions {
  position: relative;
  margin-top: var(--space-8);
  max-width: none;
  justify-content: flex-start;
}
.split-dark .cta-band-actions .ferme-note { text-align: left; }

/* Le filigrane : le nom du cabinet, assez grand pour sortir du cadre, assez
   pâle pour ne jamais concurrencer le titre. Il donne au panneau une
   profondeur qu'un aplat n'a pas. */
.split-mark {
  position: absolute;
  left: -0.05em;
  bottom: -0.30em;
  color: rgba(255, 255, 255, .045);
  font-size: 13rem;
  font-weight: var(--w-display);
  letter-spacing: -.05em;
  line-height: 1;
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
}

/* La moitié claire, et la carte qui flotte dessus */
.split-light {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  padding-block: var(--space-12);
  background: var(--color-surface-2);
}
/* Deux formes molles derrière la carte. Sans elles, une carte blanche sur un
   fond pâle ne flotte pas, elle se pose. */
.split-light::before,
.split-light::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}
.split-light::before {
  top: -14%; right: -10%;
  width: 300px; height: 300px;
  background: radial-gradient(circle, rgba(65, 89, 219, .10) 0%, rgba(65, 89, 219, 0) 68%);
}
.split-light::after {
  bottom: -18%; left: -12%;
  width: 340px; height: 340px;
  background: radial-gradient(circle, rgba(65, 89, 219, .08) 0%, rgba(65, 89, 219, 0) 68%);
}

.rdv-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 400px;
  margin-inline: auto;
  padding: var(--space-8);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: 0 22px 48px rgba(20, 28, 70, .13), 0 2px 8px rgba(20, 28, 70, .05);
}
.rdv-card h3 {
  margin: 0 0 var(--space-2);
  color: var(--color-ink);
  font-size: var(--text-title);
  letter-spacing: -.01em;
}
.rdv-card > p {
  margin: 0 0 var(--space-5);
  max-width: none;
  color: var(--color-ink-soft);
  font-size: var(--text-small);
}

/* La note des horaires est un renseignement, pas un argument : elle se lit
   après coup, ou pas du tout. Plus petite que tout le reste de la carte, et
   posée assez loin du bouton pour ne pas se lire comme sa conséquence. */
.rdv-card .form-note {
  margin-top: var(--space-6);
  font-size: 0.6875rem;
  line-height: 1.75;
  color: var(--color-ink-soft);
  opacity: .85;
}

.rdv-card .form-grid { gap: var(--space-3); }
.rdv-card .field-box { position: relative; display: block; }

.rdv-card .field-icon {
  position: absolute;
  top: 50%; left: var(--space-4);
  width: 19px; height: 19px;
  margin-top: -9.5px;
  color: var(--color-ink-soft);
  opacity: .7;
  pointer-events: none;
  transition: color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}

.rdv-card .form-grid input,
.rdv-card .form-grid select {
  padding-left: var(--space-12);
  background-color: var(--color-blue-50);
  border-color: transparent;
  box-shadow: inset 0 0 0 1px var(--color-border);
  transition: background-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.rdv-card .form-grid input::placeholder { color: var(--color-ink-soft); opacity: .6; }

.rdv-card .form-grid input:hover,
.rdv-card .form-grid select:hover { box-shadow: inset 0 0 0 1px var(--color-border-strong); }

/* Le focus éclaire le champ, et l'icône passe à la couleur du cabinet. */
.rdv-card .form-grid input:focus-visible,
.rdv-card .form-grid select:focus-visible {
  background-color: var(--color-surface);
  border-color: transparent;
  box-shadow: inset 0 0 0 1.5px var(--color-cta), var(--shadow-focus);
}
.rdv-card .field-box:focus-within .field-icon { color: var(--color-cta); opacity: 1; }
.rdv-card .field:focus-within label { color: var(--color-cta); }
.rdv-card .form-grid label { transition: color var(--dur-fast) var(--ease-out); }

/* La coche du champ rempli. Bleue, parce que la planche du site n'a pas de vert
   et qu'un vert importé pour une coche est un vert qui reviendra ailleurs.
   `:has` est l'implémentation ; là où il manque, la coche ne paraît pas et
   rien d'autre ne change. */
.rdv-card .field-check {
  position: absolute;
  top: 50%; right: var(--space-4);
  width: 17px; height: 17px;
  margin-top: -8.5px;
  color: var(--color-cta);
  opacity: 0;
  transform: scale(.6);
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.rdv-card .field-box:has(input:required:valid) .field-check { opacity: 1; transform: none; }
.rdv-card .field-box:has(input:focus-visible) .field-check { opacity: 0; transform: scale(.6); }

/* La note de fermeture, posée par horaires.js sous un groupe d'actions qui
   porte un numéro. Petite et sans fond : c'est un renseignement d'état, pas un
   avertissement. Un bandeau coloré dirait « attention » à quelqu'un qui n'a
   qu'à écrire au lieu d'appeler. */
.ferme-note {
  flex-basis: 100%;
  width: 100%;
  margin: 10px 0 0;
  color: var(--color-ink-soft);
  font-size: var(--text-micro);
  line-height: var(--lh-small);
}
/* Sur la bande sombre, l'encre du texte courant ne se lit plus.
   ⚠️ Et la taille doit être redite : `.split-dark p` vaut 0,1,1 contre 0,1,0
   pour `.ferme-note`, donc la note y repassait à 16 px, deux fois plus grosse
   que la même note ailleurs sur la page. Vu à la mesure, pas à l'œil. */
.split-dark .ferme-note,
.cta-band .ferme-note {
  color: rgba(255, 255, 255, .66);
  font-size: var(--text-micro);
  line-height: var(--lh-small);
  max-width: none;
}

/* Fermé : dans les deux barres collantes, WhatsApp prend la couleur et l'appel
   la perd. Rien n'est caché, seule l'insistance change. */
[data-cabinet="ferme"] .callbar a.is-primary,
[data-cabinet="ferme"] .dock-item.is-primary {
  color: var(--color-ink-soft);
}
[data-cabinet="ferme"] .callbar a.is-primary::before { background: var(--color-ink-soft); opacity: .4; }
[data-cabinet="ferme"] .dock-item.is-primary {
  background: var(--color-surface);
  border-color: var(--color-border);
}
[data-cabinet="ferme"] .dock-item.is-primary:hover {
  background: var(--color-surface);
  color: var(--color-ink);
}
[data-cabinet="ferme"] .callbar a[href*="wa.me"] { color: var(--color-cta); }
[data-cabinet="ferme"] .dock-item[href*="wa.me"] {
  background: var(--color-cta);
  border-color: transparent;
  color: #FFFFFF;
}
[data-cabinet="ferme"] .dock-item[href*="wa.me"]:hover { background: var(--color-cta-hover); color: #FFFFFF; }

/* Le pot de miel. Découpé à zéro plutôt que masqué : `display:none` et
   `hidden` sont exactement ce qu'un robot un peu sérieux contrôle avant de
   remplir. Et retiré des tabulations, pour qu'aucun humain n'y tombe.
   ⚠️ Pas `left: -9999px` : ça marche aussi, mais l'élément se retrouve alors
   dans le relevé « hors écran » de `measure.js` à chaque contrôle, et un
   avertissement permanent qu'on apprend à ignorer est un avertissement mort. */
.pot {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  border: 0 !important;
  overflow: hidden !important;
  clip-path: inset(50%);
  white-space: nowrap;
  pointer-events: none;
}

/* Le retour d'envoi, sous le bouton. */
.form-retour {
  grid-column: 1 / -1;
  margin: var(--space-2) 0 0;
  font-size: var(--text-small);
  line-height: var(--lh-small);
  color: var(--color-cta);
}
.form-retour.is-erreur { color: #B0344B; }

/* La confirmation prend la place du formulaire : ce qui a été envoyé n'a plus à
   rester sous les yeux. Elle ne dit pas seulement « c'est parti », elle donne au
   patient ce dont il a besoin ensuite : où venir, quand, et le numéro s'il veut
   une réponse tout de suite. Le formulaire ne demande pas d'e-mail, donc cet
   écran EST l'accusé de réception ; il n'y en aura pas d'autre. */
.rdv-merci { display: grid; justify-items: center; text-align: center; }
/* Sans cette ligne, le `display: grid` ci-dessus l'emporterait sur l'attribut
   `hidden` et la confirmation serait visible dès le chargement. */
.rdv-merci[hidden] { display: none; }
/* 🔴 Et le formulaire lui-même (2026-09-19, premier vrai envoi, sur téléphone) :
   `.form-grid { display: grid }` l'emportait aussi sur `hidden`. Après l'envoi,
   le formulaire restait affiché au-dessus de la confirmation, bouton figé sur
   « Envoi… ». Même piège que la ligne ci-dessus, vu pour la confirmation et pas
   pour ce qu'elle remplace. */
.rdv-card > [hidden] { display: none; }
/* 🔴 2026-09-21, audit avant bascule : la règle ci-dessus DÉPEND DU CONTENEUR, et
   `/contactez-nous/` est la seule des 15 pages à formulaire dont le conteneur est
   `.reach-card` et non `.rdv-card`. Sur la page de plus haute intention du site, le
   formulaire restait donc affiché après un envoi réussi, bouton figé sur « Envoi… »,
   la confirmation dessous. La demande partait, l'écran disait le contraire.
   Le correctif de 2026-09-19 avait traité le symptôme sur 14 pages et manqué la 15e,
   parce qu'il visait une classe de carte au lieu de viser le formulaire.
   Cette règle-ci ne dépend d'aucun conteneur : une 16e page avec une 3e carte marchera. */
.form-grid[hidden] { display: none; }
.rdv-merci:focus-visible { outline: none; }

.rdv-merci-coche {
  width: 46px; height: 46px;
  margin-bottom: var(--space-3);
  color: var(--color-cta);
}
.rdv-merci h3 { margin: 0 0 var(--space-2); color: var(--color-ink); font-size: var(--text-heading); }
.rdv-merci-mot {
  margin: 0;
  max-width: 34ch;
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  line-height: var(--lh-lead);
}
/* 🔴 2026-09-19 : `.cta-band p` peint en blanc tout paragraphe de la bande, pour
   sa moitié sombre, et pèse plus lourd (0,1,1) que les deux règles ci-dessus
   (0,1,0). Or la carte du formulaire est blanche : la phrase de confirmation y
   était écrite BLANC SUR BLANC, invisible, sur les 14 pages de prix. Mesuré au
   vrai navigateur : rgba(255, 255, 255, .88). Le retour d'envoi prenait aussi
   la taille de la bande. Ces trois lignes rendent à la carte ce qui est à elle. */
.cta-band .rdv-merci-mot { color: var(--color-ink-soft); font-size: var(--text-small); }
.cta-band .form-retour { color: var(--color-cta); font-size: var(--text-small); }
.cta-band .form-retour.is-erreur { color: #B0344B; }

/* L'adresse et les horaires, alignés à gauche : une adresse centrée se relit
   deux fois avant d'être comprise. */
.rdv-infos {
  width: 100%;
  margin: var(--space-6) 0 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
  display: grid;
  gap: var(--space-4);
  text-align: left;
}
.rdv-infos dt {
  margin-bottom: 2px;
  color: var(--color-ink-soft);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.rdv-infos dd {
  margin: 0;
  color: var(--color-ink);
  font-size: var(--text-small);
  line-height: var(--lh-lead);
}

.rdv-merci-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  width: 100%;
  margin-top: var(--space-5);
}
.rdv-merci-actions .btn { flex: 1 1 auto; justify-content: center; gap: 6px; }

/* Le bouton : la flèche part vers la droite, c'est-à-dire vers l'envoi. */
.btn--send { gap: var(--space-2); }
.btn--send svg {
  width: 18px; height: 18px;
  transition: transform var(--dur-base) var(--ease-out);
}
.btn--send:hover svg { transform: translateX(4px); }
.btn--send:active { transform: translateY(1px); }

/* Le repli : sous 1024 les deux moitiés s'empilent, la sombre au-dessus. */
@media (max-width: 1024px) {
  .split-panel { grid-template-columns: 1fr; }
  .split-dark, .split-light { align-items: center; padding-block: var(--space-10); }
  .split-inner { max-width: var(--container); }
  .split-dark h2 { max-width: none; }
  .split-mark { font-size: 9rem; }
  .rdv-card { max-width: 480px; }
}
@media (max-width: 560px) {
  .split-dark, .split-light { padding-block: var(--space-8); }
  .split-mark { font-size: 6.5rem; }
  .rdv-card { padding: var(--space-6); box-shadow: var(--shadow-lift); }
  .form-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Pied de page
   -------------------------------------------------------------------------- */
.site-footer { background: var(--color-dark); color: rgba(255, 255, 255, .72); }

.footer-top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: var(--space-10);
  padding: var(--space-16) 0 var(--space-12);
}

.footer-brand .brand { color: #fff; margin-bottom: var(--space-5); }
.footer-brand .brand-logo { height: 44px; }
.footer-brand p { margin: 0 0 var(--space-5); max-width: 30ch; font-size: var(--text-small); line-height: var(--lh-body); }
.footer-social { display: flex; gap: var(--space-2); }
.footer-social a {
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 50%;
  color: rgba(255, 255, 255, .8);
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.footer-social a:hover { color: #fff; border-color: rgba(255, 255, 255, .4); background: rgba(255, 255, 255, .08); }

.footer-col h3 {
  margin: 0 0 var(--space-4);
  color: #fff;
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.footer-col ul { margin: 0; padding: 0; list-style: none; }
.footer-col li { margin: 0 0 var(--space-3); }
.footer-col a {
  color: rgba(255, 255, 255, .72);
  font-size: var(--text-small);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
.footer-col a:hover { color: #fff; }

/* L'écart est nul et le remplissage le remplace : avec 12 px d'écart entre deux
   liens de 22 px, les zones de 44 px se recouvraient et celle du voisin gagnait.
   Le téléphone n'atteignait que 34 px. Le bloc grandit d'une quarantaine de
   pixels, c'est le prix de la règle des 44 px tenue jusqu'au bout. */
.footer-contact { display: flex; flex-direction: column; gap: 0; font-size: var(--text-small); line-height: var(--lh-lead); }
.footer-contact a { color: #fff; text-decoration: none; padding-block: 11px; }
.footer-contact a:hover { color: var(--color-on-dark); }

.footer-bottom {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
  padding: var(--space-5) 0 var(--space-8);
  border-top: 1px solid rgba(255, 255, 255, .12);
  font-size: var(--text-micro);
  color: rgba(255, 255, 255, .55);
}
.footer-bottom p { margin: 0; }
.footer-bottom .footer-hours { margin-left: auto; }

@media (max-width: 1024px) {
  .footer-top { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
}
@media (max-width: 560px) {
  .footer-top { grid-template-columns: 1fr; padding-top: var(--space-12); }
  .footer-bottom .footer-hours { margin-left: 0; }
}

/* --------------------------------------------------------------------------
   Entrée du hero au chargement
   Le contenu monte une fois, en cascade. Sans ça la page est simplement là,
   ce qui est exactement la sensation « vieux site ».
   -------------------------------------------------------------------------- */
.hero-badge, .hero .fil, .hero h1, .hero .lede {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--dur-enter) var(--ease-out),
              transform var(--dur-enter) var(--ease-out);
}
body.is-loaded .hero-badge { opacity: 1; transform: none; transition-delay: 120ms; }
body.is-loaded .hero .fil  { opacity: 1; transform: none; transition-delay: 120ms; }
body.is-loaded .hero h1    { opacity: 1; transform: none; transition-delay: 220ms; }
body.is-loaded .hero .lede { opacity: 1; transform: none; transition-delay: 340ms; }

.action-bar {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-enter) var(--ease-out),
              transform var(--dur-enter) var(--ease-out);
}
body.is-loaded .action-bar { opacity: 1; transform: translateY(0); transition-delay: 460ms; }

@media (prefers-reduced-motion: reduce) {
  .hero-badge, .hero .fil, .hero h1, .hero .lede, .action-bar {
    opacity: 1; transform: none; transition: none;
  }
}

/* --------------------------------------------------------------------------
   Barre d'actions collante, mobile uniquement
   Trois colonnes égales, icône au-dessus d'un libellé minuscule : la grammaire
   des barres d'onglets, celle que tout le monde sait lire sans y penser.
   -------------------------------------------------------------------------- */
.callbar { display: none; }

@media (max-width: 768px) {
  .callbar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: var(--z-sticky);
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    padding-bottom: env(safe-area-inset-bottom);
    background: color-mix(in srgb, var(--color-surface) 84%, transparent);
    backdrop-filter: saturate(180%) blur(22px);
    -webkit-backdrop-filter: saturate(180%) blur(22px);
    border-top: 1px solid color-mix(in srgb, var(--color-border) 80%, transparent);
    transform: translateY(100%);
    transition: transform 380ms cubic-bezier(.32, .72, 0, 1);
  }
  .callbar.is-in { transform: none; }

  .callbar a {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-height: 54px;
    padding: 7px 4px;
    color: var(--color-ink-soft);
    font-size: var(--text-micro);
    font-weight: var(--w-medium);
    letter-spacing: var(--track-body);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--dur-fast) var(--ease-out);
  }
  .callbar a svg { transition: transform var(--dur-fast) var(--ease-out); }
  .callbar a:active { color: var(--color-ink); }
  .callbar a:active svg { transform: scale(.9); }

  /* L'appel est l'action principale : c'est la seule qui porte la couleur. */
  .callbar a.is-primary { color: var(--color-cta); }
  .callbar a.is-primary::before {
    content: "";
    position: absolute;
    top: 0; left: 50%;
    width: 26px; height: 2px;
    margin-left: -13px;
    border-radius: 0 0 2px 2px;
    background: var(--color-cta);
  }

  body { padding-bottom: 62px; }
}
@media (prefers-reduced-motion: reduce) { .callbar { transition: none; } }

/* --------------------------------------------------------------------------
   Le cabinet — moitié texte, moitié carrousel, pleine largeur
   -------------------------------------------------------------------------- */
.cabinet { display: grid; grid-template-columns: 1fr 1fr; min-height: 560px; background: var(--color-surface); }

.cabinet-info {
  display: flex;
  align-items: center;
  /* Le bord gauche du texte s'aligne sur celui du conteneur du site, pour que
     la section pleine largeur reste dans la même colonne que le reste. */
  padding: var(--space-16) var(--space-10) var(--space-16)
           max(var(--space-10), calc((100vw - var(--container)) / 2 + var(--gutter)));
}
/* Le titre fait cinquante caractères : à 520 px il tombait sur trois lignes
   avec un mot seul en dernière. La colonne passe à 620 px, le titre y tient
   sur deux. */
.cabinet-inner { width: 100%; max-width: 620px; padding-right: var(--space-6); }
.cabinet-inner h2 { max-width: none; text-wrap: balance; }
.cabinet-inner .eyebrow {
  display: block;
  margin-bottom: var(--space-3);
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.cabinet-inner h2 { margin-bottom: var(--space-4); font-size: var(--text-title); }
.cabinet-inner p { margin: 0; color: var(--color-ink); font-size: var(--text-lead); line-height: var(--lh-lead); }
.cabinet-inner p + p { margin-top: var(--space-4); }
.cabinet-inner .link-more { margin-top: var(--space-6); }

/* Le carrousel occupe sa moitié entière : ni rayon, ni ombre, ni marge. */
.carousel--fill { border-radius: 0; box-shadow: none; }
.carousel--fill .carousel-track { height: 100%; aspect-ratio: auto; }
.carousel--fill .carousel-slide figcaption { padding: var(--space-12) var(--space-6) var(--space-5); }

@media (max-width: 900px) {
  .cabinet { grid-template-columns: 1fr; min-height: 0; }
  .cabinet-info { padding: var(--space-12) var(--gutter); order: 2; }
  .cabinet-inner { max-width: none; margin-left: 0; padding-right: 0; }
  .carousel--fill { order: 1; }
  .carousel--fill .carousel-track { height: auto; aspect-ratio: 16 / 10; }
}

/* --------------------------------------------------------------------------
   Liste à coches — pour ce qui doit se scanner, pas se lire
   -------------------------------------------------------------------------- */
.ticks { margin: var(--space-5) 0 0; padding: 0; list-style: none; }
.ticks li {
  position: relative;
  margin: 0 0 var(--space-3);
  padding-left: var(--space-8);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  line-height: var(--lh-small);
}
.ticks li:last-child { margin-bottom: 0; }
.ticks li::before {
  content: "";
  position: absolute;
  left: 0; top: 1px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--color-surface-3);
}
.ticks li::after {
  content: "";
  position: absolute;
  left: 6px; top: 7px;
  width: 8px; height: 4px;
  border-left: 1.8px solid var(--color-cta);
  border-bottom: 1.8px solid var(--color-cta);
  transform: rotate(-45deg);
}

/* --------------------------------------------------------------------------
   Ouverture de page intérieure — .page-head
   -------------------------------------------------------------------------- */
/* L'accueil ouvre sur une photo pleine largeur et une nav transparente posée
   dessus. Les 29 autres pages n'ont pas ce luxe : on arrive dessus depuis
   Google, avec une question. L'ouverture doit donc répondre, pas impressionner.

   Conséquence technique : pas de hero, donc l'en-tête est collant dès le
   premier pixel (page.js). Il est fixe et fait 66 px : le rembourrage haut
   les rend, sinon le fil d'Ariane passe dessous. */
.page-head {
  padding-top: calc(66px + var(--space-16));
  padding-bottom: var(--space-20);
}

/* --------------------------------------------------------------------------
   La 404
   Un seul écran : le chiffre, la mascotte, le titre, un bouton. Rien d'autre.
   Les routes vivent dans le pied de page, le téléphone dans la barre mobile.
   -------------------------------------------------------------------------- */
.err {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* 100svh, pas 100vh : sur un téléphone la barre d'adresse mange la seconde
     et le bouton tombait sous le pli au premier rendu. */
  min-height: 100svh;
  padding: calc(66px + var(--space-8)) var(--gutter) var(--space-12);
  text-align: center;
}

/* Le chiffre est derrière, la mascotte devant, les deux centrés sur le même
   point. Le chiffre est décoratif : il est en aria-hidden, le sens est dans
   le h1 juste dessous. */
.err-scene {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  width: 100%;
  max-width: 1040px;
  /* Les chiffres débordent la scène en haut et en bas : ils sont plus hauts
     que la mascotte, qui seule occupe le flux. D'où la marge, calée sur ce
     débordement et non sur la hauteur de l'image. */
  margin-bottom: var(--space-12);
}

.err-chiffre {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -52%);
  /* L'approche est POSITIVE, et c'est tout le réglage de ce bloc. Serrés, les
     trois chiffres sont plus étroits que la mascotte : elle bouchait le zéro
     et la page annonçait « 4 4 ». Desserrés, le zéro reparaît de chaque côté
     d'elle. text-indent rattrape l'espace que l'approche ajoute après le
     dernier chiffre, sans quoi le bloc se décale vers la gauche. */
  font-size: clamp(8rem, 42vw, 30rem);
  font-weight: var(--w-display);
  line-height: 1;
  letter-spacing: 0.06em;
  text-indent: 0.06em;
  color: var(--color-blue-100);
  white-space: nowrap;
  user-select: none;
  pointer-events: none;
}

.err-mascotte {
  position: relative;
  width: clamp(150px, 22vw, 255px);
  height: auto;
}

.err h1 {
  margin: 0 0 var(--space-3);
  font-size: clamp(1.75rem, 1.1rem + 2.6vw, 2.75rem);
  font-weight: var(--w-regular);   /* 400 : voir .page-head h1 (D-040) */
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  color: var(--color-ink);
}

.err-lede {
  max-width: 44ch;
  margin: 0 0 var(--space-8);
  color: var(--color-ink-soft);
  font-size: var(--text-lead);
  line-height: var(--lh-lead);
}

.err .btn { padding: 15px 28px; font-size: var(--text-strong); }

/* La barre d'actions occupe 62 px en bas sous 768 px : sans cela le bouton se
   retrouve dessous sur un écran court. */
@media (max-width: 768px) {
  .err { min-height: calc(100svh - 62px); }
}

/* ---- La variante coupée en deux, bord à bord ---------------------------- */
/* La photo prend la moitié droite entière : pas de marge, pas de rayon, pas
   d'ombre. Elle passe sous la barre d'en-tête, qui devient opaque (page.js).

   Le texte, lui, doit rester aligné sur la grille du site alors qu'il vit dans
   une moitié d'écran, pas dans le conteneur. D'où le calcul : une boîte large
   d'au plus la moitié du conteneur, poussée à droite de sa moitié. Son bord
   gauche retombe alors exactement sur celui du conteneur, à toute largeur —
   1180, 1440, 1920 — sans un seul palier à écrire.

   Le blanc est posé sur la grille entière, pas sur la colonne de texte :
   celle-ci laisserait une bande grise sur sa gauche. La photo couvre déjà sa
   moitié à elle.

   Le bloc est centré verticalement dans sa moitié. Aligné en haut, il se
   collait sous l'en-tête et laissait un vide en bas. */
/* Le hero des huit pages piliers. Réglé une fois, mesuré sur le rendu.
   Avant : 341 px de vide vertical pour 370 px de contenu, un chapô à la taille
   du texte courant, et 13 px entre un titre de 40 px et sa phrase. */
.page-head--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  min-height: min(76vh, 720px);
  padding: 0;
  background: var(--color-surface);
}

/* Le titre prend sa place : 30 px à 375, 44 px à 1280. Il ne dépasse pas
   l'accueil, qui plafonne à 40 px sur une photo pleine largeur — ici il est
   dans une demi-colonne sur blanc, donc à poids égal il doit être plus grand. */
.page-head--split h1 {
  margin-bottom: var(--space-4);
  font-size: clamp(1.9rem, 1.2rem + 2.5vw, 2.75rem);
}

/* Le chapô n'est pas du texte courant : c'est la seule phrase du hero. */
.page-head--split .lede {
  font-size: var(--text-lead);
  line-height: var(--lh-lead);
}

.page-head--split .page-head-actions { margin-top: var(--space-8); }

/* Les deux boutons du hero, un cran au-dessus du bouton courant : le titre est
   passé à 44 px, et à 52 px de haut ils devenaient le maillon léger du bloc.
   56 px de haut, libellé à 17 px. Le contour du secondaire reste
   --color-border-strong : WCAG 1.4.11 demande 3:1 sur la limite d'un contrôle,
   et --color-border plafonne à 1,08:1. */
.page-head--split .page-head-actions .btn {
  padding: 16px 30px;
  font-size: var(--text-strong);
}

.page-head--split .page-head-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  max-width: calc(var(--container) / 2);
  margin-left: auto;
  padding: calc(66px + var(--space-10)) var(--space-12) var(--space-16) var(--gutter);
}

.page-head--split .page-head-media {
  position: relative;
  min-height: 480px;
  border-radius: 0;
  box-shadow: none;
  overflow: hidden;
}

.page-head .fil { margin-bottom: var(--space-8); }

.page-head .eyebrow {
  display: block;
  margin-bottom: var(--space-3);
  color: var(--color-cta);
}

/* Le nom seul. « dr sami kandil » est la requête, et une requête qui tombe
   mot pour mot dans le h1 vaut mieux qu'une phrase de fonction qui la dilue.
   Le métier suit, sur sa propre ligne. */
/* 400, pas --w-display : le woff2 de titrage n'existe qu'en 400 et toute autre
   valeur fait synthétiser une fausse graisse par le navigateur (D-040). C'est
   le titre des 30 pages intérieures, donc l'endroit où le défaut se serait vu
   le plus. */
.page-head h1 {
  margin: 0 0 var(--space-2);
  max-width: var(--measure-display);
  font-size: var(--text-display);
  font-weight: var(--w-regular);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  color: var(--color-ink);
}

.page-head-role {
  margin: 0 0 var(--space-5);
  max-width: var(--measure-lead);
  font-size: var(--text-lead);
  font-weight: var(--w-medium);
  line-height: var(--lh-heading);
  letter-spacing: var(--track-heading);
  color: var(--color-ink);
}

.page-head .lede {
  margin: 0;
  max-width: var(--measure-lead);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  color: var(--color-ink-soft);
}

/* Deux pastilles posées sur la photo, le dessin de .rail-tag sur l'accueil :
   surface blanche, ombre de carte, coin plein. Une ligne chacune, côte à côte. */
.media-frames {
  position: absolute;
  left: var(--space-5);
  bottom: var(--space-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.media-frames li {
  margin: 0;
  padding: 10px 18px;
  background: var(--color-surface);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-card);
  font-size: var(--text-small);
  font-weight: var(--w-regular);
  line-height: 1.3;
  letter-spacing: var(--track-body);
  color: var(--color-ink);
  white-space: nowrap;
}

.page-head-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-10);
}

/* La photo remplit sa moitié quelle que soit la hauteur du texte à côté. */
.page-head-media { position: relative; overflow: hidden; }
.page-head-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --------------------------------------------------------------------------
   Fil d'Ariane
   -------------------------------------------------------------------------- */
/* Discret par construction : il situe, il n'appelle pas. Deux crans sous le
   texte courant, et le nom de la page en cours n'est pas un lien. */
.fil {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
  padding: 0;
  list-style: none;
  font-size: var(--text-small);
  color: var(--color-ink-soft);
}
.fil li { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.fil a { color: var(--color-ink-soft); text-decoration: none; }
.fil a:hover { color: var(--color-ink); text-decoration: underline; }
.fil svg { flex: none; color: var(--color-border-strong); }
.fil [aria-current="page"] { color: var(--color-ink); font-weight: var(--w-medium); }

/* ---- 1024 ---------------------------------------------------------------- */
@media (max-width: 1024px) {
  .page-head h1 { max-width: none; }
  .page-head--split .page-head-copy { padding-right: var(--space-8); }
  .page-head--split .page-head-media { min-height: 420px; }
}

/* ---- 768 : la coupe tombe, la photo passe sous le texte ------------------ */
/* Elle reste bord à bord : sur un téléphone, une photo à 16 px des deux côtés
   ressemble à une pièce jointe, pas à une page. */
@media (max-width: 768px) {
  .page-head {
    padding-top: calc(58px + var(--space-10));
    padding-bottom: var(--space-16);
  }
  .page-head--split { grid-template-columns: 1fr; min-height: 0; padding: 0; }
  .page-head--split .page-head-copy {
    max-width: none;
    margin-left: 0;
    padding: calc(58px + var(--space-10)) var(--gutter) var(--space-12);
  }
  .page-head--split .page-head-media { min-height: 0; aspect-ratio: 3 / 2; }
  .page-head .fil { margin-bottom: var(--space-6); }
  .page-head-actions .btn { flex: 1 1 auto; justify-content: center; }

  /* Côte à côte aussi sur téléphone : le texte se replie dans la pastille au
     lieu de faire passer la seconde à la ligne. */
  .media-frames { left: var(--space-4); right: var(--space-4); bottom: var(--space-4); }
  .media-frames li {
    flex: 1 1 0;
    padding: 8px 14px;
    font-size: var(--text-micro);
    line-height: 1.35;
    white-space: normal;
    text-wrap: balance;
  }
}

/* --------------------------------------------------------------------------
   Le parcours — la liste de diplômes
   -------------------------------------------------------------------------- */
/* Une suite, pas une grille de cartes : ces cinq lignes se lisent de haut en
   bas et c'est l'enchaînement qui convainc. Découpées en cartes, elles
   deviendraient cinq faits sans ordre.

   Le titre reste à gauche pendant qu'on descend la liste : sur un écran large,
   la colonne de droite est longue et le titre disparaîtrait au tiers. */
.cv {
  display: grid;
  grid-template-columns: 1fr 1.85fr;
  gap: var(--space-16);
  align-items: start;
}

.cv-head { position: sticky; top: calc(66px + var(--space-8)); }
.cv-head .eyebrow { display: block; margin-bottom: var(--space-3); color: var(--color-cta); }
.cv-head h2 { margin: 0; font-size: var(--text-title); letter-spacing: var(--track-title); }
/* Le chapô sous le titre collant : même mesure que le titre, jamais plus large,
   sinon la colonne de gauche déborde sur celle de la liste. */
.cv-head p {
  margin: var(--space-4) 0 0;
  color: var(--color-ink-soft);
  font-size: var(--text-body);
  line-height: var(--lh-body);
}

.cv-list { margin: 0; padding: 0; list-style: none; }

/* Le filet sépare, il ne souligne pas : d'où un trait entre les lignes et
   aucun sous le dernier. */
.cv-list li {
  display: grid;
  grid-template-columns: 13rem 1fr;
  gap: var(--space-6);
  align-items: baseline;
  margin: 0;
  padding: var(--space-6) 0;
  border-top: 1px solid var(--color-border);
}
.cv-list li:first-child { border-top: 0; padding-top: 0; }
.cv-list li:last-child { padding-bottom: 0; }

/* Le lieu situe, il n'appelle pas : trois crans sous le diplôme lui-même. */
.cv-where {
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-soft);
}
.cv-what {
  margin: 0;
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  line-height: var(--lh-heading);
  letter-spacing: var(--track-heading);
  color: var(--color-ink);
}
.cv-what em { font-style: normal; color: var(--color-cta); }
.cv-note {
  margin: var(--space-2) 0 0;
  max-width: 54ch;   /* 56ch rendait 86 caractères au pire, plafond 85 */
  font-size: var(--text-small);
  line-height: var(--lh-small);
  color: var(--color-ink-soft);
}

@media (max-width: 1024px) {
  .cv { grid-template-columns: 1fr; gap: var(--space-10); }
  .cv-head { position: static; }
  .cv-list li { grid-template-columns: 11rem 1fr; gap: var(--space-5); }
}

@media (max-width: 768px) {
  /* Le lieu passe au-dessus du diplôme : à 375 px, deux colonnes laissent
     quatre mots par ligne des deux côtés. */
  .cv-list li { grid-template-columns: 1fr; gap: var(--space-2); padding: var(--space-5) 0; }
}

/* --------------------------------------------------------------------------
   .cabinet--reverse — le même bloc, image à gauche
   -------------------------------------------------------------------------- */
/* Deux blocs 50/50 dans le même sens se lisent comme un doublon. Inversé, le
   second relance l'œil sans qu'aucun composant nouveau soit inventé.

   Le texte doit alors s'aligner sur le bord DROIT du conteneur du site, d'où
   le rembourrage symétrique et la boîte poussée par margin-left. */
.cabinet--reverse .cabinet-info {
  order: 2;
  padding: var(--space-16) max(var(--space-10), calc((100vw - var(--container)) / 2 + var(--gutter)))
           var(--space-16) var(--space-10);
}
.cabinet--reverse .cabinet-inner {
  margin-left: auto;
  padding-right: 0;
  padding-left: var(--space-6);
}
.cabinet--reverse .cabinet-media { order: 1; }

/* Une photo seule, sans carrousel : le cadre est le même, le contenu non. */
.cabinet-media {
  position: relative;
  overflow: hidden;
  background: var(--color-surface-2);
}
.cabinet-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --------------------------------------------------------------------------
   .faq — questions dépliables, réponses dans le HTML
   -------------------------------------------------------------------------- */
/* `<details>` natif : aucun JS, et la réponse est dans le document même
   repliée, donc lisible par un moteur comme par un modèle. Le `FAQPage` est
   construit à partir de ce markup par l'assembleur, jamais retapé à côté. */
.faq { max-width: 760px; }

.faq-q { border-top: 1px solid var(--color-border); }
.faq-q:last-of-type { border-bottom: 1px solid var(--color-border); }

.faq-q summary {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) 0;
  cursor: pointer;
  list-style: none;
  color: var(--color-ink);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  line-height: var(--lh-heading);
}
.faq-q summary::-webkit-details-marker { display: none; }
.faq-q summary::after {
  content: "";
  flex: none;
  /* Propriété logique, pas physique : sous dir="rtl" la rangée flex s'inverse et
     `margin-left` ramenait le chevron contre le texte au lieu de le pousser au
     bord. Visible sur les quatre réponses en darija de
     /facettes-dentaires-prix-maroc/. */
  margin-inline-start: auto;
  width: 9px; height: 9px;
  border-right: 2px solid var(--color-cta);
  border-bottom: 2px solid var(--color-cta);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-fast) var(--ease-out);
}
.faq-q[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.faq-q summary:focus-visible { outline: 2px solid var(--color-cta); outline-offset: 3px; }

.faq-q p {
  margin: 0 0 var(--space-5);
  /* La réponse de FAQ est le plus long bloc du site : 95 caractères par ligne
     en moyenne sur 24 pages, 127 au pire. 49ch en rend 75 à 14 px. */
  max-width: 49ch;
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  line-height: var(--lh-small);
}

/* --------------------------------------------------------------------------
   .cases — les cas traités, deux par ligne
   -------------------------------------------------------------------------- */
/* Pas de comparateur ici : ces clichés portent déjà l'avant et l'après empilés
   dans un seul fichier. Leur imposer un rapport les recadrerait, et sur un
   cliché clinique le cadrage fait partie de la preuve. Hauteur naturelle. */
.cases { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-8); }
/* Trois cas de front : la bibliothèque esthétique en a assez, et deux par ligne
   laisserait le troisième seul sur une demi-largeur. */
.cases--trio { grid-template-columns: repeat(3, 1fr); }
.case { margin: 0; }
.case img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  background: var(--color-surface-2);
}
.case figcaption {
  margin-top: var(--space-4);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  line-height: var(--lh-small);
}
.case .link-more { margin-top: var(--space-3); font-size: var(--text-small); }

@media (max-width: 1024px) {
  .cases--trio { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 768px) {
  .cases, .cases--trio { grid-template-columns: 1fr; gap: var(--space-10); }
}

/* --------------------------------------------------------------------------
   .tbl — le tableau comparatif
   -------------------------------------------------------------------------- */
/* Premier tableau du site, et les 14 pages de prix en demanderont un : écrit
   comme composant, pas comme exception.

   Aucune colonne n'est mise en avant, pas même celle de l'implant sur la page
   de l'implant. Un tableau qui souligne sa propre réponse cesse d'être une
   comparaison, et le lecteur le voit. Les faits suffisent.

   `border-collapse: separate` + `overflow: hidden` : c'est ce qui permet à la
   bordure arrondie de tenir sur un tableau. Avec `collapse`, les angles sautent. */
.tbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.tbl {
  width: 100%;
  min-width: 540px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
  background: var(--color-surface);
  font-size: var(--text-small);
  line-height: var(--lh-small);
}

.tbl th,
.tbl td { padding: var(--space-4) var(--space-5); text-align: left; vertical-align: top; }

.tbl thead th {
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-weight: var(--w-medium);
}

.tbl tbody th {
  color: var(--color-ink-soft);
  font-weight: var(--w-regular);
}
.tbl tbody td { color: var(--color-ink); }

.tbl tbody tr + tr th,
.tbl tbody tr + tr td { border-top: 1px solid var(--color-border); }

@media (max-width: 768px) {
  .tbl th, .tbl td { padding: var(--space-3) var(--space-4); }
}

/* .tbl--map — le tableau « situation → solution » de /protheses-dentaires/.
   Sa première colonne est une phrase, pas une étiquette : à 375 px la colonne
   de réponse sortait de l'écran et le lecteur devait faire défiler le tableau
   pour lire ce qu'on lui propose. Sous 640 px chaque ligne devient donc une
   fiche empilée, l'en-tête est masqué, et chaque valeur porte son intitulé
   via data-label. Le tableau reste un tableau pour le moteur et le lecteur
   d'écran ; seule sa mise en page change. */
@media (max-width: 640px) {
  .tbl--map { min-width: 0; border: 0; background: none; overflow: visible; }

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

  .tbl--map tbody tr {
    display: block;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    padding: var(--space-4);
  }

  .tbl--map tbody tr + tr { margin-top: var(--space-3); }

  .tbl--map tbody th,
  .tbl--map tbody td { display: block; padding: 0; border-top: 0; }

  .tbl--map tbody th {
    color: var(--color-ink);
    font-weight: var(--w-medium);
    margin-bottom: var(--space-3);
  }

  .tbl--map tbody td + td { margin-top: var(--space-2); }

  .tbl--map tbody td::before {
    content: attr(data-label) " : ";
    color: var(--color-ink-soft);
  }

  /* La bordure supérieure de séparation ne vaut plus rien une fois les
     lignes séparées en fiches. */
  .tbl--map tbody tr + tr th,
  .tbl--map tbody tr + tr td { border-top: 0; }
}

/* --------------------------------------------------------------------------
   .duo — deux colonnes sur un seul fond
   -------------------------------------------------------------------------- */
/* .cabinet coupe la bande en deux surfaces, blanc à gauche et gris à droite :
   le pli se voit et la section se lit comme deux blocs collés. Ici une seule
   assise sur toute la largeur, posée par .section--alt.

   La carte n'a donc pas de fond : sur une bande blanche un aplat blanc ne
   dit rien, et un aplat teinté rouvrirait la coupure qu'on vient de fermer.
   Reste le liseré, qui groupe les trois points sans ajouter de surface.
   Pas d'ombre : D-016, les cartes reposent, elles ne lévitent pas. */
.duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-16);
  align-items: start;
}
/* Quand une colonne est bien plus haute que l'autre — un carrousel carré face à
   trois paragraphes — le calage en haut laisse le texte flotter seul. */
.duo--center { align-items: center; }

.duo-copy .eyebrow {
  display: block;
  margin-bottom: var(--space-3);
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.duo-copy h2 { margin-bottom: var(--space-5); font-size: var(--text-title); text-wrap: balance; }
/* Un h3 dans une colonne de prose : base.css met toutes les têtes à margin 0, ce
   qui le collait à son paragraphe et le faisait lire comme du gras en ligne.
   Sert les quatre matériaux de /facettes-dentaires-prix-maroc/ et les pages de
   prix qui suivent. */
.duo-copy h3 { margin-bottom: var(--space-3); }
.duo-copy > * + h3 { margin-top: var(--space-8); }

.duo-panel {
  padding: var(--space-10);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

/* La colonne droite en image plutôt qu'en carte. Le 4/5 est celui des clichés
   cliniques du cabinet : les recadrer en paysage couperait la gencive, qui est
   la moitié de ce que le cliché démontre. */
.duo-media {
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  background: var(--color-surface-2);
}
.duo-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Un schéma se montre entier. Lui imposer un rapport fixe le recadre, et un
   schéma recadré perd la moitié de ses légendes. */
.duo-media--natural { aspect-ratio: auto; background: none; border-radius: var(--radius-lg); }
.duo-media--natural img { height: auto; object-fit: contain; }

/* La légende d'une image de section. Deux fois plus de gens la lisent que le
   corps du texte, donc elle sort du cadre : le rognage arrondi passe sur l'image
   elle-même et la légende se lit comme une ligne autonome en dessous.
   Standard voix-kandil §4.3. */
.duo-media figcaption {
  margin-top: var(--space-4);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  line-height: var(--lh-small);
}
.duo-media:has(figcaption) { overflow: visible; border-radius: 0; background: none; }
.duo-media:has(figcaption) img { border-radius: var(--radius-lg); }

/* Empilées, pas en grille : dans une demi-colonne trois cartes côte à côte
   réduisent le chiffre à la taille de son libellé. */
.duo-copy .stats { grid-template-columns: 1fr; margin-top: var(--space-8); }

/* .points — la liste de la carte, plus légère que .ticks
   -------------------------------------------------------------------------- */
/* Trois coches identiques ne se scannent pas : l'œil voit trois fois le même
   signe et lit quand même les trois lignes. Un filet entre les points sépare
   mieux qu'un pictogramme répété, et il ne consomme pas le bleu. D-016 en
   demande quatre ou cinq emplois par page, pas dix.

   Pas de numéros non plus : ces trois-là ne se suivent pas, l'image les donne
   d'un coup. Numéroter aurait annoncé une séquence qui n'existe pas. */
.points { margin: 0; padding: 0; list-style: none; }

.points li {
  position: relative;
  padding: var(--space-5) 0 var(--space-5) var(--space-5);
  border-top: 1px solid var(--color-border);
}
.points li:first-child { padding-top: 0; border-top: 0; }
.points li:last-child { padding-bottom: 0; }

.points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--space-5) + 10px);
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--color-cta);
}
.points li:first-child::before { top: 10px; }

.points b {
  display: block;
  margin-bottom: 3px;
  color: var(--color-ink);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
}
.points span {
  display: block;
  /* La mesure globale vit sur <p> : un <span> en display:block y échappe et
     courait sur toute la largeur du conteneur. Mesuré à 1 128 px le 2026-09-14
     sur la page détartrage, soit près de 170 caractères par ligne. */
  max-width: var(--measure-body);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  line-height: var(--lh-small);
}

@media (max-width: 900px) {
  .cabinet--reverse .cabinet-info { order: 2; padding: var(--space-12) var(--gutter); }
  .cabinet--reverse .cabinet-inner { margin-left: 0; padding-left: 0; }
  .cabinet-media { order: 1; aspect-ratio: 16 / 10; }
  .cabinet-media img { position: absolute; }

  .duo { grid-template-columns: 1fr; gap: var(--space-10); }
  .duo-panel { padding: var(--space-8); }
  .duo-media { aspect-ratio: 4 / 3; }
}

/* --------------------------------------------------------------------------
   En consultation — la séquence, en trois temps
   -------------------------------------------------------------------------- */
/* Numérotée parce que c'est une suite dans le temps, pas une liste d'atouts.
   Un patient qui n'est jamais venu y lit ce qui va lui arriver, dans l'ordre.

   Même coque que .kit sur l'accueil : la tuile bleue y porte un chiffre au
   lieu d'une icône. Rien de nouveau n'entre dans le système. */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }

.step {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

.step-num {
  flex: none;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--color-surface-3);
  color: var(--color-cta);
  font-size: var(--text-strong);
  font-weight: var(--w-display);
  letter-spacing: 0;
}

.step h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
  line-height: var(--lh-heading);
  color: var(--color-ink);
}
.step p {
  margin: 0;
  font-size: var(--text-small);
  line-height: var(--lh-small);
  color: var(--color-ink-soft);
}

/* La troisième carte est celle qui répond à la vraie question : combien, et
   quand est-ce que je m'engage. Elle porte donc l'aplat, pas les deux autres. */
.step--devis { background: var(--color-surface-3); border-color: var(--color-blue-100); }
.step--devis .step-num { background: var(--color-surface); }

@media (max-width: 1024px) { .steps { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   La bande pratique — ce qu'il faut savoir avant de venir
   -------------------------------------------------------------------------- */
/* Trois faits, pas une phrase. Le tarif, l'adresse avec son repère, et le
   temps depuis Casablanca : les trois questions posées avant de décrocher.

   « Derb Riad 2 » ne se trouve pas tout seul — le repère visuel fait plus pour
   la venue que n'importe quelle formule d'accueil. */
.practical {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

.practical-item {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

.practical-item .kit-icon { flex: none; }

.practical-item h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-soft);
}
.practical-item p {
  margin: 0;
  font-size: var(--text-small);
  line-height: var(--lh-small);
  color: var(--color-ink);
}
.practical-item p b {
  display: block;
  font-size: var(--text-strong);
  font-weight: var(--w-display);
  letter-spacing: var(--track-heading);
  line-height: 1.2;
}
.practical-item a { color: var(--color-link); text-decoration: none; }
.practical-item a:hover { text-decoration: underline; }

@media (max-width: 900px) { .practical { grid-template-columns: 1fr; } }

/* Le chapô de la page contact porte des liens : ils doivent se voir sans
   trouer le paragraphe. */
.page-head .lede a { color: var(--color-link); text-decoration: underline; text-underline-offset: 3px; }
.page-head .lede a:hover { color: var(--color-cta-hover); }

/* --------------------------------------------------------------------------
   La pastille d'état, posée en haut de la photo
   -------------------------------------------------------------------------- */
/* C'est .hero-badge de l'accueil, sans une ligne de dessin en plus : verre
   dépoli, texte blanc, point coloré. Elle est faite pour vivre sur une image,
   c'est exactement ce qu'on lui demande ici.

   horaires.js ne connaît que [data-horaires] : le comportement est le même,
   l'état est réel, en heure de Casablanca.

   Le décalage haut rend ses pixels à l'en-tête fixe, sinon la pastille passe
   dessous. */
.page-head-media .hero-badge {
  position: absolute;
  top: calc(66px + var(--space-5));
  left: var(--space-5);
  z-index: 2;
  margin: 0;
}

/* La pastille est blanche sur une photo qui n'a pas été choisie pour elle.
   Mesuré sur le pixel rendu, pas sur les tokens : 1,44:1 sur /services/,
   4,21:1 sur /implant-dentaire/, les deux sous AA. Un voile cantonné à la
   hauteur de la pastille remonte les deux sans ternir l'image en dessous. */
/* Le voile ne suffit pas partout : sur un mur blanc en arrière-plan la pastille
   reste sous AA (3,94:1 à 375 px). Sur le hero coupé en deux, où aucun dégradé
   ne protège la photo, le verre se pose sur une base sombre au lieu d'une base
   claire. L'accueil garde sa pastille claire, son hero a son propre dégradé. */
.page-head--split .page-head-media .hero-badge {
  border-color: rgba(255, 255, 255, .34);
  background: rgba(23, 28, 46, .58);
}

.page-head--split .page-head-media::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 200px;
  background: linear-gradient(to bottom,
              rgba(23, 28, 46, .62), rgba(23, 28, 46, .28) 55%, rgba(23, 28, 46, 0));
  pointer-events: none;
  z-index: 1;
}

@media (max-width: 768px) {
  .page-head-media .hero-badge { top: var(--space-4); left: var(--space-4); }
}

/* --------------------------------------------------------------------------
   Prendre contact — formulaire à gauche, joindre le cabinet à droite
   -------------------------------------------------------------------------- */
.reach { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: var(--space-8); align-items: stretch; }

.reach-card {
  padding: var(--space-8);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
/* Le seul h2 du site qui reste en sans (D-040). Il est balisé h2 pour le plan du
   document, mais il est dimensionné en --text-heading, c'est-à-dire en
   sous-titre : à 19 px la serif perd ses empattements et ne gagne rien, et la
   même règle vaut ici que pour h3 et h4. Il garde donc sa graisse. */
.reach-card > h2 {
  margin: 0 0 var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-heading);
  font-weight: var(--w-bold);
  letter-spacing: var(--track-heading);
}
.reach-card > h2 + p {
  margin: 0 0 var(--space-6);
  max-width: none;
  font-size: var(--text-small);
  line-height: var(--lh-small);
  color: var(--color-ink-soft);
}

/* ---- Le formulaire ----
   Quatre champs. TEMPLATE est explicite : nom, téléphone, motif, créneau, et
   rien d'autre. Chaque champ ajouté est un rendez-vous perdu. */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
.field-full { grid-column: 1 / -1; }

.form-grid label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

.form-grid input,
.form-grid select {
  width: 100%;
  padding: 13px var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--text-body);
  color: var(--color-ink);
  appearance: none;
}
.form-grid select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23565D77' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  padding-right: var(--space-10);
}
.form-grid input:focus-visible,
.form-grid select:focus-visible {
  outline: none;
  border-color: var(--color-cta);
  box-shadow: var(--shadow-focus);
}
.form-grid .btn { width: 100%; justify-content: center; margin-top: var(--space-2); }

.form-note {
  margin: var(--space-4) 0 0;
  max-width: none;
  font-size: var(--text-micro);
  line-height: var(--lh-small);
  color: var(--color-ink-soft);
}

/* La photo tient toute la hauteur de la colonne : à côté d'un formulaire, une
   image plus courte laisse un vide qui se lit comme un défaut d'alignement. */
.reach-media {
  position: relative;
  overflow: hidden;
  min-height: 420px;
  border-radius: var(--radius-lg);
  background: var(--color-surface-2);
  box-shadow: var(--shadow-card);
}
.reach-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Les horaires ferment la carte du formulaire : c'est la question qui suit
   « quand est-ce que je peux venir », posée dans le même souffle. */
.reach-card .reach-hours {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}
.reach-card .reach-hours .lbl {
  display: block;
  margin-bottom: var(--space-3);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

.reach-hours { display: grid; gap: 7px; margin-top: var(--space-2); }
.reach-hours div { display: flex; justify-content: space-between; gap: var(--space-4); font-size: var(--text-small); color: var(--color-ink-soft); }
.reach-hours b { font-weight: var(--w-medium); color: var(--color-ink); white-space: nowrap; }

@media (max-width: 900px) {
  .reach { grid-template-columns: 1fr; gap: var(--space-6); }
  .reach-media { min-height: 0; aspect-ratio: 3 / 2; }
}
@media (max-width: 560px) {
  .form-grid { grid-template-columns: 1fr; }
  .reach-card { padding: var(--space-6); }
}

/* --------------------------------------------------------------------------
   La citation — à même la photo
   -------------------------------------------------------------------------- */
/* Pas de carte. Le texte est posé sur l'image, et c'est un dégradé qui tient
   la lisibilité — pas une boîte. Une carte affiche une citation ; sans carte,
   la citation appartient à la photo.

   Le dégradé monte à 0,74 en bas : mesuré sous un pixel blanc, le pire cas
   possible, le texte tient 7,4:1 et la signature 6,1:1. Il part de zéro assez
   haut pour qu'aucune arête ne se voie, et le visage reste dégagé. */
.quote-card {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: var(--space-24) var(--space-8) var(--space-8);
  color: #fff;
  background: linear-gradient(to top,
    rgba(20, 25, 42, 0.74) 0%,
    rgba(20, 25, 42, 0.62) 34%,
    rgba(20, 25, 42, 0.28) 62%,
    rgba(20, 25, 42, 0) 100%);
}

.quote-card p {
  margin: 0;
  max-width: 34ch;
  font-size: var(--text-heading);
  font-style: italic;
  font-weight: var(--w-regular);
  line-height: 1.45;
  letter-spacing: var(--track-heading);
  color: #fff;
  text-wrap: pretty;
  /* L'ombre ne remplace pas le dégradé, elle rattrape les zones les plus
     claires de l'image sans avoir à noircir tout le bas. */
  text-shadow: 0 1px 14px rgba(12, 16, 30, 0.45);
}

.quote-card footer {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.86);
}
/* Un filet clair plutôt qu'un tiret : il signe sans ouvrir une ponctuation. */
.quote-card footer::before {
  content: '';
  flex: none;
  width: 26px;
  height: 1px;
  background: var(--color-on-dark);
}

@media (max-width: 768px) {
  .quote-card { padding: var(--space-20) var(--gutter) var(--space-6); }
  .quote-card p { font-size: var(--text-body); max-width: none; }
}

/* --------------------------------------------------------------------------
   Les praticiens — trois cartes qui s'ouvrent
   La carte fermée est une photo pleine, deux puces en haut, le nom sur le
   voile sombre en bas. Celle du fondateur tient deux colonnes et deux rangs.
   À l'ouverture, .doc-inner passe en position fixe sur son propre rectangle,
   puis anime top/left/width/height jusqu'au panneau centré. Aucune mise à
   l'échelle : le texte ne se déforme donc jamais pendant le mouvement.
   -------------------------------------------------------------------------- */
/* La hauteur vient de la fenêtre, pas du ratio des photos. Deux rangs de 4/5
   donnaient près de 940 px : les trois cartes ne tenaient plus dans un écran.
   Le plancher évite l'écrasement sur un portable posé à l'horizontale, le
   plafond évite la carte géante sur un 27 pouces. */
.docs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 1fr 1fr;
  gap: var(--space-5);
  height: clamp(440px, 70vh, 640px);
}

.doc { position: relative; }
/* Deux colonnes et toute la hauteur : la carte du fondateur pèse quatre fois
   les autres sans qu'aucune valeur en pixels soit écrite. */
.doc--lead { grid-column: span 2; grid-row: 1 / -1; }

.doc-inner {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--color-dark);
  box-shadow: var(--shadow-card);
  transition: box-shadow var(--dur-base) var(--ease-out);
}
.doc:hover .doc-inner { box-shadow: var(--shadow-lift); }

.doc-shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Le sujet remonte : sur les cartes courtes de la colonne de droite, un
     cadrage centré posait le menton exactement là où s'écrit le nom. */
  object-position: 50% 14%;
}

/* 🟠 Provisoire, et s'en va avec la photo qu'elle cadre. Le plan aux loupes
   n'est pas un portrait de studio : le sujet est au milieu de l'image, pas
   en haut, et le cadrage commun le décapiterait. */
.doc-shot--work {
  object-position: 50% 26%;
  transition: transform var(--dur-slow) var(--ease-out);
}
.doc:hover .doc-shot { transform: scale(1.03); }

/* Le même voile que le hero, en plus court : la carte est plus petite, le
   dégradé doit donc monter plus vite pour porter le nom. */
.doc-inner::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(0deg,
    rgba(20, 24, 36, .90) 0%,
    rgba(20, 24, 36, .72) 26%,
    rgba(20, 24, 36, .28) 52%,
    transparent 74%);
}

.doc-chips {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  right: var(--space-4);
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  z-index: 2;
}
/* Fond sombre et non clair : ces cartes portent des photos de studio sur fond
   gris pâle, où une puce en verre blanc disparaît. L'encre blanche sur voile
   sombre tient sur n'importe quelle image, claire ou foncée. */
.doc-chips .chip-glass {
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, .16);
  background: rgba(20, 24, 36, .62);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: none;
  color: #fff;
}

.doc-head {
  position: absolute;
  left: var(--space-5);
  right: calc(var(--space-5) + 56px);
  bottom: var(--space-5);
  z-index: 2;
}
.doc-role {
  margin: 0 0 4px;
  color: rgba(255, 255, 255, .78);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.doc-name {
  margin: 0 0 var(--space-2);
  color: #fff;
  font-size: var(--text-heading);
  font-weight: var(--w-display);
  line-height: var(--lh-heading);
  letter-spacing: var(--track-heading);
  text-shadow: 0 1px 2px rgba(20, 24, 36, .3);
}
.doc--lead .doc-name { font-size: var(--text-title); }
.doc-line {
  margin: 0;
  max-width: 34ch;
  color: rgba(255, 255, 255, .86);
  font-size: var(--text-small);
  line-height: var(--lh-small);
  text-shadow: 0 1px 14px rgba(20, 24, 36, .4);
}

/* Le bouton EST la cible de clic, et il fait 48 px : la dette 3.4.1 ne se
   crée pas ici. La carte entière reste cliquable via le JS, mais le clavier
   et les lecteurs d'écran n'ont qu'un seul élément à atteindre. */
.doc-toggle {
  position: absolute;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: var(--radius-full);
  background: rgba(20, 24, 36, .55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: #fff;
  cursor: pointer;
  transition: background-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.doc-toggle:hover { background: rgba(20, 24, 36, .78); }
.doc-toggle svg { transition: transform var(--dur-base) var(--ease-out); }
.doc.is-open .doc-toggle { opacity: 0; pointer-events: none; }

/* ---- Le panneau, et l'état ouvert ---- */
/* Ouvert, la carte devient deux colonnes : la photo garde la gauche, le texte
   occupe la droite. Le panneau n'est donc pas un calque posé sur la carte,
   c'est la moitié droite de la carte agrandie. */
.doc-panel {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-10) var(--space-10) var(--space-8);
  background: var(--color-surface);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-out),
              visibility 0s linear var(--dur-base);
}
.doc.is-open .doc-panel {
  opacity: 1;
  visibility: visible;
  /* Le texte n'arrive qu'une fois la carte arrivée à destination : une lecture
     qui commence pendant que le cadre bouge encore est illisible. */
  transition: opacity var(--dur-base) var(--ease-out) 220ms, visibility 0s;
}

/* La croix de fermeture occupe le coin : le rôle et le nom lui laissent la
   place, sinon un nom long passe dessous. */
.doc-panel-role,
.doc-panel-name { padding-right: 56px; }
.doc-panel-role {
  margin: 0 0 4px;
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.doc-panel-name {
  margin: 0 0 var(--space-6);
  color: var(--color-ink);
  font-size: var(--text-title);
  font-weight: var(--w-display);
  line-height: var(--lh-heading);
  letter-spacing: var(--track-display);
}
.doc-panel p:not([class]) {
  margin: 0 0 var(--space-4);
  max-width: var(--measure-body);
  color: var(--color-ink-soft);
  font-size: var(--text-body);
  line-height: var(--lh-body);
}
.doc-panel p:not([class]):last-child { margin-bottom: 0; }

/* Les repères, dans le panneau. C'est .cv-list de la page du fondateur, resserré :
   la colonne du panneau fait 58 % de 1 060 px, une étiquette de 13 rem y laissait
   deux mots par ligne au texte. */
.doc-panel .cv-list--tight { margin-top: var(--space-6); }
.cv-list--tight li {
  grid-template-columns: 9.5rem 1fr;
  gap: var(--space-4);
  padding: var(--space-4) 0;
}
.cv-list--tight .cv-what { font-size: var(--text-small); }
@media (max-width: 600px) {
  .cv-list--tight li { grid-template-columns: 1fr; gap: 2px; padding: var(--space-3) 0; }
}

.doc-close {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-ink);
  cursor: pointer;
  transition: background-color var(--dur-base) var(--ease-out);
}
.doc-close:hover { background: var(--color-surface-2); }

/* Pendant l'ouverture, .doc-inner est en position: fixe et le JS écrit
   top/left/width/height. La transition porte sur ces quatre valeurs, pas sur
   une échelle : c'est ce qui garde le texte net du début à la fin. */
.doc-inner.is-flying {
  position: fixed;
  z-index: var(--z-modal);
  transition: top var(--dur-slow) var(--ease-inout),
              left var(--dur-slow) var(--ease-inout),
              width var(--dur-slow) var(--ease-inout),
              height var(--dur-slow) var(--ease-inout),
              border-radius var(--dur-slow) var(--ease-inout),
              box-shadow var(--dur-slow) var(--ease-out);
}
.doc.is-open .doc-inner { box-shadow: var(--shadow-deep); }
/* La photo se resserre sur la colonne de gauche pendant que le cadre vole. Le
   voile et le nom s'effacent : le nom est repris en tête du panneau, le garder
   des deux côtés ferait doublon. */
.doc.is-open .doc-shot { width: 42%; }
.doc.is-open .doc-inner::after,
.doc.is-open .doc-chips,
.doc.is-open .doc-head { opacity: 0; transition: opacity var(--dur-base) var(--ease-out); }
.doc-shot { transition: transform var(--dur-slow) var(--ease-out),
                        width var(--dur-slow) var(--ease-inout); }
.doc.is-open .doc-panel { left: 42%; }

/* Le voile reste dans le document entre deux ouvertures, pour ne pas être
   recréé à chaque fois. Il doit donc être RÉELLEMENT inerte quand il est
   éteint : à opacité 0 seule, il couvrait toujours la fenêtre entière et
   avalait tous les clics et toute sélection de texte de la page. */
.doc-scrim {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-modal) - 1);
  background: rgba(20, 24, 36, .55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out),
              visibility 0s linear var(--dur-slow);
}
.doc-scrim.is-on {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--dur-slow) var(--ease-out), visibility 0s;
}

/* ---- Paliers ---- */
@media (max-width: 1024px) {
  .doc-panel { padding: var(--space-8) var(--space-8) var(--space-6); }
}

@media (max-width: 768px) {
  /* Deux colonnes : le fondateur prend la largeur entière, les deux autres se
     partagent le rang du dessous. La hiérarchie tient sans carte géante. */
  /* Le fondateur prend le rang du haut sur toute la largeur, les deux autres
     se partagent celui du bas. La hauteur reste pilotée par la fenêtre : sur
     une tablette en portrait, trois cartes au ratio des photos débordaient
     de l'écran exactement comme sur le bureau. */
  .docs {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: 1.15fr 1fr;
    gap: var(--space-4);
    height: clamp(460px, 72vh, 680px);
  }
  .doc--lead { grid-column: 1 / -1; grid-row: 1; }
  .doc.is-open .doc-shot { opacity: 0; }
  .doc.is-open .doc-panel { left: 0; }
  .doc-head { left: var(--space-4); right: calc(var(--space-4) + 52px); bottom: var(--space-4); }
  .doc-toggle { right: var(--space-4); bottom: var(--space-4); }
  .doc-chips { top: var(--space-3); left: var(--space-3); right: var(--space-3); }
}

@media (max-width: 480px) {
  /* Une colonne : à cette largeur, deux cartes côte à côte rendent le nom
     illisible et la cible de clic trop petite. */
  /* Une colonne, et là seulement on revient au ratio : trois cartes empilées
     dans une hauteur imposée deviendraient des bandeaux. 3/2 les met bout à
     bout dans un écran de téléphone sans les écraser. */
  .docs {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    height: auto;
  }
  .doc { aspect-ratio: 3 / 2; }
  .doc--lead { grid-row: auto; aspect-ratio: 3 / 2; }
  .doc--lead .doc-name { font-size: var(--text-heading); }
  .doc-line { display: none; }
  .doc-panel { padding: var(--space-6) var(--space-5) var(--space-5); }
  .doc-panel-name { font-size: var(--text-heading); }
  .doc-close { top: var(--space-4); right: var(--space-4); }
}

@media (prefers-reduced-motion: reduce) {
  .doc-inner.is-flying { transition: none; }
  .doc-shot, .doc:hover .doc-shot { transition: none; transform: none; }
  .doc.is-open .doc-panel { transition: none; }
}

/* ======================================================================
   Les huit disciplines — /services/
   ====================================================================== */
/* Grille à 6 colonnes, cartes de proportions inégales : le rythme vient des
   spans, jamais d'une valeur en pixels. Une carte ouverte prend toute la
   largeur et les autres se replacent autour — le mouvement est animé en FLIP
   dans assets/js/disciplines.js, un changement de span ne se transitionnant
   pas en CSS.
   La photo reprend le langage du carrousel d'accueil (.rail-shot) : format
   portrait, zoom léger au survol, étiquette en bas à gauche. */
.disc-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

.disc {
  position: relative;
  scroll-margin-top: 90px;   /* la barre collée fait 66 px, plus une marge */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: border-color var(--dur-base) var(--ease-out),
              background   var(--dur-base) var(--ease-out);
}

.disc:hover { border-color: var(--color-accent); background: var(--color-surface); }
.disc.is-open {
  grid-column: 1 / -1;
  background: var(--color-surface);
  border-color: var(--color-accent);
  box-shadow: var(--shadow-card);
}

.disc-shot {
  position: relative;
  display: block;
  height: 208px;
  flex: none;
  overflow: hidden;
  background: var(--color-surface-2);
}
.disc-shot img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 30%;
  transition: transform var(--dur-slow) var(--ease-out);
}
.disc:hover .disc-shot img { transform: scale(1.035); }
/* Ouverte, la photo disparaît : gardée en bandeau elle écrasait la lecture du
   panneau, qui est la seule raison d'ouvrir la carte. */
.disc.is-open .disc-shot { display: none; }

.disc-tag {
  position: absolute;
  left: var(--space-3);
  bottom: var(--space-3);
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(20, 24, 36, .62);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  font-size: var(--text-xs);
  font-weight: 600;
}

.disc-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-5);
}
.disc h3 { margin: 0 0 var(--space-2); font-size: var(--text-lg); }
.disc-line { margin: 0; color: var(--color-ink-soft); }
/* Le bouton couvre la carte : la cible est la carte entière, pas un lien. */
.disc-toggle {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
.disc-toggle:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }

.disc-panel {
  display: none;
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.disc.is-open .disc-panel { display: block; animation: disc-in .3s var(--ease-out) both; }
@keyframes disc-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.disc-panel p { margin: 0 0 var(--space-3); max-width: var(--measure-body); color: var(--color-ink-soft); }
.disc-panel .btn { position: relative; z-index: 2; }

.disc-close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 2;
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: rgba(20, 24, 36, .62);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  cursor: pointer;
}
.disc.is-open .disc-close { display: inline-flex; }

@media (max-width: 900px) {
  .disc-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .disc-grid { grid-template-columns: 1fr; }
  .disc-shot { height: 190px; }
}

/* --------------------------------------------------------------------------
   La facture d'une couronne : le calculateur de /couronne-dentaire-au-maroc/.
   Les montants arrivent de content/prices.json au build ({{tarifs:…}} dans
   build-pages.js) : ni ce fichier ni facture.js ne portent un chiffre. Sans JS,
   le bloc reste masqué et le tableau des trois cas fait le travail.
   -------------------------------------------------------------------------- */
.facture {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: var(--space-8);
  margin-top: var(--space-12);
  padding: var(--space-8);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.facture[hidden] { display: none; }

.facture-form { display: grid; gap: var(--space-6); align-content: start; }
.facture-form fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.facture-form legend {
  padding: 0;
  margin-bottom: var(--space-3);
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--color-ink);
}
.facture-options { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.facture-options label { position: relative; }
/* L'input reste dans le flux d'accessibilité : caché à l'œil, pas au clavier ni
   au lecteur d'écran. La pastille qui le suit porte l'état. */
.facture-options input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.facture-options span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 18px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface-2);
  color: var(--color-ink);
  font-size: var(--text-small);
  transition: background .2s var(--ease-out), border-color .2s var(--ease-out), color .2s var(--ease-out);
}
.facture-options label:hover span { border-color: var(--color-border-strong); }
.facture-options input:checked + span {
  background: var(--color-cta);
  border-color: var(--color-cta);
  color: #fff;
}
.facture-options input:focus-visible + span { box-shadow: var(--shadow-focus); }

.facture-result {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--color-dark);
  color: #fff;
  border-radius: var(--radius-md);
}
.facture-title {
  margin: 0;
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--color-on-dark);
}
.facture-lines { display: grid; gap: var(--space-2); margin: 0; }
.facture-lines > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-4);
  font-size: var(--text-small);
}
.facture-lines dt { color: rgba(255, 255, 255, .78); }
.facture-lines dd { margin: 0; white-space: nowrap; font-variant-numeric: tabular-nums; }
.facture-lines .facture-total,
.facture-lines .facture-reste {
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.facture-lines .facture-total dt,
.facture-lines .facture-reste dt { color: #fff; font-weight: 600; }
.facture-lines .facture-reste dd { font-size: var(--text-heading); font-weight: 700; }
.facture-note { margin: 0; font-size: var(--text-micro); color: rgba(255, 255, 255, .7); }
.facture-result .btn { align-self: flex-start; margin-top: auto; }

/* La carte de la bouche : trois zones, un matériau conseillé par zone. */
.bouche-wrap {
  padding: var(--space-8);
  background: var(--color-surface-2);
  border-radius: var(--radius-lg);
}
.bouche { display: block; width: 100%; max-width: 420px; height: auto; margin-inline: auto; }
.bouche-zone { fill: none; stroke-width: 30; }
.bouche-zone--devant { stroke: var(--color-blue-300); }
.bouche-zone--premolaires { stroke: var(--color-blue-600); }
.bouche-zone--molaires { stroke: var(--color-dark); }
.bouche-cle--devant { fill: var(--color-blue-300); }
.bouche-cle--premolaires { fill: var(--color-blue-600); }
.bouche-cle--molaires { fill: var(--color-dark); }
.bouche-nom { font-size: 13px; font-weight: 600; fill: var(--color-ink); }
.bouche-mat { font-size: 12px; fill: var(--color-ink-soft); }

@media (max-width: 1024px) {
  /* minmax(0,…) et non 1fr : un track 1fr conserve un plancher min-content,
     et une ligne de pastilles qui ne se coupe pas élargit alors la grille au-delà
     du conteneur. Mesuré à 375 px le 2026-09-14 sur la page détartrage : 409 de
     large pour 375 de fenêtre. */
  .facture { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 768px) {
  .facture { margin-top: var(--space-10); padding: var(--space-6); }
  .bouche-wrap { padding: var(--space-6); }
}
@media (max-width: 480px) {
  .facture { padding: var(--space-5); }
  .facture-result { padding: var(--space-5); }
  .facture-result .btn { align-self: stretch; justify-content: center; }
  .bouche-wrap { padding: var(--space-4); }
}

/* Quatre étapes : la grille de base en tient trois et laisse la quatrième
   orpheline sur une seconde ligne. Écrit pour /gouttiere-dentaire-dalignement-au-maroc/
   le 2026-09-12. Les deux paliers reprennent ceux de .steps, une colonne de moins
   à chaque fois plutôt qu'un passage brutal à une seule. */
.steps--quatre { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1180px) { .steps--quatre { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .steps--quatre { grid-template-columns: 1fr; } }

/* .kit n'avait jamais porté plus d'un paragraphe : les trois du bloc
   remboursement de /gouttiere-dentaire-dalignement-au-maroc/ se collaient,
   mesuré à 0 px le 2026-09-12. */
.kit p + p { margin-top: var(--space-3); }

/* --------------------------------------------------------------------------
   Le parcours : le qualificateur de /greffe-osseuse-dentaire-prix-maroc/.
   Quatre questions, une estimation, et un message WhatsApp déjà rempli avec
   les réponses. Réemploie tout le visuel de .facture ; n'ajoute que les
   étapes, les jalons et la navigation. Aucun chiffre ici non plus : les
   montants arrivent de content/prices.json au build ({{tarifs:…}}).
   Sans JS, le bloc reste masqué et les deux tableaux de prix font le travail.
   -------------------------------------------------------------------------- */
.facture--parcours { grid-template-columns: 1.15fr 1fr; }
.facture-form fieldset[hidden] { display: none; }

.parcours-jalons {
  display: flex;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  padding: 0;
  list-style: none;
}
.parcours-jalons li {
  flex: 1;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-border);
}
.parcours-jalons li[data-fait] { background: var(--color-cta); }

.parcours-compte {
  margin: 0;
  font-size: var(--text-micro);
  color: var(--color-ink-soft);
}

/* Les réponses de ce parcours sont des phrases, pas des mots : elles
   s'empilent au lieu de s'aligner en pastilles. */
.facture-options--colonne { flex-direction: column; align-items: stretch; }
.facture-options--colonne span { justify-content: flex-start; border-radius: var(--radius-md); }

.parcours-nav { display: flex; gap: var(--space-3); align-items: center; }
.parcours-nav .btn { min-height: 44px; }
.parcours-nav [data-parcours-retour][hidden] { display: none; }

@media (max-width: 1024px) {
  .facture--parcours { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
  .parcours-nav { flex-direction: column-reverse; align-items: stretch; }
  .parcours-nav .btn { justify-content: center; }
}

/* --------------------------------------------------------------------------
   Le repère post-opératoire de /douleur-apres-implant-dentaire/ (suites.js).
   Deux questions, un verdict daté, et un message WhatsApp déjà rempli avec le
   jour et le signe. Réemploie tout le visuel de .facture ; n'ajoute que la
   sortie, qui est une phrase et non un montant : le libellé passe au-dessus et
   la valeur perd le nowrap réservé aux chiffres.
   Sans JS, le bloc reste masqué et les deux tableaux de la page le remplacent.
   -------------------------------------------------------------------------- */
.facture-verdict > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-1);
}
.facture-verdict dt {
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: .06em;
}
.facture-verdict dd {
  white-space: normal;
  font-variant-numeric: normal;
  line-height: 1.5;
}
/* Le signe qui sort du cadre est le seul élément en gras de la carte : c'est
   l'unique information que le lecteur doit emporter s'il ne lit rien d'autre. */
.facture-verdict .est-alerte {
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.facture-verdict .est-alerte dt { color: #fff; font-weight: 600; }
.facture-verdict .est-alerte dd { font-weight: 600; }

/* La ligne de relecture, sous le chapô d'une page de santé. Le nœud
   MedicalWebPage.reviewedBy déclare au moteur qu'un praticien nommé a relu la
   page ; cette ligne le déclare au lecteur. Un nœud qui affirme ce qui ne se
   voit nulle part est une déclaration invérifiable. */
.page-signature {
  margin-top: var(--space-4);
  font-size: var(--text-micro);
  color: var(--color-ink-soft);
}
.page-signature a { color: inherit; text-underline-offset: 3px; }
.page-signature a:hover { color: var(--color-ink); }

/* La ligne de nuit de /urgence-dentaire-mohammedia/, révélée par urgence.js
   quand le cabinet est fermé. Le h1 de cette page promet « reçu le jour même »
   à quelqu'un qui peut la lire à 2 h du matin : la grappe « rage de dent » est
   saturée de variantes nocturnes. Sans cette ligne, la page ment à celui qui en
   a le plus besoin.

   Elle porte --color-warn et non --color-danger : le cabinet fermé est une
   contrainte, pas une alerte médicale. Le rouge reste réservé aux signes de
   gravité. */
/* 2026-09-17, demande du client : une ligne de texte, petite et simple, pas
   un encadré. Le filet jaune disait « avertissement » quand le message n'est
   qu'un renseignement d'état. Même traitement que .ferme-note, pour que le
   site n'ait qu'une seule façon de dire « c'est fermé ». */
.urgence-nuit {
  margin-top: 10px;
  color: var(--color-ink-soft);
  font-size: var(--text-micro);
  line-height: var(--lh-small);
}
.urgence-nuit a {
  margin-left: .35em;
  color: inherit;
  text-underline-offset: 3px;
}

/* .page-head--split pose le chapô sur un fond sombre : la ligne y reprend les
   valeurs claires du bloc plutôt que l'encre du corps. */
.page-head--split .urgence-nuit { color: rgba(255, 255, 255, .66); }



/* --------------------------------------------------------------------------
   La barre d'article : qui a relu, quand, et ce que la lecture coûte en temps.
   Posée sous le premier écran, avant la première section.

   Sur un contenu de santé, la relecture par un praticien nommé est le signal
   E-E-A-T que Google documente, et le nœud MedicalWebPage.reviewedBy le déclare
   au moteur. Cette barre le déclare au lecteur : un nœud qui affirme ce qui ne
   se voit nulle part est une déclaration invérifiable.
   -------------------------------------------------------------------------- */
/* Elle n'est pas une carte et n'est pas une section : c'est la ligne de crédit
   posée en tête de la première section, sur son fond, séparée du chapeau par un
   filet et rien d'autre. Une bande à part, entre le premier écran et le premier
   titre, lisait comme un encart publicitaire. */
.article-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-5) var(--space-8);
  margin-bottom: var(--space-8);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border);
}
.article-meta-who { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }
.article-meta-who img {
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: var(--radius-full);
  object-fit: cover;
}
.article-meta-name { margin: 0; font-size: var(--text-small); font-weight: 600; color: var(--color-ink); }
.article-meta-name a { color: inherit; text-underline-offset: 3px; }
.article-meta-name a:hover { color: var(--color-link); }
.article-meta-role { margin: var(--space-1) 0 0; font-size: var(--text-micro); color: var(--color-ink-soft); }
.article-meta-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  margin: 0 0 0 auto;
  padding: 0;
  list-style: none;
}
.article-meta-facts li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-micro);
  color: var(--color-ink-soft);
}
.article-meta-facts svg { flex: none; color: var(--color-border-strong); }

@media (max-width: 768px) {
  .article-meta-facts { margin-left: 0; width: 100%; }
}

/* --------------------------------------------------------------------------
   Le repère post-opératoire de /douleur-apres-implant-dentaire/ (suites.js).
   Deux questions numérotées, un statut, un verdict, et le geste à faire.
   Réemploie la carte et les pastilles de .facture ; n'ajoute que la sortie,
   qui n'est pas un montant mais une conduite à tenir, et qui doit se lire
   d'un coup d'œil sur un téléphone à deux heures du matin.

   La couleur porte l'information : le statut est la seule chose qu'un lecteur
   inquiet retient s'il ne lit rien d'autre.
   Sans JS, le bloc reste masqué et les deux tableaux de la page le remplacent.
   -------------------------------------------------------------------------- */
/* Une colonne, pas deux : les deux questions sont devenues des listes
   déroulantes le 2026-09-15, et quatorze pastilles empilées faisaient une
   section deux fois plus haute que ce qu'elle disait. Les deux listes tiennent
   sur une ligne, la sortie prend toute la largeur en dessous. */
.facture--triage { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
.facture--triage .facture-form {
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
  align-items: end;
}
.facture-field { display: grid; gap: var(--space-3); min-width: 0; }
.facture-field[hidden] { display: none; }
.facture-label {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--color-ink);
}
.facture-select {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--space-10) 0 var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23565D77' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  background-size: 16px 16px;
  color: var(--color-ink);
  font: inherit;
  font-size: var(--text-small);
  appearance: none;
  cursor: pointer;
}
.facture-select:hover { border-color: var(--color-ink-soft); }
.facture-select:focus-visible { outline: none; border-color: var(--color-cta); box-shadow: var(--shadow-focus); }
.facture-step-n {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-surface-3);
  color: var(--color-blue-700);
  font-size: var(--text-micro);
  font-weight: 700;
}

.triage-out {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--color-dark);
  color: #fff;
  border-radius: var(--radius-md);
}
.triage-statut {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  align-self: flex-start;
  margin: 0;
  padding: 6px 12px;
  border-radius: var(--radius-full);
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.triage-statut[data-niveau="ok"]     { background: rgba(255, 255, 255, .12); color: #8FE3B4; }
.triage-statut[data-niveau="avant"]  { background: rgba(255, 255, 255, .12); color: var(--color-on-dark); }
.triage-statut[data-niveau="alerte"] { background: var(--color-danger); color: #fff; }

.triage-verdict {
  margin: 0;
  font-size: var(--text-strong);
  line-height: var(--lh-lead);
  color: #fff;
}
.triage-detail {
  margin: 0;
  padding-top: var(--space-4);
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.triage-detail-label {
  margin: 0 0 var(--space-1);
  font-size: var(--text-micro);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .62);
}
.triage-detail p { margin: 0; font-size: var(--text-small); line-height: 1.5; color: rgba(255, 255, 255, .88); }
.triage-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: auto; }
.triage-actions .btn[hidden] { display: none; }
.triage-note { margin: 0; font-size: var(--text-micro); color: rgba(255, 255, 255, .6); }

@media (max-width: 768px) {
  .facture--triage .facture-form { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
  .triage-actions .btn { flex: 1 1 100%; justify-content: center; }
}

/* --------------------------------------------------------------------------
   Le rappel : une phrase qui appelle un geste, et le geste juste dessous.
   Écrit sous le tableau des seuils de /douleur-apres-implant-dentaire/, où la
   phrase et le numéro de téléphone étaient séparés par huit cents mots.
   Le liseré rouge est le seul usage de --color-danger en dehors d'un statut.
   -------------------------------------------------------------------------- */
.rappel {
  display: grid;
  gap: var(--space-5);
  margin-top: var(--space-10);
  /* 🔧 2026-09-16 : --space-7 n'existe pas dans tokens.css (1 2 3 4 5 6 8 10 12
     16 20 24). Une valeur invalide dans un raccourci annule la déclaration
     ENTIÈRE, sans erreur : ce bloc avait donc un remplissage de zéro, texte
     collé au liseré rouge. Latent, aucune page n'emploie encore .rappel. */
  padding: var(--space-6) var(--space-8);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-danger);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.rappel p { margin: 0; }
.rappel-lede {
  max-width: 60ch;
  font-size: var(--text-strong);
  line-height: var(--lh-lead);
  color: var(--color-ink);
}
.rappel-note { font-size: var(--text-small); color: var(--color-ink-soft); }
.rappel-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

@media (max-width: 480px) {
  .rappel { padding: var(--space-6); }
  .rappel-actions .btn { flex: 1 1 100%; justify-content: center; }
}


/* --------------------------------------------------------------------------
   Cibles tactiles — `3.4.1`
   Le doigt demande 44 px, l'œil n'en demande pas. Ces commandes gardent leur
   dessin exact au pixel près : un pseudo-élément centré sur elles étend la
   zone touchée, sans rien peindre et sans rien déplacer. `max(100%, 44px)`
   garantit que la zone ne rétrécit jamais l'élément qu'elle recouvre.

   Aucune de ces règles n'écrase un `::after` existant, vérifié avant d'écrire.

   Trois-quarts des manques étaient de la hauteur et non de la largeur : un
   numéro de téléphone en pied de page fait 335 px de large et 22 px de haut.
   Élargir n'aurait rien donné, c'est la hauteur qu'il fallait aller chercher.

   Les pastilles du carrousel ne sont pas ici : 7 px espacés de 7 px ne peuvent
   pas recevoir 44 px chacune sans se recouvrir, et la zone du voisin gagnerait.
   Cas laissé ouvert, il demande une décision de dessin.
   -------------------------------------------------------------------------- */
.brand,
.nav-toggle,
.drawer-close,
.footer-social a,
.footer-contact a,
.profile-social a,
.link-more,
.rail-arrows button { position: relative; }
/* `.carousel-nav` n'est pas dans la liste ci-dessus, et c'est le point à ne pas
   manquer : elle est déjà `position: absolute`, donc déjà un bloc de référence
   pour son propre `::after`. Lui poser `relative` lui retirait son placement et
   l'envoyait 90 px sous le carrousel. Invisible jusqu'ici parce qu'elle vivait à
   `opacity: 0` hors survol ; la rendre visible sur mobile l'a révélé. */

.brand::after,
.nav-toggle::after,
.drawer-close::after,
.footer-social a::after,
.footer-contact a::after,
.profile-social a::after,
.link-more::after,
.carousel-nav::after,
.rail-arrows button::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, 44px);
  height: max(100%, 44px);
  transform: translate(-50%, -50%);
}

/* --------------------------------------------------------------------------
   Le trieur — quatre cartes de choix
   Ajouté le 2026-09-16 pour /gouttiere-dentaire/, additif, une seule page
   l'emploie pour l'instant. Refait le même jour sur une référence apportée
   par le client : carte haute, sujet posé sur un panneau teinté, bloc de texte
   en pied, bouton rond sombre à droite.

   Pourquoi pas .kit-grid. Cette grille est réglée pour CINQ cartes (2+2+2 puis
   3+3) : avec quatre, la rangée du haut s'arrête aux deux tiers et laisse un
   trou de deux colonnes à droite. Mesuré en capture, et c'est ce trou qui
   faisait lire la bande comme un bug.

   🔑 LE PANNEAU EST DÉJÀ AU FORMAT DES PHOTOS. `aspect-ratio: 1/1` et un `img`
   en `object-fit: cover` : le jour où le cabinet livre la photo de gouttière
   occlusale qui manque à la bibliothèque, les quatre `svg` deviennent quatre
   `img` sans une ligne de CSS à changer. Les 88 images du dépôt couvrent
   l'alignement, le blanchiment et la contention ; aucune ne montre une
   gouttière de bruxisme. Quatre icônes valent mieux que trois photos et une
   icône, qui romprait la série sur la seule carte que l'œil irait chercher.

   ⚠️ CE QUI N'A PAS ÉTÉ REPRIS DE LA RÉFÉRENCE : la pastille « Sale » et
   « Best Sellers ». Elle vient d'une boutique. Sur un appareil médical elle
   ferait exactement ce que le client a demandé d'éviter, le registre catalogue.
   Le rail, les flèches et la barre de progression ne sont pas repris non plus :
   quatre cartes tiennent en largeur, et cacher la quatrième derrière une flèche
   coûterait un tri au lecteur au lieu de le lui rendre.

   Trois lignes en pied, et chacune porte quelque chose : le BESOIN en petit,
   parce que le lecteur arrive avec son problème et non avec le nom de
   l'appareil ; le NOM CLINIQUE en fort, le mot qu'il devra prononcer au
   cabinet ; un FAIT à la place du prix, voix-kandil §3.1.

   Les durées passent par --dur-*, donc `prefers-reduced-motion` les met à zéro
   sans une règle de plus (tokens.css ligne 170).
   -------------------------------------------------------------------------- */
.tri-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }

.tri {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Sans panneau, la carte respire par le haut. Le retrait des icônes est une
     décision du client du 2026-09-16 : elles ne portaient pas, et un carré
     teinté vide en attendant les photos aurait lu comme une image cassée. */
  padding: var(--space-6) var(--space-5) var(--space-5);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  text-decoration: none;
  color: inherit;
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.tri:hover,
.tri:focus-visible { transform: translateY(-4px); box-shadow: var(--shadow-deep); }
.tri:focus-visible { outline: none; box-shadow: var(--shadow-deep), var(--shadow-focus); }

/* Le panneau du sujet. Carré, donc prêt pour les photos 1080x1080 du cabinet. */
.tri-shot {
  display: grid;
  place-items: center;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md);
  background: var(--color-surface-3);
  overflow: hidden;
  color: var(--color-blue-600);
  transition: background var(--dur-base) var(--ease-out);
}
.tri-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tri-shot svg { width: 52%; height: auto; }
.tri:hover .tri-shot { background: var(--color-blue-100); }

/* Une carte qui porte un panneau reprend le rythme serré du haut. Les règles
   ci-dessus sont gardées telles quelles pour le jour où le cabinet livre les
   quatre photos : il suffira de remettre le <span class="tri-shot"> dans le
   markup, sans rien changer ici. */
.tri:has(.tri-shot) { padding: var(--space-4) var(--space-4) var(--space-5); }
.tri:has(.tri-shot) .tri-body { padding-top: var(--space-5); }

.tri-body { padding: 0 var(--space-1) 0; }

/* Le besoin, en tête. Il prend la forme du sur-titre du site plutôt que celle
   d'un paragraphe : sans cela il pesait autant que le fait en pied, et les deux
   lignes grises encadraient le nom sans hiérarchie. C'est la place que la
   référence donne à la catégorie du produit. */
.tri-need {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--color-cta);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.tri h3 {
  margin: 0;
  font-size: var(--text-strong);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-heading);
  line-height: var(--lh-heading);
  color: var(--color-ink);
}
/* Le fait, à la place où la référence met le prix. Règle 6 : jamais un montant. */
.tri-fact {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-small);
  line-height: var(--lh-small);
  color: var(--color-ink-soft);
}

/* Le bouton rond. 44 px pleins : c'est la cible tactile minimale, et il porte
   l'affordance que quatre lignes de texte ne portent pas.

   Il est DANS LE FLUX, poussé en bas par margin-top:auto, et non posé en
   absolu dans le coin. En absolu il fallait lui réserver 60 px sur toute la
   hauteur de la colonne de texte, alors qu'il n'en croise que la dernière
   ligne : le fait tombait sur quatre lignes au lieu de deux. Dans le flux, le
   texte prend toute la largeur et les quatre boutons restent alignés, puisque
   c'est la même marge automatique qui les pousse. */
.tri-go {
  margin-top: auto;
  align-self: flex-end;
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-full);
  background: var(--color-dark);
  color: var(--color-surface);
  transition: background var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.tri:hover .tri-go,
.tri:focus-visible .tri-go { background: var(--color-blue-600); transform: scale(1.06); }

/* L'écart minimal entre le fait et le bouton quand la carte est la plus haute
   de la rangée : sans lui, margin-top:auto vaut zéro et les deux se touchent. */
.tri-body { margin-bottom: var(--space-5); }

@media (max-width: 1024px) { .tri-grid { grid-template-columns: repeat(2, 1fr); } }

/* Sous 480 px, une colonne. La carte couchée en rangée a été retirée le
   2026-09-16 avec les icônes : elle n'existait que pour empêcher le panneau
   carré de faire 327 px de haut sur un téléphone. Sans panneau, la carte de
   texte tient déjà en 150 px et la rangée n'a plus d'objet. Elle reviendra avec
   les photos, si elles arrivent. */
@media (max-width: 480px) {
  .tri-grid { grid-template-columns: 1fr; gap: var(--space-3); }
  .tri { padding: var(--space-5); }
}

/* Variante à trois cartes, 2026-09-16, pour /hollywood-smile/ et ses trois
   chemins. Additive : .tri-grid reste réglée pour quatre.
   🔑 Elle ne passe JAMAIS par deux colonnes. C'est la leçon de la première
   version du trieur de /gouttiere-dentaire/, qui employait .kit-grid réglée
   pour cinq : avec quatre cartes, la rangée s'arrêtait aux deux tiers et
   laissait un trou. Trois cartes dans deux colonnes font le même trou, donc
   le trio garde ses trois colonnes jusqu'à 768 px, puis tombe à une seule. */
.tri-grid--trio { grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }

/* 🔧 2026-09-16. .tri est réglée pour QUATRE colonnes, soit des cartes de
   277 px : ses marges de 20 px y sont justes. À trois colonnes la carte fait
   372 px et les mêmes 20 px deviennent un liseré. Le remplissage et l'écart
   suivent donc la largeur, et l'écart rejoint les 24 px des autres grilles de
   cartes du site, .ba-grid en tête. Rien de tout cela ne touche le trieur à
   quatre cartes de /gouttiere-dentaire/. */
.tri-grid--trio .tri { padding: var(--space-8) var(--space-6) var(--space-6); }
.tri-grid--trio .tri-body { padding: 0; }

/* 🔧 2026-09-16. Le bloc de texte qui suit les cartes les touchait : écart
   mesuré à ZÉRO, quand l'en-tête de section en a 48 au-dessus des mêmes cartes.
   La grille n'a pas de marge basse, et .duo-copy pas de marge haute. On reprend
   les 48 px de l'en-tête pour que la section respire au même rythme en haut et
   en bas des cartes. */
.tri-grid--trio + .duo-copy { margin-top: var(--space-12); }
@media (max-width: 1024px) { .tri-grid--trio { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 768px)  { .tri-grid--trio { grid-template-columns: 1fr; } }


/* ═══ Le déroulé en diapositives · /hollywood-smile/ · 2026-09-16 ══════════════
   Deux colonnes qui changent ENSEMBLE : le texte à gauche, la photo à droite.
   Bâti sur carousel.js, sans second moteur, parce qu'il porte déjà le fondu,
   les points, les flèches, le clavier, la pause au survol et le respect de
   `prefers-reduced-motion`.

   🔑 LES DIAPOSITIVES SONT EMPILÉES EN GRILLE, PAS EN ABSOLU. Le carrousel
   d'origine pose ses vues en `position: absolute` dans une piste au format
   carré : il montre des images, dont la hauteur est connue d'avance. Ici chaque
   vue porte un texte de longueur différente. Toutes occupent la même cellule
   `grid-area: 1 / 1`, donc la piste prend la hauteur de la PLUS HAUTE et rien
   ne saute d'une vue à l'autre.

   🔑 LES COMMANDES SORTENT DE L'IMAGE. Les flèches du carrousel d'origine
   flottent au milieu, en blanc sur un fond sombre, et n'apparaissent qu'au
   survol. Sur deux colonnes, la flèche de gauche tomberait en blanc sur le
   texte, et « au survol » n'existe pas au doigt. Elles descendent donc sous la
   piste, visibles en permanence, et à 44 px. */

.etapes { position: relative; border-radius: 0; overflow: visible; background: none; box-shadow: none; }

.etapes .carousel-track {
  /* La jauge s'y accroche en absolu : sans cette ligne elle se poserait sur
     .etapes, qui descend jusque sous la rangée de boutons. */
  position: relative;
  display: grid;
  aspect-ratio: auto;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}

.etapes .carousel-slide {
  position: relative;
  inset: auto;
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
}

.etape-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-3);
  /* 🔧 Le remplissage valait ZÉRO jusqu'au 2026-09-16, et le texte touchait le
     bord du cadre. --space-7 n'existe pas, et une valeur invalide dans un
     raccourci `padding` annule la déclaration entière sans rien signaler.
     40 px donnent ici une mesure de 493 px, soit une soixantaine de signes par
     ligne, dans la fourchette lisible. */
  padding: var(--space-10);
}

.etape-rang {
  color: var(--color-ink-soft);
  font-size: var(--text-micro);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.etape-copy h3 { margin: 0; color: var(--color-ink); }
.etape-copy p  { margin: 0; color: var(--color-ink-soft); }

.etape-shot { position: relative; min-height: 340px; background: var(--color-surface-2); }
.etape-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* La barre de commandes, sous la piste, alignée sur la colonne de texte. */
.etapes-commandes {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.etapes .carousel-nav {
  position: static;
  flex: none;
  box-sizing: border-box;
  margin: 0;
  width: 44px; height: 44px;
  border-color: var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-ink);
  opacity: 1;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.etapes .carousel-nav:hover { background: var(--color-surface-3); }

.etapes .carousel-dots { position: static; flex: 1; justify-content: center; gap: 0; }

/* Le point reste fin à l'œil, mais sa zone touchable fait 44 px de CÔTÉ :
   c'est du remplissage transparent, pas un point plus gros.
   🔧 Passé de 26 à 44 px de large le 2026-09-16. À 26, `measure --tap` sondait
   à 22 px du centre et tombait sur le point VOISIN : la cible existait, elle
   n'était simplement pas atteignable au doigt. */
.etapes .carousel-dots button {
  flex: none;
  height: 44px;
  width: 44px;
  padding: 0;
  background: none;
  border-radius: 0;
  position: relative;
}
.etapes .carousel-dots button::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  margin: -3px 0 0 -11px;
  width: 22px; height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-border-strong);
  transition: background-color var(--dur-base) var(--ease-out);
}
.etapes .carousel-dots button[aria-selected="true"]::after { background: var(--color-cta); }

.etapes-pause {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  box-sizing: border-box;
  gap: var(--space-2);
  height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-ink-soft);
  font-size: var(--text-small);
  cursor: pointer;
}
.etapes-pause:hover { background: var(--color-surface-3); }

/* 🔑 Sans cette ligne le bouton reste VISIBLE quand carousel.js le masque.
   `display` posé par une classe d'auteur bat `[hidden] { display: none }` de la
   feuille du navigateur, qui n'est qu'une feuille utilisateur-agent. Le dépôt
   porte déjà ce correctif cinq fois, sur .facture, .facture-field, les fieldsets
   du parcours et les boutons du triage. Sans lui, le bouton d'arrêt
   s'afficherait sous `prefers-reduced-motion` en ne commandant plus rien. */
.etapes-pause[hidden] { display: none; }
.etapes-pause .etapes-pause-reprendre,
.etapes.is-held .etapes-pause .etapes-pause-arreter { display: none; }
.etapes.is-held .etapes-pause .etapes-pause-reprendre { display: inline; }

@media (max-width: 1024px) {
  .etape-copy { padding: var(--space-8); }
  .etape-shot { min-height: 300px; }
}

/* Sous 768 px la photo passe AU-DESSUS du texte. L'ordre du DOM garde le texte
   en premier, qui est l'ordre de lecture et celui du clavier ; seul l'affichage
   s'inverse, parce qu'une photo ouvre mieux une carte qu'un intertitre. */
@media (max-width: 768px) {
  .etapes .carousel-slide { grid-template-columns: 1fr; }
  .etape-shot { order: -1; min-height: 0; aspect-ratio: 16 / 10; }
  .etape-copy { padding: var(--space-6) var(--space-5) var(--space-8); }
}

/* 🔑 SOUS 768 px LES POINTS DISPARAISSENT, et rien n'est perdu. Cinq cibles de
   44 px plus deux flèches et un bouton d'arrêt ne tiennent pas dans 335 px de
   contenu : la rangée se comprimait, et `measure --tap` a vu la flèche gauche
   devenir inatteignable avant que l'œil ne voie quoi que ce soit. La position
   dans la série est déjà écrite en toutes lettres au-dessus du titre, « Étape 3
   sur 5 », donc les points ne portaient qu'une redite. */
@media (max-width: 768px) {
  .etapes .carousel-dots { display: none; }
  .etapes-commandes { justify-content: flex-start; }
  .etapes-pause { margin-left: auto; }
}

@media (max-width: 480px) {
  .etapes-commandes { gap: var(--space-2); }
  .etapes-pause { padding: 0 var(--space-3); }
}

/* ═══ Le déroulé, la part vivante · 2026-09-16 ════════════════════════════════
   Quatre mouvements, et chacun porte une information. Aucun n'est décoratif,
   et TOUS disparaissent sous `prefers-reduced-motion`.

   1. La jauge dit combien de temps il reste avant la vue suivante. C'est ce qui
      fait comprendre, sans une ligne de texte, que le bloc avance tout seul et
      qu'on peut l'arrêter.
   2. Le texte monte de dix pixels en apparaissant, en trois temps décalés :
      le rang, le titre, puis le paragraphe. L'œil suit l'ordre de lecture.
   3. La photo se détend d'un quart de pour cent sur une seconde et demie.
   4. Elle se rapproche au survol, parce qu'une image qui ne répond à rien passe
      pour un fond.

   🔑 LA JAUGE EST PILOTÉE PAR LE JS, PAS PAR :hover EN CSS. Les deux auraient
   divergé : le minuteur s'arrête aussi au focus, à la pause, et redémarre à
   zéro quand la souris repart. Une barre figée pendant qu'une vue défile est
   pire que pas de barre. carousel.js arme et fige, personne d'autre. */

.etapes-jauge {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: var(--color-border);
  overflow: hidden;
}
.etapes-jauge i {
  display: block;
  height: 100%;
  background: var(--color-cta);
  transform: scaleX(0);
  transform-origin: left center;
  animation: etapes-remplir var(--etapes-duree, 9000ms) linear forwards;
}
@keyframes etapes-remplir { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Le texte, en trois temps. Le décalage suit l'ordre de lecture et rien d'autre. */
.etapes .etape-rang,
.etapes .etape-copy h3,
.etapes .etape-copy p {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
.etapes .carousel-slide.is-active .etape-rang    { opacity: 1; transform: none; transition-delay: 60ms; }
.etapes .carousel-slide.is-active .etape-copy h3 { opacity: 1; transform: none; transition-delay: 130ms; }
.etapes .carousel-slide.is-active .etape-copy p  { opacity: 1; transform: none; transition-delay: 200ms; }

.etape-shot img {
  transform: scale(1.025);
  transition: transform 1.5s var(--ease-out);
}
.etapes .carousel-slide.is-active .etape-shot img { transform: scale(1); }
.etapes .carousel-slide.is-active:hover .etape-shot img { transform: scale(1.04); }

/* Le geste tactile : le doigt tire la vue, et le curseur dit que c'est possible. */
.etapes[data-carousel-swipe] .carousel-track { cursor: grab; touch-action: pan-y; }
.etapes[data-carousel-swipe].is-tire .carousel-track { cursor: grabbing; }

/* ⛔ Sous `prefers-reduced-motion`, il ne reste RIEN de tout cela. La jauge
   disparaît plutôt que de se figer pleine, parce qu'une barre immobile sous une
   vue qui ne défile pas ne veut plus rien dire. Le bouton d'arrêt s'efface avec
   elle, et c'est carousel.js qui s'en charge. */
@media (prefers-reduced-motion: reduce) {
  .etapes-jauge { display: none; }
  .etapes .etape-rang,
  .etapes .etape-copy h3,
  .etapes .etape-copy p { opacity: 1; transform: none; transition: none; }
  .etape-shot img,
  .etapes .carousel-slide.is-active:hover .etape-shot img { transform: none; transition: none; }
}

/* --------------------------------------------------------------------------
   Dock d'actions, desktop uniquement
   Les trois mêmes actions que la barre mobile, dans le même ordre, mais posées
   en bas à droite : c'est là qu'un lecteur d'ordinateur cherche une action
   flottante, et c'est le seul endroit qui ne recouvre jamais un titre.
   Au repos, trois pastilles rondes. Au survol ou au clavier, la pastille
   s'allonge et découvre son libellé. Le libellé est toujours dans le document,
   seulement replié : un lecteur d'écran l'annonce même fermé.
   -------------------------------------------------------------------------- */
.dock {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: var(--z-sticky);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  opacity: 0;
  transform: translateX(14px);
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.dock.is-in {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.dock-item {
  display: flex;
  align-items: center;
  height: 48px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-lift);
  color: var(--color-ink-soft);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.dock-icon {
  display: grid;
  place-items: center;
  flex: 0 0 46px;
  width: 46px;
  height: 46px;
}

/* Le libellé est une colonne de grille qui vaut zéro au repos et une fraction
   ouverte au survol : la largeur s'anime sans qu'aucun nombre soit écrit à la
   main, donc sans risque de coupure si la police charge tard. */
.dock-label {
  display: grid;
  grid-template-columns: 0fr;
  transition: grid-template-columns var(--dur-base) var(--ease-out);
}
.dock-label > span {
  overflow: hidden;
  white-space: nowrap;
  padding-right: 0;
  font-size: var(--text-small);
  font-weight: var(--w-medium);
  letter-spacing: var(--track-body);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out),
              padding-right var(--dur-base) var(--ease-out);
}
.dock-item:hover .dock-label,
.dock-item:focus-visible .dock-label { grid-template-columns: 1fr; }
.dock-item:hover .dock-label > span,
.dock-item:focus-visible .dock-label > span { padding-right: 18px; opacity: 1; }

.dock-item:hover { color: var(--color-ink); box-shadow: var(--shadow-deep); }
.dock-item:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

/* L'appel est l'action principale : c'est la seule qui porte la couleur,
   exactement comme dans la barre mobile. */
.dock-item.is-primary {
  background: var(--color-cta);
  border-color: transparent;
  color: #FFFFFF;
}
.dock-item.is-primary:hover { background: var(--color-cta-hover); color: #FFFFFF; }

/* Sous 768, la barre mobile prend le relais : jamais les deux à la fois. */
@media (max-width: 768px) {
  .dock { display: none; }
}

/* Deux cadrages d'image, sortis d'attributs `style=` en ligne le 2026-09-18.
   La CSP du .htaccess déclare `style-src 'self'` : un attribut `style=` y est
   bloqué comme l'est un script en ligne. Les élargir en `'unsafe-inline'` pour
   deux déclarations affaiblirait la règle sur tout le site. */
.pos-45-45 { object-position: 45% 45%; }
.pos-66-38 { object-position: 66% 38%; }


/* --------------------------------------------------------------------------
   ⚠️ CE BLOC DOIT RESTER LE DERNIER DU FICHIER, et ce n'est pas une preference
   de rangement : six regles `.<contexte> .eyebrow` plus haut dans ce fichier
   redeclarent `display: block` (profile-body, visite-inner, cabinet-inner,
   page-head, cv-head, duo-copy). Elles ont EXACTEMENT la meme specificite que
   celles ci-dessous, donc c'est la derniere ecrite qui gagne.
   Pose a sa place logique, juste apres .section-head, le bloc ne marchait que
   sur deux des huit contextes : le filet apparaissait sur l'accueil et nulle
   part ailleurs. Le defaut etait invisible autrement qu'a l'oeil, car un
   ::before inline dans un parent en display:block garde ses width/height sans
   effet et ne rend rien du tout. Vu sur la bande marine du pied de page.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   Le filet du sur-titre — troisième geste repris d'Ibn Sina (D-040)
   Un trait de 28 px devant le sur-titre. Il ne dit rien, il place : l'œil trouve
   le début du bloc avant d'avoir lu un mot, et c'est de là que vient une bonne
   part du « calme » qu'on est venu chercher.

   ⚠️ Portée VOLONTAIREMENT restreinte. `.eyebrow` est posé 337 fois dans le
   site, dans des contextes qui n'ont rien d'éditorial et dont plusieurs fixent
   déjà `display`. Une règle sur `.eyebrow` nu aurait donc changé 337 endroits
   pour en viser huit, et cassé la mise en page de ceux qui vivent dans une
   grille. Les huit ci-dessous sont les sur-titres qui ouvrent un bloc de texte.
   Sur fond marine, le filet prend la couleur claire, sinon il disparaît.
   -------------------------------------------------------------------------- */
.section-head .eyebrow,
.page-head .eyebrow,
.split-copy .eyebrow,
.duo-copy .eyebrow,
.cabinet-inner .eyebrow,
.cv-head .eyebrow,
.profile-body .eyebrow,
.visite-inner .eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.section-head .eyebrow::before,
.page-head .eyebrow::before,
.split-copy .eyebrow::before,
.duo-copy .eyebrow::before,
.cabinet-inner .eyebrow::before,
.cv-head .eyebrow::before,
.profile-body .eyebrow::before,
.visite-inner .eyebrow::before {
  content: '';
  flex: none;
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: .5;
}
/* Centré, le bloc-tête met son filet des deux côtés : un seul trait à gauche
   d'un titre centré tire l'œil de travers. */
.section-head--center .eyebrow { justify-content: center; }
.section-head--center .eyebrow::after {
  content: '';
  flex: none;
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: .5;
}
@media (max-width: 480px) {
  .section-head .eyebrow::before,
  .page-head .eyebrow::before,
  .split-copy .eyebrow::before,
  .duo-copy .eyebrow::before,
  .cabinet-inner .eyebrow::before,
  .cv-head .eyebrow::before,
  .profile-body .eyebrow::before,
  .visite-inner .eyebrow::before { width: 18px; }
}
