/* ============================================================
   C365 — CHARTE DES FICHES PÉDAGOGIQUES
   Feuille unique du site public.

   Charte C365, identique a celle des fiches pedagogiques : palette
   Primer de GitHub, bleu Microsoft en accent de marque, Inter et
   JetBrains Mono. Theme clair uniquement, comme les labs.

   Sommaire
     1.  Tokens
     2.  Reset & base
     3.  Utilitaires
     4.  En-tête & navigation
     5.  Ossature des pages
     6.  Accueil
     7.  Cartes & listes d'articles
     8.  Pagination
     9.  Page article
     10. Code & coloration syntaxique
     11. Encarts (callouts)
     12. Commentaires
     13. Carte auteur
     14. Newsletter
     15. Rubriques & catégorie
     16. Recherche
     17. Page « À propos »
     18. Séries
     19. Partenaires
     20. Page 404
     21. Formulaires génériques
     22. Pied de page
     23. Aide clavier
     24. États
     25. Responsive
     26. Impression
   ============================================================ */

/* ============================================================
   0. POLICES
   Déclarées ici plutôt qu'importées : un @import est une requête
   bloquante enchaînée après le téléchargement de cette feuille,
   soit un aller-retour de plus avant la découverte des faces.

   Inter et JetBrains Mono sont les polices de la charte C365,
   celles des fiches pédagogiques. Elles sont auto-hébergées : la
   politique de sécurité impose font-src 'self', et aucun visiteur
   n'a à être exposé à un tiers pour lire un article.

   Versions variables : un seul fichier couvre 400 à 800, contre
   121 Ko pour les cinq graisses statiques d'Inter.
   ============================================================ */

/* ── Inter — latin ── */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/inter-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;
}

/* ── Inter — latin-ext ── */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/inter-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 — latin ── */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('../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;
}

/* ── JetBrains Mono — latin-ext ── */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('../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;
}

/* ============================================================
   1. TOKENS
   Charte C365, identique à celle des fiches pédagogiques : la
   palette Primer de GitHub, avec le bleu Microsoft en accent de
   marque. Thème clair uniquement, comme les labs.
   ============================================================ */
:root {
  /* ── Surfaces ── */
  --bg:     #f6f8fa;   /* fond de page, gris froid */
  --bg-2:   #ffffff;   /* cartes et panneaux */
  --bg-3:   #f1f5f9;   /* aplats discrets, fonds de code */
  --bg-4:   #e2e8f0;
  --bg-5:   #cbd5e1;

  /* ── Traits ── */
  --border:   #d1d9e0;
  --border-2: #c0c9d2;
  --border-3: #9aa5b1;

  /* ── Texte ── */
  --text:   #24292f;   /* 12.6:1 sur le fond */
  --text-2: #424a53;
  --text-3: #57606a;   /*  5.7:1 */

  /* Titres : un cran plus noir que le texte courant. */
  --heading: #1b1f23;

  /* ── Accent ── */
  --accent:      #0969da;   /* liens, onglet actif */
  --accent-hi:   #0550ae;
  --accent-solid:    #0969da;
  --accent-solid-hi: #0550ae;
  --accent-ink:  #ffffff;
  --accent-soft: rgba(9, 105, 218, .08);
  --accent-line: rgba(9, 105, 218, .28);

  /* Bleu de marque Microsoft, distinct de l'accent d'interface. */
  --brand: #0078d4;

  /* ── Teintes d'état ── */
  --green:  #1a7f37;
  --orange: #bc4c00;
  --red:    #cf222e;
  --violet: #6639ba;
  --teal:   #0e6d6d;

  --green-soft:  #f0fdf4;
  --orange-soft: #fef3c7;
  --red-soft:    #fef2f2;
  --violet-soft: #f5f3ff;

  /* ── Rayons ── */
  --r-xs:   4px;
  --r-sm:   6px;
  --r-md:   8px;
  --r-lg:   10px;
  --r-xl:   14px;
  --r-full: 9999px;

  /* ── Ombres — les deux niveaux de la charte ── */
  --sh-1: 0 1px 3px rgba(0, 0, 0, .08), 0 1px 2px rgba(0, 0, 0, .06);
  --sh-2: 0 4px 14px rgba(0, 0, 0, .1);
  --sh-3: 0 8px 28px rgba(0, 0, 0, .12);
  --ring: 0 0 0 3px var(--accent-soft);

  /* ── Bandeau de titre ── */
  --hero-bg:   linear-gradient(135deg, #0f1729 0%, #1a2744 40%, #0a3a6b 100%);
  --hero-glow: rgba(0, 120, 212, .25);

  /* ── Typo ── */
  --sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', Consolas, monospace;

  /* ── Layout ── */
  --max-width: 1180px;   /* chrome : barre de nav, pied de page */
  --measure:   900px;    /* colonne de lecture, comme les labs */
  --aside-w:   280px;    /* colonne de droite des pages de liste */
  --toc-w:     220px;    /* sommaire, en gouttière de la page article */
  --toc-gap:   3rem;
  --nav-h:     60px;

  --code-line: 1.5rem;

  color-scheme: light;
}

/* ── Alias de compatibilité ───────────────────────────────────
   Quelques styles inline et anciens gabarits nomment encore les
   jetons d'avant. Cette couche les fait vivre sans les propager :
   ne pas s'en servir dans du code neuf.
   ------------------------------------------------------------ */
:root {
  --blue:     var(--brand);
  --primary:  var(--accent);
  --font:     var(--sans);
  --fg:       var(--text);
  --fg2:      var(--text-2);
  --fg3:      var(--text-3);
  --line:     var(--border);
  --line2:    var(--border-2);
  --surface:  var(--bg-2);
  --muted:    var(--text-3);
  --danger:   var(--red);
  --success:  var(--green);
  --radius:   var(--r-lg);
  --radius-m: var(--r-md);
  --radius-s: var(--r-sm);

  /* Le serif a disparu de la charte : tout ce qui le réclamait
     encore retombe sur le sans, sans casser. */
  --serif:    var(--sans);
}


/* ============================================================
   2. RESET & BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; padding: 0; }

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 1.5rem);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Colonne souple sur toute la hauteur de la fenêtre : c'est ce qui
   permet au pied de page de se caler en bas quand le contenu est
   court (voir .blog-footer). Les éléments positionnés — barre de nav
   collante, jauge de lecture, visionneuse — restent hors du flux et
   ne deviennent pas des éléments de cette colonne. */
body {
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: .9375rem;
  line-height: 1.7;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;

  /* Ligatures usuelles et crénage : « fi », « fl », « ff » cessent de
     se heurter. Les ligatures discrétionnaires restent désactivées,
     elles ne servent qu'à l'affiche. */
  font-kerning: normal;
  font-variant-ligatures: common-ligatures contextual;
}

/* Veuves et orphelines : le navigateur équilibre la dernière ligne
   plutôt que d'y laisser un mot seul. Sur les titres, `balance`
   répartit sur deux ou trois lignes de longueur voisine. */
p { text-wrap: pretty; }

/* Titres : le serif porte la voix éditoriale du site. */
/* Titres : Inter en graisses lourdes, encre plus noire que le texte
   courant --- comme dans les fiches. */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--sans);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.011em;
  color: var(--heading);
  text-wrap: balance;
}
h1, .home-list-row-title, .article-title, .cat-page-title { font-weight: 800; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hi); }

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

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

ul, ol { list-style: none; }

code, pre, kbd, samp { font-family: var(--mono); }

hr { border: none; height: 1px; background: var(--border); }

::selection { background: var(--accent-soft); color: var(--text); }

/* Anneau de focus unique pour tout le site : visible sur les deux
   thèmes, jamais affiché à la souris. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }

/* ============================================================
   3. UTILITAIRES
   ============================================================ */
.container {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: 1.5rem;
}

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

/* Surtitre : petites capitales espacées, en sans, au-dessus d'un
   titre serif. C'est le contraste des deux familles qui structure. */
.eyebrow,
.blog-cat-eyebrow,
.newsletter-inline-eyebrow,
.author-card-label,
.article-sources-label,
.article-series-label {
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--text-3);
}

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

.hv3-skip-link {
  position: absolute;
  top: -100px; left: 1rem;
  z-index: 200;
  padding: .7rem 1.1rem;
  background: var(--accent-solid);
  color: var(--accent-ink);
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 700;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-2);
  transition: top .15s ease;
}
.hv3-skip-link:focus {
  top: 1rem;
  color: var(--accent-ink);
}

/* ============================================================
   4. EN-TÊTE & NAVIGATION
   ============================================================ */
.snav {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  height: var(--nav-h);
  /* Enfant de la colonne souple du <body> : sans cela sa hauteur fixe
     pourrait être rabotée si l'espace venait à manquer. */
  flex-shrink: 0;
  padding-inline: 1.5rem;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
}

/* Repli si color-mix n'est pas gérée : fond plein, pas de transparence. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .snav { background: var(--bg); }
}

.snav-inner {
  display: flex;
  align-items: center;
  gap: 2rem;
  flex: 1;
  min-width: 0;
  max-width: var(--max-width);
  margin-inline: auto;
  width: 100%;
}

/* Ombre au défilement : nav.js pose data-scrolled dès 8px. Elle
   détache la barre du contenu sans la souligner en permanence. */
.snav[data-scrolled] { box-shadow: var(--sh-1); }

.snav-logo {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--text);
}
.snav-logo:hover { color: var(--text); }

