/* =========================================================================
   Altius Group — couche « Console » du site public, par-dessus Anw Theme.

   Édition 3 (août 2026) : refonte des layouts, fond sombre par défaut.
   L'édition 2 posait un système clair à filets ; celle-ci le retourne sur
   l'encre et pousse la mono partout où il y a une étiquette ou un chiffre.
   Registre visé : document technique d'un éditeur de logiciel, pas plaquette
   d'agence.

   Ce qui vient de la charte, sans écart :
     · deux fonds seulement — l'encre et le papier. Le site public tourne sur
       l'encre ; le mode clair reste servi par les mêmes règles, via les
       jetons de rôle (jamais d'hexadécimal en dur ici) ;
     · la terre cuite CLAIRE (clay-400) est l'accent sur fond encre — c'est
       la charte p. 06 qui l'impose, et c'est aussi le seul « vif » du site ;
     · Archivo pour les titres, IBM Plex Mono pour tout le reste de la
       signalétique : numéros de section, unités, libellés de navigation,
       boutons, en-têtes de colonne, légendes de graphique ;
     · aucune ombre sur les pages publiques. Un filet d'un pixel, ou rien.

   Préfixe `ag-` = pièce de CE système. Les classes sans préfixe
   (`.public-nav`, `.site-footer`, `.faq__item`…) sont celles que theme.js
   pilote : conservées et resurchargées, jamais renommées.
   ========================================================================= */

/* ═════════════════════════════════════════════════════════════════════════
   1. RÉGLAGES
   ═════════════════════════════════════════════════════════════════════════ */
[data-bs-theme="dark"] {
  /* theme.css fige `brand-300` pour le texte d'emphase en sombre ; la charte
     veut clay-400 (contraste mesuré 5,5 : 1). */
  --bs-primary-text-emphasis: var(--clay-400);
}

body.site-public {
  /* La coquille est BORNÉE et centrée ; les sections, elles, restent pleine
     largeur. Les deux à la fois : les filets de section, les fonds et la
     grille technique vont toujours d'un bord à l'autre — c'est l'identité du
     site —, mais le CONTENU se range dans une colonne centrale avec de la
     marge des deux côtés.

     Ce que corrige ce réglage : à 100 %, sur un écran de 1600 px, une section
     à deux colonnes étalait un titre à gauche et un chapô à 600 px de là,
     séparés par un vide que l'œil ne franchit pas. Le texte était borné par
     `--ag-measure` mais sa COLONNE ne l'était pas, et le blanc tombait tout
     entier au milieu au lieu de se répartir sur les bords. Deux blocs proches
     dans une page étroite se lisent comme liés ; les mêmes blocs écartés de
     600 px se lisent comme étrangers.

     Une seule valeur à régler pour toute la largeur du site. */
  --ag-shell:   1280px;
  --ag-gutter:  40px;
  --ag-measure: 62ch;
  --ag-sec-y:   84px;      /* dense : la page doit se lire, pas respirer */
  --ag-rule:    1px solid var(--color-border);
  --ag-filet-w: 26px;
  --ag-line:    rgba(16, 18, 17, .07);   /* trait de la grille de fond */
  --ag-cell:    72px;                    /* maille de la grille de fond */

  /* Échelle typographique du site public, relevée d'un cran par rapport aux
     jetons de la charte (16 px). Redéclarée ICI et non dans theme-tokens.css :
     le chrome applicatif — tableau de bord, admin — doit rester à son échelle
     dense, et la charte règle la page, pas la barre d'outils. */
  --text-body:  17px;
  --text-small: 15px;
  --text-label: 12.5px;
  --text-mono:  15px;
}
[data-bs-theme="dark"] body.site-public {
  --ag-line: rgba(244, 243, 240, .055);
}
@media (max-width: 1199.98px) { body.site-public { --ag-gutter: 28px; } }
@media (max-width: 767.98px) {
  body.site-public { --ag-gutter: 20px; --ag-sec-y: 52px; --ag-cell: 48px; }
}
body.site-public { background: var(--color-bg); color: var(--color-text); }

/* ═════════════════════════════════════════════════════════════════════════
   2. TYPOGRAPHIE
   Tout est enveloppé dans `:where()`, qui ne compte pour rien dans le calcul
   de spécificité : ces règles sont des DÉFAUTS, et le moindre composant qui
   pose sa propre taille ou sa propre couleur doit l'emporter.
   ═════════════════════════════════════════════════════════════════════════ */
:where(body.site-public) {
  font-family: var(--font-sans);
  font-weight: 400;
}
/* Le corps de texte est posé à spécificité RÉELLE, hors `:where()`. Dans le
   wrapper il pesait 0 et perdait contre l'échelle dense (13 px) que theme.css
   pose sur le body : les paragraphes non classés retombaient à 13 px juste
   sous un chapô à 17. Les défauts de titres et d'étiquettes restent, eux, dans
   `:where()` — un composant doit pouvoir les surcharger. */
body.site-public {
  font-size: var(--text-body);
  line-height: var(--text-body-lh);
}
/* Archivo n'a pas les variantes stylistiques d'Inter : les demander laissait
   le moteur chercher des tables absentes. Hors `:where()` — correction, pas
   défaut à laisser surcharger. */
body.site-public { font-feature-settings: normal; }

:where(body.site-public) :where(h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6) {
  font-family: var(--font-sans);
  font-weight: 600;
  text-wrap: pretty;
}
:where(body.site-public) :where(h1, .h1) {
  font-size: var(--text-h1); line-height: var(--text-h1-lh); letter-spacing: var(--text-h1-track);
}
:where(body.site-public) :where(h2, .h2) {
  font-size: clamp(26px, 2.6vw, 34px); line-height: 1.15; letter-spacing: -.022em;
}
:where(body.site-public) :where(h3, .h3) { font-size: 21px; line-height: 1.3; letter-spacing: -.012em; }
:where(body.site-public) :where(h4, .h4, h5, .h5, h6, .h6) { font-size: var(--text-body); line-height: 1.35; }
:where(body.site-public) :where(.display-title, .hero__title) {
  font-size: clamp(32px, 5.2vw, var(--text-display));
  line-height: var(--text-display-lh);
  letter-spacing: var(--text-display-track);
  font-weight: 600;
}
:where(body.site-public) :where(small, .small) { font-size: var(--text-small); line-height: var(--text-small-lh); }
:where(body.site-public) :where(code, kbd, samp, pre) {
  font-family: var(--font-mono); font-size: var(--text-mono); line-height: var(--text-mono-lh);
}

/* L'étiquette du système : mono, capitales, +0,08 em. Elle remplace partout
   l'étiquette Archivo espacée de l'édition précédente — c'est ce qui donne au
   site son air d'appareil de mesure plutôt que de page d'accueil. */
.ag-mono,
:where(body.site-public) :where(.eyebrow, .section-label, .hero__eyebrow, .logo-row__caption) {
  font-family: var(--font-mono);
  font-size: var(--text-label);
  line-height: var(--text-label-lh);
  letter-spacing: .09em;
  text-transform: uppercase;
  font-weight: 400;
}
.ag-mono--sm { font-size: 10.5px; letter-spacing: .12em; }
.ag-muted { color: var(--color-text-muted); }
.ag-accent { color: var(--accent-fg); }

/* Le filet : 26 × 2 px de terre cuite. Seul ornement du système — reprise du
   filet qui sépare ALTIUS de GROUP dans le logotype. */
.ag-filet {
  display: block; width: var(--ag-filet-w); height: 2px;
  background: var(--accent-fg); border: 0; flex: none;
}

/* ═════════════════════════════════════════════════════════════════════════
   3. GRILLE TECHNIQUE DE FOND
   Une maille de 72 px, tracée en pseudo-élément et non en image : elle suit
   la couleur du thème et ne coûte aucune requête. `mask-image` l'estompe vers
   le bas pour qu'elle n'entre jamais en concurrence avec le texte.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-mesh { position: relative; isolation: isolate; }
.ag-mesh > * { position: relative; z-index: 1; }
.ag-mesh::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right,  var(--ag-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--ag-line) 1px, transparent 1px);
  background-size: var(--ag-cell) var(--ag-cell);
  mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 55%, transparent 100%);
}

/* ═════════════════════════════════════════════════════════════════════════
   4. PRIMITIVES DE MISE EN PAGE
   ═════════════════════════════════════════════════════════════════════════ */
.ag-shell {
  width: 100%; max-width: var(--ag-shell);
  margin-inline: auto; padding-inline: var(--ag-gutter);
}

/* Section : un filet pleine largeur en tête. C'est lui qui donne le rythme —
   pas un fond alterné à chaque bloc. */
.ag-section { padding-block: var(--ag-sec-y); border-top: var(--ag-rule); }
.ag-section--flush { border-top: 0; }
.ag-section--tight { padding-block: calc(var(--ag-sec-y) * .55); }

/* Bloc tranché : la surface remonte d'un cran et le bloc se referme sur deux
   filets. Deux fonds seulement dans tout le site — celui de la page et
   celui-ci ; aucune troisième teinte n'est introduite. */
.ag-section--panel {
  background: var(--color-surface);
  border-top: var(--ag-rule);
  border-bottom: var(--ag-rule);
}

/* En-tête de section : 5/7 asymétrique, numéro et filet à gauche, chapô à
   droite. Le texte centré est banni — il fait ressembler chaque page à
   toutes les autres. */
.ag-sec-head {
  display: grid; grid-template-columns: 1fr;
  gap: 18px 40px; margin-bottom: 44px;
}
@media (min-width: 992px) { .ag-sec-head { grid-template-columns: 5fr 7fr; align-items: start; } }
.ag-sec-head__left { display: flex; flex-direction: column; gap: 14px; }
.ag-sec-head__title { margin: 0; }
.ag-num {
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .1em; color: var(--color-text-muted);
  display: flex; align-items: center; gap: 12px;
}
.ag-num__id { color: var(--accent-fg); }
.ag-lede {
  margin: 0; color: var(--color-text-muted); max-width: var(--ag-measure);
  font-size: var(--text-body); line-height: var(--text-body-lh); text-wrap: pretty;
}

