@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');

/* ============ TOKENS ============ */
:root{
  --bg:#F7F9FD;
  --ink:#0B1220;
  --ink-soft:#55637A;
  --primary:#1685FF;
  --primary-deep:#0B63F6;
  --cyan:#38B7FF;
  --violet:#5B3DF5;
  --green:#00966A;
  --amber:#D97706;
  --line:rgba(11,18,32,.09);

  --space-2:16px; --space-3:24px; --space-4:32px;
  --space-5:48px; --space-6:64px; --space-7:96px;

  --r-md:20px; --r-lg:28px; --r-xl:36px;
  --ease:cubic-bezier(.22,1,.36,1);
  --shadow-sm:0 2px 8px rgba(11,18,32,.06);
  --shadow-md:0 14px 36px rgba(11,18,32,.09);
  --shadow-lg:0 30px 72px rgba(11,18,32,.15);

  --glass:rgba(255,255,255,.66);
  --glass-strong:rgba(255,255,255,.86);
  --glass-line:rgba(255,255,255,.94);
}

*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none !important}
html{scroll-behavior:smooth;overflow-x:hidden}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font:16px/1.62 'DM Sans',system-ui,-apple-system,sans-serif;
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
}
/* halo + trame technique */
body::before{
  content:'';position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(40rem 40rem at 14% 6%, rgba(22,133,255,.16), transparent 62%),
    radial-gradient(34rem 34rem at 88% 22%, rgba(56,183,255,.14), transparent 60%),
    radial-gradient(30rem 30rem at 62% 98%, rgba(91,61,245,.09), transparent 62%),
    linear-gradient(90deg,rgba(11,18,32,.035) 1px,transparent 1px),
    linear-gradient(0deg,rgba(11,18,32,.03) 1px,transparent 1px);
  background-size:auto,auto,auto,112px 112px,112px 112px;
}
h1,h2,h3{font-family:'Space Grotesk',system-ui,sans-serif;letter-spacing:-.028em;line-height:1.05}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer}
:focus-visible{outline:3px solid var(--primary);outline-offset:3px;border-radius:6px}

/* Largeur utile : plafond relevé pour ne pas laisser d'immenses marges sur grand écran. */
/* Largeur utile : gouttière fluide mais bornée — assez large sur mobile,
   sans laisser d'immenses marges sur grand écran. */
.wrap{--gutter:clamp(24px,4.6vw,130px);
  width:min(var(--wrap,1960px),calc(100% - 2*var(--gutter)));
  margin-inline:auto;position:relative;z-index:1}
/* main centre déjà verticalement : ce rembourrage ne déplaçait rien,
   il ne faisait qu'amputer la hauteur disponible pour le contenu. */
.section{padding:clamp(16px,2.6vh,40px) 0}
.eyebrow{
  display:inline-flex;align-items:center;gap:9px;
  font-size:clamp(13px,.55vw + .35vh,17px);font-weight:700;letter-spacing:.13em;
  line-height:1;   /* sans ça, 7 px de strut poussent tout le bloc vers le bas */
  text-transform:uppercase;color:var(--primary-deep);
}
.eyebrow::before{content:'';width:clamp(22px,1.9vw,38px);height:3px;border-radius:2px;background:var(--primary)}
.section-head{max-width:min(100%,1180px);margin-bottom:clamp(28px,4.2vh,64px)}
.section-head h2{font-size:clamp(36px,3.4vw + 2.6vh,104px);
  margin:clamp(12px,1.7vh,24px) 0 0;max-width:17ch;text-wrap:balance}
.lead{color:var(--ink-soft);font-size:clamp(18px,1.15vw + .55vh,30px);line-height:1.5;
  margin:clamp(16px,2.2vh,28px) 0 0}

/* ============ GLASS ============ */
.glass{
  background:var(--glass);
  -webkit-backdrop-filter:blur(18px) saturate(1.4);backdrop-filter:blur(18px) saturate(1.4);
  border:1px solid var(--glass-line);border-radius:var(--r-lg);box-shadow:var(--shadow-md);
}

