/* ============================================================
   FICHIER GÉNÉRÉ par build.mjs — ne pas éditer.

   Concaténation des feuilles listées par assets/css/main.css, qui reste
   le manifeste de développement. C'est ce fichier qui est servi : un seul
   aller-retour au lieu de vingt-cinq découverts en cascade.
   ============================================================ */

/* ===== tokens/variables.css ===== */
/* ============================================================
   THEME TOKENS — MP-i
   Palette : Graphite + Orange + Crème + Vert sapin (sémantique)

   Trois couches, dans cet ordre :
     1. primitives de marque — les hex, jamais utilisés directement
     2. couche sémantique    — ce que les composants consomment
     3. alias de compatibilité — anciens noms, le temps de la migration

   Seul fichier à éditer pour retoucher la charte.
   ============================================================ */

/* ===== 1. Primitives de marque ============================== */
/* Ces valeurs ne dépendent pas du thème. Un composant ne doit
   jamais les lire : il lit la couche sémantique, qui bascule. */
:root {
  --brand-graphite:       #2F2E2C;
  --brand-graphite-deep:  #1a1a18;
  --brand-graphite-dark:  #141412;
  --brand-graphite-lift:  #232320;
  --brand-graphite-soft:  #6e6d6a;

  --brand-orange:         #E97423;
  --brand-orange-deep:    #c96818;
  /* Orange assombri pour l'orange EN TEXTE sur fond clair : c'est la seule
     variante qui tient le 4,5:1 de WCAG AA sur crème (4,99:1) et sur blanc
     (5,33:1). #E97423 y plafonne à 2,82:1 et #c96818 à 3,42:1. */
  --brand-orange-ink:     #a85410;

  --brand-cream:          #fbf7f1;
  --brand-cream-2:        #f3ece0;
  --brand-cream-3:        #ebe2d2;
  --brand-cream-rule:     #ddd2bd;

  --brand-fir:            #2d5f3f;
  --brand-fir-light:      #5fa86f;

  --brand-ink:            #1f1f1f;

  /* Plaque des logos clients. Blanc pur et indépendant du thème : trois des
     cinq logos ont un fond blanc opaque intégré à l'image, qu'une plaque
     crème ou sombre laisserait voir comme un rectangle. */
  --logo-plate:           #ffffff;
  --brand-red:            #c62828;
  --brand-red-light:      #ef6b6b;
}

/* ===== 2. Couche sémantique — thème clair =================== */
:root {
  /* Le clair est le thème par défaut, quel que soit le réglage du système :
     le crème est la marque. Le sombre ne s'applique que sur choix explicite,
     via la bascule. `light` seul, sinon le navigateur dessinerait barres de
     défilement et contrôles de formulaire en sombre sur une page crème. */
  color-scheme: light;

  /* --- Surfaces --- */
  --surface-page:     var(--brand-cream);        /* fond global */
  --surface-raised:   #ffffff;                   /* cartes, champs */
  --surface-sunken:   var(--brand-cream-2);      /* alternance de sections */
  --surface-hover:    var(--brand-cream-3);      /* survol discret */
  /* Surface qui reste sombre dans les DEUX thèmes : sections inversées,
     footer, hero. En sombre elle descend sous le fond de page, sans quoi
     une section graphite sur une page graphite serait invisible. */
  --surface-inverse:  var(--brand-graphite-deep);
  --surface-inverse-lift: var(--brand-graphite);   /* deuxième palier sombre */

  /* --- Encres --- */
  --ink:              var(--brand-ink);
  --ink-muted:        #5a5347;                   /* 7,23:1 sur crème */
  --ink-subtle:       #8a8470;
  /* Encres posées sur --surface-inverse : crème dans les deux thèmes,
     puisque cette surface reste sombre dans les deux. */
  --ink-on-inverse:        var(--brand-cream);
  --ink-on-inverse-muted:  rgb(251 247 241 / 75%);
  --ink-on-inverse-subtle: rgb(251 247 241 / 52%);

  /* --- Filets --- */
  --rule:             var(--brand-cream-rule);
  --rule-strong:      var(--brand-graphite-soft);
  --rule-on-inverse:  rgb(251 247 241 / 13%);

  /* --- Accent --- */
  --accent:           var(--brand-orange);       /* remplissages, graphismes */
  --accent-hover:     var(--brand-orange-deep);
  /* Encre POSÉE SUR un remplissage orange. Le blanc n'y atteint que 3,00:1 et
     échouait donc sur le CTA principal. Le graphite #2F2E2C y passe de justesse
     (4,51:1, pour un seuil à 4,5) ; le graphite profond, déjà dans la palette,
     donne 5,80:1 pour le même rendu d'encre noire. */
  --accent-ink:       var(--brand-graphite-deep);
  /* Orange UTILISÉ COMME texte. Voir --brand-orange-ink. */
  --accent-text:      var(--brand-orange-ink);

  /* --- Boutons pleins ---
     Volontairement hors de portée de `data-tone` : un ton sombre redéfinit
     --ink, et un bouton bâti dessus se retrouverait crème sur crème à
     l'intérieur d'une section inversée en thème clair. */
  --btn-solid-bg:       var(--brand-graphite);
  --btn-solid-bg-hover: var(--brand-graphite-deep);
  --btn-solid-ink:      var(--brand-cream);

  /* --- États sémantiques --- */
  --valid:            var(--brand-fir);
  --danger:           var(--brand-red);
  /* Anneau de focus clavier. Seule teinte de la palette tenant le 3:1 exigé
     par WCAG 1.4.11 sur les trois fonds du site : crème 3,56:1,
     graphite 3,54:1, page sombre 4,77:1. */
  --focus:            var(--brand-orange-deep);

  /* --- Ombres — la couleur bascule, les géométries non --- */
  --shadow-color:     47 46 44;
  --shadow-alpha:     1;
}

/* ===== 2b. Couche sémantique — thème sombre ================= */
/* Le crème devient l'encre, le graphite devient le papier. L'orange y gagne :
   il passe de 2,82:1 (texte sur crème) à 5,80:1 (texte sur page sombre).

   Il ne s'applique QUE sur choix explicite de la bascule, jamais d'après
   `prefers-color-scheme` : le thème clair est le défaut voulu. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --surface-page:     var(--brand-graphite-deep);
  --surface-raised:   var(--brand-graphite-lift);
  --surface-sunken:   var(--brand-graphite-dark);
  --surface-hover:    #2b2b27;
  --surface-inverse:  var(--brand-graphite-dark);
  --surface-inverse-lift: var(--brand-graphite-lift);

  --ink:              var(--brand-cream);
  --ink-muted:        #a8a29a;
  --ink-subtle:       #7d776e;

  --rule:             rgb(251 247 241 / 12%);
  --rule-strong:      rgb(251 247 241 / 28%);

  --accent-text:      var(--brand-orange);
  --btn-solid-bg:       var(--brand-cream);
  --btn-solid-bg-hover: var(--brand-cream-2);
  --btn-solid-ink:      var(--brand-graphite);
  --valid:            var(--brand-fir-light);
  --danger:           var(--brand-red-light);

  --shadow-color:     0 0 0;
  --shadow-alpha:     2.2;
}


/* ===== 3. Typographie ======================================= */
:root {
  --font-display: 'Sora', system-ui, sans-serif;
  --font-body:    'DM Sans', system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, monospace;

  /* Échelle fluide, interpolée entre 390 px et 1440 px de viewport.
     Elle remplace les paliers en media queries : plus de saut de taille
     au franchissement d'un breakpoint. */
  --step--2: clamp(0.69rem, 0.67rem + 0.07vw, 0.75rem);
  --step--1: clamp(0.83rem, 0.79rem + 0.15vw, 0.94rem);
  --step-0:  clamp(1rem,    0.95rem + 0.19vw, 1.13rem);
  --step-1:  clamp(1.2rem,  1.12rem + 0.32vw, 1.42rem);
  --step-2:  clamp(1.44rem, 1.31rem + 0.51vw, 1.8rem);
  --step-3:  clamp(1.73rem, 1.53rem + 0.79vw, 2.28rem);
  --step-4:  clamp(2.07rem, 1.77rem + 1.19vw, 2.89rem);
  --step-5:  clamp(2.49rem, 2.05rem + 1.76vw, 3.66rem);
  --step-6:  clamp(2.99rem, 2.36rem + 2.54vw, 4.63rem);

  --leading-tight:  1.15;
  --leading-snug:   1.35;
  --leading-normal: 1.6;
  --leading-loose:  1.75;

  --tracking-tight: -0.02em;
  --tracking-wide:  0.14em;      /* libellés mono en capitales */

  --font-weight-normal:    400;
  --font-weight-medium:    500;
  --font-weight-semibold:  600;
  --font-weight-bold:      700;
  --font-weight-extrabold: 800;
}

/* ===== 4. Espace et mise en page ============================ */
:root {
  --space-1:  0.25rem;
  --space-2:  0.5rem;
  --space-3:  0.75rem;
  --space-4:  1rem;
  --space-5:  1.25rem;
  --space-6:  1.5rem;
  --space-8:  2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;

  --container-max: 1200px;
  --container-pad: 1.5rem;
  /* Respiration verticale des sections. L'ancienne valeur était 2,5rem,
     ce qui étouffait tout. */
  --section-pad:   clamp(4rem, 10vw, 8rem);
  /* Largeur de justification confortable pour la prose. */
  --measure:       68ch;

  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:   14px;
  --radius-xl:   22px;
  --radius-full: 9999px;
}

/* ===== 5. Ombres, transitions, empilement =================== */
:root {
  --shadow-sm: 0 2px 8px  rgb(var(--shadow-color) / calc(0.08 * var(--shadow-alpha)));
  --shadow-md: 0 4px 20px rgb(var(--shadow-color) / calc(0.12 * var(--shadow-alpha)));
  --shadow-lg: 0 8px 40px rgb(var(--shadow-color) / calc(0.16 * var(--shadow-alpha)));

  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
  --transition-slow: 400ms ease;

  --z-base:     0;
  --z-raised:   10;
  --z-dropdown: 100;
  --z-sticky:   200;
  --z-toast:    400;
}

/* ===== 6. Alias de compatibilité ============================ */
/* Les composants n'ont pas encore migré vers la couche sémantique. Ces alias
   la leur branchent, ce qui donne le thème sombre sans les toucher un par un.
   À retirer au fil de la migration des composants. */