.ag-grid { display: grid; gap: 20px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .ag-grid--2, .ag-grid--3, .ag-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 992px) {
  .ag-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ag-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* Au-delà de 1440 px la page ne s'arrête plus : trois cartes étirées sur un
   écran large donnent des pavés de texte de 40 mots par ligne. `auto-fill`
   ajoute donc des colonnes au lieu d'élargir les existantes — cinq services
   se rangent en cinq colonnes sur un 1920, en trois sur un portable. */
@media (min-width: 1440px) {
  .ag-grid--3, .ag-grid--4 { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
}
.ag-split { display: grid; grid-template-columns: 1fr; gap: 32px; align-items: start; }
@media (min-width: 992px) {
  /* 48px et non 64 : deux colonnes séparées par un vide plus large qu'un
     paragraphe cessent d'être lues comme un même bloc. */
  .ag-split { grid-template-columns: 5fr 7fr; gap: 48px; }
  .ag-split--7-5 { grid-template-columns: 7fr 5fr; }
  .ag-split--even { grid-template-columns: 1fr 1fr; }
}
/* Une colonne absente ne se réserve pas. Le cas se produit dès qu'un gabarit
   rend son second bloc sous condition — l'image d'`about.html` par exemple :
   sans image, la grille gardait 5 colonnes sur 12 de vide à droite, et le
   texte paraissait poussé dans un coin. `:only-child` ne compte que les
   éléments, donc un commentaire de gabarit ne trompe pas la règle. */
.ag-split:has(> :only-child) { grid-template-columns: minmax(0, 1fr); }
/* Et le bloc restant se CENTRE. Sans cette ligne il garde sa mesure de lecture
   — c'est voulu — mais collée au bord gauche, et tout le blanc tombe du même
   côté : c'est précisément l'effet « texte poussé dans un coin » qu'on
   corrige. Le texte, lui, reste ferré à gauche : on centre la colonne, pas les
   lignes. */
.ag-split:has(> :only-child) > * { margin-inline: auto; }

/* Liste à filets : le motif de liste par défaut du système. */
.ag-rows { list-style: none; margin: 0; padding: 0; border-top: var(--ag-rule); }
.ag-rows > li {
  border-bottom: var(--ag-rule); padding: 13px 0;
  display: flex; gap: 14px; align-items: baseline; font-size: var(--text-small);
}
.ag-rows__key {
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .08em; color: var(--accent-fg); flex: none; min-width: 2.5em;
}

/* ═════════════════════════════════════════════════════════════════════════
   5. BOUTONS DES PAGES PUBLIQUES
   `.ag-btn` est indépendant du `.btn` du thème : les formulaires gardent le
   bouton dense du thème, les appels à l'action de page prennent celui-ci —
   mono, angle vif, aucune ombre.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 13px 18px;
  border-radius: var(--radius-xs);
  border: 1px solid transparent;
  /* Déclaré, et non laissé au défaut. Sur le site public tous les `.ag-btn`
     sont des `<a>`, transparents par nature ; le premier `<button>` posé avec
     la variante « contour » est ressorti en gris système, parce qu'un bouton
     porte un fond tant qu'on ne le lui retire pas. */
  background: transparent;
  font-family: var(--font-mono);
  font-size: var(--text-label);
  letter-spacing: .09em;
  text-transform: uppercase;
  font-weight: 400;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t-base), color var(--t-base), border-color var(--t-base);
}
.ag-btn i { font-size: 1.15em; }
.ag-btn--solid  { background: var(--color-primary); color: var(--color-on-primary); }
.ag-btn--solid:hover  { background: var(--color-primary-hover); color: var(--color-on-primary); }
.ag-btn--outline { border-color: var(--color-border); color: var(--color-text); }
.ag-btn--outline:hover { border-color: var(--color-text); color: var(--color-text); }
.ag-btn--accent  { border-color: var(--accent-fg); color: var(--accent-fg); }
.ag-btn--accent:hover { background: var(--accent-fg); color: var(--color-bg); }
.ag-btn:focus-visible { outline: 0; box-shadow: var(--shadow-focus); }

/* Lien fléché : le « en savoir plus » du système. */
.ag-link {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--accent-fg); text-decoration: none;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .08em; text-transform: uppercase;
}
.ag-link i { transition: transform var(--t-base); }
.ag-link:hover { color: var(--accent-fg); }
.ag-link:hover i { transform: translateX(3px); }

/* ═════════════════════════════════════════════════════════════════════════
   6. BANDEAU SUPÉRIEUR
   ═════════════════════════════════════════════════════════════════════════ */
.topbar {
  background: var(--color-bg);
  border-bottom: var(--ag-rule);
  padding: 8px 0;
}
.topbar__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}
.topbar__link {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--color-text-muted); text-decoration: none; max-width: 100%;
}
.topbar__link:hover { color: var(--color-text); }
.topbar__social { font-size: 13px; }
.topbar__right { display: flex; align-items: center; gap: 16px; }
.topbar__langs { display: flex; align-items: center; gap: 1px; margin: 0; }
.topbar__lang {
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .1em; border: 0; background: none;
  color: var(--color-text-muted); padding: 2px 5px; cursor: pointer;
}
.topbar__lang:hover { color: var(--color-text); }
.topbar__lang.is-active { color: var(--accent-fg); cursor: default; }

/* ═════════════════════════════════════════════════════════════════════════
   7. BARRE DE NAVIGATION
   Surcharge de `.public-nav` (theme.css). Les crochets que theme.js utilise
   — `.public-nav__hamburger`, `.is-open`, `[data-theme-toggle]` — restent.
   ═════════════════════════════════════════════════════════════════════════ */
body.site-public .public-nav {
  position: sticky; top: 0; z-index: 1030;
  background: color-mix(in srgb, var(--color-bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: var(--ag-rule);
  box-shadow: none;
}
body.site-public .public-nav__container {
  width: 100%; max-width: var(--ag-shell); margin-inline: auto;
  padding: 0 var(--ag-gutter);
  height: 68px; display: flex; align-items: center; gap: 28px;
}
body.site-public .public-nav__brand {
  display: inline-flex; align-items: center; flex: none;
  text-decoration: none; margin-right: auto;
}
/* La liste occupe TOUTE la hauteur de la barre, et chaque `li` avec elle.
   Sans cela le `li` ne fait que la hauteur de son lien : il reste 18 px de
   vide entre son bas et le haut du méga-menu, la souris y perd le survol en
   descendant, et le sous-menu se referme avant d'être atteint. Il était
   proprement inaccessible. */
body.site-public .public-nav__menu {
  display: flex; align-items: stretch; align-self: stretch; gap: 2px;
  list-style: none; margin: 0; padding: 0;
}
@media (max-width: 991.98px) { body.site-public .public-nav__menu { display: none; } }

body.site-public .public-nav__link {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 11px;
  color: var(--color-text-muted); text-decoration: none;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .08em; text-transform: uppercase;
  border-radius: var(--radius-xs); position: relative;
}
body.site-public .public-nav__link:hover { color: var(--color-text); background: none; }
/* L'état actif est un filet, pas une pastille. */
body.site-public .public-nav__link.is-active { color: var(--color-text); }
body.site-public .public-nav__link.is-active::after {
  content: ""; position: absolute; left: 11px; right: 11px; bottom: 0;
  height: 2px; background: var(--accent-fg);
}
body.site-public .public-nav__link i { font-size: .75em; opacity: .55; }

body.site-public .public-nav__cta { display: flex; align-items: center; gap: 8px; flex: none; }
body.site-public .public-nav__icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: var(--ag-rule); border-radius: var(--radius-xs);
  background: transparent; color: var(--color-text-muted);
  cursor: pointer; font-size: 14px;
}
body.site-public .public-nav__icon-btn:hover { color: var(--color-text); border-color: var(--color-text); }
body.site-public .public-nav__hamburger {
  display: none; border: var(--ag-rule); border-radius: var(--radius-xs);
  background: transparent; color: var(--color-text);
  width: 36px; height: 36px; align-items: center; justify-content: center;
  font-size: 18px; cursor: pointer;
}
@media (max-width: 991.98px) { body.site-public .public-nav__hamburger { display: inline-flex; } }
@media (max-width: 767.98px) { body.site-public .public-nav__cta .ag-btn { display: none; } }

/* Méga-menu : une planche à filets, sans ombre ni rayon. */
body.site-public .public-nav__menu > li {
  position: static;
  display: flex; align-items: center;
}
body.site-public .mega-menu {
  display: none; position: absolute; top: 100%; left: 0; right: 0;
  background: var(--color-surface);
  border-top: var(--ag-rule); border-bottom: var(--ag-rule);
  box-shadow: none; border-radius: 0; padding: 0;
}
/* `:focus-within` en plus du survol : un menu qui ne s'ouvre qu'à la souris
   est inatteignable au clavier, et ses liens sont pourtant dans l'ordre de
   tabulation — on tabulait dans le vide. */
body.site-public .public-nav__menu > li:hover > .mega-menu,
body.site-public .public-nav__menu > li:focus-within > .mega-menu {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 0 40px;
  max-width: var(--ag-shell); margin-inline: auto;
  padding: 30px var(--ag-gutter) 26px;
}
body.site-public .mega-menu__col > h6 {
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--color-text-muted); font-weight: 400;
  margin: 0 0 10px; padding-bottom: 10px; border-bottom: var(--ag-rule);
}
body.site-public .mega-menu__item {
  display: flex; gap: 12px; padding: 9px 0;
  text-decoration: none; color: var(--color-text); border-bottom: var(--ag-rule);
}
body.site-public .mega-menu__item:last-child { border-bottom: 0; }
body.site-public .mega-menu__item:hover .mega-menu__item-title { color: var(--accent-fg); }
body.site-public .mega-menu__item-icon { color: var(--color-text-muted); flex: none; font-size: 14px; line-height: 1.5; }
body.site-public .mega-menu__item-title { font-size: var(--text-small); font-weight: 500; }
body.site-public .mega-menu__item-desc {
  font-size: var(--text-label); line-height: 1.45;
  color: var(--color-text-muted); margin-top: 3px;
}