.snav-logo-text {
  font-family: var(--sans);
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--text);
}

.snav-logo-img { height: 30px; width: auto; }

.snav-right {
  display: flex;
  align-items: center;
  gap: .35rem;
  margin-left: auto;
  flex-shrink: 0;
}

.snav-search-link {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .45rem .85rem;
  font-family: var(--sans);
  font-size: .8125rem;
  font-weight: 500;
  color: var(--text-2);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  transition: color .15s ease, border-color .15s ease;
}

.snav-search-link:hover {
  color: var(--text);
  border-color: var(--border-2);
}

/* ============================================================
   5. OSSATURE DES PAGES
   ============================================================ */
.blog-main {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding: 3rem 1.5rem 4rem;
}

.blog-col-main {
  max-width: var(--measure);
  margin-inline: auto;
}

/* ── Pages de liste : contenu + colonne de droite ──────────────
   L'en-tête ne porte plus les rubriques : elles vivent ici, où
   elles ont la place d'afficher leur nombre d'articles. La colonne
   suit le défilement, la lecture reste calée sur --measure.
   ------------------------------------------------------------ */
.blog-main.has-aside {
  display: grid;
  grid-template-columns: minmax(0, var(--measure)) var(--aside-w);
  gap: 4rem;
  align-items: start;
  justify-content: center;
}

/* Dans la grille, la colonne borne déjà la largeur : le centrage
   automatique de .blog-col-main décalerait le texte vers la droite. */
.blog-main.has-aside .blog-col-main {
  max-width: none;
  margin-inline: 0;
}

.blog-aside {
  position: sticky;
  top: calc(var(--nav-h) + 2.5rem);
  display: flex;
  flex-direction: column;
  gap: 2rem;
  max-height: calc(100vh - var(--nav-h) - 4rem);
  overflow-y: auto;
  scrollbar-width: thin;
  font-family: var(--sans);
}

.blog-aside-title {
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: .875rem;
}

/* ── Liste des rubriques ── */
.blog-aside-cats { display: flex; flex-direction: column; }

.blog-aside-cat {
  display: flex;
  align-items: center;
  gap: .625rem;
  padding: .5rem 0;
  font-size: .875rem;
  color: var(--text-2);
  border-bottom: 1px solid var(--border);
  transition: color .15s ease;
}
.blog-aside-cats li:last-child .blog-aside-cat { border-bottom: none; }
.blog-aside-cat:hover { color: var(--text); }

/* Le nom ne s'étire plus : le compteur le suit immédiatement. Aligné à
   droite, il laissait un long vide dès qu'une rubrique portait un nom
   court — spectaculaire quand il n'y en a qu'une. L'alignement des
   nombres en colonne servait un tableau, pas une liste de navigation. */
.blog-aside-cat-name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.blog-aside-cat-count {
  flex-shrink: 0;
  font-size: .75rem;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

.blog-aside-cat.is-active {
  color: var(--text);
  font-weight: 600;
}

.blog-aside-feed {
  font-size: .8125rem;
  color: var(--text-3);
}
.blog-aside-feed a { color: var(--text-3); }
.blog-aside-feed a:hover { color: var(--accent); }

/* Fil d'Ariane */
.blog-crumb,
.article-breadcrumb {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
  font-family: var(--sans);
  font-size: .8125rem;
  color: var(--text-3);
}
.blog-crumb a { color: var(--text-3); }
.blog-crumb a:hover { color: var(--accent); }
.blog-crumb-sep { color: var(--border-2); }
.blog-crumb-current { color: var(--text-2); }

/* En-tête de page simple (contact, désinscription…) */
.simple-header { margin-bottom: 2.5rem; }
.simple-title {
  font-size: clamp(2rem, 4.5vw, 2.75rem);
  margin-bottom: .75rem;
}
.simple-lead {
  font-family: var(--sans);
  font-size: 1.1875rem;
  line-height: 1.65;
  color: var(--text-2);
}

/* Boutons génériques */
.blog-cta,
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .7rem 1.35rem;
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 600;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.blog-cta-primary,
.button-primary {
  background: var(--accent-solid);
  color: var(--accent-ink);
}
.blog-cta-primary:hover,
.button-primary:hover {
  background: var(--accent-solid-hi);
  color: var(--accent-ink);
}

.blog-cta-ghost,
.button-secondary,
.button-link {
  background: transparent;
  color: var(--text-2);
  border-color: var(--border-2);
}
.blog-cta-ghost:hover,
.button-secondary:hover,
.button-link:hover {
  color: var(--text);
  border-color: var(--border-3);
  background: var(--bg-3);
}

.button-sm { padding: .45rem .9rem; font-size: .8125rem; }

.blog-intro-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  margin-top: 1.5rem;
}

/* ============================================================
   6. ACCUEIL
   ============================================================ */


/* ── Liste chronologique ───────────────────────────────────────
   Rangées séparées par un filet, sans illustration : accueil et
   rubrique se lisent au titre. Les styles de vignette plus bas ne
   servent plus qu'à la page de recherche.
   ------------------------------------------------------------ */
.home-list { display: flex; flex-direction: column; }

/* Intertitre de section : petites capitales et filet, comme le
   sommaire d'un journal annonce ses rubriques. */
.home-list-section-label {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: .5rem;
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--text-3);
}
.home-list-section-label::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ── Ligne de méta ─────────────────────────────────────────────
   Date, rubrique et durée sur une seule ligne, séparées par des
   points médians. La date a occupé un temps une gouttière de gauche :
   la colonne se lisait bien, mais elle laissait sous elle une bande
   vide sur toute la hauteur de la rangée dès qu'un titre tenait en
   une ligne — et elle créait un second registre de méta en plus de
   celui posé au-dessus du titre.
   ------------------------------------------------------------ */
.home-list-row-day,
.home-list-row-read {
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.home-list-row {
  display: flex;
  align-items: flex-start;
  gap: 1.75rem;
  padding: 1.75rem 0;
  border-bottom: 1px solid var(--border);
}
.home-list-row:first-child { padding-top: .5rem; }

/* L'accueil place la vignette après le corps, la recherche avant.
   L'ordre visuel est fixé ici pour que les deux gabarits rendent
   la même rangée : texte à gauche, vignette à droite. */
.home-list-row-body { order: 1; flex: 1; min-width: 0; }

/* Ligne de service : elle situe l'article, elle ne le vend pas. Petite
   et pâle, elle laisse toute la place au titre juste en dessous. */
.home-list-row-meta {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  flex-wrap: wrap;
  margin-bottom: .5rem;
  font-family: var(--sans);
  font-size: .6875rem;
  color: var(--text-3);
}

/* Apparence portée par « Marqueur de rubrique », section 7. */
.home-list-row-date { color: var(--text-3); }

.home-list-row-link { display: block; color: var(--text); }
.home-list-row-link:hover { color: var(--text); }

/* Le titre reste l'élément dominant, mais la vignette lui reprend
   190px de largeur : à 32px il aurait débité les titres longs en
   cinq lignes. 28px au maximum, interlignage serré, approche
   négative — deux lignes de titre forment un bloc, pas deux phrases
   empilées. */
.home-list-row-title {
  font-size: clamp(1.375rem, 2.2vw, 1.75rem);
  line-height: 1.16;
  letter-spacing: -.018em;
  margin-bottom: .5rem;
  text-wrap: balance;
}
.home-list-row-link:hover .home-list-row-title { color: var(--accent); }

/* L'accroche s'efface derrière le titre : plus claire, et plafonnée à
   deux lignes pour qu'une méta-description bavarde ne fasse pas enfler
   la rangée. */
.home-list-row-excerpt {
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.55;
  color: var(--text-3);
  max-width: 62ch;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.home-list-row-thumb-wrap {
  order: 2;
  flex-shrink: 0;
  width: 220px;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--bg-3);
  border: 1px solid var(--border);
}

/* Aucun format imposé : largeur fixe, hauteur libre. Les visuels de
   C365 portent du texte, donc on ne les rogne pas — mais forcer un
   cadre et y caler l'image au centre revient à l'entourer de bandes
   dès que son format diffère. À son ratio naturel, l'image est vue
   entière et remplit exactement son emplacement.

   Contrepartie assumée : deux images de formats différents donnent
   deux vignettes de hauteurs différentes. Avec des visuels réguliers,
   cela ne se voit pas. Pas de zoom au survol non plus : il ferait
   déborder l'image du cadre, donc la rognerait. */
.home-list-row-thumb {
  display: block;
  width: 100%;
  height: auto;
  /* Garde-fou : une image en portrait ferait une vignette plus haute
     que le texte à côté. Au-delà, elle se contient au lieu de pousser
     la rangée. Un visuel au format paysage n'atteint jamais ce seuil
     et n'est donc pas concerné. */
  max-height: 280px;
  object-fit: contain;
}

/* ── Rangée sans illustration ──────────────────────────────────
   Un aplat au nom de la rubrique occupe la place de la vignette.
   Sans lui, une rangée sur trois s'étalerait sur toute la largeur
   pendant que les autres gardent leur image : c'est ce mélange,
   plus que l'absence d'image, qui donne une liste bancale.
   ------------------------------------------------------------ */
.home-list-row-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  padding: .5rem;
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-align: center;
  color: var(--text-3);
  background: var(--bg-3);
}

/* Le jeton vide de la page de recherche, lui, reste invisible. */
.home-list-row-thumb-ph { display: none; }