:root {
  --color-bg:             var(--surface-page);
  --color-surface:        var(--surface-sunken);
  --color-surface-alt:    var(--surface-hover);
  --color-card:           var(--surface-raised);
  --color-border:         var(--rule);
  --color-text:           var(--ink);
  --color-text-muted:     var(--ink-muted);
  --color-text-light:     var(--ink-subtle);
  /* Dans tous ses usages, --color-white désignait « l'encre sur fond sombre »
     et non un fond blanc : les cartes passent par --color-card. */
  --color-white:          var(--ink-on-inverse);

  --color-primary:        var(--brand-graphite);
  --color-primary-dark:   var(--brand-graphite-deep);
  --color-primary-light:  #4a4a49;
  --color-primary-soft:   var(--brand-graphite-soft);
  --color-secondary:      var(--accent);
  --color-secondary-dark: var(--accent-hover);
  --color-focus:          var(--focus);

  --color-tertiary:       var(--valid);
  --color-tertiary-light: var(--brand-fir-light);
  --color-accent:         var(--valid);

  /* Alias du sprite SVG (classes de assets/svg/sprites.svg). */
  --brand-1:              var(--ink);
  --brand-2:              var(--accent);
  --brand-1-contrast:     var(--surface-raised);
  --color-muted:          var(--ink-subtle);
  --border-1:             var(--rule);

  /* Ancienne échelle typographique en paliers fixes. */
  --text-xs:   0.75rem;
  --text-sm:   0.875rem;
  --text-base: 1rem;
  --text-lg:   1.125rem;
  --text-xl:   1.25rem;
  --text-2xl:  1.5rem;
  --text-3xl:  1.875rem;
  --text-4xl:  2.25rem;
  --text-5xl:  3rem;
  --text-6xl:  3.75rem;

  --shadow-xs: 0 1px 2px rgb(var(--shadow-color) / calc(0.06 * var(--shadow-alpha)));
  --shadow-xl: 0 16px 64px rgb(var(--shadow-color) / calc(0.20 * var(--shadow-alpha)));
  --transition-spring: 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
  --z-below: -1;
  --z-modal: 300;
}
/* ===== base/fonts.css ===== */
/* ============================================================
   POLICES AUTO-HÉBERGÉES — fichier généré

   Régénérer avec : node tools/fetch-fonts.mjs
   Ne pas éditer à la main.

   Elles venaient de fonts.googleapis.com par une feuille bloquante.
   L'auto-hébergement supprime d'un coup le saut de domaine sur le chemin
   critique, les deux `preconnect`, deux exceptions dans la CSP, et la
   déclaration RGPD de transfert d'adresse IP vers Google LLC.

   Polices VARIABLES : un fichier par famille et par sous-ensemble couvre
   toute la plage de graisses. Seuls `latin` et `latin-ext` sont embarqués.

   Licences : SIL Open Font License 1.1 pour les trois familles.
   ============================================================ */

/* DM Sans — italic, latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/dm-sans-italic-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* DM Sans — italic, latin */
@font-face {
  font-family: 'DM Sans';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/dm-sans-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* DM Sans — normal, latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/assets/fonts/dm-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* DM Sans — normal, latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('/assets/fonts/dm-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* JetBrains Mono — normal, latin-ext */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* JetBrains Mono — normal, latin */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Sora — normal, latin-ext */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/assets/fonts/sora-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Sora — normal, latin */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/assets/fonts/sora-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* ===== base/reset.css ===== */
/* ============================================================
   RESET — modern CSS reset
   ============================================================ */

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
}

body {
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background-color: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}

input, button, textarea, select {
  font: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: var(--font-weight-bold);
  line-height: var(--leading-tight);
}

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

ul, ol {
  list-style: none;
}

button {
  cursor: pointer;
  border: none;
  background: none;
}

/* Visible focus for keyboard navigation */
:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* Remove focus ring for mouse users */
:focus:not(:focus-visible) {
  outline: none;
}

/* ============================================================
   Mouvement réduit (WCAG 2.3.3)
   ------------------------------------------------------------
   Neutralise le défilement animé, les transitions et les animations.
   `.observe` n'a pas besoin d'exception ici : son état caché n'est défini
   que sous prefers-reduced-motion: no-preference (utils/helpers.css).
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
/* ===== base/typography.css ===== */
/* ============================================================
   TYPOGRAPHY
   ============================================================ */

.text-xs     { font-size: var(--text-xs); }
.text-sm     { font-size: var(--text-sm); }
.text-base   { font-size: var(--text-base); }
.text-lg     { font-size: var(--text-lg); }
.text-xl     { font-size: var(--text-xl); }
.text-2xl    { font-size: var(--text-2xl); }
.text-3xl    { font-size: var(--text-3xl); }
.text-4xl    { font-size: var(--text-4xl); }
.text-5xl    { font-size: var(--text-5xl); }
.text-6xl    { font-size: var(--text-6xl); }

.font-normal    { font-weight: var(--font-weight-normal); }
.font-medium    { font-weight: var(--font-weight-medium); }
.font-semibold  { font-weight: var(--font-weight-semibold); }
.font-bold      { font-weight: var(--font-weight-bold); }
.font-extrabold { font-weight: var(--font-weight-extrabold); }

.font-display { font-family: var(--font-display); }
.font-body    { font-family: var(--font-body); }
.font-mono    { font-family: var(--font-mono); }

.text-muted   { color: var(--color-text-muted); }
.text-primary { color: var(--color-primary); }
.text-accent  { color: var(--color-accent); }
.text-white   { color: var(--color-white); }

.text-center { text-align: center; }
.text-left   { text-align: left; }
.text-right  { text-align: right; }

.leading-tight  { line-height: var(--leading-tight); }
.leading-normal { line-height: var(--leading-normal); }

/* Section headings
   `.section-label` est désormais dans components/kicker.css : la pilule
   teintée a laissé place à un libellé mono encré. */

/* Les tailles sont fluides : elles interpolent entre 390 et 1440 px au lieu
   de sauter d'un palier à l'autre en franchissant un breakpoint. Les deux
   media queries qui ramifiaient `.section-title` ont donc disparu. */
.section-title {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: var(--font-weight-extrabold);
  color: var(--ink);
  margin-bottom: var(--space-4);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

.section-subtitle {
  font-size: var(--step-1);
  color: var(--ink-muted);
  line-height: var(--leading-normal);
  max-width: var(--measure);
}

.section-header {
  margin-bottom: var(--space-12);
}

.section-header.centered {
  text-align: center;
}

.section-header.centered .section-subtitle {
  margin: 0 auto;
}

/* Bloc de prose : la mesure est bornée, sinon la ligne devient illisible
   sur un écran large. */
.prose > p {
  font-size: var(--step-0);
  line-height: var(--leading-loose);
  color: var(--ink-muted);
  max-width: var(--measure);
}

.prose > p + p { margin-top: var(--space-5); }
.prose strong  { color: var(--ink); font-weight: var(--font-weight-semibold); }
/* ===== layout/grid.css ===== */
/* ============================================================
   GRID & LAYOUT
   ============================================================ */

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

.grid {
  display: grid;
  gap: var(--space-6);
}

.grid-2 { grid-template-columns: repeat(1, 1fr); }
.grid-3 { grid-template-columns: repeat(1, 1fr); }
.grid-4 { grid-template-columns: repeat(1, 1fr); }

@media (min-width: 640px) {
  .grid-2 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 768px) {
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}

/* Flex helpers */
.flex         { display: flex; }
.flex-center  { display: flex; align-items: center; justify-content: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-col     { display: flex; flex-direction: column; }
.flex-wrap    { flex-wrap: wrap; }
.gap-2        { gap: var(--space-2); }
.gap-4        { gap: var(--space-4); }
.gap-6        { gap: var(--space-6); }
.gap-8        { gap: var(--space-8); }
/* ===== layout/sections.css ===== */
/* ============================================================
   SECTIONS — spacing, alternating backgrounds, section-level components
   ============================================================ */

/* clamp(4rem, 10vw, 8rem) — l'ancienne valeur, 2,5 rem puis 3 rem au-delà de
   768 px, étouffait chaque section. Fluide, donc sans saut au breakpoint. */
section {
  padding-block: var(--section-pad);
}

/* La barre de navigation est `position: fixed` : la première section d'une
   page qui n'a pas de hero doit lui laisser sa hauteur, sans quoi le titre
   passe dessous.
   :where() ramène la spécificité à (0,0,2) : assez pour battre `section`
   ci-dessus, pas assez pour battre une classe. Le blog charge cette feuille
   — il sert le /assets/ de ce dépôt — et pose ses propres marges sur
   .blog-header et .mpi-hero. En (0,2,2), cette règle les écrasait : chaque
   page du blog gagnait 70 à 117 px en haut. */
main > section:where(:first-child:not(.hero)) {
  padding-block-start: calc(var(--section-pad) * 0.6 + 5rem);
}

/* Les fonds de section sont définis dans layout/tones.css. */

/* ---- Hero — badges techniques (sur fond sombre) ---- */
.hero__tags-tech {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-6);
}

.hero__tag-tech {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-medium);
  color: rgba(255, 255, 255, 0.85);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-full);
  letter-spacing: 0.02em;
}

/* ---- Sections persona — espacement entre blocs ---- */

/* ---- Statut produit (encart "lancement été 2026") ---- */

/* ---- À propos — récit en prose ---- */
.about-story p {
  font-size: var(--text-base);
  color: var(--color-text);
  line-height: var(--leading-loose);
  margin-bottom: var(--space-4);
}

.about-story p:last-child {
  margin-bottom: 0;
}

.about-story strong {
  color: var(--color-text);
  font-weight: var(--font-weight-semibold);
}

/* ---- Bandeau logos partenaires / références ---- */
.partners-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
}

/* Rendu des logos : components/logo-plate.css. */
.partner-logo {
  height: 40px;
}

/* ---- Bande logos de confiance dans la hero ---- */
.hero__trust {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-8);
  flex-wrap: wrap;
}

.hero__trust-label {
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.45);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.hero__trust-logos {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex-wrap: wrap;
}

/* Rendu des logos : components/logo-plate.css. Le `brightness(10)` qui les
   « détourait » en blanc transformait en rectangle gris les trois logos à
   fond blanc opaque. */
.hero__trust-logos img {
  height: 24px;
}

@media (max-width: 640px) {
  .hero__trust { margin-top: var(--space-6); gap: var(--space-3); }
  .hero__trust-logos { gap: var(--space-4); }
  .hero__trust-logos img { height: 18px; }
}
/* ===== layout/tones.css ===== */
/* ============================================================
   TONS DE SECTION

   Une section déclare une INTENTION (`data-tone`), pas une couleur, et
   chaque thème la résout. Sans ça, une section graphite posée sur une page
   graphite disparaît en thème sombre.

   L'essentiel est que les tons sombres REDÉFINISSENT localement les tokens
   d'encre et de filet. Tout composant placé dans la section suit
   automatiquement, sans règle de surcharge du type
   `.section-bg-dark .mon-composant`. C'est ce qui permettra de retirer la
   trentaine de `rgba(255, 255, 255, α)` littéraux dispersés dans les
   composants.
   ============================================================ */

/* --- Tons clairs — suivent le thème ------------------------- */
[data-tone="page"] {
  background-color: var(--surface-page);
  color: var(--ink);
}

[data-tone="sunken"] {
  background-color: var(--surface-sunken);
  color: var(--ink);
}

/* --- Tons sombres — sombres dans les DEUX thèmes ------------ */
/* `--surface-raised` est réassigné : une carte blanche sur une section
   inversée serait un trou de lumière. */
[data-tone="inverse"],
[data-tone="inverse-lift"] {
  color: var(--ink-on-inverse);

  --ink:            var(--ink-on-inverse);
  --ink-muted:      var(--ink-on-inverse-muted);
  --ink-subtle:     var(--ink-on-inverse-subtle);
  --rule:           var(--rule-on-inverse);
  --rule-strong:    rgb(251 247 241 / 28%);
  --surface-raised: rgb(251 247 241 / 6%);
  --surface-hover:  rgb(251 247 241 / 11%);
  /* Sur fond sombre l'orange plein est lisible : 5,80:1. Pas besoin de la
     variante assombrie réservée aux fonds clairs. */
  --accent-text:    var(--accent);
  --shadow-color:   0 0 0;
  --shadow-alpha:   2.2;
}