/* Menu mobile — public-nav n'embarque ni JS ni CSS pour cela. */
@media (max-width: 991.98px) {
  body.site-public .public-nav.is-open .public-nav__menu {
    display: flex; flex-direction: column; align-items: stretch; align-self: auto;
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--color-bg); border-bottom: var(--ag-rule);
    padding: 4px var(--ag-gutter) 20px; gap: 0;
    max-height: calc(100vh - 68px); overflow-y: auto;
  }
  body.site-public .public-nav.is-open .public-nav__menu > li {
    width: 100%; border-bottom: var(--ag-rule);
    display: block;
  }
  body.site-public .public-nav.is-open .public-nav__link { width: 100%; padding: 15px 0; }
  body.site-public .public-nav.is-open .public-nav__link.is-active::after { left: 0; right: auto; width: var(--ag-filet-w); bottom: 9px; }
  body.site-public .public-nav.is-open .mega-menu {
    display: block !important; position: static; box-shadow: none;
    border: 0; padding: 0 0 12px; background: none;
  }
}

/* ═════════════════════════════════════════════════════════════════════════
   8. LOGOTYPE
   La zone de protection de la charte (une hauteur de capitale sur les quatre
   côtés) se règle ICI, en marge — jamais dans le fichier image, qui doit
   rester recadré au contenu pour ne pas rapetisser le logo dans une barre.
   ═════════════════════════════════════════════════════════════════════════ */
.theme-logo { height: 32px; width: auto; margin: 0; }
.theme-logo--light { display: inline-block; }
.theme-logo--dark { display: none; }
[data-bs-theme="dark"] .theme-logo--light { display: none; }
[data-bs-theme="dark"] .theme-logo--dark { display: inline-block; }
.site-footer__brand-logo-img { height: 38px; width: auto; }

/* ═════════════════════════════════════════════════════════════════════════
   9. HERO
   Deux colonnes : la parole à gauche, une planche technique à droite. Pas de
   photographie — la charte n'en a pas, et une capture d'écran inventée serait
   un mensonge. La planche affiche de vraies charges utiles.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-hero { padding-block: clamp(48px, 8vw, 100px); border-bottom: var(--ag-rule); }
.ag-hero__grid { display: grid; grid-template-columns: 1fr; gap: 44px; align-items: center; }
@media (min-width: 992px) { .ag-hero__grid { grid-template-columns: 6fr 6fr; gap: 64px; } }

.ag-hero__eyebrow { display: flex; align-items: center; gap: 14px; color: var(--color-text-muted); margin-bottom: 22px; }
.ag-hero__title {
  margin: 0 0 22px;
  font-size: clamp(34px, 5vw, 58px); line-height: 1.02;
  letter-spacing: -.03em; font-weight: 600;
  text-wrap: balance; max-width: 20ch;
}
.ag-hero__title em { font-style: normal; color: var(--accent-fg); }
.ag-hero__desc {
  margin: 0 0 30px; max-width: 52ch;
  font-size: 19px; line-height: 1.6;
  color: var(--color-text-muted); text-wrap: pretty;
}
.ag-hero__actions { display: flex; flex-wrap: wrap; gap: 10px; }

/* Planche technique : cadre à filets, barre de titre en mono, corps en mono. */
.ag-plate {
  border: var(--ag-rule); border-radius: var(--radius-xs);
  background: var(--color-surface); overflow: hidden;
}
.ag-plate__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 11px 14px; border-bottom: var(--ag-rule);
  color: var(--color-text-muted); background: var(--color-surface-muted);
}
.ag-plate__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-fg); flex: none; }
.ag-plate__body { padding: 18px 16px; font-family: var(--font-mono); font-size: 13.5px; line-height: 1.85; }
.ag-plate__row { display: flex; gap: 12px; white-space: pre; overflow: hidden; }
.ag-plate__ln { color: var(--color-text-muted); opacity: .5; flex: none; width: 2ch; text-align: right; }
.ag-plate__k { color: var(--accent-fg); }
.ag-plate__v { color: var(--color-text); }
.ag-plate__c { color: var(--color-text-muted); }
.ag-plate__foot {
  display: flex; flex-wrap: wrap; gap: 8px 20px;
  padding: 11px 14px; border-top: var(--ag-rule); color: var(--color-text-muted);
}

/* ═════════════════════════════════════════════════════════════════════════
   10. CARTES
   Un filet, un numéro, aucun rayon marqué, aucune ombre. Au survol le filet
   de tête passe en terre cuite : c'est tout le mouvement du système.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-card {
  position: relative; display: flex; flex-direction: column; gap: 11px;
  padding: 24px 22px 22px;
  background: var(--color-bg);
  border: var(--ag-rule); border-radius: var(--radius-xs);
  transition: border-color var(--t-base), background var(--t-base);
}
.ag-section--panel .ag-card { background: var(--color-bg); }
/* ── Logo de produit ───────────────────────────────────────────────────────
   Contraint en HAUTEUR et non en largeur : les logos arrivent dans tous les
   rapports — un carré, un lettrage six fois plus large — et c'est la hauteur
   qui doit s'aligner d'un produit à l'autre. `max-width` borne les plus larges
   pour qu'un lettrage ne pousse pas la colonne.

   Pas de filtre ni d'opacité : ce sont NOS produits, pas des logos clients
   qu'on estompe pour ne pas voler la vedette au texte. */
.ag-prodlogo {
  display: block; height: 44px; width: auto; max-width: 220px;
  object-fit: contain; margin-bottom: 18px;
}
.ag-prodlogo--sm { height: 26px; max-width: 130px; margin-bottom: 0; }

/* ── Les trois teintes de la charte, en rotation sur les cartes ─────────────
   Le site n'employait qu'une couleur : terre cuite. Les jetons en déclarent
   trois autres qui dormaient — forêt, laiton, ardoise. Trois teintes qui
   tournent sur les cartes, ce n'est pas un arc-en-ciel : c'est la palette de
   la charte enfin sortie du tiroir, et la grille cesse d'être six fois la
   même carte.

   Trois et non six : au-delà, la couleur cesse de distinguer et se met à
   décorer. Sur une grille de trois colonnes, le cycle donne une teinte par
   COLONNE, ce qui se lit comme un système et non comme un tirage.

   La couleur ne porte que deux éléments par carte — l'icône et le repère
   d'angle. Ni le titre, ni le numéro, ni le cadre : la marque reste terre
   cuite partout ailleurs (boutons, liens, en-têtes de section), et c'est elle
   qu'on doit retenir. */
.ag-card { --card-accent: var(--clay-500); }
/* Le 500 et non le 600 : sur papier, #3F5344 se lit comme du noir. */
.ag-grid > .ag-card:nth-child(3n + 2) { --card-accent: var(--success-500); }
.ag-grid > .ag-card:nth-child(3n + 3) { --card-accent: var(--warning-600); }
/* Sur fond encre, les tons 600 tombent sous le seuil de lisibilité : on monte
   d'un cran dans chaque rampe. */
[data-bs-theme="dark"] .ag-card { --card-accent: var(--clay-400); }
[data-bs-theme="dark"] .ag-grid > .ag-card:nth-child(3n + 2) { --card-accent: var(--success-400); }
[data-bs-theme="dark"] .ag-grid > .ag-card:nth-child(3n + 3) { --card-accent: var(--warning-500); }

.ag-card::before {
  content: ""; position: absolute; top: -1px; left: -1px;
  /* Le repère existait déjà, mais transparent au repos : il n'apparaissait
     qu'au survol, c'est-à-dire jamais sur un écran tactile et jamais sur une
     capture. Il est maintenant là en permanence, court et discret, et c'est sa
     LARGEUR qui répond au survol. Une animation déclenchée par un geste, pas
     une entrée en fondu au défilement. */
  width: 10px; height: 2px;
  background: var(--card-accent);
  transition: width var(--t-base), background var(--t-base);
}
.ag-card:hover { border-color: var(--card-accent); }
.ag-card:hover::before { width: var(--ag-filet-w); }
@media (prefers-reduced-motion: reduce) {
  .ag-card::before { transition: none; }
}
.ag-card__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ag-card__idx { font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: .1em; color: var(--color-text-muted); }
.ag-card__icon { font-size: 18px; color: var(--card-accent); line-height: 1; }
.ag-card__title { margin: 0; font-size: 18px; line-height: 1.3; letter-spacing: -.012em; font-weight: 600; }
.ag-card__desc { margin: 0; color: var(--color-text-muted); font-size: var(--text-small); line-height: 1.6; text-wrap: pretty; }
.ag-card__foot { margin-top: auto; padding-top: 10px; }

/* ═════════════════════════════════════════════════════════════════════════
   11. CHIFFRES
   Colonnes séparées par un filet vertical, valeurs en mono : un tableau de
   mesures, pas trois pastilles marketing.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-figures { display: grid; grid-template-columns: 1fr; border-top: var(--ag-rule); }
@media (min-width: 640px) { .ag-figures { grid-template-columns: repeat(3, 1fr); } }
.ag-figures__item { padding: 24px 0; border-bottom: var(--ag-rule); }
@media (min-width: 640px) {
  .ag-figures__item { padding: 28px 26px; border-bottom: 0; border-left: var(--ag-rule); }
  .ag-figures__item:first-child { border-left: 0; padding-left: 0; }
}
.ag-figures__value {
  font-family: var(--font-mono);
  font-size: clamp(38px, 5.5vw, 58px); line-height: 1;
  font-weight: 500; letter-spacing: -.035em; font-variant-numeric: tabular-nums;
}
.ag-figures__label { margin-top: 12px; color: var(--color-text-muted); font-size: var(--text-small); line-height: 1.4; }

/* ═════════════════════════════════════════════════════════════════════════
   12. CITATION CLIENT
   Le guillemet géant du thème disparaît : la citation se signale par son
   filet de tête et son corps un cran plus grand.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-quote { display: flex; flex-direction: column; gap: 18px; padding-top: 18px; border-top: 2px solid var(--color-border); }
.ag-quote__rating { display: flex; gap: 3px; color: var(--accent-fg); font-size: 11px; }
.ag-quote__rating .empty { color: var(--color-border); }
.ag-quote__text { margin: 0; font-size: 17px; line-height: 1.6; text-wrap: pretty; }
.ag-quote__author { display: flex; align-items: center; gap: 12px; margin-top: auto; }
.ag-quote__avatar {
  width: 36px; height: 36px; border-radius: 50%; object-fit: cover; flex: none;
  background: var(--color-surface-muted);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: var(--text-label);
  color: var(--color-text-muted); border: var(--ag-rule);
}
.ag-quote__name { font-size: var(--text-small); font-weight: 500; }
.ag-quote__meta {
  font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: .06em;
  color: var(--color-text-muted); margin-top: 3px;
}

/* ═════════════════════════════════════════════════════════════════════════
   13. FAQ
   Crochets conservés pour theme.js : `.faq__item.is-open`, `.faq__question`,
   `.faq__answer` (hauteur animée par le script).
   ═════════════════════════════════════════════════════════════════════════ */