.home-partner-strip { margin: 3rem 0; }

.blog-flux-empty-inline {
  padding: 4rem 0;
  text-align: center;
  font-family: var(--sans);
  font-size: 1.125rem;
  color: var(--text-3);
}

/* ============================================================
   7. CARTES & LISTES D'ARTICLES
   ============================================================ */

/* ── Liste des pages 2+, recherche, rubrique ── */
.blog-list { display: flex; flex-direction: column; }

.blog-list-entry {
  display: flex;
  align-items: flex-start;
  gap: 1.75rem;
  padding: 1.75rem 0;
  border-bottom: 1px solid var(--border);
}
.blog-list-entry:first-child { padding-top: 0; }

.blog-list-entry-inner { display: contents; }

.blog-list-entry-body { flex: 1; min-width: 0; }

.blog-list-entry-dateline {
  display: flex;
  align-items: center;
  gap: .625rem;
  flex-wrap: wrap;
  margin-bottom: .625rem;
  font-family: var(--sans);
  font-size: .75rem;
}

/* Apparence portée par « Marqueur de rubrique », plus bas. */
.blog-list-entry-date { color: var(--text-3); }

.blog-list-entry-link { display: block; color: var(--text); }
.blog-list-entry-link:hover { color: var(--text); }

.blog-list-entry-title {
  font-size: 1.4375rem;
  line-height: 1.28;
  margin-bottom: .5rem;
}
.blog-list-entry-link:hover .blog-list-entry-title { color: var(--accent); }

.blog-list-entry-excerpt {
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-2);
  max-width: 62ch;
}

/* ── Carte d'article ───────────────────────────────────────────
   Utilisée là où la grille remplace la liste. Panneau blanc sur
   papier, filet fin, ombre courte au survol.
   ------------------------------------------------------------ */
.post-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .2s ease, transform .2s ease;
}
.post-card:hover {
  border-color: var(--border-2);
  box-shadow: var(--sh-2);
  transform: translateY(-2px);
}

.post-card-image {
  display: block;
  background: var(--bg-3);
  border-bottom: 1px solid var(--border);
}
.post-card-image img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.post-card-image-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  background: var(--bg-3);
}
.post-card-placeholder-initial {
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--text-3);
}

.post-card-top {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  padding: 1rem 1.25rem 0;
  font-family: var(--sans);
  font-size: .6875rem;
}

/* ── Marqueur de rubrique ──────────────────────────────────────
   Libellé seul, en petites capitales espacées. Aucune couleur de
   rubrique : ni pastille, ni texte teinté. Les teintes stockées en
   base ayant été choisies pour un fond noir, aucune ne pouvait
   habiller du texte sur le papier sans tomber sous le seuil de
   contraste — la question ne se pose plus.
   ------------------------------------------------------------ */
.post-card-cat-chip,
.article-header-cat-badge,
.home-list-row-cat,
.blog-list-entry-cat-tag,
.serie-cat {
  display: inline-flex;
  align-items: center;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--text-2);
  transition: color .15s ease;
}
.post-card-cat-chip:hover,
.article-header-cat-badge:hover,
.home-list-row-cat:hover,
.blog-list-entry-cat-tag:hover,
.serie-cat:hover { color: var(--text); }

.post-card-avail {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .2rem .5rem;
  font-weight: 600;
  border-radius: var(--r-full);
}
.post-card-avail.is-released  { color: var(--green);  background: var(--green-soft); }
.post-card-avail.is-announced { color: var(--orange); background: var(--orange-soft); }

.post-card-date { color: var(--text-3); }

.post-card-body { flex: 1; padding: .75rem 1.25rem 1rem; }

.post-card-title {
  font-size: 1.125rem;
  line-height: 1.3;
  margin-bottom: .5rem;
}
.post-card-title a { color: var(--text); }
.post-card-title a:hover { color: var(--accent); }

.post-card-excerpt {
  font-family: var(--sans);
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--text-2);
}

.post-card-footer {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .75rem 1.25rem;
  border-top: 1px solid var(--border);
  font-family: var(--sans);
  font-size: .75rem;
  color: var(--text-3);
}

.post-card-stat { display: inline-flex; align-items: center; gap: .3rem; }
.post-card-stat svg { opacity: .8; }

.empty-state-card {
  padding: 3.5rem 2rem;
  text-align: center;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text-3);
}

/* ============================================================
   8. PAGINATION
   ============================================================ */
.blog-pg,
.blog-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  font-family: var(--sans);
}

.blog-pg-arrow {
  font-size: .875rem;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
}
.blog-pg-arrow:hover { color: var(--accent); }
.blog-pg-arrow.is-disabled { color: var(--border-2); pointer-events: none; }

.blog-pg-list {
  display: flex;
  align-items: center;
  gap: .25rem;
}

.blog-pg-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px; height: 34px;
  padding-inline: .5rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--text-2);
  border-radius: var(--r-sm);
  transition: background .15s ease, color .15s ease;
}
a.blog-pg-num:hover { background: var(--bg-3); color: var(--text); }

.blog-pg-num.is-current {
  background: var(--accent-solid);
  color: var(--accent-ink);
}

.blog-pg-ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  color: var(--text-3);
}

/* ============================================================
   9. PAGE ARTICLE
   ============================================================ */

/* Jauge de lecture : un filet d'accent en haut de fenêtre.
   Largeur pilotée par article-page.js. */
#readingProgress {
  position: fixed;
  top: 0; left: 0;
  z-index: 150;
  width: 0;
  height: 2px;
  background: var(--accent);
  transition: width .1s linear;
}

.article-main { padding-top: 2rem; }

/* ── Alignement de la page article ────────────────────────────
   Un seul bord gauche pour tout : fil d'Ariane, en-tête, couverture,
   corps, commentaires. Chaque bloc avait sa propre largeur et son
   propre centrage dans ce conteneur, ce qui donnait quatre bords
   décalés jusqu'à 150px les uns des autres — le titre paraissait
   rentré par rapport au texte qu'il annonçait.

   Le conteneur fait donc exactement la colonne de lecture plus la
   gouttière du sommaire, la grille se cale à gauche, et les blocs
   pleine mesure s'y adossent au lieu de se centrer.
   ------------------------------------------------------------ */
.article-columns {
  max-width: calc(var(--measure) + var(--toc-gap) + var(--toc-w));
  margin-inline: auto;
}

/* Sans sommaire — article sans intertitre, ou écran trop étroit — la
   gouttière n'a plus lieu d'être : la lecture se recentre. */
.article-columns:has(.article-toc-sidebar[hidden]) {
  max-width: var(--measure);
}
.article-columns:has(.article-toc-sidebar[hidden]) .article-body {
  grid-template-columns: minmax(0, 1fr);
}

/* ── En-tête ── */
.article-header {
  max-width: var(--measure);
  margin: 0 0 2rem;
}

.article-header-meta-top {
  display: flex;
  align-items: center;
  gap: .625rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  font-family: var(--sans);
  font-size: .75rem;
}

.article-header-date,
.article-header-read { color: var(--text-3); }
.article-meta-sep { color: var(--border-2); }

.article-header-new {
  padding: .15rem .5rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--green);
  background: var(--green-soft);
  border-radius: var(--r-full);
}

.article-title {
  font-size: clamp(2.125rem, 5vw, 3.125rem);
  line-height: 1.1;
  letter-spacing: -.023em;
}

/* ── Image de couverture ── */
.article-cover {
  max-width: var(--measure);
  margin: 0 0 2.5rem;
}
.article-cover img {
  width: 100%;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--bg-3);
}
.article-cover-caption {
  margin-top: .75rem;
  font-family: var(--sans);
  font-size: .75rem;
  color: var(--text-3);
  text-align: center;
}

/* ── Carrousel ── */
.article-carousel {
  max-width: var(--measure);
  margin: 0 0 2.5rem;
}

.article-carousel-track-wrap {
  position: relative;
  border-radius: var(--r-md);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--bg-3);
}

.article-carousel-track {
  display: flex;
  transition: transform .4s cubic-bezier(.2, .7, .3, 1);
}

.article-carousel-slide { flex: 0 0 100%; }
/* Ces images portent du texte : on ne les rogne pas. Contrairement aux
   vignettes de liste, le format reste imposé — des diapositives de
   hauteurs différentes feraient sauter la page à chaque changement
   d'image. Le cadre est donc calé sur le 4/3 des visuels C365, ce qui
   les fait remplir exactement, sans bande ni recadrage. */
.article-carousel-slide img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  background: var(--bg-3);
}

.article-carousel-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  color: var(--text);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  box-shadow: var(--sh-2);
  transition: background .15s ease, border-color .15s ease;
}
.article-carousel-btn:hover { background: var(--bg-3); border-color: var(--border-2); }
.article-carousel-prev { left: .75rem; }
.article-carousel-next { right: .75rem; }

.article-carousel-counter {
  position: absolute;
  right: .75rem; bottom: .75rem;
  padding: .2rem .6rem;
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 600;
  color: var(--text-2);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
}

.article-carousel-thumbs {
  display: flex;
  gap: .5rem;
  margin-top: .75rem;
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: .25rem;
}

.article-carousel-thumb {
  flex-shrink: 0;
  width: 76px;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-xs);
  border: 1px solid var(--border);
  opacity: .55;
  cursor: pointer;
  transition: opacity .15s ease, border-color .15s ease;
}
.article-carousel-thumb:hover { opacity: .85; }
.article-carousel-thumb.is-active {
  opacity: 1;
  border-color: var(--accent);
}