[data-tone="inverse"]      { background-color: var(--surface-inverse); }
[data-tone="inverse-lift"] { background-color: var(--surface-inverse-lift); }

/* --- Ton accent — la bande orange -------------------------- */
/* Le graphite profond sur orange tient 5,80:1 ; le blanc n'y atteint que
   3,00:1. C'est pour ça que ce ton impose son encre. */
[data-tone="accent"] {
  background-color: var(--accent);
  color: var(--accent-ink);

  --ink:            var(--accent-ink);
  --ink-muted:      var(--accent-ink);
  --ink-subtle:     rgb(26 26 24 / 72%);
  --rule:           rgb(26 26 24 / 22%);
  --accent-text:    var(--accent-ink);
  --surface-raised: rgb(255 255 255 / 16%);
}

/* --- Alias des anciennes classes ---------------------------- */
/* Les pages actuelles portent encore `.section-bg-*`. Elles héritent des
   mêmes tokens, donc du thème sombre, sans être touchées. */
.section-bg-default { background-color: var(--surface-page);   color: var(--ink); }
.section-bg-surface { background-color: var(--surface-sunken);  color: var(--ink); }

.section-bg-dark,
.section-bg-primary {
  color: var(--ink-on-inverse);

  --ink:            var(--ink-on-inverse);
  --ink-muted:      var(--ink-on-inverse-muted);
  --ink-subtle:     var(--ink-on-inverse-subtle);
  --rule:           var(--rule-on-inverse);
  --rule-strong:    rgb(251 247 241 / 28%);
  --surface-raised: rgb(251 247 241 / 6%);
  --surface-hover:  rgb(251 247 241 / 11%);
  --accent-text:    var(--accent);
  --shadow-color:   0 0 0;
  --shadow-alpha:   2.2;
}

.section-bg-dark    { background-color: var(--surface-inverse); }
.section-bg-primary { background-color: var(--surface-inverse-lift); }

/* Le footer et le hero sont sombres dans les deux thèmes : ils reçoivent le
   même traitement, sans quoi leurs encres suivraient le thème et
   disparaîtraient. */
.footer,
.hero {
  --ink:            var(--ink-on-inverse);
  --ink-muted:      var(--ink-on-inverse-muted);
  --ink-subtle:     var(--ink-on-inverse-subtle);
  --rule:           var(--rule-on-inverse);
  --surface-raised: rgb(251 247 241 / 6%);
  --surface-hover:  rgb(251 247 241 / 11%);
  --accent-text:    var(--accent);
}
/* ===== layout/editorial.css ===== */
/* ============================================================
   PRIMITIVES ÉDITORIALES

   Trois mises en page qui reviennent dans le récit : le libellé en marge, le
   titre avec son chapeau à droite, et le portrait avec sa biographie.
   ============================================================ */

/* --- Libellé en marge, corps à droite ----------------------- */
/* Sur large écran le kicker quitte le flux du texte et devient une cote en
   marge, comme sur un plan. En dessous il repasse simplement au-dessus. */
.split {
  display: grid;
  gap: var(--space-6);
}

.split__body > .section-title:first-child,
.split__aside > .section-title {
  margin-top: 0;
}

/* Un titre placé dans la marge descend d'un cran : la hiérarchie se lit
   ainsi sans qu'il ait à tenir sur sept caractères par ligne. */
.split__aside .section-title {
  font-size: var(--step-2);
}

@media (min-width: 1000px) {
  .split {
    grid-template-columns: 20rem 1fr;
    gap: var(--space-16);
  }
  .split__aside {
    position: sticky;
    top: calc(80px + var(--space-8));
    align-self: start;
  }

  /* Variante fiche technique : le corps d'abord dans le DOM — c'est l'ordre
     de lecture — et la colonne fixe passe donc à droite. */
  .split--specs {
    grid-template-columns: 1fr 23rem;
  }
}

/* --- En-tête de section : titre à gauche, chapeau à droite --- */
.section-head {
  display: grid;
  gap: var(--space-5);
  margin-bottom: var(--space-12);
}

.section-head .section-title {
  margin-bottom: 0;
}

.section-head__lede {
  margin: 0;
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}

@media (min-width: 900px) {
  .section-head {
    grid-template-columns: 1fr 26rem;
    align-items: end;
    gap: var(--space-12);
  }
}

/* --- Portrait et biographie --------------------------------- */
.bio {
  display: grid;
  gap: var(--space-8);
  align-items: start;
}

.bio__portrait img {
  width: 100%;
  max-width: 300px;
  height: auto;
  border-radius: var(--radius-lg);
  object-fit: cover;
  box-shadow: var(--shadow-lg);
}

@media (min-width: 900px) {
  .bio {
    grid-template-columns: 300px 1fr;
    gap: var(--space-16);
  }
}

/* --- Bande d'appel à l'action ------------------------------- */
.cta-row {
  display: grid;
  gap: var(--space-6);
  align-items: center;
}

.cta-row .section-title {
  margin-bottom: var(--space-3);
}

.cta-row__text {
  margin: 0;
  font-size: var(--step-1);
  line-height: var(--leading-normal);
  max-width: var(--measure);
}

@media (min-width: 900px) {
  .cta-row {
    grid-template-columns: 1fr auto;
    gap: var(--space-12);
  }
}

/* --- Chapeau de section ------------------------------------- */
.lede {
  margin: 0 0 var(--space-5);
  font-size: var(--step-1);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-snug);
  color: var(--ink);
  max-width: var(--measure);
}

/* --- Éléments de page de références ------------------------- */
/* Rendu du logo : components/logo-plate.css. */
.case-logo {
  display: block;
  height: 34px;
  margin: var(--space-5) 0 0;
}

.case-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  margin: var(--space-8) 0 0;
  padding-top: var(--space-5);
  border-top: 1px solid var(--rule);
}

.case-links a {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--accent-text);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.case-links a:hover {
  border-color: var(--accent);
}

.demos__caveat {
  margin-top: var(--space-8);
}

/* --- Sous-parties d'un cas détaillé ------------------------- */
.case-part {
  margin-top: var(--space-10);
}

.case-part__title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--font-weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
}
/* ===== layout/page-head.css ===== */
/* ============================================================
   EN-TÊTE DE PAGE

   Les pages intérieures n'ont pas besoin d'un hero pleine hauteur : le
   visiteur y arrive avec une intention. Un en-tête posé, avec fil d'Ariane et
   sommaire, l'amène plus vite à ce qu'il cherche.
   ============================================================ */

.page-head {
  padding-block-end: calc(var(--section-pad) * 0.6);
  border-bottom: 1px solid var(--rule);
}

.page-head__grid {
  display: grid;
  gap: var(--space-10);
}

.page-head__title {
  margin: var(--space-5) 0 var(--space-6);
  font-family: var(--font-display);
  font-size: var(--step-5);
  font-weight: var(--font-weight-extrabold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
  max-width: 18em;
}

.page-head__title em {
  font-style: normal;
  color: var(--accent-text);
}

.page-head__lede {
  margin: 0;
  font-size: var(--step-1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}

@media (min-width: 1000px) {
  .page-head__grid {
    grid-template-columns: 1fr 21rem;
    gap: var(--space-16);
    align-items: start;
  }
}

/* --- Fil d'Ariane ------------------------------------------- */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.breadcrumb a {
  color: var(--ink-muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.breadcrumb a:hover {
  color: var(--ink);
  border-color: var(--rule-strong);
}

.breadcrumb [aria-current="page"] { color: var(--accent-text); }
.breadcrumb [aria-hidden="true"]  { color: var(--rule-strong); }

/* --- Sommaire / encart de marge ----------------------------- */
.toc {
  padding: var(--space-6);
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
}

.toc__label {
  margin: 0 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

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

.toc__list li + li { border-top: 1px solid var(--rule); }

.toc__list a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  text-decoration: none;
  color: inherit;
}

.toc__list a:hover .toc__name { color: var(--accent-text); }

.toc__num {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  color: var(--accent-text);
}

.toc__name {
  flex-grow: 1;
  font-size: var(--step--1);
  font-weight: var(--font-weight-semibold);
  transition: color var(--transition-fast);
}

.toc__note {
  margin: var(--space-4) 0 0;
  font-size: var(--step--2);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

/* Le bandeau de logos dans l'encart : plus serré qu'en pleine largeur. */
.partners-strip--compact {
  justify-content: flex-start;
  gap: var(--space-5);
}

.partners-strip--compact .partner-logo { height: 26px; }
/* ===== components/navbar.css ===== */
/* ============================================================
   NAVBAR
   Animation concept: scaleX underline (from MPINavbar component)
   ============================================================ */

.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-sticky);
  padding-block: var(--space-4);
  transition: background var(--transition-base),
              box-shadow var(--transition-base),
              padding var(--transition-base);
}

/* `--solid` : l'état posé, pour les pages sans hero sombre derrière la barre.
   Sans lui, les pages légales, le 404 et les pages intérieures affichaient des
   liens blancs sur une première section crème — illisibles avant tout
   défilement. Il partage le rendu de `.scrolled`, d'où le `:is()` ci-dessus. */
.navbar.transparent {
  background: linear-gradient(
    to bottom,
    rgba(20, 20, 18, 0.65) 0%,
    rgba(20, 20, 18, 0.0) 100%
  );
}

.navbar:is(.scrolled, .navbar--solid) {
  background: color-mix(in srgb, var(--surface-page) 96%, transparent);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-sm);
  padding-block: var(--space-3);
}

.navbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}

/* Bascule, CTA et burger forment un bloc à droite. Sans ce groupe, avec
   `space-between` et les liens masqués sous 768 px, la bascule se retrouvait
   au milieu de la barre. */
.navbar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

/* --- Logo --- */
.navbar__logo {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--font-weight-extrabold);
  color: var(--color-white);
  letter-spacing: -0.02em;
  transition: color var(--transition-fast);
  flex-shrink: 0;
  text-decoration: none;
}

.navbar:is(.scrolled, .navbar--solid) .navbar__logo {
  color: var(--color-primary);
}

.navbar__logo span {
  color: var(--color-secondary);
}

/* Logo SVG inline — trait adaptatif selon état navbar */
.navbar__logo-svg path {
  stroke: var(--ink);
  transition: stroke 0.25s ease;
}
/* Contexte transparent (landing hero sombre) : blanc au repos, anthracite après scroll */
.navbar.transparent .navbar__logo-svg path {
  stroke: var(--ink-on-inverse);
}
.navbar.transparent.scrolled .navbar__logo-svg path {
  stroke: var(--ink);
}

/* Logo SVG inline — footer fond sombre */
.footer__logo .navbar__logo-svg path {
  stroke: var(--ink-on-inverse-muted);
}

/* --- Nav links --- */
.navbar__links {
  display: none;
  align-items: center;
  gap: var(--space-8);
}

/* Link base — force color in all states */
.navbar__links > li > a,
.navbar__links > li > a:visited,
.navbar__links > li > a:hover,
.navbar__links > li > a:focus,
.navbar__links > li > a:active {
  position: relative;
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  color: var(--ink-on-inverse);
  text-decoration: none;
  padding-block: 0.15rem;
  transition: color var(--transition-fast);
}