/* ============ BUTTONS ============ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  min-height:52px;padding:0 26px;border-radius:999px;border:1px solid transparent;font-weight:700;
  transition:transform .22s var(--ease),box-shadow .22s var(--ease),background-color .22s var(--ease);
}
.btn svg{width:19px;height:19px;flex:none}
.btn-primary{background:var(--ink);color:#fff;box-shadow:0 10px 26px rgba(11,18,32,.24)}
.btn-primary:hover{transform:translateY(-3px);box-shadow:0 18px 42px rgba(11,18,32,.32)}
/* Fond assombri : blanc sur #1685FF ne donnait que 3,60:1, sous le seuil AA (4,5).
   #0B63F6 monte à 5,03:1. */
.btn-accent{background:var(--primary-deep);color:#fff;box-shadow:0 10px 26px rgba(22,133,255,.36)}
.btn-accent:hover{transform:translateY(-3px);background:#0A55D0;box-shadow:0 18px 44px rgba(22,133,255,.46)}
.btn-ghost{background:var(--glass-strong);border-color:var(--glass-line);box-shadow:var(--shadow-sm);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
.btn-ghost:hover{transform:translateY(-3px);box-shadow:var(--shadow-md)}
.btn-sm{min-height:44px;padding:0 20px;font-size:15px}

/* ============ NAV ============ */
.nav{position:sticky;top:0;z-index:50;padding:13px 0;
  background:rgba(247,249,253,.74);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  border-bottom:1px solid var(--line)}
.nav-inner{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:clamp(16px,2vw,var(--space-4))}
.nav-inner > .brand{justify-self:start}
.nav-inner > .btn{justify-self:end}
.brand{display:flex;align-items:center;gap:10px;font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:20px}
.brand svg{width:38px;height:38px;flex:none}
.brand .t{color:var(--primary)}
.nav-links{display:flex;justify-content:center;gap:clamp(14px,1.6vw,28px);font-weight:500;color:var(--ink-soft)}
.nav-links a{padding:11px 4px;position:relative;transition:color .2s var(--ease);display:inline-block}
.nav-links a::after{content:'';position:absolute;left:4px;right:4px;bottom:2px;height:2px;border-radius:2px;
  background:var(--primary);transform:scaleX(0);transform-origin:left;transition:transform .25s var(--ease)}
.nav-links a:hover{color:var(--ink)}
.nav-links a:hover::after{transform:scaleX(1)}
/* Le bouton n'existe qu'en dessous de 980px, là où la barre de liens se replie. */
.nav-toggle{display:none}
.nav-toggle svg{width:24px;height:24px}
.nav-toggle .nav-close{display:none}
.nav-toggle[aria-expanded="true"] .nav-burger{display:none}
.nav-toggle[aria-expanded="true"] .nav-close{display:block}

/* ============ HERO ============ */
/* main centre déjà le hero : une marge dissymétrique le ferait descendre. */
/* Rembourrage volontairement dissymétrique : main centre la boîte, donc une
   réserve plus grande en bas remonte optiquement le bloc — la nav occupe déjà
   le haut de la fenêtre et tirait la composition vers le bas. */
.hero{padding:clamp(8px,1.4vh,20px) 0 clamp(20px,3.2vh,44px)}
/* Répartition 60 / 40 : le texte porte la page, l'image l'accompagne. */
.hero-grid{display:grid;grid-template-columns:63fr 37fr;
  column-gap:clamp(28px,3vw,72px);row-gap:var(--space-5);align-items:center}
.hero h1{font-size:clamp(42px,min(7.8vw,14vh),168px);line-height:1.02;
  margin:clamp(14px,1.8vh,26px) 0 0}
.hero h1 .hl{
  background:linear-gradient(98deg,var(--primary-deep) 0%,var(--primary) 42%,var(--cyan) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.hero .lead{max-width:40ch}   /* la grille avance sur la gauche : le texte ne doit pas passer dessous */
.hero-cta{display:flex;flex-wrap:wrap;gap:14px;margin-top:clamp(22px,3.2vh,42px)}
.stats{display:flex;gap:clamp(32px,4vw,80px);margin-top:clamp(26px,3.6vh,52px);flex-wrap:wrap}
.stat b{display:block;font-family:'Space Grotesk',sans-serif;font-size:clamp(32px,2.9vw,60px);line-height:1}
.stat span{font-size:clamp(14px,.85vw,18px);color:var(--ink-soft)}

/* ============ GRILLE ISOMETRIQUE ============ */
.iso-stage{position:relative;aspect-ratio:1/.82;display:grid;place-items:center;
  perspective:1400px;perspective-origin:50% 50%}
.iso-glow{position:absolute;inset:10%;border-radius:50%;
  background:radial-gradient(circle,rgba(22,133,255,.20),transparent 66%);filter:blur(30px)}
.iso-plane{position:relative;width:88%;aspect-ratio:1/1;transform-style:preserve-3d;
  --rx:56deg; --rz:45deg;
  /* Calage optique de la masse dans son cadre. Deux variables pour ajuster
     sans toucher au reste : négatif = vers la gauche / vers le haut. */
  --nudge-x:-2%; --nudge-y:-1%;
  transform:translate(var(--nudge-x),var(--nudge-y)) rotateX(var(--rx)) rotateZ(var(--rz))}
.iso-grid{position:absolute;inset:0;display:grid;
  grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:9%;transform-style:preserve-3d}

.tile{position:relative;transform-style:preserve-3d;border:0;padding:0;background:none;cursor:pointer;
  transform:scale(var(--s,1))}
/* La zone de survol reste immobile ; c'est l'intérieur qui monte.
   Sinon la tuile sort de sous le curseur, le survol se perd, elle retombe : ça clignote. */
.tile-lift{position:absolute;inset:0;transform-style:preserve-3d;
  transform:translateZ(var(--z,0px));transition:transform .38s var(--ease)}
.tile:hover,.tile:focus-visible,.tile.is-held{--z:52px}
.tile.is-hub{--z:26px}
.tile.is-hub:hover,.tile.is-hub:focus-visible,.tile.is-hub.is-held{--z:70px}
.tile-face{position:absolute;inset:0;border-radius:22%;
  background:var(--glass-strong);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  border:1px solid var(--glass-line);
  box-shadow:0 18px 34px rgba(11,18,32,.15), inset 0 1px 0 rgba(255,255,255,.95);
  display:grid;place-items:center;transition:box-shadow .38s var(--ease)}
.tile:hover .tile-face,.tile:focus-visible .tile-face{box-shadow:0 40px 62px rgba(11,18,32,.2), inset 0 1px 0 #fff}
.tile.is-held .tile-face{box-shadow:0 40px 62px rgba(11,18,32,.2), inset 0 1px 0 #fff, 0 0 0 2px var(--tint,var(--primary))}

.tile.is-hub .tile-face{background:linear-gradient(150deg,#fff,#EFF6FF);
  box-shadow:0 26px 46px rgba(22,133,255,.28), inset 0 1px 0 #fff}
.tile-icon{width:56%;aspect-ratio:1;display:grid;place-items:center;
  transform:rotateZ(calc(-1 * var(--rz,45deg))) rotateX(calc(-1 * var(--rx,56deg)))}
.tile-icon svg{width:100%;height:100%;display:block;color:var(--tint,var(--primary))}
.tile.is-hub .tile-icon{width:92%}   /* la marque a du blanc dans son viewBox : on compense */

/* ============ CARTES EXPERTISE ============ */
.cards{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(18px,1.7vw,34px);
  min-height:clamp(240px,36.5vh,560px)}
/* space-between plutôt qu'une marge auto sur l'icône : le surplus de hauteur
   se répartit sur les intervalles au lieu de creuser un seul trou. */
.card{padding:clamp(26px,2.2vw,44px);display:flex;flex-direction:column;
  justify-content:space-between;gap:clamp(12px,1.1vw,18px);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.card:hover{transform:translateY(-8px);box-shadow:var(--shadow-lg)}
.card-ico{width:clamp(52px,3.8vw,74px);height:clamp(52px,3.8vw,74px);
  border-radius:clamp(15px,1.3vw,22px);display:grid;place-items:center;
  background:linear-gradient(140deg,var(--tint,var(--primary)),color-mix(in srgb,var(--tint,var(--primary)) 60%,#fff));
  box-shadow:0 10px 22px color-mix(in srgb,var(--tint,var(--primary)) 34%,transparent)}
.card-ico svg{width:52%;height:52%;color:#fff}
.card h3{font-size:clamp(21px,1.62vw,32px);margin:4px 0 0;text-wrap:balance}
.card p{margin:0;color:var(--ink-soft);font-size:clamp(16px,1.22vw,23px);line-height:1.55}

/* ============ OPEN SOURCE ============ */
.os{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(18px,1.7vw,34px);
  min-height:clamp(260px,38vh,580px)}
.os-card{padding:clamp(26px,2.2vw,46px);display:flex;flex-direction:column;
  gap:clamp(14px,1.4vw,24px);justify-content:space-between;position:relative;overflow:hidden;
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.os-card:hover{transform:translateY(-8px);box-shadow:var(--shadow-lg)}
.os-card::before{content:'';position:absolute;inset:-40% -30% auto auto;width:64%;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--tint) 22%,transparent),transparent 68%)}
.os-mark{width:clamp(64px,5.6vw,112px);height:clamp(64px,5.6vw,112px);position:relative;flex:none}
.os-card h3{font-size:clamp(24px,1.9vw,38px);margin:0;position:relative}
.os-card p{margin:0;color:var(--ink-soft);font-size:clamp(16px,1.1vw,22px);line-height:1.55;position:relative}
.os-link{display:inline-flex;align-items:center;gap:8px;font-weight:700;color:var(--tint);
  position:relative;font-size:clamp(16px,1.05vw,20px);min-height:44px}
.os-link svg{width:18px;height:18px;transition:transform .25s var(--ease)}
.os-card:hover .os-link svg{transform:translateX(4px)}

/* ============ CTA ============ */
/* Le panneau avait une hauteur intrinsèque figée (452 px) : plus l'écran
   grandissait, plus il paraissait perdu au milieu du vide. */
.cta{position:relative;overflow:hidden;border-radius:clamp(28px,2.6vw,52px);
  padding:clamp(44px,6vh,88px) clamp(28px,4vw,80px);
  min-height:min(74vh,1040px);display:flex;flex-direction:column;justify-content:center;
  text-align:center;color:#fff;
  background:linear-gradient(140deg,#0B1220,#132A52 52%,#0B63F6);box-shadow:var(--shadow-lg)}
.cta::before{content:'';position:absolute;inset:-34% -12% auto auto;width:54%;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,rgba(56,183,255,.34),transparent 66%)}
.cta h2{font-size:clamp(38px,3.6vw + 2.8vh,112px);line-height:1.03;margin:0;position:relative}
.cta p{position:relative;color:rgba(255,255,255,.92);max-width:34ch;text-wrap:balance;
  font-size:clamp(18px,1.15vw + .6vh,30px);line-height:1.45;margin:clamp(16px,2.4vh,32px) auto 0}
.cta-actions{position:relative;display:flex;gap:18px;justify-content:center;flex-wrap:wrap;
  margin-top:clamp(26px,3.6vh,48px)}
.cta .btn{min-height:clamp(54px,5.6vh,66px);font-size:clamp(16px,1vw,19px);padding:0 clamp(26px,2.2vw,40px)}
.cta .btn svg{width:21px;height:21px}
.cta .btn-primary{background:#fff;color:var(--ink)}
.cta .btn-ghost{background:rgba(11,18,32,.34);border-color:rgba(255,255,255,.58);color:#fff;
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);box-shadow:none}
.cta .btn-ghost:hover{background:rgba(11,18,32,.46);border-color:rgba(255,255,255,.8)}

/* ============ RÉVÉLATIONS ============ */
/* Volontairement en CSS et non en JavaScript. Une animation JS pose son état
   de départ immédiatement mais dépend de requestAnimationFrame pour en sortir :
   quand l'horloge ne tourne pas (onglet en arrière-plan, rendu suspendu), le
   contenu reste bloqué à opacity:0. Une animation CSS se termine toujours
   d'elle-même dès que la page est peinte — et la page reste lisible sans JS.
   L'état de repos est donc toujours l'état visible. */
@media (prefers-reduced-motion:no-preference){
  [data-anim]{animation:kb-apparait .5s var(--ease) both}
  /* Décalage en cascade : les éléments d'un même bloc entrent l'un après
     l'autre. nth-child suffit, les blocs comptent moins de six éléments. */
  [data-anim]:nth-child(2){animation-delay:.06s}
  [data-anim]:nth-child(3){animation-delay:.12s}
  [data-anim]:nth-child(4){animation-delay:.18s}
  [data-anim]:nth-child(5){animation-delay:.24s}
  [data-anim]:nth-child(n+6){animation-delay:.3s}

  /* Les tuiles entrent en vague depuis le centre du damier.
     L'ordre du DOM est 1..9 ; les délais suivent la distance au centre. */
  [data-tile]{animation:kb-tuile .5s var(--ease) both}
  [data-tile]:nth-child(5){animation-delay:.10s}
  [data-tile]:nth-child(2),[data-tile]:nth-child(4),
  [data-tile]:nth-child(6),[data-tile]:nth-child(8){animation-delay:.18s}
  [data-tile]:nth-child(1),[data-tile]:nth-child(3),
  [data-tile]:nth-child(7),[data-tile]:nth-child(9){animation-delay:.26s}

}
@keyframes kb-apparait{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
/* scale() seul : les tuiles portent déjà leur propre transform 3D via .tile-lift,
   animer transform ici l'écraserait. */
@keyframes kb-tuile{from{opacity:0;scale:.88}to{opacity:1;scale:1}}

/* ============ ARTICLES ============ */
/* Un article se lit en défilant : contrairement aux autres pages, son contenu
   ne doit pas être centré verticalement mais partir du haut. */
main[data-flow]{justify-content:flex-start}

.posts-tete{display:flex;align-items:center;justify-content:space-between;
  flex-wrap:wrap;gap:16px;margin-bottom:16px}
.posts-compte{margin:0;color:var(--ink-soft);font-size:clamp(14px,.9vw,16px)}
.recherche{position:relative;flex:none;width:min(100%,340px)}
.recherche svg{position:absolute;left:15px;top:50%;transform:translateY(-50%);
  width:18px;height:18px;color:var(--ink-soft);pointer-events:none}
.recherche input{width:100%;min-height:46px;padding:0 42px;border-radius:999px;
  border:1px solid var(--line);background:var(--glass-strong);color:var(--ink);
  font:inherit;font-size:16px;   /* 16px : en dessous, iOS zoome à la mise au point */
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}
.recherche input::placeholder{color:var(--ink-soft)}
/* Chrome ajoute sa propre croix d'effacement sur type="search" : elle
   doublonnait avec la nôtre dès qu'on tapait. */
.recherche input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
.recherche input:focus-visible{border-color:var(--primary);outline:3px solid rgba(22,133,255,.28);
  outline-offset:1px}
.recherche-vider{position:absolute;right:6px;top:50%;transform:translateY(-50%);
  width:34px;height:34px;display:none;place-items:center;border:0;border-radius:50%;
  background:rgba(11,18,32,.07);color:var(--ink);line-height:1}
.recherche.a-du-texte .recherche-vider{display:grid}
.recherche-vider svg{position:static;transform:none;width:14px;height:14px;color:inherit}

.posts-vide{margin:var(--space-5) 0;color:var(--ink-soft);text-align:center;
  font-size:clamp(16px,1.1vw,19px)}

.pagination{display:flex;align-items:center;justify-content:center;gap:10px;
  margin-top:clamp(26px,3.4vh,44px)}
.page-nums{display:flex;gap:6px}
.page-num,.page-fleche{display:grid;place-items:center;min-width:44px;min-height:44px;
  padding:0 8px;border-radius:12px;font-weight:700;font-size:15px;color:var(--ink-soft);
  transition:background-color .2s var(--ease),color .2s var(--ease)}
.page-num:hover,.page-fleche:not(.is-off):hover{background:rgba(11,18,32,.06);color:var(--ink)}
.page-num.is-current{background:var(--ink);color:#fff}
.page-fleche svg{width:18px;height:18px}
.page-fleche[rel="prev"] svg{transform:rotate(180deg)}
.page-fleche.is-off{opacity:.32;pointer-events:none}

.posts{display:flex;flex-direction:column;gap:10px}
.post{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;
  gap:clamp(18px,2.4vw,48px);padding:clamp(20px,1.7vw,30px) clamp(22px,1.9vw,34px);
  transition:transform .25s var(--ease),box-shadow .25s var(--ease),background-color .25s var(--ease)}
.post:hover{transform:translateY(-3px);box-shadow:var(--shadow-md)}
.post-meta{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  font-size:clamp(13px,.82vw,15px);color:var(--ink-soft)}
.post-tag{display:inline-flex;align-items:center;padding:4px 11px;border-radius:999px;
  background:color-mix(in srgb,var(--tint,var(--primary)) 15%,transparent);
  color:color-mix(in srgb,var(--tint,var(--primary)) 55%,var(--ink));
  font-weight:700;letter-spacing:.02em}
.post h3{font-size:clamp(19px,1.32vw,26px);margin:9px 0 0;text-wrap:balance}
.post p{margin:7px 0 0;color:var(--ink-soft);
  font-size:clamp(16px,.98vw,18px);line-height:1.5;max-width:104ch;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.post-go{display:grid;place-items:center;flex:none;
  width:44px;aspect-ratio:1;border-radius:50%;
  background:rgba(11,18,32,.06);color:var(--ink);
  transition:transform .25s var(--ease),background-color .25s var(--ease)}
.post-go svg{width:19px;height:19px}
.post:hover .post-go{transform:translateX(4px);background:var(--ink);color:#fff}

/* ---- corps d'article ---- */
/* Trois colonnes symétriques : le sommaire est calé sur le bord gauche de la
   zone utile, le texte reste optiquement centré sur la page. La colonne vide
   de droite n'est pas du gâchis, c'est ce qui empêche le texte de partir en
   biais. Le texte gagne en corps plutôt qu'en largeur : à nombre de caractères
   par ligne constant, une typo plus grande occupe plus de place et se lit
   mieux qu'une ligne longue. */
.article{max-width:100%;margin-inline:auto;display:grid}
.article-head,.article-body{min-width:0;max-width:680px;margin-inline:auto;width:100%}

.toc{display:none}
@media (min-width:1150px){
  .article{grid-template-columns:clamp(190px,15vw,250px) minmax(0,1fr) clamp(190px,15vw,250px);
    column-gap:clamp(32px,3vw,64px)}
  .article-head{grid-column:2;grid-row:1;max-width:820px}
  .toc{display:block;grid-column:1;grid-row:2;position:sticky;top:96px;align-self:start}
  .article-body{grid-column:2;grid-row:2}
}
@media (min-width:1600px){
  .article-head,.article-body{max-width:820px}
}
.toc-titre{font-size:12.5px;font-weight:700;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-soft);margin:0 0 14px}
.toc ol{list-style:none;margin:0;padding:0;border-left:2px solid var(--line)}
.toc li{margin:0}
.toc a{display:flex;align-items:center;min-height:44px;padding:8px 0 8px 16px;margin-left:-2px;
  border-left:2px solid transparent;color:var(--ink-soft);
  font-size:14.5px;line-height:1.4;transition:color .2s var(--ease),border-color .2s var(--ease)}
.toc a:hover{color:var(--ink)}
.toc a[aria-current="true"]{color:var(--primary-deep);border-left-color:var(--primary);font-weight:700}
.article-head{margin-bottom:clamp(28px,4vh,56px)}
.article h1{font-size:clamp(34px,2.6vw + 2vh,72px);line-height:1.06;
  margin:clamp(14px,1.8vh,24px) 0 0;text-wrap:balance}
.article-sub{color:var(--ink-soft);font-size:clamp(19px,1.2vw + .5vh,28px);
  line-height:1.5;margin:clamp(16px,2.2vh,28px) 0 0}
.article-body > * + *{margin-top:clamp(18px,2.4vh,30px)}
.article-body p,.article-body li{font-size:clamp(17px,1.28vw,22px);line-height:1.68;color:#26324A}
.article-body h2{font-size:clamp(26px,1.9vw,40px);margin-top:clamp(38px,5vh,72px);text-wrap:balance}
.article-body h3{font-size:clamp(20px,1.35vw,27px);margin-top:clamp(26px,3.4vh,44px)}
.article-body ul{padding-left:1.2em;margin-block:0}
.article-body li + li{margin-top:10px}
.article-body li::marker{color:var(--primary)}
.article-body strong{color:var(--ink)}
.article-body a{color:var(--primary-deep);text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px}
.article-body a:hover{text-decoration-thickness:2px}
.article-body code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em;
  background:rgba(11,18,32,.06);padding:.15em .45em;border-radius:6px}
.article-body pre{overflow-x:auto;margin-inline:clamp(-80px,-4.4vw,0px);padding:clamp(18px,1.8vw,28px);border-radius:var(--r-md);
  background:#0B1220;color:#D7E3F5;font-size:clamp(13px,.92vw,15.5px);line-height:1.62}
.article-body pre code{background:none;padding:0;font-size:inherit;color:inherit}
.article-body blockquote{margin:0;padding-left:clamp(18px,1.6vw,26px);
  border-left:3px solid var(--primary);color:var(--ink-soft);font-style:italic}
.article-note{padding:clamp(20px,2vw,32px);border-radius:var(--r-md);
  background:rgba(22,133,255,.07);border:1px solid rgba(22,133,255,.2)}
.article-note p{margin:0;font-size:clamp(16px,.98vw,19px);line-height:1.6}
.article-back{display:inline-flex;align-items:center;gap:8px;min-height:44px;
  font-weight:700;color:var(--primary-deep);font-size:clamp(15px,.95vw,17px)}
.article-back svg{width:17px;height:17px;transform:rotate(180deg);transition:transform .25s var(--ease)}
.article-back:hover svg{transform:rotate(180deg) translateX(4px)}
.article-end{margin-top:clamp(44px,6vh,88px);padding-top:clamp(28px,3.4vh,48px);
  border-top:1px solid var(--line);display:flex;flex-wrap:wrap;align-items:center;
  justify-content:space-between;gap:var(--space-3)}

@media (max-width:720px){
  .post{grid-template-columns:1fr}
  /* La carte entière est déjà cliquable : la pastille fléchée n'ajoute rien et
     coûte une soixantaine de pixels par ligne sur un écran étroit. */
  .post-go{display:none}
  /* Le point médian ne doit pas se retrouver seul en fin de ligne. */
  .post-meta > [aria-hidden]{display:none}
  .post-meta{gap:8px 12px}
}

/* ============ FOOTER ============ */
/* Pas de pied de page : le contenu occupe exactement la fenêtre.
   main devient une colonne centrée, donc rien ne traîne en bas sur une page courte
   et le défilement ne démarre que si le contenu dépasse vraiment. */
body{min-height:100dvh;display:flex;flex-direction:column}
main{flex:1 0 auto;display:flex;flex-direction:column;
  justify-content:center;
  /* « safe » : si le contenu dépasse (petit écran), on repart du haut
     au lieu de le centrer et d'en rendre le début inatteignable. */
  justify-content:safe center;
  /* La nav occupe le haut de la fenêtre : un centrage géométrique exact dans
     l'espace restant fait lire le bloc comme trop bas. Cette réserve basse le
     remonte de la moitié de sa valeur. */
  padding-bottom:clamp(18px,3vh,48px)}


/* ============ RESPONSIVE ============ */
@media (max-width:1080px){ .cards{grid-template-columns:repeat(2,1fr)} }
@media (max-width:980px){
  .hero-grid{grid-template-columns:1fr;gap:var(--space-5)}
  .iso-stage{max-width:520px;width:100%;margin-inline:auto}
  .os{grid-template-columns:1fr;max-width:460px;margin-inline:auto}
  .nav-inner{grid-template-columns:1fr auto auto}
  .nav-toggle{display:grid;place-items:center;width:46px;height:46px;
    border-radius:14px;border:1px solid var(--line);color:var(--ink);
    background:var(--glass-strong);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}
  .nav-links{display:none;position:absolute;top:calc(100% + 14px);left:0;right:0;
    flex-direction:column;gap:4px;padding:8px;border-radius:var(--r-md);
    /* Fond plein : sous une nav qui porte deja un backdrop-filter, le flou du
       panneau ne s applique pas et le texte de la page se lit au travers. */
    background:#fff;border:1px solid var(--line);box-shadow:var(--shadow-lg)}
  .nav-links.is-open{display:flex}
  .nav-links a{display:flex;align-items:center;min-height:48px;padding:0 16px;
    border-radius:12px;font-size:17px}
  .nav-links a::after{display:none}
  .nav-links a:hover{background:rgba(11,18,32,.05)}
  .nav-links a[aria-current="page"]{background:rgba(22,133,255,.12);color:var(--primary-deep)}
}
@media (max-width:560px){
  :root{--space-7:64px;--space-6:48px}
  .cards{grid-template-columns:1fr}
  .stats{gap:var(--space-3)}
  /* tourné à 45°, le plan occupe ~1.41x sa largeur : on le rétrécit pour éviter le scroll horizontal */
  .iso-plane{width:70%}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
}

/* wordmark : un seul flex item, sinon le gap coupe le mot en deux */
.brand .wm{display:inline-block;white-space:nowrap}
.brand .wm .t{font-style:normal;color:var(--primary)}

/* ============ LOCKUP : ACCORD MARQUE / MOT ============ */
/* La marque a un viewBox resserré sur son encre : sa hauteur CSS = sa hauteur réelle.
   On la cale sur la hauteur de capitale du mot (rapport ~1,3), centres alignés.       */
.brand{
  --wm-size: 25px;                 /* capitale ≈ 17,9 px */
  --mark-h: 24px;                  /* encre ≈ 23 px, soit ~1,27 × la capitale */
  gap: 10px;
}
.brand .brand-mark{
  height: var(--mark-h);
  width: auto;                     /* la marque est haute et étroite : on pilote par la hauteur */
  flex: none;
  overflow: visible;               /* la jambe qui pivote ne doit pas être rognée */
}
.brand .wm{ font-size: var(--wm-size); line-height: 1 }

/* jambes animées */
.brand-mark{ will-change: transform }

/* ============ LA MARQUE TIENT LIEU DE « K » ============ */
/* Le mot ne porte plus son K initial : c'est la marque qui le fournit.
   Elle se cale donc sur la hauteur de capitale, comme une vraie lettre,
   et l'espace qui la sépare du mot est une chasse de lettre, pas une marge. */
.brand{ --mark-h: 26px; gap: 2px }        /* encre ≈ 1,45 × la capitale */

.sr-only{
  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;
}

/* ============ TUILES : LOGOS OFFICIELS + RELIEF ============ */
/* Les tracés simple-icons sont monochromes : on les colore par --tint. */
.tile-icon svg{ fill: var(--tint, var(--primary)); }
.tile.is-hub .tile-icon svg{ fill: none; }   /* la marque porte ses propres dégradés */




.tile-icon{ width: 46% }
.tile.is-hub .tile-icon{ width: 52% }

/* la tuile centrale domine la grille, comme dans la référence */
.tile.is-hub{ --s: 1.26; z-index: 4 }

/* ============ VRAIE ÉPAISSEUR : LE CORPS DU BLOC ============ */
/* Chaque tuile est un solide : des plans empilés en Z, pas une ombre.
   Quand la scène tourne, on voit vraiment la tranche.                */
.tile{ --depth: 16px; --body: #DCE4EF }

.tile-body{
  position:absolute; inset:0; border-radius:22%;
  background:var(--body);
  transform-style:preserve-3d;
  pointer-events:none;
}
.tile-face{ transform:translateZ(1px) }   /* la face repose sur le corps */

.iso-stage{ perspective:1100px }          /* perspective plus courte : le relief se voit */

/* pilotage au curseur */
.iso-stage{ cursor:grab }
.iso-stage.is-dragging{ cursor:grabbing }
.iso-hint{
  position:absolute; left:50%; bottom:2%; transform:translateX(-50%);
  font-size:12px; color:var(--ink-soft); white-space:nowrap; pointer-events:none;
  opacity:.85; transition:opacity .3s var(--ease);
}
.iso-stage.is-dragging .iso-hint{ opacity:0 }

/* ============ HERO : ÉCART TEXTE / GRILLE STABLE ============ */
/* La grille était centrée dans sa colonne : plus l'écran s'élargit, plus elle
   s'éloigne du texte. On l'ancre à gauche et on plafonne sa largeur : l'écart
   ne dépend plus de la taille de l'écran. */
.hero-grid > div:last-child{ justify-self:center; width:min(100%, 720px) }

/* ============ CIBLES TACTILES ============ */
/* Repérées sous les 44 px recommandés : le lien de marque et les liens open source. */
.brand{ min-height:44px }
.os-link{ min-height:44px; align-items:center }

/* ============ NAV : PAGE COURANTE ============ */
/* Le lien de la page affichée reste souligné et en couleur d'encre. */
.nav-links a[aria-current="page"]{ color:var(--ink) }
.nav-links a[aria-current="page"]::after{ transform:scaleX(1) }