/* ── Corps : texte + sommaire ─────────────────────────────────
   Le sommaire occupe la gouttière de droite et suit le défilement.
   Sous 1120px il disparaît : la colonne de lecture prime.
   ------------------------------------------------------------ */
.article-body {
  display: grid;
  grid-template-columns: minmax(0, var(--measure)) var(--toc-w);
  gap: var(--toc-gap);
  justify-content: start;
  align-items: start;
}

.article-body-text { min-width: 0; }

.article-toc-sidebar {
  position: sticky;
  top: calc(var(--nav-h) + 2rem);
  font-family: var(--sans);
}
.article-toc-sidebar[hidden] { display: none; }

.article-toc-details { border-left: 1px solid var(--border); padding-left: 1.125rem; }

.article-toc-heading {
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--text-3);
  cursor: pointer;
  list-style: none;
  margin-bottom: .875rem;
}
.article-toc-heading::-webkit-details-marker { display: none; }

.article-toc-nav { max-height: 65vh; overflow-y: auto; }

.article-toc-list { display: flex; flex-direction: column; gap: .5rem; }
.article-toc-list ol { padding-left: .875rem; margin-top: .5rem; gap: .45rem; display: flex; flex-direction: column; }

.article-toc-link {
  display: block;
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--text-3);
  transition: color .15s ease;
}
.article-toc-link:hover { color: var(--text); }
.article-toc-link.is-active { color: var(--accent); font-weight: 600; }

/* Sommaire replié en tête d'article, sur petit écran. */
.article-toc-inline-list { display: flex; flex-direction: column; gap: .5rem; }

/* ── Série ── */
.article-series {
  padding: 1.125rem 1.25rem;
  margin-bottom: 2.5rem;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.article-series-label { display: block; margin-bottom: .75rem; }
.article-series-list { display: flex; flex-direction: column; gap: .5rem; }
.article-series-item {
  display: flex;
  gap: .625rem;
  font-family: var(--sans);
  font-size: .875rem;
  color: var(--text-2);
}
.article-series-item a { color: var(--text-2); }
.article-series-item a:hover { color: var(--accent); }
.article-series-item.is-current { color: var(--text); font-weight: 600; }
.article-series-num { color: var(--text-3); font-variant-numeric: tabular-nums; }

/* ── Contenu rédactionnel ─────────────────────────────────────
   Le serif prend la main. Corps à 1.1875rem sur 680px, soit une
   soixantaine de signes par ligne : la mesure de lecture confort.
   ------------------------------------------------------------ */
.article-content {
  font-family: var(--sans);
  font-size: 1.1875rem;
  line-height: 1.72;
  color: var(--text);

  /* Ponctuation suspendue : guillemets et tirets débordent dans la
     marge, la colonne de texte retrouve un bord net. Safari seul le
     gère ; ailleurs, aucun effet et aucun dégât. */
  hanging-punctuation: first last;
}

.article-content > * + * { margin-top: 1.3em; }

/* ── Lettrine ──────────────────────────────────────────────────
   Sur le premier paragraphe seulement, et seulement s'il ouvre
   vraiment l'article : le sélecteur ne vise que les enfants directs,
   donc un encart ou une figure en tête n'en déclenche pas.
   ------------------------------------------------------------ */
.article-content > p:first-of-type::first-letter {
  float: left;
  margin: .02em .1em 0 0;
  font-size: 3.3em;
  line-height: .84;
  font-weight: 600;
  color: var(--text);
}

.article-content h2,
.article-content h3,
.article-content h4 {
  position: relative;
  color: var(--text);
  letter-spacing: -.015em;
}
.article-content h2 {
  font-size: 1.75rem;
  line-height: 1.22;
  margin-top: 2em;
  margin-bottom: .1em;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}
.article-content h3 {
  font-size: 1.3125rem;
  line-height: 1.3;
  margin-top: 1.75em;
  margin-bottom: .1em;
}
.article-content h4 {
  font-size: 1.125rem;
  margin-top: 1.75em;
  margin-bottom: .1em;
}
.article-content h2 + *,
.article-content h3 + *,
.article-content h4 + * { margin-top: .7em; }

/* Ancre de titre : révélée au survol, dans la marge. */
.article-heading-anchor,
.h2-anchor {
  position: absolute;
  left: -1.5rem;
  font-family: var(--sans);
  font-weight: 400;
  color: var(--text-3);
  opacity: 0;
  transition: opacity .15s ease;
}
.article-content h2:hover .article-heading-anchor,
.article-content h3:hover .article-heading-anchor,
.article-content h2:hover .h2-anchor,
.article-content h3:hover .h2-anchor,
.article-heading-anchor:focus-visible,
.h2-anchor:focus-visible { opacity: 1; }

.article-content a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
  text-decoration-color: var(--accent-line);
  transition: text-decoration-color .15s ease;
}
.article-content a:hover {
  color: var(--accent-hi);
  text-decoration-color: currentColor;
}

.ext-icon { display: inline; margin-left: .15em; opacity: .6; vertical-align: baseline; }

.article-content strong { font-weight: 600; color: var(--text); }
.article-content em { font-style: italic; }

.article-content ul,
.article-content ol { padding-left: 1.35em; }
.article-content ul > li { list-style: disc; }
.article-content ol > li { list-style: decimal; }
.article-content li { margin-top: .45em; }
.article-content li::marker { color: var(--text-3); }
.article-content li > ul,
.article-content li > ol { margin-top: .45em; }

.article-content blockquote {
  padding-left: 1.25rem;
  border-left: 2px solid var(--accent-line);
  font-style: italic;
  color: var(--text-2);
}

.article-content figure { margin-block: 2em; }
.article-content figcaption {
  margin-top: .625rem;
  font-family: var(--sans);
  font-size: .8125rem;
  color: var(--text-3);
  text-align: center;
}

.article-content img {
  width: 100%;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--bg-3);
}
.lb-zoomable { cursor: zoom-in; }

.article-content hr { margin-block: 2.5em; }

/* Tableaux : ils débordent la mesure sur mobile, jamais la page. */
.article-content table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--sans);
  font-size: .9375rem;
}
.article-content th,
.article-content td {
  padding: .7rem .9rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
}
.article-content th {
  font-weight: 700;
  font-size: .75rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-3);
  border-bottom-color: var(--border-2);
}
.article-content td { color: var(--text-2); }
.article-content tbody tr:last-child td { border-bottom: none; }
.article-content tbody tr:hover td { background: var(--bg-3); }

/* ── Ressources associées ── */
/* ── Ressources associées ──────────────────────────────────────
   Traité comme une destination, pas comme une note de bas de page.
   Un simple filet et un lien en petit corps donnaient à la ressource
   l'apparence d'une référence bibliographique, alors que c'est
   souvent la suite naturelle de la lecture — le lab dont l'article
   parle, par exemple. Le panneau teinté d'accent le désigne comme une
   action sans crier.
   ------------------------------------------------------------ */
.article-sources {
  margin-top: 2.5rem;
  padding: 1.25rem 1.5rem;
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-md);
}
.article-sources-label { display: block; margin-bottom: .625rem; }
.article-sources-list { display: flex; flex-direction: column; gap: .625rem; }
.article-sources-list li {
  font-family: var(--sans);
  font-size: .9375rem;
  line-height: 1.5;
  color: var(--text-2);
}
/* La ressource est un lien qu'on veut voir cliqué : elle se présente
   comme tel. En pastille grise minuscule, elle ressemblait à une
   étiquette décorative et n'appelait pas le clic. */
/* Chaque ressource occupe sa propre rangée cliquable, assez haute pour
   être visée sans effort. Le décalage négatif compense le rembourrage
   pour que le texte reste aligné sur le surtitre au-dessus. */
a.article-source-pill {
  display: inline-flex;
  align-items: baseline;
  gap: .4rem;
  margin-inline: -.625rem;
  padding: .45rem .625rem;
  font-family: var(--sans);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--accent);
  border-radius: var(--r-sm);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .2em;
  text-decoration-color: var(--accent-line);
  transition: text-decoration-color .15s ease, color .15s ease, background .15s ease;
}
a.article-source-pill:hover {
  color: var(--accent-hi);
  text-decoration-color: currentColor;
  background: var(--accent-soft);
}

/* Ressource sans lien : simple mention, pas d'affordance trompeuse. */
span.article-source-pill {
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--text-2);
}


/* ── Actions : j'aime, partage ── */
.article-actions {
  display: flex;
  align-items: center;
  gap: .25rem;
  flex-wrap: wrap;
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
  font-family: var(--sans);
}

/* Zones de clic réelles : en simple texte, « J'aime » offrait une
   cible de 14px de haut, inconfortable à la souris et hors normes au
   doigt. Le décalage négatif du premier élément le remet d'aplomb sur
   la marge de la colonne malgré son rembourrage. */
.article-like-btn,
.article-action-link {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .875rem;
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 600;
  color: var(--text-2);
  border-radius: var(--r-full);
  transition: color .15s ease, background .15s ease;
}
.article-actions > :first-child { margin-left: -.875rem; }
.article-like-btn:hover,
.article-action-link:hover {
  color: var(--accent);
  background: var(--accent-soft);
}