/* Le thème dessine chaque question comme une CARTE : `border: 1px solid` sur
   les quatre côtés, coins arrondis, aplat de surface, et `gap: 1px` entre les
   éléments. La surcouche ne redéfinissait que `border-bottom` — les bords
   gauche, droit et haut du thème survivaient, et l'accordéon s'affichait en
   rectangle encadré au milieu d'une page qui n'a que des filets. D'où le
   `border: 0` qui remet tout à plat AVANT de reposer le seul trait voulu.

   Le `gap: 1px` doublait en outre les filets là où deux éléments se touchent. */
body.site-public .faq { border-top: var(--ag-rule); gap: 0; }
body.site-public .faq__item {
  border: 0; border-bottom: var(--ag-rule);
  background: none; border-radius: 0;
}
body.site-public .faq__question {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: 20px; padding: 20px 0;
  background: none; border: 0; text-align: left;
  font-family: var(--font-sans); font-size: 17px; font-weight: 500;
  letter-spacing: -.012em; color: var(--color-text); cursor: pointer;
}
body.site-public .faq__question:hover { color: var(--accent-fg); }
body.site-public .faq__question i { flex: none; font-size: 12px; color: var(--color-text-muted); transition: transform var(--t-base); }
body.site-public .faq__item.is-open .faq__question i { transform: rotate(180deg); }
/* La réponse s'ouvre à sa hauteur RÉELLE. Le thème l'anime avec un plafond
   `max-height: 320px`, hérité d'une époque où la réponse était une ligne de
   texte brut ; elle se saisit désormais en éditeur enrichi, avec des
   paragraphes et des listes, et tout ce qui dépasse 320 px était coupé net —
   sans barre de défilement, donc sans que rien ne le signale.

   La bascule passe par `grid-template-rows: 0fr → 1fr`, la seule façon
   d'animer vers une hauteur automatique. `max-height: none` neutralise le
   plafond du thème dans les deux états, et `min-height: 0` autorise la piste
   de grille à se replier — sans lui, elle garde la hauteur de son contenu et
   la question reste ouverte. */
body.site-public .faq__answer {
  display: grid; grid-template-rows: 0fr;
  max-height: none; overflow: hidden;
  transition: grid-template-rows var(--t-base);
}
body.site-public .faq__item.is-open .faq__answer {
  grid-template-rows: 1fr; max-height: none;
}
/* Aucun REMBOURRAGE sur cette boîte. `min-height: 0` replie le contenu d'un
   élément de grille, jamais son rembourrage : les 22 px du bas subsistaient et
   la piste se stabilisait à 22 px au lieu de 0 — les deux premières lignes de
   chaque réponse restaient donc visibles sous une question fermée. Mesuré,
   pas supposé : `grid-template-rows` valait « 22px ».

   L'air sous le dernier paragraphe est donc posé en MARGE sur celui-ci. Une
   marge est du contenu, elle se replie avec lui. */
body.site-public .faq__answer-inner {
  min-height: 0; overflow: hidden;
  padding: 0; max-width: var(--ag-measure);
  color: var(--color-text-muted); font-size: var(--text-small); line-height: 1.65;
}

/* La réponse vient de l'éditeur enrichi : elle arrive en paragraphes. Leurs
   marges par défaut décollaient le texte de sa question et laissaient un blanc
   sous le dernier paragraphe, au-dessus du filet. */
body.site-public .faq__answer-inner > :first-child { margin-top: 0; }
body.site-public .faq__answer-inner > :last-child { margin-bottom: 22px; }
body.site-public .faq__answer-inner :is(ul, ol) { margin: .6em 0; padding-left: 1.2em; }
body.site-public .faq__answer-inner a { color: var(--accent-fg); }

/* Le chevron : même graisse que le reste de la signalétique. */
body.site-public .faq__question i { font-size: 13px; }

/* ═════════════════════════════════════════════════════════════════════════
   14. BLOC D'APPEL (bas de page)
   ═════════════════════════════════════════════════════════════════════════ */
.ag-cta { border-top: var(--ag-rule); padding-block: clamp(52px, 7vw, 88px); }
.ag-cta__grid { display: grid; grid-template-columns: 1fr; gap: 30px; align-items: end; }
@media (min-width: 992px) { .ag-cta__grid { grid-template-columns: 7fr 5fr; gap: 56px; } }
.ag-cta__eyebrow { display: flex; align-items: center; gap: 14px; color: var(--accent-fg); margin-bottom: 18px; }
.ag-cta__title {
  margin: 0 0 14px; font-size: clamp(28px, 3.4vw, 40px);
  line-height: 1.08; letter-spacing: -.025em; font-weight: 600;
  max-width: 24ch; text-wrap: balance;
}
.ag-cta__desc { margin: 0; color: var(--color-text-muted); max-width: 50ch; line-height: 1.6; text-wrap: pretty; }
.ag-cta__actions { display: flex; flex-wrap: wrap; gap: 10px; }
@media (min-width: 992px) { .ag-cta__actions { justify-content: flex-end; } }

/* ═════════════════════════════════════════════════════════════════════════
   15. PIED DE PAGE
   ═════════════════════════════════════════════════════════════════════════ */