/* Soulignement en scaleX — l'animation signature de la barre.
   Toutes ces règles visent `.navbar__links > li > a`, le PREMIER niveau
   seulement. Écrites en `.navbar__links a`, elles attrapaient aussi les liens
   du sous-menu « Expertises » : plus spécifiques que les règles du panneau,
   elles les passaient en crème au survol et une fois visités — crème sur le
   blanc du panneau en thème clair, donc illisibles. */
.navbar__links > li > a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -4px;
  width: 100%;
  height: 2px;
  background: var(--accent);
  border-radius: var(--radius-full);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--transition-base);
}

.navbar__links > li > a:hover::after,
.navbar__links > li > a.active::after,
.navbar__links > li > a[aria-current]::after {
  transform: scaleX(1);
}

/* Scrolled state: dark text */
.navbar:is(.scrolled, .navbar--solid) .navbar__links > li > a,
.navbar:is(.scrolled, .navbar--solid) .navbar__links > li > a:visited {
  color: var(--color-text-muted);
}

.navbar:is(.scrolled, .navbar--solid) .navbar__links > li > a:hover {
  color: var(--color-primary);
}

/* --- CTA button in navbar --- */
.navbar__cta {
  display: none;
}

/* --- Burger button --- */
.navbar__burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 36px;
  height: 36px;
  padding: 6px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color var(--transition-fast);
}

.navbar:is(.scrolled, .navbar--solid) .navbar__burger {
  border-color: var(--color-border);
}

.navbar__burger span {
  display: block;
  height: 2px;
  background: var(--color-white);
  border-radius: var(--radius-full);
  transition: transform var(--transition-base),
              opacity var(--transition-base),
              background var(--transition-fast);
}

.navbar:is(.scrolled, .navbar--solid) .navbar__burger span {
  background: var(--color-text);
}

.navbar__burger[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.navbar__burger[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.navbar__burger[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* --- Mobile menu (slide from right) --- */
.navbar__mobile {
  position: fixed;
  inset: 0;
  top: 64px;
  background: var(--color-bg);
  padding: var(--space-6);
  transform: translateX(100%);
  /* Fermé : hors flux de tabulation et d'arbre d'accessibilité. `visibility`
     plutôt que `display` pour conserver la transition de glissement. */
  visibility: hidden;
  transition: transform var(--transition-base), visibility 0s linear 250ms;
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  gap: 0;
  box-shadow: var(--shadow-lg);
  overflow-y: auto;
}

.navbar__mobile.open {
  transform: translateX(0);
  visibility: visible;
  transition: transform var(--transition-base), visibility 0s;
}

/* `:not(.btn)` : ces règles de lien s'appliquaient aussi au bouton du panneau,
   dont elles écrasaient la couleur — `.navbar__mobile a` (0,1,1) bat
   `.btn-primary` (0,1,0). « Me contacter » y tombait à 1,22:1 en thème clair,
   crème sur crème en sombre. */
.navbar__mobile a:not(.btn) {
  position: relative;
  font-size: var(--text-lg);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  text-decoration: none;
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--color-border);
  transition: color var(--transition-fast);
}

/* scaleX underline on mobile links too */
.navbar__mobile a:not(.btn)::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: var(--color-secondary);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--transition-base);
}

.navbar__mobile a:not(.btn):hover {
  color: var(--color-primary);
}

.navbar__mobile a:not(.btn):hover::after,
.navbar__mobile a:not(.btn).active::after,
.navbar__mobile a:not(.btn)[aria-current]::after {
  transform: scaleX(1);
}

.navbar__mobile .btn {
  margin-top: var(--space-6);
}

/* --- Mobile overlay (close on tap outside) --- */
.navbar__overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: calc(var(--z-dropdown) - 1);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-base);
}

.navbar__overlay.open {
  opacity: 1;
  pointer-events: auto;
}

/* --- Scroll-to-top button --- */
.scroll-top-btn {
  position: fixed;
  right: var(--space-6);
  bottom: var(--space-6);
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  border: none;
  cursor: pointer;
  background: var(--color-secondary);
  color: var(--color-white);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: var(--z-toast);

  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: opacity var(--transition-fast),
              transform var(--transition-fast);
  box-shadow: var(--shadow-md);
}

.scroll-top-btn.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.scroll-top-btn:hover {
  background: var(--color-secondary-dark);
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}

.scroll-top-btn svg {
  width: 20px;
  height: 20px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
}

/* ============================================================
   LANG SWITCHER
   ============================================================ */

.lang-switcher {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  flex-shrink: 0;
}

.lang-switcher span[aria-hidden] {
  color: rgba(255, 255, 255, 0.35);
  line-height: 1;
}

.navbar:is(.scrolled, .navbar--solid) .lang-switcher span[aria-hidden] {
  color: var(--color-border);
}

.lang-switcher__item {
  color: rgba(255, 255, 255, 0.65);
  text-decoration: none;
  padding: 0.2rem 0.35rem;
  border-radius: 4px;
  transition: color var(--transition-fast);
  letter-spacing: 0.04em;
}

.lang-switcher__item:hover {
  color: var(--color-white);
}

.lang-switcher__item.is-active {
  color: var(--color-secondary);
  font-weight: var(--font-weight-bold);
  pointer-events: none;
  cursor: default;
}

.navbar:is(.scrolled, .navbar--solid) .lang-switcher__item {
  color: var(--color-text-light);
}

.navbar:is(.scrolled, .navbar--solid) .lang-switcher__item:hover {
  color: var(--color-primary);
}

.navbar:is(.scrolled, .navbar--solid) .lang-switcher__item.is-active {
  color: var(--accent-text);
}

/* Mobile variant */
.lang-switcher--mobile {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
  justify-content: center;
  font-size: var(--text-base);
}

.lang-switcher--mobile .lang-switcher__item {
  color: var(--color-text-light);
  padding: 0.3rem 0.6rem;
}

.lang-switcher--mobile .lang-switcher__item.is-active {
  color: var(--color-primary);
}

.lang-switcher--mobile span[aria-hidden] {
  color: var(--color-border);
}

/* ============================================================
   Bascule mobile / desktop — point unique
   ------------------------------------------------------------
   Les règles de base (plus haut) sont déjà mobile-first : liens et CTA
   masqués, burger visible. Seul le CTA a besoin d'un renfort : il porte
   .btn ET .navbar__cta, et button.css étant importé après navbar.css,
   `.btn { display:inline-flex }` gagnait à spécificité égale.
   `.navbar__inner` monte la spécificité à 0-2-0 pour reprendre la main.

   Bascule à 768 px. Elle était à 992 px quand la barre portait six entrées,
   un sélecteur de langue et « Accueil ». Avec quatre entrées — les quatre
   services regroupés sous « Expertises » — le menu horizontal tient bien
   avant, et le burger disparaît donc plus tôt.
   ============================================================ */

.navbar__inner .navbar__cta { display: none; }

@media (min-width: 768px) {
  .navbar__inner .navbar__links  { display: flex; }
  .navbar__inner .navbar__cta    { display: inline-flex; }
  .navbar__inner .navbar__burger { display: none; }

  /* Au-delà du burger, le menu mobile n'a plus lieu d'être : sans cela il
     reste dans l'ordre de tabulation et les lecteurs d'écran annoncent la
     navigation deux fois. */
  .navbar__mobile,
  .navbar__overlay { display: none; }
}

/* ============================================================
   GROUPE DÉROULANT — « Expertises »

   Un bouton, pas un survol : au clavier comme au toucher, un menu qui ne
   s'ouvre qu'au survol est inatteignable. Les liens du sous-menu restent dans
   le HTML en permanence, donc explorés depuis chaque page — c'est tout
   l'intérêt du regroupement pour le maillage interne.
   ============================================================ */

.navbar__group {
  position: relative;
}

/* Le bouton doit être indiscernable d'un lien de la barre. */
.navbar__group-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  position: relative;
  padding: 0;
  padding-block: 0.15rem;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  color: var(--ink-on-inverse);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color var(--transition-fast);
}

.navbar:is(.scrolled, .navbar--solid) .navbar__group-toggle {
  color: var(--color-text-muted);
}

.navbar__group-toggle::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -4px;
  width: 100%;
  height: 2px;
  background: var(--accent);
  border-radius: var(--radius-full);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--transition-base);
}

.navbar__group-toggle:hover::after,
.navbar__group-toggle.is-active::after,
.navbar__group-toggle[aria-expanded="true"]::after {
  transform: scaleX(1);
}

.navbar__group-toggle:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 4px;
}

.navbar__chevron {
  width: 14px;
  height: 14px;
  transition: transform var(--transition-base);
}

.navbar__group-toggle[aria-expanded="true"] .navbar__chevron {
  transform: rotate(180deg);
}