.article-like-btn.is-liked { color: var(--red); }
.like-count { color: var(--text-3); font-variant-numeric: tabular-nums; }
.article-like-btn.is-liked .like-count { color: var(--red); }

.like-icon-heart { transition: transform .2s cubic-bezier(.2, .7, .3, 1); }
.article-like-btn.is-animating .like-icon-heart { transform: scale(1.35); }

.share-confirm { color: var(--green); font-weight: 500; }

/* Retour en haut, injecté par le JS d'article. */
.hv3-article-back {
  position: fixed;
  right: 1.5rem; bottom: 1.5rem;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  color: var(--text-2);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  box-shadow: var(--sh-2);
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease, color .15s ease;
}
.hv3-article-back.is-visible { opacity: 1; pointer-events: auto; }
.hv3-article-back:hover { color: var(--accent); }

/* ── Visionneuse d'image ── */
#lbBackdrop {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  background: rgba(10, 9, 8, .92);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
#lbImg {
  max-width: 100%;
  max-height: 85vh;
  object-fit: contain;
  border-radius: var(--r-sm);
}
#lbCaption {
  position: absolute;
  bottom: 1.5rem; left: 50%;
  transform: translateX(-50%);
  font-family: var(--sans);
  font-size: .8125rem;
  color: #D8D4CD;
  text-align: center;
}
#lbClose {
  position: absolute;
  top: 1.25rem; right: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  font-size: 1.25rem;
  color: #D8D4CD;
  border: 1px solid rgba(216, 212, 205, .25);
  border-radius: var(--r-full);
}
#lbClose:hover { color: #FFF; border-color: rgba(216, 212, 205, .5); }

/* ============================================================
   10. CODE & COLORATION SYNTAXIQUE
   ============================================================ */
.article-content code {
  padding: .12em .38em;
  font-size: .8em;
  color: var(--text);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  white-space: nowrap;
}

.article-content pre {
  position: relative;
  padding: 1.125rem 1.25rem;
  overflow-x: auto;
  font-size: .875rem;
  line-height: var(--code-line);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  tab-size: 2;
}
.article-content pre code {
  padding: 0;
  font-size: inherit;
  background: none;
  border: none;
  white-space: pre;
  color: var(--text-2);
}

/* Étiquette de langage et bouton de copie, injectés par article.js. */
.article-pre-lang,
.article-pre-copy {
  position: absolute;
  top: .5rem;
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 600;
  color: var(--text-3);
}
.article-pre-lang {
  right: .75rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  pointer-events: none;
}
.article-pre-copy {
  right: .75rem;
  padding: .2rem .55rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  opacity: 0;
  transition: opacity .15s ease, color .15s ease;
}
.article-content pre:hover .article-pre-copy,
.article-pre-copy:focus-visible { opacity: 1; }
.article-content pre:hover .article-pre-lang { opacity: 0; }
.article-pre-copy:hover { color: var(--text); }
.article-pre-copy.is-copied { color: var(--green); opacity: 1; }

/* Gouttière de numérotation : superposée à gauche du <pre>, elle
   doit reproduire exactement le pas de ligne, d'où --code-line. */
.article-content pre.has-gutter { padding-left: 3.75rem; }
.article-content .code-gutter {
  position: absolute;
  top: 1.125rem; left: 0;
  width: 2.85rem;
  padding-right: .65rem;
  font-size: .875rem;
  line-height: var(--code-line);
  color: var(--text-3);
  text-align: right;
  user-select: none;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

/* Jetons highlight.js, définis sur nos variables : le thème suit
   la bascule clair/sombre sans changer de feuille. */
.article-content .hljs { background: none; color: var(--text-2); }
.article-content .hljs-comment,
.article-content .hljs-quote          { color: var(--text-3); font-style: italic; }
.article-content .hljs-keyword,
.article-content .hljs-selector-tag,
.article-content .hljs-literal,
.article-content .hljs-type           { color: var(--violet); }
.article-content .hljs-string,
.article-content .hljs-attr,
.article-content .hljs-addition       { color: var(--green); }
.article-content .hljs-number,
.article-content .hljs-symbol,
.article-content .hljs-meta           { color: var(--orange); }
.article-content .hljs-title,
.article-content .hljs-name,
.article-content .hljs-section,
.article-content .hljs-built_in       { color: var(--accent); }
.article-content .hljs-variable,
.article-content .hljs-template-variable,
.article-content .hljs-params         { color: var(--text); }
.article-content .hljs-deletion       { color: var(--red); }
.article-content .hljs-emphasis       { font-style: italic; }
.article-content .hljs-strong         { font-weight: 700; }

/* ============================================================
   11. ENCARTS (CALLOUTS)
   ============================================================ */
.article-content blockquote.callout {
  padding: 1.125rem 1.25rem;
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: var(--accent-soft);
  font-style: normal;
  font-size: 1.0625rem;
  color: var(--text-2);
}
.article-content blockquote.callout > * + * { margin-top: .6em; }

.callout-label {
  display: block;
  margin-bottom: .5rem;
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent);
}

.article-content .callout--note  { border-left-color: var(--accent); background: var(--accent-soft); }
.article-content .callout--note  .callout-label { color: var(--accent); }
.article-content .callout--tip   { border-left-color: var(--green);  background: var(--green-soft); }
.article-content .callout--tip   .callout-label { color: var(--green); }
.article-content .callout--warn  { border-left-color: var(--orange); background: var(--orange-soft); }
.article-content .callout--warn  .callout-label { color: var(--orange); }
.article-content .callout--trap  { border-left-color: var(--red);    background: var(--red-soft); }
.article-content .callout--trap  .callout-label { color: var(--red); }

.article-quote-tip {
  font-family: var(--sans);
  font-size: .75rem;
  color: var(--text-3);
}

/* ============================================================
   12. COMMENTAIRES
   ============================================================ */
.article-comments {
  max-width: var(--measure);
  margin: 2.5rem 0 0;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
  font-family: var(--sans);
}

.article-comments-header {
  display: flex;
  align-items: baseline;
  gap: .625rem;
  margin-bottom: 1.5rem;
}
.article-comments-title { font-size: 1.5rem; }
.article-comments-count {
  padding: .1rem .5rem;
  font-size: .75rem;
  font-weight: 700;
  color: var(--text-3);
  background: var(--bg-3);
  border-radius: var(--r-full);
}

.article-comments-empty {
  font-size: .9375rem;
  color: var(--text-3);
}

.article-comment {
  display: flex;
  gap: .875rem;
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--border);
}

.article-comment-avatar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  font-size: .875rem;
  font-weight: 700;
  color: var(--text-2);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
}

.article-comment-body-wrap { flex: 1; min-width: 0; }

.article-comment-meta {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  flex-wrap: wrap;
  margin-bottom: .35rem;
  font-size: .8125rem;
}
.article-comment-meta strong { font-weight: 600; color: var(--text); }
.article-comment-meta time,
.article-comment-meta span { color: var(--text-3); }

.article-comment-body {
  font-size: .9375rem;
  line-height: 1.65;
  color: var(--text-2);
  overflow-wrap: anywhere;
}

.article-comment-notice {
  padding: .875rem 1.125rem;
  margin-bottom: 1.5rem;
  font-size: .875rem;
  color: var(--green);
  background: var(--green-soft);
  border: 1px solid var(--accent-line);
  border-color: color-mix(in srgb, var(--green) 30%, transparent);
  border-radius: var(--r-sm);
}

.article-comment-error {
  padding: .875rem 1.125rem;
  margin-bottom: 1.5rem;
  font-size: .875rem;
  color: var(--red);
  background: var(--red-soft);
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  border-radius: var(--r-sm);
}

.article-comment-form-wrap {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}

/* Le gabarit pose un <h3> sans classe : il retombait sur la règle de
   base et détonnait au milieu d'un bloc en sans. */
.article-comment-form-wrap h3 {
  font-size: 1.125rem;
  font-weight: 600;
  margin-bottom: 1.25rem;
}

.article-comment-form { display: flex; flex-direction: column; gap: 1rem; }

.article-comment-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.article-comment-field { display: flex; flex-direction: column; gap: .375rem; }

.article-comment-field label {
  font-size: .75rem;
  font-weight: 600;
  color: var(--text-2);
}

.article-comment-field input,
.article-comment-field textarea {
  width: 100%;
  padding: .7rem .875rem;
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--text);
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.article-comment-field textarea { min-height: 120px; resize: vertical; line-height: 1.6; }
.article-comment-field input::placeholder,
.article-comment-field textarea::placeholder { color: var(--text-3); }
.article-comment-field input:focus,
.article-comment-field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.article-comment-field-hint,
.article-comment-privacy {
  font-size: .75rem;
  color: var(--text-3);
}

/* Aligné sous l'angle bas-droit de la zone de saisie, là où l'œil le
   cherche quand il surveille une limite. */
.article-comment-char-count {
  align-self: flex-end;
  font-size: .75rem;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

/* La mention de modération suit le bouton au lieu d'être renvoyée à
   l'autre bout de la ligne : à 680px de large, elle s'en trouvait
   séparée de six cents pixels et ne se lisait plus comme s'y
   rapportant. */
.article-comment-submit-row {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
  margin-top: .25rem;
}

/* ============================================================
   13. CARTE AUTEUR
   ============================================================ */
.author-card {
  max-width: var(--measure);
  margin: 3rem 0 0;
  padding: 1.5rem;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--sans);
}

.author-card-label { display: block; margin-bottom: 1rem; }