body.site-public .site-footer {
  background: var(--color-surface); color: var(--color-text-muted);
  border-top: var(--ag-rule); padding: 0;
}
body.site-public .site-footer__container {
  width: 100%; max-width: var(--ag-shell); margin-inline: auto;
  padding: 56px var(--ag-gutter) 44px;
  display: grid; grid-template-columns: 1fr; gap: 36px;
}
@media (min-width: 768px) { body.site-public .site-footer__container { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 992px) { body.site-public .site-footer__container { grid-template-columns: 4fr 2fr 2fr 3fr; gap: 44px; } }

body.site-public .site-footer__brand { display: flex; flex-direction: column; gap: 14px; }
body.site-public .site-footer__brand p {
  margin: 0; font-size: var(--text-small); line-height: 1.65;
  color: var(--color-text-muted); max-width: 40ch;
}
body.site-public .site-footer__social { display: flex; gap: 8px; }
body.site-public .site-footer__social a {
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  border: var(--ag-rule); border-radius: var(--radius-xs);
  color: var(--color-text-muted); text-decoration: none;
}
body.site-public .site-footer__social a:hover { color: var(--color-text); border-color: var(--color-text); }

body.site-public .site-footer__col h6 {
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .09em; text-transform: uppercase; font-weight: 400;
  color: var(--color-text-muted);
  margin: 0 0 2px; padding-bottom: 11px; border-bottom: var(--ag-rule);
}
body.site-public .site-footer__col ul { list-style: none; margin: 0; padding: 0; }
body.site-public .site-footer__col li { border-bottom: var(--ag-rule); }
body.site-public .site-footer__col a {
  display: block; padding: 10px 0; color: var(--color-text);
  text-decoration: none; font-size: var(--text-small);
}
body.site-public .site-footer__col a:hover { color: var(--accent-fg); }

body.site-public .site-footer__bottom {
  border-top: var(--ag-rule);
  padding: 18px var(--ag-gutter);
  max-width: var(--ag-shell); margin-inline: auto;
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: space-between;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .04em; color: var(--color-text-muted);
}
body.site-public .site-footer__bottom a { color: var(--color-text-muted); text-decoration: none; }
body.site-public .site-footer__bottom a:hover { color: var(--accent-fg); }
body.site-public .site-footer__bottom strong { font-weight: 400; color: var(--color-text); }

/* ═════════════════════════════════════════════════════════════════════════
   16. LOGOS CLIENTS (Swiper)
   ═════════════════════════════════════════════════════════════════════════ */
/* `.card` (Bootstrap) est display:flex; flex-direction:column : `.clients-slider`
   n'obtient de largeur que par le « stretch » de l'axe transverse — il n'en a
   pas en propre. Swiper mesure clientWidth pour dimensionner ses slides, et
   contre une largeur non explicite la résolution devient circulaire : Chrome
   la borne à sa taille de mise en page interne (~2^25 px), ce qui corrompt le
   calcul de Swiper et laisse chaque slide à zéro. Une largeur explicite casse
   le cycle. */
.clients-slider { padding: 4px 0; width: 100%; min-width: 0; }
.clients-slider .swiper-slide { text-align: center; }
/* Deux bornes, et il faut les deux. `height` décide de la taille des logos
   CARRÉS — la plupart le sont, et 40px les rendait minuscules. `max-width`
   décide de celle des logos LARGES : plafonnée trop bas, elle les encadre et
   leur hauteur réelle retombe sous la borne (Names Legal, 6.6:1, sortait à
   23px de haut pour 40px annoncés). Les deux montent donc ensemble. */
.clients-slider__img {
  height: 52px; width: auto; max-width: 240px; object-fit: contain;
  display: inline-block; vertical-align: middle;
  filter: grayscale(1) invert(0); opacity: .45;
  transition: filter var(--t-base), opacity var(--t-base);
}
[data-bs-theme="dark"] .clients-slider__img { filter: grayscale(1) brightness(2.2); }
.clients-slider__img:hover { filter: none; opacity: 1; }
.ag-clients { display: grid; grid-template-columns: 1fr; gap: 20px; align-items: center; }
@media (min-width: 768px) { .ag-clients { grid-template-columns: auto 1fr; gap: 36px; } }
.ag-clients__caption { color: var(--color-text-muted); flex: none; }

/* ═════════════════════════════════════════════════════════════════════════
   17. GRAPHIQUES D3
   ═════════════════════════════════════════════════════════════════════════ */
.ag-chart { width: 100%; }
.ag-chart__caption { margin-top: 12px; padding-top: 11px; border-top: var(--ag-rule); color: var(--color-text-muted); }

/* ═════════════════════════════════════════════════════════════════════════
   18. CORPS DE TEXTE ENRICHI (rendu du filtre `richtext`)
   Ce que l'éditeur montre doit être ce que le site rend : les mêmes règles
   sont reprises dans la zone d'écriture de Quill (medialibrary/quill.css).
   ═════════════════════════════════════════════════════════════════════════ */
.prose { color: var(--color-text); font-size: var(--text-body); line-height: var(--text-body-lh); }
.prose :is(h2, h3, h4) {
  color: var(--color-text-strong); font-weight: 600;
  letter-spacing: var(--text-h1-track); margin: 1.6em 0 .5em;
}
.prose h2 { font-size: var(--text-h2); line-height: var(--text-h2-lh); }
.prose h3 { font-size: 20px; line-height: 1.3; }
.prose h4 { font-size: var(--text-body); line-height: 1.35; }
.prose p { margin: 0 0 1em; }
.prose ul, .prose ol { margin: 0 0 1em 1.25em; }
.prose li { margin: 0 0 .35em; }
.prose a { color: var(--accent-fg); text-underline-offset: .2em; }
.prose img { max-width: 100%; height: auto; border-radius: var(--radius-xs); }
.prose pre, .prose code { font-family: var(--font-mono); font-size: var(--text-mono); }
.prose pre {
  background: var(--color-surface-muted); border: var(--ag-rule);
  border-radius: var(--radius-xs); padding: var(--space-4); overflow-x: auto;
}
/* ── Bloc de code écrit dans l'éditeur ─────────────────────────────────────
   Quill ne produit PAS de `<pre>`. Un bloc de code sort en
   `div.ql-code-block-container` contenant un `div.ql-code-block` PAR LIGNE — et
   la feuille du site ne stylait que `pre`. Conséquence sur les pages publiées :
   pas de fond, pas de cadre, pas de gouttière, et surtout `white-space: normal`,
   c'est-à-dire du code REFLUÉ COMME DE LA PROSE. Les retours à la ligne et
   l'indentation disparaissaient, et le bloc débordait de l'écran sur un
   téléphone puisque rien ne le faisait défiler.

   Les mêmes réglages que `pre`, à la même place, pour que les deux écritures —
   Markdown semé et texte saisi à l'éditeur — rendent identiquement. */
.prose .ql-code-block-container {
  font-family: var(--font-mono); font-size: var(--text-mono);
  background: var(--color-surface-muted); border: var(--ag-rule);
  border-radius: var(--radius-xs); padding: var(--space-4);
  overflow-x: auto; margin: 0 0 1.5em;
}
/* Une ligne par `div` : `pre` conserve l'indentation, et c'est le CONTENEUR qui
   défile — une barre pour le bloc, pas une par ligne. */
.prose .ql-code-block { white-space: pre; }

.prose blockquote {
  margin: 1.5em 0; padding: 0 0 0 var(--space-4);
  /* Le filet est terre cuite : c'est le seul endroit du corps de texte où la
     couleur de la marque a droit de cité. */
  border-left: 2px solid var(--accent-fg);
  color: var(--color-text-muted);
}
.prose hr { border: 0; border-top: var(--ag-rule); margin: 2.5em 0; }
.prose table { width: 100%; border-collapse: collapse; font-size: var(--text-small); margin: 0 0 1.5em; }
.prose :is(th, td) { border: var(--ag-rule); padding: var(--space-2) var(--space-3); text-align: left; }
.prose th { font-weight: 500; background: var(--color-surface-muted); }

/* --- Taille et habillage d'un média inséré au fil du texte --------------
   Posés par l'éditeur sous forme de CLASSES et non de style en ligne : seule
   une classe peut retomber pleine largeur sous 600 px, une media query ne
   pouvant pas s'appliquer à un attribut `style`.
   Deux familles de préfixes, et elles doivent rester distinctes :
   `ag-w`/`ag-pos` pour une image (blot en ligne), `ag-wb`/`ag-posb` pour une
   vidéo (blot de bloc). Voir le commentaire de medialibrary/quill.js. */
.prose .ag-w-full,  .prose .ag-wb-full  { width: 100%; }
.prose .ag-w-half,  .prose .ag-wb-half  { width: 50%; }
.prose .ag-w-third, .prose .ag-wb-third { width: 33.333%; }
.prose .ag-pos-center, .prose .ag-posb-center { display: block; margin-left: auto; margin-right: auto; }
.prose .ag-pos-left,  .prose .ag-posb-left  { float: left;  margin: .35em 1.5em 1em 0; }
.prose .ag-pos-right, .prose .ag-posb-right { float: right; margin: .35em 0 1em 1.5em; }

/* Image accompagnée de son crédit. `<span>` passé en bloc et non `<figure>` :
   l'éditeur place toujours l'image dans un `<p>`, où un `<figure>` — contenu
   de bloc — ferait refermer le paragraphe par le navigateur, laissant deux
   trous verticaux dans le texte. */
.prose .ag-inlinefig { display: block; margin: 1.5em 0; }
.prose .ag-inlinefig img { width: 100%; display: block; }
.prose .ag-figure__credit {
  display: block; margin-top: .5em;
  font-family: var(--font-mono); font-size: var(--text-label);
  line-height: var(--text-label-lh); letter-spacing: .08em;
  text-transform: uppercase; color: var(--color-text-muted);
}
.prose iframe { aspect-ratio: 16 / 9; width: 100%; border: 0; border-radius: var(--radius-xs); display: block; margin: 1.5em 0; }

/* Sous 600 px, tout retombe pleine largeur : un tiers de colonne sur un
   téléphone n'est plus une image, c'est une vignette. */
@media (max-width: 600px) {
  .prose :is(.ag-w-half, .ag-w-third, .ag-wb-half, .ag-wb-third, .ag-inlinefig) {
    width: 100%; float: none; margin-left: 0; margin-right: 0;
  }
}

/* ═════════════════════════════════════════════════════════════════════════
   19. EN-TÊTE DE PAGE INTÉRIEURE
   Le pendant, pour une page de second niveau, de ce que `.ag-hero` est pour
   l'accueil : pas un bandeau à image de fond, mais un fil d'Ariane en mono, un
   titre, un chapô, et le filet qui referme. Une page intérieure s'annonce ;
   elle ne se met pas en scène.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-page-head {
  padding-block: clamp(36px, 5vw, 64px) 0;
  border-bottom: var(--ag-rule);
}
.ag-page-head__inner { padding-bottom: clamp(28px, 4vw, 44px); }
.ag-page-head__title { margin: 14px 0 0; }
.ag-page-head__lede { margin: 16px 0 0; }
.ag-page-head__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }

/* Fil d'Ariane : mono, capitales, séparateur en barre oblique. Il remplace le
   `.breadcrumb` de Bootstrap, dont les pastilles arrondies n'ont pas cours
   ici. */
.ag-crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  list-style: none; margin: 0; padding: 0;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--color-text-muted);
}
.ag-crumbs a { color: var(--color-text-muted); text-decoration: none; }
.ag-crumbs a:hover { color: var(--accent-fg); }
.ag-crumbs li + li::before { content: "/"; margin-right: 10px; opacity: .45; }
.ag-crumbs .is-current { color: var(--color-text); }

/* ═════════════════════════════════════════════════════════════════════════
   20. ARTICLE ET COLONNE LATÉRALE
   ═════════════════════════════════════════════════════════════════════════ */
/* 8/4 plutôt que le 5/7 des sections : un corps d'article a besoin de la
   largeur, la colonne latérale n'est qu'un index. */
.ag-with-side { display: grid; grid-template-columns: 1fr; gap: 44px; align-items: start; }
@media (min-width: 992px) {
  .ag-with-side { grid-template-columns: minmax(0, 8fr) minmax(0, 4fr); gap: 40px; }
}

/* ═════════════════════════════════════════════════════════════════════════
   21. ARTICLE EN LISTE
   Pas une carte : une entrée. Le filet du haut la sépare de la précédente, et
   la couverture reste un accessoire — c'est le titre qu'on lit.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-posts { display: grid; grid-template-columns: 1fr; gap: 0; }
@media (min-width: 768px) { .ag-posts--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 32px; } }
.ag-post {
  position: relative;
  display: flex; flex-direction: column; gap: 12px;
  padding: 26px 0; border-top: var(--ag-rule);
}
.ag-post__cover {
  display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border: var(--ag-rule); border-radius: var(--radius-xs);
  filter: saturate(.85); transition: filter var(--t-base);
}
.ag-post:hover .ag-post__cover { filter: none; }
.ag-post__title { margin: 0; font-size: 19px; line-height: 1.3; letter-spacing: -.012em; font-weight: 600; }
.ag-post__title a { color: inherit; text-decoration: none; }
.ag-post:hover .ag-post__title a { color: var(--accent-fg); }
.ag-post__desc { margin: 0; color: var(--color-text-muted); font-size: var(--text-small); line-height: 1.6; text-wrap: pretty; }

/* Pastille de rubrique. Rectangulaire et en mono : le système n'a pas de
   pastilles arrondies. */
.ag-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ag-chip {
  display: inline-block; padding: 4px 9px;
  border: var(--ag-rule); border-radius: var(--radius-xs);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--color-text-muted); text-decoration: none;
  transition: border-color var(--t-base), color var(--t-base);
}
a.ag-chip:hover { border-color: var(--accent-fg); color: var(--accent-fg); }
.ag-chip--accent { border-color: var(--accent-fg); color: var(--accent-fg); }

/* ═════════════════════════════════════════════════════════════════════════
   22. PAGINATION
   ═════════════════════════════════════════════════════════════════════════ */