/* Fermé : hors flux de tabulation et d'arbre d'accessibilité. */
.navbar__submenu {
  display: none;
  position: absolute;
  top: calc(100% + var(--space-4));
  left: calc(-1 * var(--space-4));
  min-width: 15rem;
  margin: 0;
  padding: var(--space-2);
  list-style: none;
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.navbar__group.is-open .navbar__submenu {
  display: block;
}

.navbar__submenu a {
  display: block;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  font-weight: var(--font-weight-medium);
  color: var(--ink);
  text-decoration: none;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.navbar__submenu a:hover {
  background: var(--surface-hover);
}

/* Le sous-lien de la page courante porte l'accent, et c'est lui qui a
   l'aria-current — le bouton du groupe se contente d'une classe. */
.navbar__submenu a[aria-current="page"] {
  color: var(--accent-text);
  background: var(--surface-hover);
}

.navbar__submenu a:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: -3px;
}
/* ===== components/button.css ===== */
/* ============================================================
   BUTTONS
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  border-radius: var(--radius-md);
  border: 2px solid transparent;
  cursor: pointer;
  transition: all var(--transition-base);
  text-decoration: none;
  white-space: nowrap;
}

.btn:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* Primary */
.btn-primary {
  background: var(--btn-solid-bg);
  color: var(--btn-solid-ink);
  border-color: var(--btn-solid-bg);
}

.btn-primary:hover {
  background: var(--btn-solid-bg-hover);
  border-color: var(--btn-solid-bg-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

/* Secondary */
/* Le blanc sur orange n'atteignait que 3,00:1 : le CTA principal du site
   échouait au critère AA, sur vingt occurrences. L'encre graphite y tient
   5,80:1. */
.btn-secondary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

.btn-secondary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

/* Outline */
/* Le contour lit --ink, que les tons sombres redéfinissent : il s'adapte
   donc à la section qui le contient, ce qui est l'effet voulu. */
.btn-outline {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}

.btn-outline:hover {
  background: var(--ink);
  color: var(--surface-page);
  transform: translateY(-1px);
}

/* Outline white (for dark backgrounds) */
.btn-outline-white {
  background: rgba(255, 255, 255, 0.12);
  color: var(--color-white);
  border-color: rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.btn-outline-white:hover {
  background: var(--color-white);
  color: var(--color-primary);
  border-color: var(--color-white);
}

/* Ghost */
.btn-ghost {
  background: transparent;
  color: var(--color-text-muted);
  border-color: transparent;
}

.btn-ghost:hover {
  background: var(--color-surface);
  color: var(--color-text);
}

/* Sizes */
.btn-sm {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-xs);
}

.btn-lg {
  padding: var(--space-4) var(--space-8);
  font-size: var(--text-base);
  border-radius: var(--radius-lg);
}

/* Full width */
.btn-full { width: 100%; }

/* Loading state */
.btn[disabled],
.btn.loading {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none !important;
}
/* ===== components/hero.css ===== */
/* ============================================================
   HERO

   Anciennement slider.css. Le carrousel n'a plus lieu d'être : le hero n'a
   jamais eu qu'une slide, son module JS était neutralisé par son propre
   garde-fou, et les enveloppes `hero__slides` / `hero__slide` ne servaient
   plus qu'à porter le fond. Points, flèches, effet Ken Burns et l'animation
   de flottement du logo sont partis avec.
   ============================================================ */

.hero {
  position: relative;
  /* Une hauteur MINIMALE, pas une hauteur : le contenu doit pouvoir la
     dépasser. Avec `height: 100svh` et `overflow: hidden`, un portable de
     768 px de haut coupait les tags et les logos clients en bas du hero. */
  min-height: min(100svh, 880px);
  overflow: hidden;
  padding-block: 0 var(--space-12); /* annule la respiration globale, garde de l'air sous les logos */
  display: flex;
  align-items: center;
}

/* La section qui suit le hero garde moins d'air en haut — la rupture de ton
   fait déjà la séparation — mais reste proportionnée au rythme général. */
.hero + section {
  padding-block-start: calc(var(--section-pad) * 0.7);
}

/* Fond : un dégradé de graphite, plus une surcouche qui garantit le contraste
   du texte quel que soit le dégradé posé en attribut de style. */
.hero__bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

.hero__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    rgb(26 26 24 / 78%) 0%,
    rgb(47 46 44 / 45%) 60%,
    rgb(0 0 0 / 18%) 100%
  );
}

.hero__content {
  position: relative;
  z-index: var(--z-raised);
  padding-top: 80px; /* dégagement sous la barre fixe */
  width: 100%;
}

.hero__layout {
  position: relative; /* ancre du motif, posé en arrière-plan */
  width: 100%;
}

.hero__text {
  position: relative;
  z-index: 1;
  /* Pas de largeur maximale ici : chaque enfant borne la sienne — le titre en
     em, le sous-titre à la mesure de lecture. Plafonner le conteneur coupait
     le titre de l'accueil en trois lignes au lieu de deux. */
}

/* Le motif passe DERRIÈRE le texte au lieu d'occuper une colonne. En colonne,
   il comprimait le titre sur quatre lignes au lieu des deux de la maquette. */
.hero__visual {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 0;
  opacity: 0.6;
  pointer-events: none;
}

/* ---- Contenu ---- */
.hero__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-5);
}

.hero__title {
  font-family: var(--font-display);
  /* --step-5, soit ~59 px au plus : l'échelle du site d'origine (60 px). Les
     titres des pages de service sont des phrases ; à 74 px, celui d'Odoo
     tenait sur quatre lignes.
     Plafonné pour qu'un mot de 8 em tienne dans la largeur utile — l'écran
     moins les deux marges du conteneur —, sans quoi le navigateur le coupe
     en deux : « géographiques » (7,7 em) se brisait à 320 px. Le plafond ne
     joue que sous 367 px ; au-delà, --step-5 est plus petit que lui. */
  font-size: min(var(--step-5), calc((100vw - 2 * var(--container-pad)) / 8));
  font-weight: var(--font-weight-extrabold);
  color: var(--ink-on-inverse);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  margin-bottom: var(--space-5);
  max-width: 15em;
}

/* Le slogan de l'accueil, lui, est un titre d'affiche : il garde la taille
   maximale de l'échelle. Même plafond, à 7,5 em : « l'organisation, »
   (7,18 em) débordait de 320 à 390 px, et la virgule finissait seule sur sa
   ligne. Sans effet au-dessus de 407 px. */
.hero__title--display {
  font-size: min(var(--step-6), calc((100vw - 2 * var(--container-pad)) / 7.5));
}

/* Sur le graphite du hero, l'orange plein tient 5,80:1 : pas besoin de la
   variante assombrie réservée aux fonds clairs. */
.hero__title em {
  font-style: normal;
  color: var(--accent);
}

.hero__subtitle {
  font-size: var(--step-1);
  color: var(--ink-on-inverse-muted);
  line-height: var(--leading-normal);
  margin-bottom: var(--space-8);
  max-width: var(--measure);
}

.hero__note {
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-on-inverse-subtle);
  margin: calc(-1 * var(--space-5)) 0 var(--space-8);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

/* ---- Motif de courbes de niveau ----
   Remplace l'illustration. Inline, donc rien sur le chemin critique : le LCP
   redevient le titre. */
.hero__plot {
  width: clamp(260px, 34vw, 520px);
  height: auto;
  overflow: visible;
}

.hero__plot-grid path {
  fill: none;
  stroke: var(--rule-on-inverse);
  stroke-width: 1;
  stroke-dasharray: 3 7;
}

.hero__plot-contours path {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.2;
  opacity: 0.4;
}

.hero__plot-points circle {
  fill: var(--accent);
}

@media (max-width: 900px) {
  .hero__visual {
    top: auto;
    bottom: 0;
    right: -12%;
    transform: none;
    opacity: 0.35;
  }
  .hero__plot { width: clamp(240px, 70vw, 420px); }
}
/* ===== components/card.css ===== */
/* ============================================================
   CARDS
   ============================================================ */

.card {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  transition: box-shadow var(--transition-base), transform var(--transition-base);
}

/* Hover limité aux cartes réellement cliquables/interactives.
   card-info et card-step sont des blocs de contenu statique — pas de lift. */

/* ---- Service card ---- */
.card-service {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.card-service__icon {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-md);
  background: rgba(233, 116, 35, 0.10);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  flex-shrink: 0;
}

.card-service__title {
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
}

.card-service__text {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

/* ---- Testimonial card ---- */

/* ---- Persona card — 3 portes d'entrée ---- */
.card-persona {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  height: 100%;
}

.card-persona__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.card-persona__list li {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
  padding-left: var(--space-4);
  position: relative;
}

.card-persona__list li::before {
  content: "—";
  position: absolute;
  left: 0;
  color: var(--accent-text);
}

.card-persona__footer {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.card-persona__cta {
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--accent-text);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  transition: color var(--transition-fast), gap var(--transition-base);
}

.card-persona__cta:hover {
  color: var(--ink);
  gap: var(--space-3);
}

/* ---- Step card — étapes numérotées ---- */
.card-step {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
}

.card-step__number {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-light);
  letter-spacing: 0.08em;
}

.card-step__title {
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
  margin: 0;
}

.card-step__duration {
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--accent-text);
}

.card-step__text {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
  margin: 0;
}

/* ---- Info card — blocs latéraux (stack technique, pourquoi ça marche, etc.) ---- */
.card-info {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}

.card-info__label {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-4);
}

.card-info__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.card-info__list li {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

.card-info__list li strong {
  color: var(--color-text);
  font-weight: var(--font-weight-semibold);
}
/* ===== components/faq.css ===== */
/* ============================================================
   FAQ ACCORDION
   ============================================================ */

.faq-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 760px;
  margin: 0 auto;
}

.faq-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-card);
  transition: box-shadow var(--transition-base);
}

.faq-item.open {
  box-shadow: var(--shadow-sm);
}

.faq-question {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  font-size: var(--text-base);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  transition: color var(--transition-fast);
}

.faq-question:hover {
  color: var(--color-primary);
}

.faq-item.open .faq-question {
  color: var(--color-primary);
}

.faq-icon {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background var(--transition-fast), transform var(--transition-base);
  font-size: var(--text-lg);
  color: var(--color-text-muted);
  line-height: 1;
}

.faq-item.open .faq-icon {
  background: var(--color-primary);
  color: var(--color-white);
  transform: rotate(45deg);
}

.faq-answer {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--transition-slow);
}

.faq-answer__inner {
  padding: 0 var(--space-6) var(--space-5);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-loose);
}
/* ===== components/form.css ===== */
/* ============================================================
   FORM
   ============================================================ */

.form {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.form-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}

@media (min-width: 640px) {
  .form-row { grid-template-columns: 1fr 1fr; }
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.form-label {
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
}

.form-label .required {
  color: var(--danger);
  margin-left: 2px;
}

.form-input,
.form-textarea,
.form-select {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--color-text);
  background: var(--color-card);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
  appearance: none;
}

.form-input::placeholder,
.form-textarea::placeholder {
  color: var(--color-text-muted);
}

.form-input:focus,
.form-textarea:focus,
.form-select:focus {
  border-color: var(--color-primary-light);
  box-shadow: 0 0 0 3px rgba(47, 46, 44, 0.1);
}

.form-input.error,
.form-textarea.error {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(229, 62, 62, 0.1);
}

.form-input.valid,
.form-textarea.valid {
  border-color: var(--color-tertiary);
  box-shadow: 0 0 0 3px rgb(45 95 63 / 12%);
}

.form-textarea {
  min-height: 140px;
  resize: vertical;
}

.form-error {
  font-size: var(--text-xs);
  color: #c62828;   /* 5,2:1 sur crème — var(--danger) plafonnait à 3,9:1 */
  display: none;
}

.form-error.visible {
  display: block;
}

.form-helper {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: var(--space-1);
}

.form-rgpd {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
  margin-top: var(--space-3);
}

.form-rgpd a {
  color: var(--color-primary);
  text-decoration: underline;
}

/* Honeypot - invisible to humans */
.form-honeypot {
  position: absolute;
  left: -9999px;
  opacity: 0;
  pointer-events: none;
}

/* ---- Form feedback messages ---- */
.form-feedback {
  display: none;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  align-items: center;
  gap: var(--space-3);
}

.form-feedback.visible {
  display: flex;
}

.form-feedback.success {
  background: rgb(45 95 63 / 8%);
  color: var(--color-tertiary);
  border: 1px solid rgb(45 95 63 / 25%);
}

.form-feedback.error {
  background: rgba(229, 62, 62, 0.08);
  color: #c53030;
  border: 1px solid rgba(229, 62, 62, 0.25);
}

/* ---- Radio buttons stylés en cartes (formulaire segmenté) ---- */
.form-radio-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.form-radio-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-card);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.form-radio-card:hover {
  border-color: var(--color-primary-soft);
}

.form-radio-card input[type="radio"] {
  margin-top: 3px;
  flex-shrink: 0;
  accent-color: var(--color-secondary);
}

.form-radio-card:has(input[type="radio"]:checked) {
  border-color: var(--color-secondary);
  background: rgba(233, 116, 35, 0.04);
}

.form-radio-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.form-radio-card__title {
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

.form-radio-card__desc {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

/* ---- Contact section layout ---- */
.contact-wrapper {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-12);
}

@media (min-width: 1024px) {
  .contact-wrapper { grid-template-columns: 1fr 1.5fr; }
}

.contact-info {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.contact-info__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}

.contact-info__icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: rgba(47, 46, 44, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex-shrink: 0;
}

.contact-info__label {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}

.contact-info__value {
  font-size: var(--text-base);
  color: var(--color-text);
  font-weight: var(--font-weight-medium);
}

/* ---- Engagement grid — 3 cartes courtes en ligne (section contact) ---- */
.engagement-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-8);
}