.author-card-inner { display: flex; gap: 1.125rem; align-items: flex-start; }

.author-card-avatar,
.author-card-avatar-placeholder {
  flex-shrink: 0;
  width: 52px; height: 52px;
  object-fit: cover;
  border-radius: var(--r-full);
  border: 1px solid var(--border-2);
}
.author-card-avatar-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--text-2);
  background: var(--bg-4);
}

.author-card-body { flex: 1; min-width: 0; }

.author-card-name {
  font-family: var(--sans);
  font-size: 1.125rem;
  font-weight: 600;
  margin-bottom: .25rem;
}

.author-card-bio {
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--text-2);
}

.author-card-linkedin {
  display: inline-block;
  margin-top: .625rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--accent);
}

/* ============================================================
   14. NEWSLETTER
   ============================================================ */

/* ── Encart pleine largeur ── */
.newsletter-inline {
  max-width: var(--measure);
  margin: 3rem auto 0;
  padding: 2rem;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  font-family: var(--sans);
}

.newsletter-inline-eyebrow { display: block; margin-bottom: .75rem; }

.newsletter-inline-title {
  font-family: var(--sans);
  font-size: 1.625rem;
  font-weight: 600;
  line-height: 1.2;
  margin-bottom: .625rem;
}

.newsletter-inline-desc {
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--text-2);
  margin-bottom: 1.25rem;
  max-width: 52ch;
}

.newsletter-inline-form,
.newsletter-widget-form { display: flex; flex-direction: column; gap: .75rem; }

.newsletter-inline-input,
.newsletter-widget-input,
.blog-404-search-input,
.search-input {
  width: 100%;
  padding: .75rem 1rem;
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--text);
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.newsletter-inline-input::placeholder,
.newsletter-widget-input::placeholder,
.blog-404-search-input::placeholder,
.search-input::placeholder { color: var(--text-3); }
.newsletter-inline-input:focus,
.newsletter-widget-input:focus,
.blog-404-search-input:focus,
.search-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.newsletter-inline-consent,
.newsletter-widget-consent,
.newsletter-inline-privacy {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  font-size: .75rem;
  line-height: 1.5;
  color: var(--text-3);
}
.newsletter-inline-consent input,
.newsletter-widget-consent input {
  flex-shrink: 0;
  margin-top: .15rem;
  accent-color: var(--accent);
}

.newsletter-inline-msg,
.newsletter-widget-msg {
  font-size: .8125rem;
  color: var(--text-2);
}
.newsletter-inline-msg.is-error,
.newsletter-widget-msg.is-error { color: var(--red); }
.newsletter-inline-msg.is-success,
.newsletter-widget-msg.is-success { color: var(--green); }

.newsletter-inline-confirm {
  display: flex;
  align-items: center;
  gap: .625rem;
  color: var(--green);
}
.newsletter-inline-confirm-text {
  font-size: .9375rem;
  font-weight: 600;
}

/* ── Widget de colonne ── */
.newsletter-widget {
  padding: 1.25rem;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--sans);
}

.newsletter-widget-title {
  font-family: var(--sans);
  font-size: 1.125rem;
  font-weight: 600;
  margin-bottom: .5rem;
}

.newsletter-widget-desc {
  font-size: .875rem;
  line-height: 1.55;
  color: var(--text-2);
  margin-bottom: 1rem;
}

.newsletter-widget-btn {
  width: 100%;
  padding: .7rem 1rem;
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent-solid);
  border-radius: var(--r-sm);
  transition: background .15s ease;
}
.newsletter-widget-btn:hover { background: var(--accent-solid-hi); }
.newsletter-widget-btn:disabled { opacity: .6; cursor: not-allowed; }

.nl-btn-label { display: inline; }
.nl-btn-spinner {
  display: none;
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--r-full);
  animation: c365-spin .6s linear infinite;
}
.is-loading .nl-btn-label { display: none; }
.is-loading .nl-btn-spinner { display: inline-block; }

@keyframes c365-spin { to { transform: rotate(360deg); } }

/* ── Page newsletter ───────────────────────────────────────────
   Page ordinaire du site : ossature .blog-main / .blog-col-main,
   alignée à gauche sur la colonne de lecture. Aucune mise en scène
   centrée — s'abonner tient en un champ et un bouton.
   ------------------------------------------------------------ */
.nl-page-title {
  font-size: clamp(1.875rem, 4.5vw, 2.5rem);
  line-height: 1.12;
  letter-spacing: -.022em;
  margin: .625rem 0 1rem;
}

.nl-page-desc {
  font-family: var(--sans);
  font-size: 1.125rem;
  line-height: 1.65;
  color: var(--text-2);
  max-width: 58ch;
}

/* ── Formulaire nu ─────────────────────────────────────────────
   Ni cadre ni fond : il n'y a rien d'autre sur la page dont il
   faudrait se détacher. Champ et bouton sur une ligne, mention de
   consentement dessous.

   La grille place explicitement les trois éléments visibles. Les
   champs cachés (jeton CSRF, origine, pot de miel) ne créent pas de
   cellule : un input hidden ne se rend pas, et le pot de miel porte
   déjà display:none.
   ------------------------------------------------------------ */
.newsletter-inline--bare {
  max-width: 460px;
  margin: 1.75rem 0 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
}

.newsletter-inline--bare .newsletter-inline-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .875rem .625rem;
}

.newsletter-inline--bare .newsletter-inline-input { grid-area: 1 / 1; }
.newsletter-inline--bare .blog-cta { grid-area: 1 / 2; white-space: nowrap; }
.newsletter-inline--bare .newsletter-inline-consent { grid-area: 2 / 1 / 2 / -1; }
.newsletter-inline--bare .newsletter-inline-msg { grid-area: 3 / 1 / 3 / -1; }

@media (max-width: 480px) {
  /* Le bouton passe sous le champ : côte à côte, il ne reste plus
     assez de place pour saisir une adresse. */
  .newsletter-inline--bare .newsletter-inline-form { grid-template-columns: minmax(0, 1fr); }
  .newsletter-inline--bare .newsletter-inline-input { grid-area: 1 / 1; }
  .newsletter-inline--bare .blog-cta { grid-area: 2 / 1; }
  .newsletter-inline--bare .newsletter-inline-consent { grid-area: 3 / 1; }
  .newsletter-inline--bare .newsletter-inline-msg { grid-area: 4 / 1; }
}

.nl-page-rss {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  font-family: var(--sans);
  font-size: .875rem;
  color: var(--text-3);
}

.nl-cat-prefs { margin-top: 1rem; }
.nl-cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: .5rem;
  margin-top: .625rem;
}
.nl-cat-label {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .75rem;
  font-size: .8125rem;
  color: var(--text-2);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: border-color .15s ease;
}
.nl-cat-label:hover { border-color: var(--border-2); }
.nl-cat-label input { accent-color: var(--accent); }


/* ============================================================
   15. RUBRIQUES & CATÉGORIE
   ============================================================ */

/* Le hero des rubriques portait deux halos décoratifs, retirés du
   balisage : en éditorial, le blanc fait le travail. */
.cats-hero {
  padding: 3.5rem 0 2.5rem;
  border-bottom: 1px solid var(--border);
}
.cats-hero-inner { max-width: var(--measure); }

.cats-hero-title {
  font-size: clamp(2.125rem, 5vw, 3rem);
  line-height: 1.12;
  letter-spacing: -.022em;
  margin-bottom: 1rem;
}
.cats-hero-highlight { color: var(--accent); }

.cats-hero-lead {
  font-family: var(--sans);
  font-size: 1.1875rem;
  line-height: 1.6;
  color: var(--text-2);
  max-width: 58ch;
}

.cats-section { padding: 2.5rem 0 1rem; }
.cats-section .container { max-width: var(--measure); }

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

.cats-item {
  display: flex;
  align-items: center;
  gap: 1.125rem;
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  transition: padding-inline .18s ease;
}
.cats-item:hover { color: var(--text); padding-inline: .5rem; }

.cats-item-body { flex: 1; min-width: 0; }

.cats-item-name {
  font-size: 1.1875rem;
  line-height: 1.3;
  margin-bottom: .2rem;
}
.cats-item:hover .cats-item-name { color: var(--accent); }

.cats-item-desc {
  font-family: var(--sans);
  font-size: .9375rem;
  line-height: 1.55;
  color: var(--text-2);
}

.cats-item-end {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: .875rem;
}

/* Badge de comptage neutre : il portait autrefois une teinte pâle
   posée en ligne, pensée pour le fond noir. Les jetons suivent le
   thème, un style en ligne non. */
.cats-item-count {
  padding: .2rem .6rem;
  font-family: var(--sans);
  font-size: .75rem;
  font-weight: 700;
  white-space: nowrap;
  color: var(--text-3);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
}

.cats-item-arrow {
  color: var(--text-3);
  transition: transform .18s ease, color .18s ease;
}
.cats-item:hover .cats-item-arrow { transform: translateX(3px); color: var(--accent); }

/* ── En-tête de rubrique ─────────────────────────────────────
   La page rubrique reprend le corps de l'accueil : une liste, sans
   article mis en avant. Cet en-tête ne fait que nommer la page.
   Le filet est revenu ici : c'était le bloc « À la une », supprimé
   depuis, qui séparait l'en-tête de la liste.
   ------------------------------------------------------------ */