.ag-pager {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 36px; padding-top: 22px; border-top: var(--ag-rule);
}
.ag-pager__item {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; height: 36px; padding: 0 10px;
  border: 1px solid transparent; border-radius: var(--radius-xs);
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .06em; color: var(--color-text-muted); text-decoration: none;
  transition: border-color var(--t-base), color var(--t-base);
}
.ag-pager__item:hover { border-color: var(--color-border); color: var(--color-text); }
.ag-pager__item.is-active { border-color: var(--accent-fg); color: var(--accent-fg); cursor: default; }
.ag-pager__item.is-disabled { opacity: .35; pointer-events: none; }
.ag-pager__gap { color: var(--color-text-muted); padding: 0 4px; }

/* ═════════════════════════════════════════════════════════════════════════
   23. FORMULAIRES
   Les classes viennent de Python (`form-control`, `form-label`) : on ne les
   renomme pas, on les rhabille sous `.ag-form`. Renommer aurait obligé à
   toucher six fichiers de formulaires pour un changement d'apparence.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-form { display: flex; flex-direction: column; gap: 20px; }
.ag-form__row { display: grid; grid-template-columns: 1fr; gap: 20px; }
@media (min-width: 640px) { .ag-form__row--2 { grid-template-columns: 1fr 1fr; } }
.ag-form__field { display: flex; flex-direction: column; gap: 7px; }

.ag-form :is(.form-label, label) {
  margin: 0;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--color-text-muted);
}
.ag-form :is(.form-control, .form-select, input[type="text"], input[type="email"],
              input[type="tel"], input[type="url"], input[type="date"], input[type="file"],
              select, textarea) {
  width: 100%;
  padding: 12px 14px;
  background: var(--color-bg);
  border: var(--ag-rule);
  border-radius: var(--radius-xs);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--text-small);
  line-height: 1.5;
  box-shadow: none;
  transition: border-color var(--t-base);
}
/* Dans un bloc tranché, le champ reprend le fond de la page : sans quoi il
   disparaît dans la surface qui le porte. */
.ag-section--panel .ag-form :is(.form-control, .form-select, textarea, select) {
  background: var(--color-bg);
}
.ag-form :is(.form-control, .form-select, textarea, select):focus {
  outline: 0; border-color: var(--accent-fg); box-shadow: none;
}
.ag-form :is(.form-control, textarea)::placeholder { color: var(--color-text-muted); opacity: .8; }
.ag-form textarea { min-height: 150px; resize: vertical; }
.ag-form .form-text { font-size: 12px; color: var(--color-text-muted); }
.ag-form .invalid-feedback {
  font-family: var(--font-mono); font-size: 11.5px;
  letter-spacing: .05em; color: var(--color-danger);
}
.ag-form__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }

/* Message d'état. Un filet à gauche, aucun aplat coloré. */
.ag-note {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 13px 16px;
  border: var(--ag-rule); border-left: 2px solid var(--color-text-muted);
  border-radius: var(--radius-xs);
  font-size: var(--text-small); line-height: 1.5;
  color: var(--color-text-muted);
}
.ag-note--error { border-left-color: var(--color-danger); color: var(--color-danger); }
.ag-note--ok { border-left-color: var(--accent-fg); color: var(--color-text); }

/* ═════════════════════════════════════════════════════════════════════════
   24. FICHE TECHNIQUE (informations d'un projet, d'un produit)
   Deux colonnes séparées par un filet : c'est un tableau de valeurs, pas une
   carte. La clé est en mono, la valeur en Archivo.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-specs { list-style: none; margin: 0; padding: 0; border-top: var(--ag-rule); }
.ag-specs > li {
  display: grid; grid-template-columns: 1fr; gap: 4px;
  padding: 13px 0; border-bottom: var(--ag-rule);
}
@media (min-width: 480px) { .ag-specs > li { grid-template-columns: 10em 1fr; gap: 16px; align-items: baseline; } }
.ag-specs__key {
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .08em; text-transform: uppercase; color: var(--color-text-muted);
}
.ag-specs__val { font-size: var(--text-small); line-height: 1.5; overflow-wrap: anywhere; }
.ag-specs__val a { color: var(--accent-fg); }

/* ═════════════════════════════════════════════════════════════════════════
   25. GALERIE DE PROJET
   ═════════════════════════════════════════════════════════════════════════ */
.ag-gallery { position: relative; border: var(--ag-rule); border-radius: var(--radius-xs); overflow: hidden; }
.ag-gallery .swiper-slide img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.ag-gallery .swiper-button-prev,
.ag-gallery .swiper-button-next { color: var(--accent-fg); }
.ag-gallery .swiper-button-prev::after,
.ag-gallery .swiper-button-next::after { font-size: 20px; font-weight: 700; }
.ag-gallery .swiper-pagination-bullet { background: var(--color-text-muted); border-radius: 0; width: 14px; height: 2px; }
.ag-gallery .swiper-pagination-bullet-active { background: var(--accent-fg); }

/* ═════════════════════════════════════════════════════════════════════════
   26. PAGE D'ERREUR
   Le code d'erreur en mono, à la taille d'un titre : c'est une donnée, pas un
   ornement. Aucun dégradé — le système n'en a aucun ailleurs.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-error { padding-block: clamp(64px, 12vw, 140px); max-width: 56ch; }
.ag-error__code {
  font-family: var(--font-mono); font-weight: 500;
  font-size: clamp(48px, 9vw, 96px); line-height: 1;
  letter-spacing: -.02em; color: var(--accent-fg);
}
.ag-error__title { margin: 24px 0 0; }
.ag-error__desc { margin: 16px 0 0; color: var(--color-text-muted); line-height: 1.6; text-wrap: pretty; }
.ag-error__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 32px; }

/* ═════════════════════════════════════════════════════════════════════════
   27. AJUSTEMENTS D'ESPACEMENT
   `.ag-section` porte le rythme vertical. Ces deux utilitaires servent aux
   blocs qui vivent DANS une section et n'en sont pas une.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-stack   { display: flex; flex-direction: column; gap: 24px; }
.ag-stack--lg { gap: 44px; }
.ag-measure { max-width: var(--ag-measure); }

/* ═════════════════════════════════════════════════════════════════════════
   28. TABLEAU DE DONNÉES
   Les écrans de dépouillement (contacts reçus, cahiers des charges déposés)
   sont rendus dans la coquille PUBLIQUE. Ils gardent donc son vocabulaire :
   en-têtes en mono, filets horizontaux, aucune bordure verticale ni zébrure —
   un tableau se lit en lignes.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-table-wrap { width: 100%; overflow-x: auto; }
.ag-table { width: 100%; border-collapse: collapse; font-size: var(--text-small); }
.ag-table th {
  text-align: left; white-space: nowrap;
  padding: 0 18px 11px 0; border-bottom: 2px solid var(--color-border);
  font-family: var(--font-mono); font-size: var(--text-label); font-weight: 400;
  letter-spacing: .09em; text-transform: uppercase; color: var(--color-text-muted);
}
.ag-table td {
  padding: 13px 18px 13px 0; border-bottom: var(--ag-rule);
  line-height: 1.45; vertical-align: top;
}
.ag-table tr:hover td { background: var(--color-surface); }
.ag-table :is(th, td):last-child { padding-right: 0; }
.ag-table__num { font-family: var(--font-mono); color: var(--color-text-muted); white-space: nowrap; }
.ag-table__empty { padding: 28px 0; color: var(--color-text-muted); }

/* ═════════════════════════════════════════════════════════════════════════
   29. SOMMAIRE D'ARTICLE
   La colonne entière est collante ET bornée à la hauteur de l'écran
   (`.ag-side`) : le sommaire suit la lecture sans code supplémentaire.

   Il n'a plus d'ascenseur à lui. Il en avait un — `max-height` sur la liste —
   du temps où la colonne débordait librement ; depuis qu'elle défile
   elle-même, deux ascenseurs imbriqués se disputaient la molette, et l'un des
   deux bougeait toujours à la place de l'autre.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-toc__list { list-style: none; margin: 0; padding: 0; }
/* Le niveau 3 est décalé et rapetissé : c'est la seule chose qui distingue une
   sous-partie d'une partie une fois les numéros retirés. */
.ag-toc__item--l3 .ag-toc__link { padding-left: 30px; font-size: 13px; }
/* Un FILET CONTINU, et non une suite de lignes séparées par des traits.
   Les traits horizontaux faisaient de dix titres un tableau ; le filet
   vertical en fait un parcours, et laisse le marqueur de position dire
   quelque chose. */
.ag-toc__list { border-left: var(--ag-rule); }
.ag-toc__link {
  display: block; padding: 9px 0 9px 16px;
  color: var(--color-text-muted); text-decoration: none;
  font-size: var(--text-small); line-height: 1.45;
  border-left: 2px solid transparent; margin-left: -1px;
  transition: color var(--t-base), border-color var(--t-base);
}
.ag-toc__link:hover { color: var(--color-text); }
/* Titre en cours de lecture, posé par main.js. */
.ag-toc__link.is-current { color: var(--accent-fg); border-left-color: var(--accent-fg); }

/* Le titre visé ne doit pas se glisser sous la barre de navigation collante
   quand on suit une ancre. */
.prose :is(h2, h3)[id] { scroll-margin-top: 92px; }

/* ═════════════════════════════════════════════════════════════════════════
   30. CONNEXION AU TABLEAU DE BORD
   L'écran vit dans la coquille PUBLIQUE — c'est la seule page du back-office
   qu'on atteint sans être connecté, et lui donner la coquille applicative
   afficherait une barre latérale de navigation à quelqu'un qui n'a encore le
   droit d'ouvrir aucun de ses écrans.
   ═════════════════════════════════════════════════════════════════════════ */
.db-login { max-width: 380px; padding-block: clamp(40px, 8vw, 90px); }
.db-login__title { margin: 14px 0 0; }