@media (min-width: 640px) {
  .engagement-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.engagement-card {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.engagement-card__icon {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: rgba(233, 116, 35, 0.10);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.engagement-card__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.engagement-card__label {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  font-weight: var(--font-weight-medium);
}

.engagement-card__value {
  font-size: var(--text-base);
  color: var(--color-text);
  font-weight: var(--font-weight-bold);
}
/* ===== components/footer.css ===== */
/* ============================================================
   FOOTER
   ============================================================ */

.footer {
  background: var(--color-primary-dark);
  color: rgba(255, 255, 255, 0.75);
  padding-top: var(--space-16);
  padding-bottom: var(--space-8);
}

.footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-10);
  margin-bottom: var(--space-12);
}

@media (min-width: 640px) {
  .footer__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .footer__grid { grid-template-columns: 2fr 1fr 1fr 1fr; }
}

.footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Le logo seul sur mp-i.pro, accompagné du wordmark sur le blog : les règles
   typographiques servent encore à ce dernier. `inline-block` + `line-height: 0`
   suppriment l'espace de baseline sous l'image quand le texte est absent. */
.footer__logo {
  display: inline-block;
  line-height: 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--font-weight-extrabold);
  color: var(--color-white);
  letter-spacing: -0.02em;
}

.footer__logo span { color: var(--color-secondary); }

.footer__tagline {
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  max-width: 280px;
}

.footer__social {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.footer__social-link {
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.7);
  font-size: var(--text-sm);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.footer__social-link:hover {
  background: var(--color-primary);
  color: var(--color-white);
}

.footer__col-title {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-white);
  margin-bottom: var(--space-4);
}

.footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.footer__links a {
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.6);
  transition: color var(--transition-fast);
}

.footer__links a:hover {
  color: var(--color-white);
}

.footer__bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  padding-top: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: center;
  text-align: center;
}

@media (min-width: 768px) {
  .footer__bottom {
    flex-direction: row;
    justify-content: space-between;
    text-align: left;
  }
}

.footer__copy {
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.4);
}

.footer__legal {
  display: flex;
  gap: var(--space-5);
}

.footer__legal a {
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.4);
  transition: color var(--transition-fast);
}

.footer__legal a:hover {
  color: rgba(255, 255, 255, 0.7);
}

/* Cookie consent trigger — styled identically to footer links */
.footer__legal-btn {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-size: var(--text-xs);
  font-family: inherit;
  color: rgba(255, 255, 255, 0.4);
  transition: color var(--transition-fast);
}

.footer__legal-btn:hover {
  color: rgba(255, 255, 255, 0.7);
}

/* CTA Banner */
.cta-banner {
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  padding-block: var(--space-16);
  text-align: center;
  position: relative;
  overflow: hidden;
}

.cta-banner::before {
  content: '';
  position: absolute;
  top: -50%;
  right: -10%;
  width: 500px;
  height: 500px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.04);
  pointer-events: none;
}

.cta-banner .section-title { color: var(--color-white); }
.cta-banner .section-subtitle { color: rgba(255,255,255,0.75); margin: 0 auto var(--space-8); }

.partners-label {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: center;
  margin-bottom: var(--space-6);
}
/* ===== components/kicker.css ===== */
/* ============================================================
   KICKER — libellé de section

   Remplace la pilule `.section-label` (fond teinté, coins arrondis), qui
   était la signature visuelle de n'importe quel template. Ici c'est du mono
   encré : le même rôle, mais qui dit « ingénieur » plutôt que « landing ».
   ============================================================ */

.kicker,
.section-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: var(--space-4);
  /* Annule la pilule : ces pages portent encore `.section-label`. */
  background: none;
  padding: 0;
  border-radius: 0;
}

/* Variante avec filet — le libellé ouvre la section comme une cote de plan. */
.kicker--rule {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
}

.kicker--rule::after {
  content: "";
  flex-grow: 1;
  height: 1px;
  background: var(--rule);
}
/* ===== components/theme-toggle.css ===== */
/* ============================================================
   BASCULE DE THÈME
   ============================================================ */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  padding: 0;
  background: transparent;
  border: 1px solid var(--rule-on-inverse);
  border-radius: var(--radius-md);
  color: var(--ink-on-inverse);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast),
              color var(--transition-fast);
}

.theme-toggle:hover {
  background: rgb(251 247 241 / 10%);
}

.theme-toggle:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* Après défilement la navbar passe en fond de page : la bascule suit. */
.navbar:is(.scrolled, .navbar--solid) .theme-toggle {
  border-color: var(--rule);
  color: var(--ink);
}

.navbar:is(.scrolled, .navbar--solid) .theme-toggle:hover {
  background: var(--surface-hover);
}

.theme-toggle svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Une seule des deux icônes est visible : le soleil propose le thème clair,
   la lune le thème sombre. C'est l'ACTION qui est montrée, pas l'état. */
.theme-toggle__moon { display: block; }
.theme-toggle__sun  { display: none; }

:root[data-theme="dark"] .theme-toggle__moon { display: none; }
:root[data-theme="dark"] .theme-toggle__sun  { display: block; }


/* Le panneau mobile est sur fond de page : la bascule qu'il contient prend
   les encres du thème, pas celles des sections sombres. */
.navbar__mobile .theme-toggle {
  border-color: var(--rule);
  color: var(--ink);
  margin-top: var(--space-4);
}

.navbar__mobile .theme-toggle:hover {
  background: var(--surface-hover);
}
/* ===== components/lang-switch.css ===== */
/* ============================================================
   SÉLECTEUR DE LANGUE — FR / EN

   Il avait été retiré de la barre quand /en/ n'était qu'une page « coming
   soon » en noindex : il menait à une impasse. /en/ est désormais un vrai
   résumé du site en anglais ; le sélecteur revient.

   Seule l'accueil a son équivalent anglais. Depuis une page de service, EN
   mène donc au résumé, pas à une traduction de la page — la pratique
   courante d'un site partiellement traduit.

   Nom neuf, et non `.lang-switcher` : l'ancienne classe est peut-être encore
   employée par le blog, qui partage assets/.
   ============================================================ */

.lang-switch {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.1em;
}

/* 40 px, comme la bascule de thème voisine : une cible tactile confortable
   pour un lien qui n'affiche que deux lettres. */
.lang-switch__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.5rem;
  min-height: 2.5rem;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--ink-on-inverse-muted);
  transition: color var(--transition-fast), background var(--transition-fast);
}

/* La langue courante n'est pas un lien : on ne se déplace pas vers la page où
   l'on est déjà. Sur fond sombre l'orange plein tient 5,80:1. */
.lang-switch__item.is-current {
  color: var(--accent);
}

a.lang-switch__item:hover {
  color: var(--ink-on-inverse);
  background: rgb(251 247 241 / 10%);
}

a.lang-switch__item:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.lang-switch__sep {
  color: var(--ink-on-inverse-subtle);
}

/* Barre posée ou défilée : fond de page, donc encres du thème. */
.navbar:is(.scrolled, .navbar--solid) .lang-switch__item {
  color: var(--ink-muted);
}

.navbar:is(.scrolled, .navbar--solid) .lang-switch__item.is-current {
  color: var(--accent-text);
}

.navbar:is(.scrolled, .navbar--solid) a.lang-switch__item:hover {
  color: var(--ink);
  background: var(--surface-hover);
}

.navbar:is(.scrolled, .navbar--solid) .lang-switch__sep {
  color: var(--ink-subtle);
}
/* ===== components/pull-quote.css ===== */
/* ============================================================
   EXERGUE — filet orange à gauche, comme une annotation de marge
   ============================================================ */

.pull-quote {
  margin: var(--space-8) 0 0;
  padding: var(--space-6) var(--space-8);
  background: var(--surface-sunken);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.pull-quote p {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--font-weight-semibold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
}

.pull-quote cite {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-style: normal;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
}
/* ===== components/entry-list.css ===== */
/* ============================================================
   LISTE ÉDITORIALE — remplace la rangée de quatre cartes égales

   Quatre cartes identiques se lisent comme un menu. Une liste numérotée se
   lit comme un texte, et un LLM en extrait mieux la structure.
   ============================================================ */

.entry-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--rule);
}

.entry {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "num  title"
    ".    lede";
  align-items: baseline;
  gap: var(--space-2) var(--space-5);
  padding: var(--space-6) var(--space-1);
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: inherit;
  transition: background var(--transition-fast);
}

.entry:hover,
.entry:focus-visible {
  background: var(--surface-hover);
}

.entry:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: -3px;
}

.entry__num {
  grid-area: num;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: var(--font-weight-bold);
  color: var(--accent-text);
}

.entry__title {
  grid-area: title;
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
}

/* La phrase de symptôme : en italique, c'est la voix du client, pas la mienne. */
.entry__lede {
  grid-area: lede;
  margin: 0;
  font-size: var(--step-0);
  font-style: italic;
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}

.entry__arrow {
  font-family: var(--font-mono);
  color: var(--accent-text);
  transition: transform var(--transition-base);
}

.entry:hover .entry__arrow {
  transform: translateX(4px);
}

/* Au-delà de 900 px, titre et symptôme passent sur la même ligne : le
   balayage vertical de l'œil sur les titres n'est plus interrompu. */
@media (min-width: 900px) {
  .entry {
    grid-template-columns: auto 20rem 1fr auto;
    grid-template-areas: "num title lede arrow";
    gap: var(--space-8);
    padding-block: var(--space-8);
  }
  .entry__arrow { grid-area: arrow; align-self: center; }
}

/* Variante « articles » : les liens vers le blog, en fin de page de service.
   Pas de phrase de symptôme — ce n'est pas la voix du client : le titre de
   l'article occupe aussi sa colonne, et celle du numéro porte le temps de
   lecture. Les lignes nommées viennent de grid-template-areas, et valent sur
   mobile comme au-delà de 900 px. */
.entry-list--articles .entry__title {
  grid-column: title-start / lede-end;
  font-size: var(--step-1);
}

/* Chaque ligne est sa propre grille : sans largeur commune, le titre d'un
   article de « 7 min » partirait un caractère plus à gauche que ceux de
   « 13 min ». Six caractères de mono, ceux de « 10 min ». */
.entry-list--articles .entry__num {
  min-width: 6ch;
}
/* ===== components/step-rail.css ===== */
/* ============================================================
   RAIL DE MÉTHODE — les étapes le long d'un filet, pas dans des cartes
   ============================================================ */

.step-rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding-left: var(--space-8);
  border-left: 1px solid var(--rule);
}

.step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Le point de jalon. Le premier est orange : il marque l'entrée du parcours. */
.step::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--space-8) - 5px);
  top: 0.45em;
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--rule-strong);
}

.step:first-child::before {
  background: var(--accent);
}

.step__meta {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.step:first-child .step__meta {
  color: var(--accent-text);
}

.step__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: var(--font-weight-bold);
  line-height: var(--leading-tight);
}

.step__text {
  margin: 0;
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}
/* ===== components/spec-table.css ===== */
/* ============================================================
   TABLEAU DE SPECS — clé en mono, valeur en corps

   Ressuscité de `.card-info__table`, supprimé par le commit de nettoyage
   0b3a8cc. C'est le composant qui porte les faits vérifiables d'un produit :
   licence, version, dépôt, moteur. Rien ne crédibilise autant qu'un chiffre
   qu'on peut aller vérifier.
   ============================================================ */