.cat-page-header {
  padding-bottom: 1.5rem;
  margin-bottom: 1.75rem;
  border-bottom: 1px solid var(--border);
}

.cat-page-header .blog-cat-eyebrow { display: block; margin-bottom: .5rem; }

.cat-page-title {
  font-size: 1.75rem;
  line-height: 1.2;
  letter-spacing: -.018em;
  margin-bottom: .5rem;
}
.cat-page-desc {
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-2);
  margin-bottom: .5rem;
  max-width: 58ch;
}
.cat-page-count {
  font-family: var(--sans);
  font-size: .8125rem;
  color: var(--text-3);
}

/* ============================================================
   16. RECHERCHE
   ============================================================ */
.search-form {
  display: flex;
  gap: .625rem;
  margin-top: 1.5rem;
}
.search-form .search-input { flex: 1; }

.search-suggest {
  position: absolute;
  top: calc(100% + .375rem);
  left: 0; right: 0;
  z-index: 60;
  max-height: 320px;
  overflow-y: auto;
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md);
  box-shadow: var(--sh-3);
}
.search-suggest[hidden] { display: none; }
.search-suggest li { border-bottom: 1px solid var(--border); }
.search-suggest li:last-child { border-bottom: none; }
.search-suggest a {
  display: block;
  padding: .7rem 1rem;
  font-family: var(--sans);
  font-size: .875rem;
  color: var(--text-2);
}
.search-suggest a:hover,
.search-suggest li.is-active a { background: var(--bg-3); color: var(--text); }

.search-empty-state { padding: 3rem 0; }
.search-empty-message {
  font-family: var(--sans);
  font-size: 1.25rem;
  color: var(--text);
  margin-bottom: .625rem;
}
.search-empty-hint {
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--text-3);
  margin-bottom: 1.25rem;
}

.blog-cat-simple-list { display: flex; flex-wrap: wrap; gap: .5rem; }
.blog-cat-simple-chip {
  padding: .4rem .875rem;
  font-family: var(--sans);
  font-size: .8125rem;
  font-weight: 500;
  color: var(--text-2);
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  transition: border-color .15s ease, color .15s ease;
}
.blog-cat-simple-chip:hover { color: var(--accent); border-color: var(--accent-line); }

/* Terme trouvé, surligné dans les résultats. */
.home-list-row-title mark,
.home-list-row-excerpt mark {
  padding: 0 .1em;
  color: var(--text);
  background: var(--accent-soft);
  border-radius: var(--r-xs);
}

/* ============================================================
   17. PAGE « À PROPOS »
   ============================================================ */
.about-header {
  display: flex;
  align-items: flex-start;
  gap: 1.5rem;
  max-width: var(--measure);
  margin: 0 auto 2.5rem;
  padding-bottom: 2rem;
  border-bottom: 1px solid var(--border);
}

.about-avatar {
  flex-shrink: 0;
  width: 84px; height: 84px;
  object-fit: cover;
  border-radius: var(--r-full);
  border: 1px solid var(--border-2);
}
.about-avatar--initials {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 2rem;
  font-weight: 600;
  color: var(--text-2);
  background: var(--bg-3);
}

.about-header-body { flex: 1; min-width: 0; }

.about-name {
  font-size: clamp(1.75rem, 4vw, 2.375rem);
  letter-spacing: -.02em;
  margin-bottom: .5rem;
}

.about-tagline {
  font-family: var(--sans);
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--text-2);
}

/* Unique moyen de joindre l'auteur : traité comme une action, pas
   comme un lien de plus dans une ligne de texte. */
.about-linkedin {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin-top: 1.125rem;
  padding: .5rem 1rem;
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 600;
  color: var(--accent);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-full);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.about-linkedin:hover {
  color: var(--accent-hi);
  background: var(--accent-soft);
  border-color: var(--accent);
}
.about-linkedin svg { flex-shrink: 0; }

.about-content { max-width: var(--measure); margin-inline: auto; }

.about-newsletter {
  max-width: var(--measure);
  margin: 3rem auto 0;
  padding: 2rem;
  text-align: center;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.about-newsletter-title {
  font-family: var(--sans);
  font-size: 1.375rem;
  font-weight: 600;
  margin-bottom: .5rem;
}
.about-newsletter-desc {
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--text-2);
  margin-bottom: 1.25rem;
}

/* ============================================================
   18. SÉRIES
   ============================================================ */
/* Surtitre de la page série, ancien style en ligne remonté ici. */
.serie-eyebrow {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .875rem;
  font-family: var(--sans);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--accent);
}

.serie-count {
  margin-top: .75rem;
  font-family: var(--sans);
  font-size: .8125rem;
  color: var(--text-3);
}

.serie-list {
  max-width: var(--measure);
  margin-inline: auto;
  counter-reset: serie;
}

.serie-item {
  display: flex;
  align-items: flex-start;
  gap: 1.125rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--border);
}

.serie-num {
  flex-shrink: 0;
  width: 2rem;
  font-family: var(--sans);
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

.serie-content { display: flex; gap: 1.125rem; flex: 1; min-width: 0; }

/* Même principe que les vignettes de liste : format naturel, ni
   recadrage ni bandes. */
.serie-thumb {
  order: 2;
  flex-shrink: 0;
  display: block;
  width: 150px;
  height: auto;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
}

.serie-text { order: 1; flex: 1; min-width: 0; }

/* Le marqueur lui-même vient de la section 7 : ici, seulement sa
   place dans la rangée de série. */
.serie-cat {
  margin-bottom: .35rem;
  font-family: var(--sans);
  font-size: .6875rem;
}

.serie-title {
  display: block;
  font-family: var(--sans);
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text);
  margin-bottom: .35rem;
}
.serie-title:hover { color: var(--accent); }

.serie-meta {
  font-family: var(--sans);
  font-size: .8125rem;
  color: var(--text-3);
}

/* ============================================================
   19. PARTENAIRES
   ============================================================ */
.partner-leaderboard,
.partner-tile {
  position: relative;
  display: block;
  padding: 1rem;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color .15s ease;
}
.partner-leaderboard:hover,
.partner-tile:hover { border-color: var(--border-2); }

.partner-badge {
  position: absolute;
  top: .5rem; right: .75rem;
  font-family: var(--sans);
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--text-3);
}

.partner-leaderboard-banner-img,
.partner-tile-banner-img {
  width: 100%;
  border-radius: var(--r-sm);
}

.partner-leaderboard-logo,
.partner-tile-logo-img {
  max-height: 40px;
  width: auto;
  margin-inline: auto;
}

.partner-logo-text {
  display: block;
  font-family: var(--sans);
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text-2);
  text-align: center;
}

/* ============================================================
   20. PAGE 404
   ============================================================ */
.blog-404-hero {
  max-width: var(--measure);
  margin: 0 auto 4rem;
  padding: 3rem 0;
  text-align: center;
}

.blog-404-code {
  display: block;
  font-family: var(--sans);
  font-size: clamp(4rem, 14vw, 7rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.04em;
  color: var(--border-2);
  margin-bottom: 1rem;
}

.blog-404-title {
  font-size: clamp(1.75rem, 4vw, 2.375rem);
  letter-spacing: -.02em;
  margin-bottom: .875rem;
}

.blog-404-lead {
  font-family: var(--sans);
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--text-2);
  margin-bottom: 2rem;
}

.blog-404-search {
  display: flex;
  gap: .625rem;
  max-width: 420px;
  margin: 0 auto 1.5rem;
}
.blog-404-search-input { flex: 1; }

.blog-404-search-btn {
  padding: .75rem 1.25rem;
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent-solid);
  border-radius: var(--r-sm);
  white-space: nowrap;
  transition: background .15s ease;
}
.blog-404-search-btn:hover { background: var(--accent-solid-hi); }

.blog-404-actions {
  display: flex;
  justify-content: center;
  gap: 1rem;
}
.blog-404-btn-home {
  font-family: var(--sans);
  font-size: .875rem;
  font-weight: 600;
  color: var(--text-2);
}
.blog-404-btn-home:hover { color: var(--accent); }

.blog-404-recent { max-width: var(--measure); margin-inline: auto; }
.blog-404-recent-title {
  font-size: 1.375rem;
  margin-bottom: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}

/* ============================================================
   21. FORMULAIRES GÉNÉRIQUES
   ============================================================ */