/* ═════════════════════════════════════════════════════════════════════════
   31. RÉALISATION EN LISTE
   Même registre que `.ag-card` — filet, coin d'accent au survol — mais la
   figure déborde jusqu'aux bords : une image de projet coupée par une marge
   intérieure ressemble à une vignette d'illustration, pas à une livraison.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-work {
  position: relative; display: flex; flex-direction: column;
  background: var(--color-bg);
  border: var(--ag-rule); border-radius: var(--radius-xs);
  overflow: hidden;
  transition: border-color var(--t-base);
}
.ag-work::before {
  content: ""; position: absolute; top: -1px; left: -1px; z-index: 2;
  width: var(--ag-filet-w); height: 2px;
  background: transparent; transition: background var(--t-base);
}
.ag-work:hover { border-color: var(--color-text-muted); }
.ag-work:hover::before { background: var(--accent-fg); }

/* ═════════════════════════════════════════════════════════════════════════
   CADRE D'IMAGE — `.ag-figure`
   Quatre gabarits la posaient (équipe, portfolio, à-propos, article) et RIEN
   ne la stylait : ni ici, ni dans `theme.css`. Le commentaire de `.ag-work__fig`
   juste en dessous la décrivait déjà — filet, marge basse — sans qu'elle existe.

   La ligne qui compte est `width: 100%`. Sans elle une image garde sa largeur
   INTRINSÈQUE : le portrait 1024×1024 de l'équipe sortait à 1024 px de large
   dans une colonne de 290, et `aspect-ratio: 4/5` en déduisait 1280 px de haut.
   Il n'y a pas de `img { max-width: 100% }` global dans ce projet pour rattraper
   ça — d'où une seule photo qui poussait la page sur trois écrans.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-figure {
  margin: 0 0 18px;
  border: var(--ag-rule);
  border-radius: var(--radius-xs);
  /* `overflow: hidden` : c'est lui qui fait tenir la promesse de `--crop` et
     du `scale(1.03)` au survol d'une carte de projet. */
  overflow: hidden;
}
.ag-figure img { display: block; width: 100%; height: auto; }
/* Recadrer plutôt que déformer : l'image remplit le cadre et déborde par où
   elle veut, la partie en trop est coupée par l'`overflow` ci-dessus. */
.ag-figure--crop img { object-fit: cover; }

/* La figure porte déjà `.ag-figure`, qui pose un filet et une marge basse : ni
   l'un ni l'autre n'ont de sens à l'intérieur d'une carte qui a son propre
   cadre. */
.ag-work__fig { margin: 0; border: 0; border-bottom: var(--ag-rule); border-radius: 0; }
.ag-work__fig img { transition: transform var(--t-slow, .5s) ease; }
.ag-work:hover .ag-work__fig img { transform: scale(1.03); }

.ag-work__body { display: flex; flex-direction: column; gap: 11px; padding: 22px; }
.ag-work__meta {
  display: flex; justify-content: space-between; gap: 12px;
  padding-top: 10px; border-top: var(--ag-rule);
  color: var(--color-text-muted);
}

/* ═════════════════════════════════════════════════════════════════════════
   32. MEMBRE DE L'ÉQUIPE
   Sans cadre : une colonne de la grille, un portrait, un nom. Le filet du
   haut suffit à séparer — encadrer chaque personne donnerait quatre boîtes
   alignées, ce que la charte réserve aux objets qu'on ouvre.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-person { display: flex; flex-direction: column; gap: 8px; }
.ag-person__fig { margin: 0 0 6px; }
.ag-person__fig img { aspect-ratio: 4 / 5; object-fit: cover; }
.ag-person__role { color: var(--color-text-muted); margin: 0; }
.ag-person__social { display: flex; gap: 12px; margin-top: 6px; }
.ag-person__social a {
  color: var(--color-text-muted); text-decoration: none; font-size: 15px;
  transition: color var(--t-base);
}
.ag-person__social a:hover { color: var(--accent-fg); }

/* ═════════════════════════════════════════════════════════════════════════
   34. PAQUETS LIBRES
   Un index, pas une galerie. Chaque entrée tient sur trois lignes — le nom en
   mono, ce que le paquet fait, où le trouver — et se sépare de la suivante
   par un filet, comme les articles en liste.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-pkgs { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
@media (min-width: 900px) { .ag-pkgs { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 40px; } }

.ag-pkg { padding: 22px 0; border-top: var(--ag-rule); }
.ag-pkg__head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px; }
.ag-pkg__name { font-size: 15px; color: var(--color-text); letter-spacing: .01em; }
.ag-pkg__ver { color: var(--color-text-muted); }
.ag-pkg__desc { margin: 8px 0 12px; color: var(--color-text-muted); max-width: 52ch; }

.ag-pkg__links { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
.ag-pkg__links a {
  color: var(--color-text-muted); text-decoration: none;
  border-bottom: 1px solid var(--color-border);
  transition: color var(--t-base), border-color var(--t-base);
}
.ag-pkg__links a:hover { color: var(--accent-fg); border-bottom-color: var(--accent-fg); }
.ag-pkg__date { color: var(--color-text-muted); opacity: .6; margin-left: auto; }

/* ═════════════════════════════════════════════════════════════════════════
   35. CARROUSEL DES PAQUETS (hero)
   Une planche qui change de contenu, pas un diaporama d'images : le cadre,
   la barre de titre et le pied ne bougent jamais, seul le bloc de code est
   remplacé. C'est ce qui permet à l'œil de revenir au même endroit à chaque
   diapositive au lieu de chercher où l'information a glissé.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-deck__label { display: inline-flex; align-items: center; gap: 8px; }
.ag-deck__nav { display: flex; align-items: center; gap: 4px; }
.ag-deck__count { color: var(--color-text-muted); min-width: 4ch; text-align: center; }
.ag-deck__count b { color: var(--color-text); font-weight: inherit; }

.ag-deck__arrow {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 2px 6px; border-radius: var(--radius-xs);
  color: var(--color-text-muted); font-size: 13px; line-height: 1;
  transition: color var(--t-base), background var(--t-base);
}
.ag-deck__arrow:hover { color: var(--accent-fg); background: var(--color-primary-bg); }
.ag-deck__arrow:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 1px; }

.ag-deck__head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px;
  padding: 14px 16px 0;
}
.ag-deck__name { font-size: 15px; color: var(--accent-fg); }
.ag-deck__ver, .ag-deck__lang { color: var(--color-text-muted); }
.ag-deck__lang { margin-left: auto; }

/* Le code des sept paquets n'a pas le même nombre de lignes : sans plancher,
   changer de diapositive ferait sauter le titre du hero, qui est aligné sur
   cette planche. La ligne la plus longue défile plutôt que de déborder. */
.ag-deck .ag-plate__body { min-height: 216px; padding-top: 14px; overflow-x: auto; }

.ag-deck__summary {
  padding: 0 16px 16px; color: var(--color-text-muted);
  font-size: var(--text-small); line-height: 1.5;
}

.ag-deck__dots { display: flex; align-items: center; gap: 7px; }
.ag-deck__dot {
  appearance: none; border: 0; padding: 0; cursor: pointer;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--color-border);
  transition: background var(--t-base), transform var(--t-base);
}
.ag-deck__dot:hover { background: var(--color-text-muted); }
.ag-deck__dot.is-active { background: var(--accent-fg); transform: scale(1.3); }
.ag-deck__dot:focus-visible { outline: 2px solid var(--accent-fg); outline-offset: 2px; }

.ag-deck__links { display: flex; gap: 16px; margin-left: auto; }
.ag-deck__links > span { display: flex; gap: 16px; }
.ag-deck__links a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
.ag-deck__links a:hover { color: var(--accent-fg); border-bottom-color: var(--accent-fg); }

/* Les extraits réels sont plus larges que la planche inventée qu'ils
   remplacent : `print(f"{r.distance_km} km, ...")  # 551.8 km, 681.0 min` fait
   73 caractères. `.ag-plate__row` porte un `overflow: hidden` qui coupait
   chaque ligne net, sans barre de défilement — le lecteur ne voyait même pas
   qu'il manquait quelque chose. Ici la ligne déborde, et c'est le CORPS qui
   défile, une seule barre pour tout le bloc. */
.ag-deck .ag-plate__row { overflow: visible; }
.ag-deck .ag-plate__body { font-size: 12px; line-height: 1.9; }

/* La barre de défilement, discrète mais présente : elle est le seul indice
   qu'une ligne continue à droite. */
.ag-deck .ag-plate__body { scrollbar-width: thin; scrollbar-color: var(--color-border) transparent; }
.ag-deck .ag-plate__body::-webkit-scrollbar { height: 6px; }
.ag-deck .ag-plate__body::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 3px; }
.ag-deck .ag-plate__body::-webkit-scrollbar-track { background: transparent; }

/* ═════════════════════════════════════════════════════════════════════════
   34. PAGE D'ARTICLE — trois colonnes
   Chacune répond à une question distincte, et c'est ce qui justifie qu'elles
   soient trois plutôt qu'une colonne fourre-tout :

     · à GAUCHE   où j'en suis (sommaire) et où aller ensuite (récents) ;
     · au CENTRE  le texte, borné à la mesure de lecture ;
     · à DROITE   sous quoi CET article est classé.

   La version précédente empilait tout à droite — sommaire, récents, rubriques
   du site, mots-clés du site — soit 1586 px dans un écran de 961, et un
   ascenseur gris en travers de la page pour le rattraper.
   ═════════════════════════════════════════════════════════════════════════ */
/* `minmax(0, 1fr)` et NON `1fr`. Un enfant de grille a `min-width: auto` : il
   refuse de descendre sous la largeur min-content de son contenu, et une
   grille d'une seule colonne s'élargit alors jusqu'à ce contenu. Mesuré sur un
   téléphone de 377 px : la page sortait à 818 px, soit plus du double, et il
   fallait la faire glisser pour lire. Le zéro autorise la colonne à rétrécir ;
   ce qui ne rentre pas défile dans son propre bloc. */
.ag-article { display: grid; grid-template-columns: minmax(0, 1fr); gap: 40px; }