.spec-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
}

.spec-table th,
.spec-table td {
  padding: var(--space-3) 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--rule);
}

.spec-table tr:last-child th,
.spec-table tr:last-child td {
  border-bottom: 0;
}

.spec-table th {
  width: 38%;
  /* Sans marge, un libellé qui remplit sa colonne — « INTÉGRATION » sous
     360 px — touchait la valeur voisine. */
  padding-right: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-normal);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.spec-table td {
  color: var(--ink);
  /* Un identifiant comme github.com/geoleaf/geoleaf-js n'a aucun point de
     coupure : sans `anywhere`, sa largeur devenait le minimum de la fiche,
     puis de la colonne de grille, et la page défilait de côté sous 390 px.
     `anywhere`, et non `break-word`, parce que lui seul réduit la largeur
     minimale du contenu ; il ne coupe que si la place manque vraiment.
     C'est un filet : les identifiants longs portent des <wbr> après leurs
     barres obliques, pour se couper là plutôt qu'au milieu d'un mot. */
  overflow-wrap: anywhere;
}

/* Les identifiants techniques restent en mono : un nom de paquet ou de dépôt
   se lit et se recopie, il ne se prose pas. */
.spec-table code {
  font-family: var(--font-mono);
  font-size: 0.95em;
  color: inherit;
}
/* ===== components/product-card.css ===== */
/* ============================================================
   CARTE PRODUIT — avec pastille de statut

   Ressuscitée de `.eco-brick`, supprimée par 0b3a8cc. La pastille est le
   point important : afficher « en cours » plutôt que « disponible » est ce
   qui rend le reste de la fiche croyable.
   ============================================================ */

.product-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-8);
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
}

/* Le produit qui tourne déjà est mis en avant, pas celui qui se vend. */
.product-card--featured {
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 4px rgb(233 116 35 / 8%);
}

/* `wrap` : sous ~360 px, le titre et la pastille de statut, insécable, ne
   tiennent plus côte à côte ; la pastille passe dessous au lieu d'élargir
   la fiche au-delà de l'écran. */
.product-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}

.product-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
}

.product-card__kind {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-muted);
}

.product-card__text {
  margin: 0;
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

.product-card__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: auto;
  padding-top: var(--space-1);
}

/* Réserve honnête : ce que le produit n'est pas encore. */
.product-card__caveat {
  margin: 0;
  padding-left: var(--space-4);
  border-left: 2px solid var(--rule-strong);
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

/* ---- Pastille de statut ---- */
.status {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  background: var(--surface-hover);
  color: var(--ink-muted);
}

/* Vert : le vert est réservé à la validation, et « en production » en est une. */
.status--live {
  background: var(--valid);
  color: var(--ink-on-inverse);
}

.status--soon {
  background: var(--accent);
  color: var(--accent-ink);
}

.product-card__links a {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--accent-text);
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 2px;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.product-card__links a:hover {
  color: var(--ink);
  border-color: var(--ink);
}
/* ===== components/case-card.css ===== */
/* ============================================================
   CARTE DE CAS — contexte, intervention, ce qu'il en reste

   Le troisième volet est celui qui compte : il dit ce que la mission a
   laissé derrière elle. C'est là que se lit la différence entre un
   prestataire et quelqu'un qui construit.
   ============================================================ */

.case-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.case-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-6);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--rule);
}

.case-card__tags {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Rendu du logo : components/logo-plate.css. */
.case-card__logo {
  height: 22px;
  flex-shrink: 0;
}

.case-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-6);
}

.case-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
}

.case-card__row {
  display: grid;
  gap: var(--space-1) var(--space-4);
}