.simple-form {
  max-width: var(--measure);
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  font-family: var(--sans);
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.form-field { display: flex; flex-direction: column; gap: .375rem; }

.form-field label {
  font-size: .75rem;
  font-weight: 600;
  color: var(--text-2);
}

.form-field input,
.form-field textarea,
.form-field select {
  width: 100%;
  padding: .7rem .875rem;
  font-family: var(--sans);
  font-size: .9375rem;
  color: var(--text);
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.form-field textarea { min-height: 140px; resize: vertical; line-height: 1.6; }
.form-field input::placeholder,
.form-field textarea::placeholder { color: var(--text-3); }
.form-field input:focus,
.form-field textarea:focus,
.form-field select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.form-errors,
.form-success,
.notice,
.error {
  padding: .875rem 1.125rem;
  font-family: var(--sans);
  font-size: .875rem;
  line-height: 1.55;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
}
.form-errors,
.error {
  color: var(--red);
  background: var(--red-soft);
  border-color: color-mix(in srgb, var(--red) 30%, transparent);
}
.form-success {
  color: var(--green);
  background: var(--green-soft);
  border-color: color-mix(in srgb, var(--green) 30%, transparent);
}
.notice {
  color: var(--text-2);
  background: var(--bg-3);
  border-color: var(--border);
}
.form-errors ul { padding-left: 1.1rem; }
.form-errors li { list-style: disc; }

.status-card {
  max-width: var(--measure);
  margin-inline: auto;
  padding: 2rem;
  text-align: center;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.status-pill {
  display: inline-block;
  padding: .25rem .75rem;
  font-family: var(--sans);
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  border-radius: var(--r-full);
  background: var(--bg-4);
  color: var(--text-2);
}

/* Interrupteur (préférences newsletter) */
.toggle-switch { display: inline-flex; align-items: center; cursor: pointer; }
.toggle-track {
  position: relative;
  width: 38px; height: 22px;
  background: var(--bg-5);
  border-radius: var(--r-full);
  transition: background .18s ease;
}
.toggle-thumb {
  position: absolute;
  top: 3px; left: 3px;
  width: 16px; height: 16px;
  background: var(--bg-2);
  border-radius: var(--r-full);
  box-shadow: var(--sh-1);
  transition: transform .18s cubic-bezier(.2, .7, .3, 1);
}
.toggle-switch input:checked + .toggle-track { background: var(--accent-solid); }
.toggle-switch input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); }

/* ============================================================
   22. PIED DE PAGE
   ============================================================ */
/* margin-top:auto absorbe l'espace libre de la colonne : sur une page
   courte le pied descend au bas de la fenêtre au lieu de flotter avec
   du vide dessous ; sur une page longue il n'ajoute rien, et l'écart
   avec le contenu est celui de .blog-main seul. Auparavant les deux
   marges s'additionnaient, soit 160px de blanc systématique. */
.blog-footer {
  margin-top: auto;
  padding-block: 2.5rem;
  border-top: 1px solid var(--border);
  background: var(--bg);
}

.blog-footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: 1.5rem;
  font-family: var(--sans);
  font-size: .8125rem;
}

.blog-footer-copy { color: var(--text-3); }

.blog-footer-links {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.blog-footer-links a { color: var(--text-2); }
.blog-footer-links a:hover { color: var(--accent); }

/* ============================================================
   23. AIDE CLAVIER
   ============================================================ */
.kbd-help {
  position: fixed;
  inset: 0;
  z-index: 250;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: rgba(10, 9, 8, .55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.kbd-help[hidden] { display: none; }

.kbd-help-panel {
  width: 100%;
  max-width: 380px;
  padding: 1.75rem;
  background: var(--bg-2);
  border: 1px solid var(--border-2);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
  font-family: var(--sans);
}

.kbd-help-title {
  font-family: var(--sans);
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 1.25rem;
}

.kbd-help-list { display: flex; flex-direction: column; gap: .75rem; }
.kbd-help-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  font-size: .875rem;
  color: var(--text-2);
}

.kbd-keys { display: flex; gap: .3rem; flex-shrink: 0; }

kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  padding: .15rem .35rem;
  font-family: var(--mono);
  font-size: .75rem;
  color: var(--text);
  background: var(--bg-3);
  border: 1px solid var(--border-2);
  border-bottom-width: 2px;
  border-radius: var(--r-xs);
}

.kbd-help-hint {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  font-size: .75rem;
  color: var(--text-3);
}

/* ============================================================
   24. ÉTATS
   ============================================================ */

/* Rangée sélectionnée au clavier (j / k). Le repère est un trait
   dans la marge, pas un fond : la liste reste calme. */
.home-list-row.is-focused,
.blog-list-entry.is-focused {
  box-shadow: -12px 0 0 -10px var(--accent);
}

/* Article déjà lu : la rangée s'efface un peu. */
/* Article déjà ouvert : le titre s'apaise, rien de plus. La vignette
   tombait auparavant à 65 % d'opacité — sur le fond du cadre, et plus
   encore en thème sombre, l'image virait au gris et donnait l'illusion
   d'un filtre appliqué par erreur. Un repère de lecture ne doit pas
   abîmer l'illustration. */
.home-list-row.is-read .home-list-row-title,
.blog-list-entry.is-read .blog-list-entry-title { color: var(--text-2); }

/* Le survol reprend la main : sans cela, la règle « déjà lu », de même
   poids mais déclarée plus bas, empêchait le titre de réagir. */
.home-list-row.is-read .home-list-row-link:hover .home-list-row-title,
.blog-list-entry.is-read .blog-list-entry-link:hover .blog-list-entry-title { color: var(--accent); }

[hidden] { display: none !important; }

/* ============================================================
   25. RESPONSIVE
   ============================================================ */

/* Le sommaire latéral cède avant la colonne de lecture. */
@media (max-width: 1120px) {
  /* Le conteneur perd la gouttière en même temps que le sommaire,
     sinon la colonne de lecture resterait décalée à gauche. */
  .article-columns { max-width: var(--measure); }
  .article-body { grid-template-columns: minmax(0, 1fr); }
  .article-toc-sidebar { display: none; }
}

/* La colonne de droite passe sous le contenu : en dessous, la place
   manque pour deux colonnes sans rogner la mesure de lecture. */
@media (max-width: 1040px) {
  .blog-main.has-aside {
    display: block;
    max-width: var(--measure);
  }
  .blog-main.has-aside .blog-col-main { margin-inline: auto; }

  .blog-aside {
    position: static;
    max-height: none;
    overflow: visible;
    margin-top: 4rem;
    padding-top: 2.5rem;
    border-top: 1px solid var(--border);
  }

  /* Repliée sous le contenu, la liste des rubriques s'étalerait sur
     une colonne interminable : elle passe en colonnes de texte. */
  .blog-aside-cats {
    columns: 2;
    column-gap: 2.5rem;
  }
  .blog-aside-cats li { break-inside: avoid; }
}

@media (max-width: 520px) {
  .blog-aside-cats { columns: 1; }
}

@media (max-width: 560px) {
  /* Le libellé cède, l'icône suffit à identifier la recherche. */
  .snav-search-label { display: none; }
  .snav-search-link { padding: .45rem .6rem; }
}

@media (max-width: 720px) {
  .blog-main { padding: 2.5rem 1.25rem 3.5rem; }
  .article-main { padding-top: 1.75rem; }

  .home-list-row,
  .blog-list-entry { gap: 1rem; padding: 1.375rem 0; }

  /* Vignettes : seule la page de recherche en affiche encore. */
  .home-list-row-thumb-wrap { width: 128px; }


  /* .home-list-row-title n'est pas repris ici : son clamp() descend
     déjà à 1.5rem, et l'écraser à 1.1875 annulerait le parti pris. */
  .blog-list-entry-title { font-size: 1.1875rem; }

  .article-content { font-size: 1.0625rem; line-height: 1.68; }
  .article-content h2 { font-size: 1.5rem; }
  .article-content h3 { font-size: 1.1875rem; }
  /* Trois lignes de lettrine mangent trop d'une colonne étroite. */
  .article-content > p:first-of-type::first-letter { font-size: 2.7em; line-height: .88; }

  /* Les tableaux larges défilent dans leur boîte, jamais la page. */
  .article-content table { display: block; overflow-x: auto; white-space: nowrap; }

  .article-comment-row,
  .form-row { grid-template-columns: 1fr; }

  .about-header { flex-direction: column; gap: 1rem; }
  .about-avatar { width: 64px; height: 64px; }

  .serie-content { flex-direction: column; }
  .serie-thumb { order: 0; width: 100%; max-width: 220px; }

  .blog-footer-inner { flex-direction: column; align-items: flex-start; gap: 1rem; }
  .blog-footer-links { gap: 1rem; }

  .blog-pg { flex-wrap: wrap; justify-content: center; }
  .blog-pg-list { order: -1; width: 100%; justify-content: center; margin-bottom: .75rem; }

  .search-form { flex-direction: column; }
  .blog-404-search { flex-direction: column; }
}

@media (max-width: 480px) {
  .home-list-row-thumb-wrap { width: 104px; }
  .newsletter-inline { padding: 1.5rem; }
  .cats-item-icon { width: 34px; height: 34px; }
}

/* ============================================================
   26. IMPRESSION
   ============================================================ */
@media print {
  :root {
    --bg: #FFF; --bg-2: #FFF; --bg-3: #FFF;
    --text: #000; --text-2: #222; --text-3: #555;
    --border: #CCC; --accent: #000;
  }

  .snav,
  .blog-footer,
  .article-toc-sidebar,
  .article-actions,
  .article-comments,
  .newsletter-inline,
  .newsletter-widget,
  .home-partner-strip,
  .partner-leaderboard,
  .partner-tile,
  .kbd-help,
  .hv3-article-back,
  #readingProgress,
  .article-carousel-thumbs,
  .article-carousel-btn { display: none !important; }

  body { background: #FFF; color: #000; }

  .blog-main, .article-columns, .article-body { max-width: none; display: block; }

  .article-content { font-size: 11pt; line-height: 1.5; }
  .article-content a { text-decoration: underline; }
  /* L'URL des liens externes, faute de pouvoir cliquer sur du papier. */
  .article-content a[href^="http"]::after {
    content: ' (' attr(href) ')';
    font-size: 9pt;
    color: #555;
    word-break: break-all;
  }

  .article-content pre,
  .article-content img,
  .article-content blockquote { break-inside: avoid; }

  .article-content h2,
  .article-content h3 { break-after: avoid; }
}