@media (min-width: 992px) {
  /* Deux colonnes tant que trois ne tiennent pas. Le classement passe sous le
     texte plutôt que de comprimer la mesure : une ligne trop courte coûte plus
     à la lecture qu'un bloc déplacé. */
  .ag-article {
    grid-template-columns: 230px minmax(0, var(--ag-measure));
    gap: 56px;
    justify-content: center;
    align-items: start;
  }
  .ag-facets { grid-column: 2; }
  /* Le rail colle. Il est TENU COURT pour cela — sommaire plus trois articles
     récents — parce qu'un élément collant plus haut que la fenêtre ne s'épingle
     pas, et que le borner ferait revenir l'ascenseur qu'on vient de retirer. */
  .ag-rail { position: sticky; top: 96px; }
  .ag-reading-head .ag-page-head__inner {
    max-width: calc(230px + 56px + var(--ag-measure));
    margin-inline: auto;
  }
}

@media (min-width: 1280px) {
  .ag-article {
    grid-template-columns: 230px minmax(0, var(--ag-measure)) 210px;
  }
  /* `grid-row: 1` : sans lui, le placement automatique renvoie le classement
     à la ligne suivante et laisse la troisième colonne vide. */
  .ag-facets { grid-column: 3; grid-row: 1; position: sticky; top: 96px; }
  .ag-reading-head .ag-page-head__inner {
    max-width: calc(230px + 56px + var(--ag-measure) + 56px + 210px);
  }
}

.ag-rail__block + .ag-rail__block { margin-top: 40px; }
/* Le bloc des récents sert aussi dans la colonne de la page de liste,
   qui espace ses blocs par `.ag-side__block`. Il porte les deux classes ;
   cette règle empêche les deux marges de s'additionner. */
.ag-rail__block.ag-side__block + .ag-side__block { margin-top: 36px; }
.ag-railtitle {
  margin: 0 0 14px;
  color: var(--color-text-muted); font-size: var(--text-label);
  letter-spacing: .1em; text-transform: uppercase;
}

/* --- Articles récents : des cartes ------------------------------------- */
.ag-recent { display: grid; gap: 22px; }
.ag-recent__card {
  display: block; text-decoration: none; color: inherit;
  padding-top: 14px; border-top: var(--ag-rule);
}
.ag-recent__thumb {
  display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  margin-bottom: 10px; border-radius: var(--radius-xs);
}
.ag-recent__title {
  display: block;
  font-size: var(--text-small); line-height: 1.35; color: var(--color-text);
  transition: color var(--t-base);
}
/* La date en pied, en mono : elle se lit d'un coup d'œil et ne se confond
   jamais avec le titre, ce qui était le défaut de la ligne unique. */
.ag-recent__date {
  display: block; margin-top: 8px;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .08em; color: var(--color-text-muted);
}
.ag-recent__card:hover .ag-recent__title { color: var(--accent-fg); }

/* --- Le classement de l'article ---------------------------------------- */
.ag-facets { display: grid; gap: 32px; align-content: start; }
.ag-facet__list { list-style: none; margin: 0; padding: 0; }
.ag-facet__list > li + li { margin-top: 2px; }
.ag-facet__list a {
  display: block; padding: 7px 0;
  color: var(--color-text); text-decoration: none;
  font-size: var(--text-small); line-height: 1.35;
  border-bottom: var(--ag-rule);
  transition: color var(--t-base);
}
.ag-facet__list a:hover { color: var(--accent-fg); }

/* --- Ligne d'identité, sous le titre ----------------------------------- */
.ag-artline {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  color: var(--color-text-muted);
  font-size: var(--text-label); letter-spacing: .08em; text-transform: uppercase;
}
.ag-artline__sep { color: var(--color-border); }

/* --- Le corps ---------------------------------------------------------- */
/* Le premier paragraphe est un chapô sans avoir à être saisi comme tel : un
   champ « chapô » de plus serait un champ de plus à remplir quarante-cinq
   fois, et vide la moitié du temps. */
.ag-prose--article > p:first-of-type {
  font-size: clamp(18px, 1.3vw, 21px); line-height: 1.55;
  color: var(--color-text-strong);
}
/* Le repère de section : un tiret terre cuite au-dessus de chaque titre.
   C'est le filet de la charte réduit à la longueur d'un mot, et le seul
   ornement de la page — ce qui lui laisse sa force. */
.ag-prose--article h2 { position: relative; margin-top: 2.2em; padding-top: .55em; }
.ag-prose--article h2::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 28px; height: 2px; background: var(--accent-fg);
}

.ag-artfoot {
  margin-top: var(--space-7); padding-top: var(--space-5);
  border-top: var(--ag-rule);
}

/* ═════════════════════════════════════════════════════════════════════════
   35. COLONNE LATÉRALE — styles RESTAURÉS
   Ils avaient été emportés par un nettoyage trop large : la suppression de la
   variante « lecture » de `.ag-with-side` allait jusqu'au commentaire de
   section suivant, et ces règles se trouvaient entre les deux. Effet visible
   uniquement sur la page de LISTE, seule à porter encore `.ag-side` — les
   rubriques y sont ressorties en puces bleues soulignées, c'est-à-dire sans
   feuille de style du tout.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-side__block + .ag-side__block { margin-top: 36px; }
.ag-side__title {
  margin: 0 0 14px; padding-bottom: 10px;
  border-bottom: 2px solid var(--color-border);
  color: var(--color-text-muted);
}
/* Aucune carte, aucun fond : des lignes. C'est le motif de liste du système
   (`.ag-rows`), décliné en liens. */
.ag-side__list { list-style: none; margin: 0; padding: 0; }
.ag-side__list > li { border-bottom: var(--ag-rule); }
.ag-side__link {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 0; color: var(--color-text); text-decoration: none;
  font-size: var(--text-small); line-height: 1.4;
}
.ag-side__link:hover { color: var(--accent-fg); }
.ag-side__count {
  font-family: var(--font-mono); font-size: var(--text-label);
  color: var(--color-text-muted); flex: none;
}

/* ═════════════════════════════════════════════════════════════════════════
   36. RUBRIQUES DU BLOG — une barre de filtre, pas une liste de liens
   Sur un index de quarante-cinq articles, les rubriques sont le premier geste
   de navigation : elles appartiennent au-dessus de la liste, pas au tiers
   inférieur d'une colonne, sous les articles récents, où personne ne les
   cherche.

   Elles disent aussi OÙ L'ON EST : la page d'une rubrique devient un état de
   la liste — l'entrée courante marquée — au lieu d'une page qui lui ressemble
   sans qu'on sache comment y revenir.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-cats {
  display: flex; flex-wrap: wrap;
  border-top: var(--ag-rule); border-bottom: var(--ag-rule);
  margin-bottom: var(--space-6);
}
.ag-cats__item {
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 14px 0; margin-right: 28px;
  color: var(--color-text-muted); text-decoration: none;
  font-family: var(--font-mono); font-size: var(--text-label);
  letter-spacing: .08em; text-transform: uppercase;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color var(--t-base), border-color var(--t-base);
}
.ag-cats__item:hover { color: var(--color-text); }
.ag-cats__item.is-current {
  color: var(--accent-fg); border-bottom-color: var(--accent-fg);
}
/* Le compte est un chiffre, pas un libellé : il reste en retrait tant qu'on
   ne le cherche pas. */
.ag-cats__count { color: var(--color-border); }
.ag-cats__item.is-current .ag-cats__count { color: var(--accent-fg); }

/* ═════════════════════════════════════════════════════════════════════════
   37. BANDEAU DE CONSENTEMENT
   Une barre posée au bas de l'écran, du même bois que le reste : fond plein,
   un filet d'un pixel au-dessus, la mono sur les deux boutons. Pas d'ombre
   portée, pas de carte flottante, pas de superposition qui bloque la page —
   on peut continuer à lire sans répondre, et le site fonctionne pendant ce
   temps-là.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-consent {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 1035;   /* sous les modales de Bootstrap (1055), au-dessus du reste */
  /* Le fond DOIT être opaque : la barre couvre le bas de la page. C'est aussi
     la raison du filet — sans lui, deux aplats de la même couleur se
     toucheraient sans qu'on voie où l'un finit. */
  background: var(--color-bg);
  border-top: var(--ag-rule);
}
.ag-consent__inner {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding-block: 20px;
}
.ag-consent__text { max-width: var(--ag-measure); }
.ag-consent__text p {
  margin: 8px 0 10px;
  font-size: var(--text-small);
  color: var(--color-text-muted);
}
.ag-consent__actions {
  display: flex;
  gap: 10px;
  flex: none;
}
/* Les deux boutons occupent la même largeur. C'est la règle de fond du
   bandeau, écrite ici parce qu'elle se démolit d'un `padding` : refuser doit
   être aussi facile qu'accepter, donc aussi VISIBLE. */
.ag-consent__actions .ag-btn {
  flex: 1 1 0;
  justify-content: center;
}
@media (min-width: 768px) {
  .ag-consent__inner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 40px;
  }
  .ag-consent__actions .ag-btn { flex: none; }
}

/* Le bas de page ne doit pas finir sous la barre. `:has()` évite d'ajouter une
   classe sur `<body>` depuis le gabarit : la règle suit l'existence du
   bandeau, et disparaît avec lui. */
body.site-public:has(.ag-consent) { padding-bottom: 140px; }
@media (min-width: 768px) {
  body.site-public:has(.ag-consent) { padding-bottom: 96px; }
}

/* Entrée : la barre monte d'un cran. Rien de plus — c'est une question posée,
   pas une notification à faire remarquer. */
@media (prefers-reduced-motion: no-preference) {
  /* `--t-slow` porte la durée ET la courbe : en ajouter une seconde
     invaliderait la déclaration entière. */
  .ag-consent { animation: ag-consent-in var(--t-slow) both; }
  @keyframes ag-consent-in {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
}

/* ═════════════════════════════════════════════════════════════════════════
   38. CHOIX EN COURS, SUR LA PAGE DES COOKIES
   Le bandeau disparaît une fois répondu ; sans cet encadré, revenir sur sa
   décision demanderait de vider ses cookies à la main.
   ═════════════════════════════════════════════════════════════════════════ */
.ag-choice {
  margin-top: var(--space-6);
  padding: 22px;
  border: var(--ag-rule);
}
.ag-choice__state {
  display: flex; align-items: baseline; gap: 10px;
  margin: 10px 0 16px;
}
.ag-choice__value { color: var(--accent-fg); }
.ag-choice__actions { display: flex; flex-wrap: wrap; gap: 10px; }