.case-card__label {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.case-card__value {
  margin: 0;
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

/* Le dernier volet est encré plein : c'est la conclusion, pas du détail. */
.case-card__row--outcome .case-card__value {
  color: var(--ink);
  font-weight: var(--font-weight-medium);
}

@media (min-width: 640px) {
  .case-card__row {
    grid-template-columns: 7.5rem 1fr;
  }
  .case-card__label { padding-top: 0.2em; }
}
/* ===== components/logo-plate.css ===== */
/* ============================================================
   PLAQUE DE LOGO CLIENT

   Trois des cinq logos clients — CMI, SEOR, COTEL — ont un fond blanc opaque
   intégré à l'image. Le hero les passait en `brightness(10)` pour les
   « détourer » en blanc : sur un fond transparent ça marche, sur un fond
   blanc ça donne un rectangle gris. Et ils sont en couleur, avec des parties
   claires — l'oiseau blanc de SEOR, le tourbillon bleu clair de COTEL — qu'un
   détourage par luminance ferait disparaître.

   Chaque logo est donc posé sur une plaque blanche, partout. C'est la seule
   approche qui montre la vraie marque, pour tous les logos, dans les deux
   thèmes : sur une page crème la plaque se fond presque, sur un fond sombre
   elle rend le logo lisible.

   Ce fichier est la seule source du rendu : chaque contexte ne fixe que sa
   hauteur.
   ============================================================ */

.hero__trust-logos img,
.partner-logo,
.case-card__logo,
.case-logo {
  box-sizing: content-box;
  width: auto;
  padding: var(--space-2) var(--space-3);
  background: var(--logo-plate);
  border-radius: var(--radius-sm);
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.8;
  transition: filter var(--transition-base), opacity var(--transition-base);
}

/* La couleur revient au survol : c'est la marque du client, pas un décor. */
.hero__trust-logos img:hover,
.partner-logo:hover,
.case-card:hover .case-card__logo,
.case-logo:hover {
  filter: none;
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hero__trust-logos img,
  .partner-logo,
  .case-card__logo,
  .case-logo {
    transition: none;
  }
}
/* ===== components/deflist.css ===== */
/* ============================================================
   LISTE DE DÉFINITIONS — libellé mono en regard, valeur en corps

   Le même geste que `case-card__row`, mais réutilisable hors carte : c'est la
   forme qui porte le mieux « voici le point, voici ce qu'il signifie ».
   ============================================================ */

.deflist {
  margin: var(--space-8) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.deflist__row {
  display: grid;
  gap: var(--space-1) var(--space-5);
}

.deflist__label {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.deflist__value {
  margin: 0;
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}

.deflist__value code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  white-space: nowrap;
}

/* Variante d'encart de marge : plus resserrée, valeurs courtes. */
.deflist--tight {
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.deflist--tight .deflist__value { font-size: var(--step--1); }

@media (min-width: 700px) {
  .deflist__row { grid-template-columns: 9rem 1fr; }
  .deflist__label { padding-top: 0.25em; }
  .deflist--tight .deflist__row { grid-template-columns: 1fr; }
}

/* Un lien dans une valeur doit se voir comme tel : sans soulignement, le
   courriel et LinkedIn de la page contact passaient pour du texte. */
.deflist__value a {
  color: var(--accent-text);
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.deflist__value a:hover {
  color: var(--ink);
  border-color: var(--ink);
}
/* ===== components/callout.css ===== */
/* ============================================================
   ENCART — une réserve, ou une note d'état

   Sert à dire ce que le produit n'est pas encore. Une fiche produit n'est
   croyable que si elle porte aussi ses limites.
   ============================================================ */

.callout {
  margin: var(--space-8) 0 0;
  padding: var(--space-5) var(--space-6);
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--rule-strong);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.callout__label {
  margin: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.callout__text {
  margin: 0;
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}

/* Variante « à compléter » : un trou de contenu assumé, visible en relecture
   plutôt que masqué par du texte inventé. */
.callout--todo {
  border-left-color: var(--accent);
  border-style: dashed;
  border-left-style: solid;
}

.callout--todo .callout__label { color: var(--accent-text); }

.callout__text + .callout__text {
  margin-top: var(--space-3);
}
/* ===== components/check-list.css ===== */
/* ============================================================
   LISTE DE QUALIFICATION — « c'est pour vous si », et l'inverse

   Le marqueur est en `::before` sur un `li` : il est décoratif et ne se
   retrouve pas dans le texte copié ni annoncé par un lecteur d'écran, qui
   lit simplement une liste. Le sens, lui, est porté par le libellé de
   l'encart au-dessus.
   ============================================================ */

.check-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.check-list li {
  position: relative;
  padding-left: var(--space-6);
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

.check-list li::before {
  content: "＋";
  position: absolute;
  left: 0;
  top: -0.05em;
  font-family: var(--font-mono);
  font-weight: var(--font-weight-bold);
  color: var(--valid);
}

/* Le vert est réservé à la validation ; l'exclusion n'est pas une erreur,
   donc elle prend le graphite atténué et non le rouge. */
.check-list--no li::before {
  content: "—";
  color: var(--ink-subtle);
}

.check-list a {
  color: var(--accent-text);
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
}

.check-list a:hover { color: var(--ink); border-color: var(--ink); }
/* ===== components/flow.css ===== */
/* ============================================================
   FLUX D'ARCHITECTURE — les couches, de gauche à droite

   Chaque couche ne connaît que celle juste en dessous, et aucune flèche ne
   remonte. Le composant dit cette contrainte plutôt que de la décrire.
   ============================================================ */

.flow {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-4);
}

.flow__step {
  position: relative;
  padding: var(--space-6);
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
}

/* La première couche est celle qui est libre : elle porte l'accent. */
.flow__step--accent {
  border: 2px solid var(--accent);
}

.flow__licence {
  margin: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.flow__step--accent .flow__licence { color: var(--accent-text); }

.flow__name {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: var(--font-weight-bold);
  line-height: var(--leading-tight);
}

.flow__text {
  margin: 0;
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

/* Les connecteurs sont décoratifs : ils ne sont pas dans le texte, et un
   lecteur d'écran lit simplement une liste ordonnée de quatre couches. */
.flow__step:not(.flow__step--last)::after {
  content: "";
  position: absolute;
  inset-inline-start: 50%;
  inset-block-end: calc(-1 * var(--space-4));
  width: 1px;
  height: var(--space-4);
  background: var(--accent);
}

@media (min-width: 1000px) {
  .flow {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-8);
  }
  .flow__step:not(.flow__step--last)::after {
    inset-inline-start: auto;
    inset-inline-end: calc(-1 * var(--space-8));
    inset-block-end: auto;
    top: 50%;
    width: var(--space-8);
    height: 1px;
  }
}

.flow__notes {
  margin-top: var(--space-12);
  padding-top: var(--space-8);
  border-top: 1px solid var(--rule);
}

.flow__note-text {
  margin: 0;
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
  max-width: var(--measure);
}
/* ===== components/chain.css ===== */
/* ============================================================
   CHAÎNE — les étapes d'un enchaînement automatique

   Une liste ordonnée, parce que l'ordre EST l'information : qu'est-ce qui
   déclenche quoi. Le numéro est posé par un compteur CSS et non écrit dans
   le texte : il est décoratif pour l'œil, et l'ordre du `<ol>` suffit à un
   lecteur d'écran comme à un LLM.

   Deux étapes sont marquées : le déclencheur, en trait plein, et le retour
   de boucle, en tirets — c'est lui qui referme la chaîne sur l'affaire.
   ============================================================ */

.chain {
  counter-reset: chain;
  list-style: none;
  margin: var(--space-8) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.chain__step {
  counter-increment: chain;
  position: relative;
  padding: var(--space-5) var(--space-5) var(--space-5) calc(var(--space-5) + 2.25rem);
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
}

.chain__step::before {
  content: counter(chain);
  position: absolute;
  left: var(--space-5);
  top: var(--space-5);
  font-family: var(--font-mono);
  font-size: var(--step-0);
  font-weight: var(--font-weight-bold);
  line-height: var(--leading-snug);
  color: var(--accent-text);
}

.chain__app {
  display: block;
  margin-bottom: var(--space-1);
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: var(--font-weight-bold);
  line-height: var(--leading-snug);
  color: var(--ink);
}

.chain__text {
  margin: 0;
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink-muted);
}

/* Le déclencheur unique : tout le reste en découle. */
.chain__step--trigger {
  border: 2px solid var(--accent);
}

/* Le retour de boucle traverse toute la largeur : il n'est pas une étape de
   plus, c'est la chaîne qui se referme sur l'affaire. */
.chain__step--loop {
  border: 2px dashed var(--accent);
  grid-column: 1 / -1;
}

@media (min-width: 640px) {
  .chain {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* ===== components/timeline.css ===== */
/* ============================================================
   FRISE D'ENGAGEMENTS — ce qui se passe, et quand

   Remplace les trois cartes à icône de l'ancienne page contact. Ce sont des
   délais qui s'enchaînent : une frise le dit mieux que trois cartes égales,
   qui les présentaient comme des arguments indépendants.
   ============================================================ */

.timeline {
  margin: 0;
  padding: 0 0 0 var(--space-5);
  list-style: none;
  border-left: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.timeline__step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* Le jalon. Le premier est orange : c'est l'engagement le plus proche. */
.timeline__step::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--space-5) - 4px);
  top: 0.35em;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--rule-strong);
}

.timeline__step:first-child::before {
  background: var(--accent);
}

.timeline__when {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.timeline__what {
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: var(--font-weight-semibold);
  color: var(--ink);
}
/* ===== components/icons.css ===== */
/* === Icônes du sprite === */
/* Conteneur d’icône générique */
.ds-icon {
  width: 1.4rem;
  height: 1.4rem;
  display: inline-block;
}

/* Couleurs de base du sprite */
.primary {
  stroke: var(--brand-1);
  fill: none;
}

.accent {
  stroke: var(--brand-2);
  fill: none;
}

/* Variante "claire" : icône sur fond sombre (footer, bandeau, etc.) */
.light {
  stroke: var(--brand-1-contrast);       /* Blanc de ton thème */
  fill: none;                            /* on garde les formes en tracé uniquement */
}

.accent-fill {
  fill: var(--brand-2);
  stroke: none;
}

.muted {
  stroke: var(--color-muted);
  fill: none;
}
.muted-fill {
  fill: var(--color-muted);
  stroke: none;
}

.soft {
  stroke: var(--border-1);
  fill: none;
}

.ds-icon--sm { width: 20px; height: 20px; }
.ds-icon--md { width: 32px; height: 32px; }
.ds-icon--lg { width: 48px; height: 48px; }

.ds-card:hover .ds-icon {
  fill: var(--brand-1);       /* graphite au survol */
  transform: translateY(-2px);
}
/* ===== components/code-highlight.css ===== */
/* ============================================================
   CODE HIGHLIGHT — MP-i theme for highlight.js
   Mapping des classes hljs.* sur les tokens MP-i (orange + vert + graphite).
   À utiliser avec highlight.js (chargé en JS sur les pages articles).
   ============================================================ */

/* Conteneur du code surligné */
.hljs {
  display: block;
  overflow-x: auto;
  padding: 0;
  background: transparent;
  color: #e8e5df;
  font-family: var(--font-mono);
}

/* --- Mots-clés du langage (import, from, const, def, return, if, for...) ---
   Orange MP-i (couleur la plus identitaire) */
.hljs-keyword,
.hljs-selector-tag,
.hljs-built_in,
.hljs-tag,
.hljs-name,
.hljs-meta .hljs-keyword {
  color: #e97423;
}

/* --- Meta (shebang #!/bin/bash, decorateurs Python @app.route, directives) ---
   Gris discret pour ne pas concurrencer les keywords/commentaires.
   IMPORTANT : ne pas mettre en orange, sinon les commentaires Bash (parfois
   classifiés en .hljs-meta selon le parser) ressortent comme des keywords. */
.hljs-meta {
  color: #8a8a8a;
}

/* --- Chaînes de caractères ('text', "text", `text`) ---
   Vert sapin clair MP-i (token tertiaire éclairci pour fond sombre) */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-quote,
.hljs-regexp {
  color: #5fa86f;
}

/* --- Clés/attributs (clés JSON, attributs HTML/XML, propriétés CSS) ---
   Blanc cassé : on garde la couleur de base pour ne pas tout colorer en vert.
   Important pour le JSON où la moitié du contenu sont des clés. */
.hljs-attr,
.hljs-attribute,
.hljs-property,
.hljs-template-tag,
.hljs-meta .hljs-string {
  color: #e8e5df;
}

/* --- Commentaires (// foo, # foo, /* foo */ ) ---
   Gris froid en italique, lisibles mais discrets */
.hljs-comment,
.hljs-code,
.hljs-formula {
  color: #8a8a8a;
  font-style: italic;
}

/* --- Nombres et constantes (true, false, null, 42, 3.14) ---
   Orange clair / abricot pour distinguer des keywords */
.hljs-number,
.hljs-literal,
.hljs-variable.constant_,
.hljs-template-variable {
  color: #f0c080;
}

/* --- Noms de fonctions et titres ---
   Beige clair, pour ne pas concurrencer les keywords */
.hljs-title,
.hljs-title.function_,
.hljs-title.class_,
.hljs-section {
  color: #e8d5b7;
  font-weight: 600;
}

/* --- Variables et identifiants ---
   Blanc cassé (couleur de base du code) */
.hljs-variable,
.hljs-params {
  color: #e8e5df;
}

/* --- Types et classes (str, int, MyClass, Promise) ---
   Orange foncé MP-i */
.hljs-type,
.hljs-class .hljs-title,
.hljs-built_in.type_ {
  color: #c96818;
}

/* --- Sélecteurs CSS et liens ---
   Vert clair, comme les strings */
.hljs-link,
.hljs-selector-class,
.hljs-selector-id,
.hljs-selector-attr,
.hljs-selector-pseudo {
  color: #5fa86f;
}

/* --- Tags HTML/XML, balisage ---
   Orange, comme les keywords */
.hljs-doctag,
.hljs-strong {
  color: #e97423;
  font-weight: 700;
}

/* --- Diff: lignes ajoutées/supprimées ---
   Vert / rouge sobre */
.hljs-addition {
  color: #5fa86f;
  background-color: rgb(95 168 111 / 10%);
}
.hljs-deletion {
  color: #e97373;
  background-color: rgb(231 115 115 / 10%);
}

/* --- Mise en emphase (italique, gras) ---
   Conserve le style typographique */
.hljs-emphasis { font-style: italic; }
.hljs-strong { font-weight: 700; }

/* --- Caractères d'échappement ---
   Couleur muted */
.hljs-char.escape_ {
  color: #c0c0a0;
}

/* --- Opérateurs et ponctuation ---
   Hérite du blanc cassé par défaut, pas besoin de surcharger */
.hljs-operator,
.hljs-punctuation { color: inherit; }
/* ===== utils/helpers.css ===== */
/* ============================================================
   UTILITIES
   ============================================================ */

/* Skip to content link (WCAG 2.1 AA) */
.skip-link {
  position: absolute;
  top: -100%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  padding: var(--space-2) var(--space-4);
  background: var(--color-primary);
  color: var(--color-white);
  font-weight: var(--font-weight-semibold);
  font-size: var(--text-sm);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  text-decoration: none;
  white-space: nowrap;
  transition: top var(--transition-fast);
}

.skip-link:focus {
  top: 0;
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}

/* Display */
.hidden   { display: none !important; }
.block    { display: block; }
.relative { position: relative; }

/* Spacing */
.mt-auto { margin-top: auto; }
.mx-auto { margin-inline: auto; }

/* Width */
.w-full  { width: 100%; }
.max-w-prose { max-width: 65ch; }

/* Overflow */
.overflow-hidden { overflow: hidden; }

/* Aspect ratios */
.aspect-video { aspect-ratio: 16 / 9; }
.aspect-square { aspect-ratio: 1; }

/* Visually hidden (accessible) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Animations */
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.animate-fade-in-up {
  animation: fadeInUp 0.6s ease both;
}

/* Stagger helpers */
.delay-100 { animation-delay: 100ms; }
.delay-200 { animation-delay: 200ms; }
.delay-300 { animation-delay: 300ms; }
.delay-400 { animation-delay: 400ms; }

/* Révélation au défilement : observeElements() (utils/helpers.js) ajoute
   .visible quand l'élément entre à l'écran.
   L'état caché n'existe que sous `html.reveal`, que le script du thème pose
   dans le <head> et retire au bout de 3 s si main.js ne s'est pas signalé,
   et seulement sans demande de mouvement réduit. Sans ces gardes, un main.js
   en échec laissait invisible tout ce qui suit le hero ; la règle de
   reset.css censée tout montrer en mouvement réduit venait avant celle-ci
   dans le bundle, et perdait. */
@media (prefers-reduced-motion: no-preference) {
  .reveal .observe {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.6s ease, transform 0.6s ease;
  }

  .reveal .observe.visible {
    opacity: 1;
    transform: translateY(0);
  }

  .reveal .observe.delay-1 { transition-delay: 100ms; }
  .reveal .observe.delay-2 { transition-delay: 200ms; }
  .reveal .observe.delay-3 { transition-delay: 300ms; }
  .reveal .observe.delay-4 { transition-delay: 400ms; }
}

/* Hero tags strip */
.hero__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-6);
}

.hero__tag {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  border-radius: var(--radius-full);
  border: 1px solid rgba(255,255,255,0.25);
  color: rgba(255,255,255,0.85);
  background: rgba(255,255,255,0.08);
}

/* Reference card blockquote */

/* Service card list */
.card-service__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

.card-service__list li::before {
  content: '→';
  color: var(--accent-text);
  font-weight: var(--font-weight-bold);
  margin-right: var(--space-2);
}

/* Partners / logos strip — `.partners-strip` et `.partner-logo` sont dans
   layout/sections.css et components/logo-plate.css. Ils étaient définis ici
   une seconde fois, et ce fichier, chargé en dernier, écrasait l'autre. */
.partners-label {
  text-align: center;
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-text-light);
  margin-bottom: var(--space-6);
}

/* About section */
.about-values {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

.about-values li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.about-values li::before {
  content: '✦';
  color: var(--accent-text);
  font-weight: var(--font-weight-bold);
  flex-shrink: 0;
  margin-top: 1px;
}

/* Divider */
.divider {
  border: none;
  border-top: 1px solid var(--color-border);
  margin-block: var(--space-8);
}

/* Badge */
.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  border-radius: var(--radius-full);
}

.badge-primary {
  background: rgba(47, 46, 44, 0.08);
  color: var(--color-primary);
}

.badge-secondary {
  background: rgba(233, 116, 35, 0.12);
  color: var(--color-secondary-dark);
}

.badge-outline {
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}

.badge-accent {
  background: rgba(233, 116, 35, 0.08);
  color: var(--accent-text);
}
