layout-01-nested-bento
layout-17-quadrant-epicentre
Quadrant à épicentre
Poser quatre modules de VOLUMES très inégaux sans que la grille impose leurs tailles : chaque vignette n'est ancrée que par son coin vif, qui pointe le centre du bloc, et ses deux autres bords sont libres — donc sa largeur ET sa hauteur lui appartiennent. Les quatre coins vifs convergent sur un point unique, l'épicentre, où se serrent aussi les quatre numéros. C'est ce point qui tient la composition à la place des alignements de bord, et c'est pour ça que quatre tailles différentes ne s'y lisent pas comme un défaut de calage.
layoutwebslide1320×1498px13 benchmarks
Le fragment RÉEL, dans son propre document, sur le sol de sa charte — pas une image. Ce que vous voyez est exactement ce que vous collerez.
Employer quand
Quand quatre blocs de contenu réel — pas quatre KPI de même longueur — doivent cohabiter : un module à onze points, un à trois phrases longues, un à six points, un à une phrase. Un bento y répondrait en étirant les petits et en creusant des vides ; ici le vide n'existe pas, parce qu'aucune vignette n'est tenue de rattraper sa voisine. Vaut aussi dès que la numérotation doit se lire comme un PARCOURS et non comme quatre étiquettes indépendantes : réunis au centre, les numéros donnent l'ordre sans qu'aucune flèche ne soit dessinée.
Éviter quand
À moins de quatre modules, ou à plus : la forme est celle d'un quadrant, un cinquième bloc n'a plus de coin à occuper. Sous ~1100 px de largeur non plus — les quatre vignettes ne tiennent plus côte à côte, et le repli en pile fait disparaître l'épicentre, donc le pattern avec lui (au repli, on garde les vignettes et on rend l'ordre par l'index, pas par la position). Et jamais avec des contenus de même volume : quatre tailles identiques rendent la liberté du pattern invisible et une grille ordinaire fera mieux.
Notes
**Ce que ce pattern ajoute à `shape-02-teardrop-quadrant`, et qui n'est pas dans la primitive.** La primitive pose un carré arrondi dont un seul coin reste vif, et son coin vif désigne le coin de la planche qu'il occupe. Ici il désigne l'INVERSE — le centre — et cette inversion change la nature de la composition : les quatre formes ne bordent plus un bloc, elles RAYONNENT d'un point. Le benchmark d'orientation est donc l'exact miroir de celui de la primitive, et les deux ne peuvent pas être vrais en même temps ; c'est voulu, et c'est ce qui empêche de confondre les deux patterns. **L'ancrage par un seul coin est le mécanisme, tout le reste en découle.** Une vignette du haut est calée par le BAS (`align-self: end`) et par le CÔTÉ (`justify-self`) ; une vignette du bas par le HAUT. Ses deux autres bords ne sont tenus par rien, donc sa hauteur est celle de son contenu et sa largeur celle qu'on lui donne (`--vl-qe-w`, plafonnée par sa colonne). C'est la réponse à la faute classique du bento : quatre cellules d'une grille s'étirent à la plus grande, et le module le plus court récolte 200 px de vide qu'on essaie ensuite de meubler. Ici il n'y a rien à meubler. **Les colonnes ne fixent pas les largeurs, elles les plafonnent.** `grid-template-columns: 1.1fr 1fr` donne la place disponible de chaque côté ; la largeur réelle de chaque vignette est posée sur elle. Sans ce plafond, une vignette pourrait franchir l'axe et l'épicentre serait faux ; sans la largeur propre, on retomberait sur deux colonnes symétriques. **La pastille collée à l'angle (inset 0) n'est pas un détail de placement.** Posée à la distance du rembourrage, elle se lit comme une étiquette de coin, et les quatre se dispersent ; collée à l'arête, les quatre se rejoignent autour de l'épicentre et la numérotation devient la MARQUE du centre. C'est pour ça que la vignette réserve `badge + seam` du côté du coin vif : la place du numéro n'est pas négociable, elle est la contrepartie du geste. **Deux règles de lecture sont mesurées comme le reste, parce qu'elles ont été payées.** L'encre grise (`--fx-text-muted`, un #5A5A5A) rendait les listes molles sur bleu pâle : le corps prend désormais l'encre de sa vignette, blanche sur marine, marine sur clair. Et les filets qui séparaient les points ÉCARTAIENT autant qu'ils séparaient — supprimés, remplacés par une flèche et un interligne de 1,32. **Origine.** Reversé le 21/08/2026 depuis la section « Nos propositions de valeur » du site Finengy Advisory (refonte 2026, `finengy-refonte/site/src/pages/notre-savoir-faire.astro`), après six tours d'arbitrage avec Léo. Ce n'est donc pas un visuel tiers relevé à la pipette : les cotes du deck SONT celles de la page, mesurées à 1440 px de fenêtre.
Mots-clés
Benchmarks — la géométrie, en assertions vérifiables
Rejouables par node bin/check.mjs layout-17-quadrant-epicentre : le pattern se prouve, il ne se croit pas.
| assertion | mesure | attendu |
|---|---|---|
| UN SEUL coin droit par vignette — deux et la forme redevient une gélule, zéro et c'est un squircle | (() => { const P = ['borderTopLeftRadius','borderTopRightRadius','borderBottomRightRadius','borderBottomLeftRadius']; return ['tl','tr','bl','br'].every((k) => P.map((p) => num('.vl-qe--' + k, p)).filter((v) => v < 0.5).length === 1) ? 1 : 0; })() | 1 ± 0 |
| et ce coin regarde le CENTRE, pas le coin de la planche : bas-droit en haut-gauche, bas-gauche en haut-droit, et l'inverse en bas — c'est ce qui interdit de permuter deux vignettes | (() => { const P = ['borderTopLeftRadius','borderTopRightRadius','borderBottomRightRadius','borderBottomLeftRadius']; const want = { tl: 2, tr: 3, bl: 1, br: 0 }; return Object.entries(want).every(([k, i]) => P.map((p) => num('.vl-qe--' + k, p)).findIndex((v) => v < 0.5) === i) ? 1 : 0; })() | 1 ± 0 |
| ÉPICENTRE — les quatre coins vifs tombent sur le même point, à la couture près : leur dispersion ne dépasse pas l'écart entre vignettes | (() => { const want = { tl: 2, tr: 3, bl: 1, br: 0 }; const pts = Object.entries(want).map(([k, i]) => { const b = box('.vl-qe--' + k); return [[b.left,b.top],[b.right,b.top],[b.right,b.bottom],[b.left,b.bottom]][i]; }); const xs = pts.map((p) => p[0]), ys = pts.map((p) => p[1]); return Math.max(Math.max(...xs) - Math.min(...xs), Math.max(...ys) - Math.min(...ys)) / W; })() | ≤ 0.012 |
| AUCUNE SYMÉTRIE — les quatre largeurs sont distinctes, et les quatre hauteurs aussi : une vignette qui rattrape sa voisine trahit un étirement, donc un vide fabriqué | (() => { const b = ['tl','tr','bl','br'].map((k) => box('.vl-qe--' + k)); return (new Set(b.map((x) => Math.round(x.w))).size === 4 && new Set(b.map((x) => Math.round(x.h))).size === 4) ? 1 : 0; })() | 1 ± 0 |
| RAYON EN LONGUEUR — le même rayon en pixels sur quatre vignettes de largeurs DIFFÉRENTES : un `border-radius` en % y donnerait quatre valeurs et des arcs elliptiques | (() => { const P = ['borderTopLeftRadius','borderTopRightRadius','borderBottomRightRadius','borderBottomLeftRadius']; const all = ['tl','tr','bl','br'].flatMap((k) => P.map((p) => num('.vl-qe--' + k, p)).filter((v) => v >= 0.5)); return (Math.max(...all) - Math.min(...all)) / Math.max(...all); })() | ≤ 0.003 |
| NUMÉRO CALÉ PILE DANS L'ANGLE — la pastille touche les deux arêtes du coin vif : c'est ce qui regroupe les quatre numéros autour de l'épicentre au lieu de les laisser flotter | (() => { const want = { tl: 2, tr: 3, bl: 1, br: 0 }; let worst = 0; for (const [k, i] of Object.entries(want)) { const b = box('.vl-qe--' + k), n = box('.vl-qe--' + k + ' .vl-qe__n'); const c = [[b.left,b.top],[b.right,b.top],[b.right,b.bottom],[b.left,b.bottom]][i]; const dx = Math.min(Math.abs(n.left - c[0]), Math.abs(n.right - c[0])); const dy = Math.min(Math.abs(n.top - c[1]), Math.abs(n.bottom - c[1])); worst = Math.max(worst, (dx + dy) / W); } return worst; })() | ≤ 0.004 |
| RÉSERVE DU COIN VIF — rien ne passe sous la pastille : la vignette réserve la place du numéro du côté où il se pose | (() => { let n = 0; for (const k of ['tl','tr','bl','br']) { const art = document.querySelector('.vl-qe--' + k); const b = art.querySelector('.vl-qe__n').getBoundingClientRect(); for (const el of art.querySelectorAll('.vl-qe__h, .vl-qe__k, .vl-qe__pts li, .vl-qe__say')) { const r = el.getBoundingClientRect(); if (!(r.right <= b.left || r.left >= b.right || r.bottom <= b.top || r.top >= b.bottom)) n++; } } return n; })() | 0 ± 0 |
| AUCUNE ENCRE GRISE — le corps des listes prend exactement l'encre de son titre : un gris intermédiaire sur bleu pâle tombe sous 5:1 et se lit comme du texte désactivé | (() => { return ['tl','tr','bl'].every((k) => cs('.vl-qe--' + k + ' .vl-qe__pts li', 'color') === cs('.vl-qe--' + k + ' .vl-qe__h', 'color')) ? 1 : 0; })() | 1 ± 0 |
| PUCE = FLÈCHE, jamais un tiret : le point ne sépare pas deux lignes, il en désigne une | (() => { const c = getComputedStyle(document.querySelector('.vl-qe__pts li'), '::before').content; return c.includes('\u2192') ? 1 : 0; })() | 1 ± 0 |
| POINTS SERRÉS — interligne ≤ 1,35 et écart entre deux points ≤ 0,35 em : au-delà, la liste redevient une suite de paragraphes | (() => { const fs = num('.vl-qe__pts li', 'fontSize'); const lh = num('.vl-qe__pts li', 'lineHeight') / fs; const gap = num('.vl-qe__pts li + li', 'marginTop') / fs; return Math.max(lh / 1.35, gap / 0.35); })() | ≤ 1 |
| PLANCHER DE CORPS — les points font au moins 0,0145 de la largeur du bloc (19 px sur 1320) : c'est le petit texte que le pattern est venu supprimer | num('.vl-qe__pts li', 'fontSize') / W | ≥ 0.0145 |
| SAUT DE HIÉRARCHIE — le titre de vignette fait au moins 2,4 fois le corps : entre les deux il n'y a que la pilule catégorielle, à 1,1 fois le corps | num('.vl-qe__h', 'fontSize') / num('.vl-qe__pts li', 'fontSize') | ≥ 2.4 |
| TITRE CATÉGORIEL EN PILULE — rayon plein (≥ la demi-hauteur) et contour : c'est la MÊME forme qui sert les deux niveaux d'étiquette du bloc | (() => { const b = box('.vl-qe__k'); return num('.vl-qe__k', 'borderTopLeftRadius') / (b.h / 2); })() | ≥ 1 |
Le code
patterns/layout-17-quadrant-epicentre.html
Télécharger<!-- layout-17-quadrant-epicentre — fragment autonome. Couleurs via --vl-* uniquement.
QUATRE modules, quatre poses d'UNE SEULE forme : le carré arrondi à un seul coin droit de
`shape-02-teardrop-quadrant`. Ce que ce pattern-ci ajoute à la primitive, et qui est toute
sa raison d'être :
• le coin vif ne pointe PAS le coin de la planche — il pointe le CENTRE. Les quatre
convergent sur un point unique, l'ÉPICENTRE, et c'est ce point qui tient la
composition ;
• chaque vignette n'est ANCRÉE que par ce coin. Ses deux autres bords sont libres, donc
sa LARGEUR et sa HAUTEUR lui appartiennent : quatre tailles différentes, aucune
symétrie, et pourtant un seul point de convergence. C'est l'inverse d'un bento, où
c'est la grille qui impose les tailles et les vides qui en résultent ;
• le NUMÉRO du module se pose dans ce coin vif, en pastille ronde collée aux deux
arêtes (inset 0). Les quatre numéros se serrent donc autour de l'épicentre : la
numérotation devient la marque du centre, pas une étiquette de coin.
Le rayon s'écrit en LONGUEUR, jamais en `%` — un pourcentage se résout par axe, donc sur
une boîte qui n'est pas carrée (aucune ne l'est ici) les trois arrondis deviennent des
arcs elliptiques et la forme cesse d'être elle-même.
Deux règles de LECTURE, mesurées elles aussi, parce qu'une composition qui porte du texte
se juge aussi à son texte : aucune encre grise (le corps prend l'encre de sa vignette,
blanche sur le fond sombre, sombre sur les fonds clairs), et des points SERRÉS, marqués
d'une flèche — pas d'un tiret, qui sépare au lieu de désigner. -->
<div class="vl-quadrant-epicentre">
<article class="vl-qe vl-qe--tl">
<span class="vl-qe__n">01</span>
<h3 class="vl-qe__h">Maîtrise de la fonction comptable et financière</h3>
<div class="vl-qe__body">
<div class="vl-qe__fam">
<p class="vl-qe__k">Production des reporting</p>
<ul class="vl-qe__pts">
<li>IFRS & Consolidation</li>
<li>Reporting Solvency II (QRT), ENS et rapports narratifs (SFCR, RSR)</li>
<li>Reporting CSRD</li>
<li>Externalisation des comptes IFRS et Solvency II</li>
<li>Comptabilité technique assurance</li>
<li>Comptabilité placement</li>
<li>Fiscalité assurance, groupe TVA</li>
</ul>
</div>
<div class="vl-qe__fam">
<p class="vl-qe__k">Implémentation des nouvelles réglementations</p>
<ul class="vl-qe__pts">
<li>IFRS 17 — Contrats d’assurances</li>
<li>CSRD — Corporate Sustainability Reporting Directive</li>
<li>IFRS 9 — Actifs financiers</li>
<li>Référentiel ICS — impacts financiers, GAP analysis Data et système</li>
</ul>
</div>
</div>
</article>
<article class="vl-qe vl-qe--tr">
<span class="vl-qe__n">02</span>
<h3 class="vl-qe__h">Transformation des systèmes d’information</h3>
<div class="vl-qe__body">
<div class="vl-qe__fam">
<p class="vl-qe__k">Migrations systèmes IT</p>
<ul class="vl-qe__pts">
<li>Prise en charge de l’ensemble des cycles de migration (préparation des données, contrôle de la qualité, bascule et validation).</li>
</ul>
</div>
<div class="vl-qe__fam">
<p class="vl-qe__k">Audit efficience système</p>
<ul class="vl-qe__pts">
<li>Mission Fast Close et validation des 4 piliers de l’efficience : ressources, temps, fiabilité, respect des normes.</li>
</ul>
</div>
<div class="vl-qe__fam">
<p class="vl-qe__k">Industrialisation des reporting</p>
<ul class="vl-qe__pts">
<li>Diminution de la dépendance aux ERP et mise en place d’un Data Warehouse centralisé à tous les services.</li>
</ul>
</div>
</div>
</article>
<article class="vl-qe vl-qe--bl">
<span class="vl-qe__n">03</span>
<h3 class="vl-qe__h">Risques, Contrôles et Conformité</h3>
<div class="vl-qe__body">
<div class="vl-qe__fam">
<p class="vl-qe__k">Prise en charge du contrôle interne</p>
<ul class="vl-qe__pts">
<li>Optimisation, rationalisation cartographie des risques et plan de contrôle</li>
<li>Outsourcing du contrôle interne</li>
<li>Réalisation des reporting réglementaires</li>
</ul>
</div>
<div class="vl-qe__fam">
<p class="vl-qe__k">Conduite des évolutions réglementaires</p>
<ul class="vl-qe__pts">
<li>Analyse des nouvelles obligations et leurs impacts (SOX, Sapin 2, LCB-FT…)</li>
<li>Prise en charge des transformations</li>
<li>Mise en place des matrices de contrôles et mise en œuvre opérationnelle</li>
</ul>
</div>
</div>
</article>
<article class="vl-qe vl-qe--br vl-qe--say">
<span class="vl-qe__n">04</span>
<h3 class="vl-qe__h">Formations</h3>
<p class="vl-qe__say">Des sessions de formation régulières, relatives aux trois autres practices.</p>
</article>
</div>
<style>
/* Les paramètres du quadrant, et il n'y en a pas d'autres :
--vl-qe-seam la couture entre les vignettes — c'est le seul écart admis à l'épicentre ;
--vl-qe-radius le rayon des TROIS coins arrondis, en LONGUEUR (jamais un %) ;
--vl-qe-badge le diamètre de la pastille de numéro, collée dans le coin vif ;
--vl-qe-pad le rembourrage de vignette — plancher : 0,46 × le rayon, sinon la
première ligne frôle l'arc au lieu de passer au large. */
.vl-quadrant-epicentre {
/* Le bloc porte sa LARGEUR : la composition se joue entre 1100 et 1400 px (au-dessous, les
quatre vignettes ne tiennent plus côte à côte et le quadrant se replie en pile). Un
fragment dont les cotes dépendent d'une largeur ne peut pas hériter celle de la page qui
le colle — sinon rien n'est mesurable. */
--vl-qe-block: 1320px;
--vl-qe-seam: 14px;
--vl-qe-radius: 92px;
--vl-qe-badge: 60px;
--vl-qe-pad: 44px;
display: grid;
/* Les colonnes ne fixent PAS la largeur des vignettes : elles en donnent le plafond. */
grid-template-columns: 1.1fr 1fr;
grid-template-rows: auto auto;
gap: var(--vl-qe-seam);
width: var(--vl-qe-block);
font-family: var(--vl-font, Inter, system-ui, sans-serif);
}
.vl-qe {
--vl-qe-ink: var(--vl-ink); /* l'encre de la vignette — jamais un gris */
position: relative;
display: flex; flex-direction: column;
padding: max(var(--vl-qe-pad), calc(var(--vl-qe-radius) * 0.46));
background: var(--vl-card); border: 1px solid var(--vl-edge);
width: min(100%, var(--vl-qe-w));
}
/* L'ÉPICENTRE en deux règles : les vignettes du HAUT sont calées par le bas sur la ligne de
refend et par le côté sur la ligne de partage ; celles du BAS par le haut. Leur coin vif
tombe donc sur le même point, à la couture près, pendant que les deux bords OPPOSÉS restent
libres — c'est ce qui autorise quatre tailles différentes sans qu'aucune ne s'étire. */
.vl-qe--tl { grid-area: 1 / 1; align-self: end; justify-self: end; }
.vl-qe--tr { grid-area: 1 / 2; align-self: end; justify-self: start; }
.vl-qe--bl { grid-area: 2 / 1; align-self: start; justify-self: end; }
.vl-qe--br { grid-area: 2 / 2; align-self: start; justify-self: start; }
/* Ordre de `border-radius` : haut-gauche, haut-droit, bas-droit, bas-gauche. Le 0 est le coin
vif, et il désigne toujours le CENTRE du bloc. La réserve de place suit le coin vif : c'est
là que se pose la pastille, et rien ne doit passer dessous. */
.vl-qe--tl { border-radius: var(--vl-qe-radius) var(--vl-qe-radius) 0 var(--vl-qe-radius);
padding-bottom: calc(var(--vl-qe-badge) + var(--vl-qe-seam)); }
.vl-qe--tr { border-radius: var(--vl-qe-radius) var(--vl-qe-radius) var(--vl-qe-radius) 0;
padding-bottom: calc(var(--vl-qe-badge) + var(--vl-qe-seam)); }
.vl-qe--bl { border-radius: var(--vl-qe-radius) 0 var(--vl-qe-radius) var(--vl-qe-radius);
padding-top: calc(var(--vl-qe-badge) + var(--vl-qe-seam)); }
.vl-qe--br { border-radius: 0 var(--vl-qe-radius) var(--vl-qe-radius) var(--vl-qe-radius);
padding-top: calc(var(--vl-qe-badge) + var(--vl-qe-seam)); }
/* La LARGEUR est propre à chaque vignette, mesurée sur sa substance — pas déduite de la
voisine. La HAUTEUR l'est par construction (le contenu la donne). */
.vl-qe--tl { --vl-qe-w: 665px; }
.vl-qe--tr { --vl-qe-w: 585px; }
.vl-qe--bl { --vl-qe-w: 610px; }
.vl-qe--br { --vl-qe-w: 480px; }
/* Deux pôles de la charte sur une DIAGONALE : ils se touchent à l'épicentre, et les deux
vignettes claires font le contrepoint sur l'autre diagonale. */
.vl-qe--tl { background: var(--vl-invert); border-color: var(--vl-invert); --vl-qe-ink: var(--vl-on-invert); }
/* Le filet de pilule est une encre, pas un objet : sur le fond inversé il s'inverse avec elle,
sinon la pilule EXISTE sur les vignettes claires et disparaît sur la sombre. */
.vl-qe--tl .vl-qe__k { border-color: var(--vl-hairline-invert); }
.vl-qe--br { background: var(--vl-accent); border-color: var(--vl-accent); }
/* Le numéro, calé PILE dans l'angle vif : la pastille touche les deux arêtes qui le forment. */
.vl-qe__n {
position: absolute; z-index: 1;
width: var(--vl-qe-badge); height: var(--vl-qe-badge);
display: grid; place-items: center;
border-radius: 50%; border: 1.5px solid var(--vl-hairline);
font-weight: 600; font-size: calc(var(--vl-qe-badge) * 0.38);
font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
color: var(--vl-ink);
}
.vl-qe--tl .vl-qe__n { right: 0; bottom: 0; border-color: var(--vl-accent); color: var(--vl-accent); }
.vl-qe--tr .vl-qe__n { left: 0; bottom: 0; }
.vl-qe--bl .vl-qe__n { right: 0; top: 0; }
.vl-qe--br .vl-qe__n { left: 0; top: 0; }
.vl-qe__h {
margin: 0 0 calc(var(--vl-qe-pad) * 0.41);
font-size: 54px; font-weight: 600; line-height: 1.02; letter-spacing: -0.028em;
color: var(--vl-qe-ink); text-wrap: balance;
}
.vl-qe__body { display: grid; align-content: start; gap: calc(var(--vl-qe-pad) * 0.68); }
/* Le titre CATÉGORIEL est une pilule contournée — la même forme sert les deux niveaux
d'étiquette. Pas de `text-wrap: balance` ici : sur une boîte qui se dimensionne à son
contenu, équilibrer les lignes REFUSE la largeur disponible et casse en deux un libellé
qui tenait sur une ligne. */
.vl-qe__k {
display: inline-block; margin: 0 0 0.8rem; padding: 10px 22px;
border: 1px solid var(--vl-hairline); border-radius: 999px;
font-size: 23px; font-weight: 600; line-height: 1.22; letter-spacing: -0.008em;
color: var(--vl-qe-ink);
}
/* Points SERRÉS, encre PLEINE, et une FLÈCHE : le point ne sépare pas, il désigne. */
.vl-qe__pts { list-style: none; padding: 0 0 0 0.2rem; margin: 0; }
.vl-qe__pts li {
position: relative; padding-left: 1.7rem;
font-size: 21px; line-height: 1.32; color: var(--vl-qe-ink); text-wrap: pretty;
}
.vl-qe__pts li + li { margin-top: 0.32rem; }
.vl-qe__pts li::before {
content: "\2192"; position: absolute; left: 0; top: 0;
font-weight: 500; line-height: inherit; color: var(--vl-accent);
}
/* Un module sans point n'est pas une vignette vide : c'est une phrase, et elle se lit en grand. */
.vl-qe__say {
margin: 0; max-width: 24ch;
font-size: 34px; font-weight: 500; line-height: 1.24; color: var(--vl-qe-ink);
}
</style>
systems/ref-21-finengy-quadrant-clair.json
Télécharger:root {
--vl-bg: #FFFFFF;
--vl-card: #F2F7FB;
--vl-edge: #DFE9F1;
--vl-hairline: rgba(13,42,61,0.28);
--vl-hairline-invert: rgba(255,255,255,0.32);
--vl-ink: #0D2A3D;
--vl-invert: #0D2A3D;
--vl-on-invert: #FFFFFF;
--vl-accent: #F5C518;
--vl-on-accent: #0D2A3D;
--vl-seam: 14px;
--vl-radius: 92px;
--vl-badge: 60px;
--vl-pad: 44px;
--vl-block: 1320px;
}
les métadonnées — patterns/layout-17-quadrant-epicentre.json (télécharger)
{
"name": "Quadrant à épicentre",
"family": "layout",
"ref": "ref-21-finengy-quadrant-clair",
"intent": "Poser quatre modules de VOLUMES très inégaux sans que la grille impose leurs tailles : chaque vignette n'est ancrée que par son coin vif, qui pointe le centre du bloc, et ses deux autres bords sont libres — donc sa largeur ET sa hauteur lui appartiennent. Les quatre coins vifs convergent sur un point unique, l'épicentre, où se serrent aussi les quatre numéros. C'est ce point qui tient la composition à la place des alignements de bord, et c'est pour ça que quatre tailles différentes ne s'y lisent pas comme un défaut de calage.",
"when_to_use": "Quand quatre blocs de contenu réel — pas quatre KPI de même longueur — doivent cohabiter : un module à onze points, un à trois phrases longues, un à six points, un à une phrase. Un bento y répondrait en étirant les petits et en creusant des vides ; ici le vide n'existe pas, parce qu'aucune vignette n'est tenue de rattraper sa voisine. Vaut aussi dès que la numérotation doit se lire comme un PARCOURS et non comme quatre étiquettes indépendantes : réunis au centre, les numéros donnent l'ordre sans qu'aucune flèche ne soit dessinée.",
"avoid_when": "À moins de quatre modules, ou à plus : la forme est celle d'un quadrant, un cinquième bloc n'a plus de coin à occuper. Sous ~1100 px de largeur non plus — les quatre vignettes ne tiennent plus côte à côte, et le repli en pile fait disparaître l'épicentre, donc le pattern avec lui (au repli, on garde les vignettes et on rend l'ordre par l'index, pas par la position). Et jamais avec des contenus de même volume : quatre tailles identiques rendent la liberté du pattern invisible et une grille ordinaire fera mieux.",
"media": [
"web",
"slide"
],
"tags": [
"quadrant",
"epicentre",
"bento",
"coin",
"rayon",
"asymetrie",
"numerotation",
"pastille",
"module",
"pilule",
"liste",
"fleche"
],
"vars": [
{
"name": "--vl-qe-block",
"role": "la largeur du bloc — le pattern vit entre 1100 et 1400 px, au-dessous il se replie en pile",
"default": "1320px"
},
{
"name": "--vl-qe-seam",
"role": "la couture entre vignettes, et le SEUL écart admis à l'épicentre : c'est elle que mesure le benchmark de convergence",
"default": "14px"
},
{
"name": "--vl-qe-radius",
"role": "le rayon des TROIS coins arrondis, en LONGUEUR (jamais un %) — 0,070 de la largeur du bloc ici",
"default": "92px"
},
{
"name": "--vl-qe-badge",
"role": "le diamètre de la pastille de numéro, collée dans le coin vif ; elle dicte la réserve de place de ce côté",
"default": "60px"
},
{
"name": "--vl-qe-pad",
"role": "le rembourrage de vignette — plancher 0,46 × le rayon, sinon la première ligne frôle l'arc",
"default": "44px"
},
{
"name": "--vl-qe-w",
"role": "la largeur PROPRE d'une vignette, posée sur elle et mesurée sur sa substance (plafonnée par sa colonne)",
"default": "665px / 585px / 610px / 480px"
}
],
"slots": [
{
"name": "numero",
"selector": ".vl-qe__n",
"role": "le rang du module, dans le coin vif"
},
{
"name": "titre",
"selector": ".vl-qe__h",
"role": "le nom du module, en très gros"
},
{
"name": "categorie",
"selector": ".vl-qe__k",
"role": "le titre catégoriel, en pilule contournée"
},
{
"name": "points",
"selector": ".vl-qe__pts li",
"role": "un point de liste, marqué d'une flèche"
},
{
"name": "phrase",
"selector": ".vl-qe__say",
"role": "le module sans liste : une phrase, lue en grand"
}
],
"geometry": {
"root": ".vl-quadrant-epicentre",
"frame": [
1320,
1498
],
"ratios": {
"seam_over_block": 0.0106,
"radius_over_block": 0.0697,
"badge_over_block": 0.0455,
"pad_over_block": 0.0333,
"pad_floor_over_radius": 0.46,
"colonnes_fr": [
1.1,
1
],
"largeurs_over_block": [
0.5038,
0.4432,
0.4621,
0.3636
],
"coins_vifs": 1,
"vignettes": 4
},
"type_px": {
"titre": 54,
"etiquette": 23,
"corps": 21,
"numero": 23,
"phrase": 34
},
"pad_ratio": {
"top": 0.0333,
"x": 0.0333,
"bottom": 0.0333
}
},
"benchmarks": [
{
"name": "UN SEUL coin droit par vignette — deux et la forme redevient une gélule, zéro et c'est un squircle",
"measure": "(() => { const P = ['borderTopLeftRadius','borderTopRightRadius','borderBottomRightRadius','borderBottomLeftRadius']; return ['tl','tr','bl','br'].every((k) => P.map((p) => num('.vl-qe--' + k, p)).filter((v) => v < 0.5).length === 1) ? 1 : 0; })()",
"expect": 1,
"tol": 0
},
{
"name": "et ce coin regarde le CENTRE, pas le coin de la planche : bas-droit en haut-gauche, bas-gauche en haut-droit, et l'inverse en bas — c'est ce qui interdit de permuter deux vignettes",
"measure": "(() => { const P = ['borderTopLeftRadius','borderTopRightRadius','borderBottomRightRadius','borderBottomLeftRadius']; const want = { tl: 2, tr: 3, bl: 1, br: 0 }; return Object.entries(want).every(([k, i]) => P.map((p) => num('.vl-qe--' + k, p)).findIndex((v) => v < 0.5) === i) ? 1 : 0; })()",
"expect": 1,
"tol": 0
},
{
"name": "ÉPICENTRE — les quatre coins vifs tombent sur le même point, à la couture près : leur dispersion ne dépasse pas l'écart entre vignettes",
"measure": "(() => { const want = { tl: 2, tr: 3, bl: 1, br: 0 }; const pts = Object.entries(want).map(([k, i]) => { const b = box('.vl-qe--' + k); return [[b.left,b.top],[b.right,b.top],[b.right,b.bottom],[b.left,b.bottom]][i]; }); const xs = pts.map((p) => p[0]), ys = pts.map((p) => p[1]); return Math.max(Math.max(...xs) - Math.min(...xs), Math.max(...ys) - Math.min(...ys)) / W; })()",
"max": 0.012
},
{
"name": "AUCUNE SYMÉTRIE — les quatre largeurs sont distinctes, et les quatre hauteurs aussi : une vignette qui rattrape sa voisine trahit un étirement, donc un vide fabriqué",
"measure": "(() => { const b = ['tl','tr','bl','br'].map((k) => box('.vl-qe--' + k)); return (new Set(b.map((x) => Math.round(x.w))).size === 4 && new Set(b.map((x) => Math.round(x.h))).size === 4) ? 1 : 0; })()",
"expect": 1,
"tol": 0
},
{
"name": "RAYON EN LONGUEUR — le même rayon en pixels sur quatre vignettes de largeurs DIFFÉRENTES : un `border-radius` en % y donnerait quatre valeurs et des arcs elliptiques",
"measure": "(() => { const P = ['borderTopLeftRadius','borderTopRightRadius','borderBottomRightRadius','borderBottomLeftRadius']; const all = ['tl','tr','bl','br'].flatMap((k) => P.map((p) => num('.vl-qe--' + k, p)).filter((v) => v >= 0.5)); return (Math.max(...all) - Math.min(...all)) / Math.max(...all); })()",
"max": 0.003
},
{
"name": "NUMÉRO CALÉ PILE DANS L'ANGLE — la pastille touche les deux arêtes du coin vif : c'est ce qui regroupe les quatre numéros autour de l'épicentre au lieu de les laisser flotter",
"measure": "(() => { const want = { tl: 2, tr: 3, bl: 1, br: 0 }; let worst = 0; for (const [k, i] of Object.entries(want)) { const b = box('.vl-qe--' + k), n = box('.vl-qe--' + k + ' .vl-qe__n'); const c = [[b.left,b.top],[b.right,b.top],[b.right,b.bottom],[b.left,b.bottom]][i]; const dx = Math.min(Math.abs(n.left - c[0]), Math.abs(n.right - c[0])); const dy = Math.min(Math.abs(n.top - c[1]), Math.abs(n.bottom - c[1])); worst = Math.max(worst, (dx + dy) / W); } return worst; })()",
"max": 0.004
},
{
"name": "RÉSERVE DU COIN VIF — rien ne passe sous la pastille : la vignette réserve la place du numéro du côté où il se pose",
"measure": "(() => { let n = 0; for (const k of ['tl','tr','bl','br']) { const art = document.querySelector('.vl-qe--' + k); const b = art.querySelector('.vl-qe__n').getBoundingClientRect(); for (const el of art.querySelectorAll('.vl-qe__h, .vl-qe__k, .vl-qe__pts li, .vl-qe__say')) { const r = el.getBoundingClientRect(); if (!(r.right <= b.left || r.left >= b.right || r.bottom <= b.top || r.top >= b.bottom)) n++; } } return n; })()",
"expect": 0,
"tol": 0
},
{
"name": "AUCUNE ENCRE GRISE — le corps des listes prend exactement l'encre de son titre : un gris intermédiaire sur bleu pâle tombe sous 5:1 et se lit comme du texte désactivé",
"measure": "(() => { return ['tl','tr','bl'].every((k) => cs('.vl-qe--' + k + ' .vl-qe__pts li', 'color') === cs('.vl-qe--' + k + ' .vl-qe__h', 'color')) ? 1 : 0; })()",
"expect": 1,
"tol": 0
},
{
"name": "PUCE = FLÈCHE, jamais un tiret : le point ne sépare pas deux lignes, il en désigne une",
"measure": "(() => { const c = getComputedStyle(document.querySelector('.vl-qe__pts li'), '::before').content; return c.includes('\\u2192') ? 1 : 0; })()",
"expect": 1,
"tol": 0
},
{
"name": "POINTS SERRÉS — interligne ≤ 1,35 et écart entre deux points ≤ 0,35 em : au-delà, la liste redevient une suite de paragraphes",
"measure": "(() => { const fs = num('.vl-qe__pts li', 'fontSize'); const lh = num('.vl-qe__pts li', 'lineHeight') / fs; const gap = num('.vl-qe__pts li + li', 'marginTop') / fs; return Math.max(lh / 1.35, gap / 0.35); })()",
"max": 1
},
{
"name": "PLANCHER DE CORPS — les points font au moins 0,0145 de la largeur du bloc (19 px sur 1320) : c'est le petit texte que le pattern est venu supprimer",
"measure": "num('.vl-qe__pts li', 'fontSize') / W",
"min": 0.0145
},
{
"name": "SAUT DE HIÉRARCHIE — le titre de vignette fait au moins 2,4 fois le corps : entre les deux il n'y a que la pilule catégorielle, à 1,1 fois le corps",
"measure": "num('.vl-qe__h', 'fontSize') / num('.vl-qe__pts li', 'fontSize')",
"min": 2.4
},
{
"name": "TITRE CATÉGORIEL EN PILULE — rayon plein (≥ la demi-hauteur) et contour : c'est la MÊME forme qui sert les deux niveaux d'étiquette du bloc",
"measure": "(() => { const b = box('.vl-qe__k'); return num('.vl-qe__k', 'borderTopLeftRadius') / (b.h / 2); })()",
"min": 1
}
],
"notes": "**Ce que ce pattern ajoute à `shape-02-teardrop-quadrant`, et qui n'est pas dans la primitive.** La primitive pose un carré arrondi dont un seul coin reste vif, et son coin vif désigne le coin de la planche qu'il occupe. Ici il désigne l'INVERSE — le centre — et cette inversion change la nature de la composition : les quatre formes ne bordent plus un bloc, elles RAYONNENT d'un point. Le benchmark d'orientation est donc l'exact miroir de celui de la primitive, et les deux ne peuvent pas être vrais en même temps ; c'est voulu, et c'est ce qui empêche de confondre les deux patterns.\n\n**L'ancrage par un seul coin est le mécanisme, tout le reste en découle.** Une vignette du haut est calée par le BAS (`align-self: end`) et par le CÔTÉ (`justify-self`) ; une vignette du bas par le HAUT. Ses deux autres bords ne sont tenus par rien, donc sa hauteur est celle de son contenu et sa largeur celle qu'on lui donne (`--vl-qe-w`, plafonnée par sa colonne). C'est la réponse à la faute classique du bento : quatre cellules d'une grille s'étirent à la plus grande, et le module le plus court récolte 200 px de vide qu'on essaie ensuite de meubler. Ici il n'y a rien à meubler.\n\n**Les colonnes ne fixent pas les largeurs, elles les plafonnent.** `grid-template-columns: 1.1fr 1fr` donne la place disponible de chaque côté ; la largeur réelle de chaque vignette est posée sur elle. Sans ce plafond, une vignette pourrait franchir l'axe et l'épicentre serait faux ; sans la largeur propre, on retomberait sur deux colonnes symétriques.\n\n**La pastille collée à l'angle (inset 0) n'est pas un détail de placement.** Posée à la distance du rembourrage, elle se lit comme une étiquette de coin, et les quatre se dispersent ; collée à l'arête, les quatre se rejoignent autour de l'épicentre et la numérotation devient la MARQUE du centre. C'est pour ça que la vignette réserve `badge + seam` du côté du coin vif : la place du numéro n'est pas négociable, elle est la contrepartie du geste.\n\n**Deux règles de lecture sont mesurées comme le reste, parce qu'elles ont été payées.** L'encre grise (`--fx-text-muted`, un #5A5A5A) rendait les listes molles sur bleu pâle : le corps prend désormais l'encre de sa vignette, blanche sur marine, marine sur clair. Et les filets qui séparaient les points ÉCARTAIENT autant qu'ils séparaient — supprimés, remplacés par une flèche et un interligne de 1,32.\n\n**Origine.** Reversé le 21/08/2026 depuis la section « Nos propositions de valeur » du site Finengy Advisory (refonte 2026, `finengy-refonte/site/src/pages/notre-savoir-faire.astro`), après six tours d'arbitrage avec Léo. Ce n'est donc pas un visuel tiers relevé à la pipette : les cotes du deck SONT celles de la page, mesurées à 1440 px de fenêtre.",
"id": "layout-17-quadrant-epicentre"
}
À côté
layout-02-swiss-frame
Cadre suisse header / footer
layout-03-glass-board
Écran en verre dépoli — fond dégradé + modules
layout-04-graph-paper-cells
Papier millimétré et ses cases
layout-05-nav-pill-bar
Barre de menu en pilule flottante
layout-06-bento-l-span