layout-19-tiles-over-claim

Rangée de tuiles sous énoncé

Ouvrir un écran par une rangée de trois tuiles de largeurs très inégales — une grande visuelle, deux brèves dont UNE SEULE est un aplat d'accent — puis laisser un énoncé prendre toute la parole en dessous, dans le seul très grand corps du bloc. Deux lois tiennent la composition : la grande tuile et le COUPLE de brèves se partagent la largeur presque à parts égales, ce qui interdit à la rangée de se lire comme « une image et deux annexes » ; et le marqueur de marque n'est pas calé sur la marge droite mais centré sur l'AXE de la tuile d'accent, base posée sur la baseline de la dernière ligne. C'est ce second ancrage qui rattache le marqueur à l'accent au lieu d'en faire un objet de coin.

layoutslideweb1448×668px13 benchmarks

Télécharger le .zip

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 un écran doit dire une chose forte ET montrer trois entrées de rangs différents sans les hiérarchiser par la taille du texte : une couverture de section, une ouverture de proposition commerciale, un hero de page produit. Vaut particulièrement lorsque l'un des trois blocs est un VISUEL et les deux autres du texte court — la répartition 0,49 / 0,24 / 0,25 est faite pour ce cas et pour lui seul. Vaut aussi comme démonstration du geste « accent unique dit deux fois » : la tuile jaune est à la fois la seule colorée et la plus large des deux brèves.

Éviter quand

Avec trois tuiles de MÊME nature — trois brèves, trois KPI : les largeurs inégales n'ont alors aucun motif et se lisent comme un défaut de calage (prendre `card-12-inverted-kpi-row`, qui traite exactement ce cas). Avec deux aplats d'accent : la rangée perd son point d'entrée, et le marqueur n'a plus d'axe unique à viser. Et sous ~1100 px de largeur, où trois colonnes dont une à 24 % ne portent plus trois lignes de texte — au repli, on empile les brèves sous le visuel et le marqueur revient en fin d'énoncé.

Notes

**Ce que ce pattern ajoute à `card-12-inverted-kpi-row`.** Les deux emploient le même geste — accentuer UNE carte d'une rangée par le fond ET par une colonne plus large. Mais `card-12` traite trois objets de MÊME nature (trois indicateurs), où l'inégalité de largeur est la seule chose qui hiérarchise ; ici les trois tuiles sont de natures différentes (un visuel, deux brèves), et l'inégalité de largeur sert d'abord à faire tenir un visuel à côté de deux textes. C'est pour ça que le benchmark central n'est pas le même : là-bas on mesure l'écart entre colonnes, ici on mesure le QUASI-ÉQUILIBRE entre la tuile visuelle et le couple de brèves. **Le marqueur est le vrai apport, et il est contre-intuitif.** Le réflexe est de le caler sur la marge droite, en face du titre. Mesuré sur la source, il ne l'est pas : son centre tombe à 1342 px sur 1600, soit exactement l'axe de la tuile d'accent, à 182 px de la marge. L'effet est net dès qu'on compare les deux : calé sur la marge, il ferme la slide et n'appartient à rien ; calé sur l'axe, il tire une verticale invisible depuis la seule tuile colorée, et l'accent unique se prolonge dans la zone de titre au lieu de s'arrêter à la rangée. Le second benchmark de retrait (≥ 5 % du bloc) existe pour interdire le retour au réflexe. **L'interligne sous 1 a une contrepartie qu'il faut payer explicitement.** À `line-height: 0.86`, le demi-interligne devient négatif (−36 px pour un corps de 134) : la boîte du titre est plus courte que ses glyphes, et un bloc calé en bas laisse ses descendantes mordre la marge de page. La compensation de 31 px sur le `margin-bottom` de l'énoncé n'est pas un réglage à l'œil — c'est la moitié de l'excédent, et elle se recalcule si le corps ou l'interligne changent. **La coupe du paragraphe est une hauteur maximale, pas un `line-clamp`.** Le clamp ajouterait des points de suspension que la source n'a pas ; la hauteur maximale coupe net, comme un texte de gabarit tronqué. Sans coupe du tout, la brève la plus étroite passe à quatre lignes et les deux paragraphes cessent de partir de la même hauteur — c'est la seule chose que cette rangée demande à ses brèves, et c'est mesuré. **Le marqueur est OPTIONNEL, et ce qu'on perd en le retirant est nommé.** Le bloc reste valide sans lui : l'énoncé garde la même position, la même compensation d'interligne, et les trois benchmarks qui le concernent se neutralisent quand il est absent (ils testent une loi conditionnelle — « s'il est là, il vise l'axe » — et non sa présence). Ce qui disparaît est réel : sans lui, la zone d'énoncé n'a plus d'ancre à droite, et l'accent unique s'arrête à la rangée au lieu de se prolonger sous elle. Deux cas où le retirer est le bon geste : quand l'énoncé est plus long et réclame la largeur, et quand le bloc est posé dans une page qui porte déjà la marque ailleurs (un hero sous une barre de navigation). Le retrait tient en une chose — supprimer le `div.vl-to__mark` ; la règle CSS peut rester, elle n'est plus appelée. **Origine.** Reversé le 24/08/2026 depuis une slide de proposition commerciale relevée à la pipette (`assets/refs/ref-22-yellow-proposal-bento.png`, 586 px de large, donc ×2,7304 pour une slide 1600×900). Les corps de la source tombent à ~15 px une fois remis à l'échelle, sous le plancher du corpus : ils sont remontés à 20, et c'est le seul écart au relevé.

Mots-clés

bentotuilerangeeenoncetitreaccentmarqueurasterisquemediabrevepropositioncouverturehero

Benchmarks — la géométrie, en assertions vérifiables

Rejouables par node bin/check.mjs layout-19-tiles-over-claim : le pattern se prouve, il ne se croit pas.

assertionmesureattendu
TROIS TUILES DE MÊME HAUTEUR — la rangée est une bande, pas trois objets posés côte à côte(() => { const h = [...document.querySelectorAll('.vl-to__tile')].map((e) => e.getBoundingClientRect().height); return (Math.max(...h) - Math.min(...h)) / W; })()≤ 0.001
QUASI-ÉQUILIBRE — la tuile visuelle et le COUPLE de brèves (gouttière comprise) se partagent la largeur à moins de 2 % près : au-delà, la rangée se lit « une image et deux annexes »(() => { const t = [...document.querySelectorAll('.vl-to__tile')].map((e) => e.getBoundingClientRect()); return Math.abs(t[0].width - (t[2].right - t[1].left)) / W; })()≤ 0.02
L'ACCENT EST AUSSI LE PLUS LARGE — la brève colorée dépasse la neutre de 3 à 10 % : la même hiérarchie dite deux fois, par la couleur ET par la largeurbox('.vl-to__tile--accent').w / box('.vl-to__tile--brief:not(.vl-to__tile--accent)').w1.03 → 1.1
UN SEUL APLAT D'ACCENT — deux et la rangée n'a plus de point d'entrée, ni le marqueur d'axe à viser(() => { const a = cs('.vl-to__tile--accent', 'backgroundColor'); return [...document.querySelectorAll('.vl-to__tile')].filter((e) => getComputedStyle(e).backgroundColor === a).length; })()1 ± 0
MARQUEUR SUR L'AXE DE L'ACCENT — s'il est là, son centre tombe sur celui de la troisième colonne, pas ailleurs(() => { if (!document.querySelector('.vl-to__mark')) return 0; const m = box('.vl-to__mark'), t = box('.vl-to__tile--accent'); return Math.abs((m.left + m.right) / 2 - (t.left + t.right) / 2) / W; })()≤ 0.004
ET DONC PAS COLLÉ À LA MARGE — il reste au moins 5 % de la largeur entre son bord droit et celui du bloc : un marqueur aligné sur la marge redevient un objet de coin(() => { if (!document.querySelector('.vl-to__mark')) return 1; return (box('.vl-tiles-over').right - box('.vl-to__mark').right) / W; })()≥ 0.05
BASE DU MARQUEUR SUR LA DERNIÈRE LIGNE — il ne descend jamais sous la boîte de l'énoncé, et n'en remonte pas de plus de 2 %(() => { if (!document.querySelector('.vl-to__mark')) return 0.01; return (box('.vl-to__say').bottom - box('.vl-to__mark').bottom) / W; })()0 → 0.02
INTERLIGNE DE L'ÉNONCÉ SOUS 1 — c'est ce qui fait lire deux lignes comme UNE masse ; au-dessus, ce sont deux phrases empiléesnum('.vl-to__say', 'lineHeight') / num('.vl-to__say', 'fontSize')≤ 1
SAUT DE HIÉRARCHIE — l'énoncé fait au moins 3,2 fois le titre d'une brève : entre les deux, aucune taille intermédiaire n'existenum('.vl-to__say', 'fontSize') / num('.vl-to__h', 'fontSize')≥ 3.2
PLANCHER DE CORPS — l'explication d'une brève ne descend pas sous 0,0120 de la largeur du bloc (18 px sur 1448) : la source, elle, tombe à ~15 px une fois remise à l'échellenum('.vl-to__p', 'fontSize') / W≥ 0.012
LES DEUX BRÈVES PARTENT DE LA MÊME HAUTEUR — leurs paragraphes sont collés au plancher ET coupés au même nombre de lignes ; sans la coupe, la plus étroite en fait une de plus et le rail se casse(() => { const p = [...document.querySelectorAll('.vl-to__p')].map((e) => e.getBoundingClientRect().top); return (Math.max(...p) - Math.min(...p)) / W; })()≤ 0.004
AUCUNE OMBRE — sur un fond plus CLAIR qu'elles, les tuiles ne se détachent que par leur aplat et leur rayon ; une ombre y fabriquerait une troisième couche[...document.querySelectorAll('.vl-to__tile')].filter((e) => getComputedStyle(e).boxShadow !== 'none').length0 ± 0
RAYON EN LONGUEUR — le même rayon en pixels sur trois tuiles de largeurs DIFFÉRENTES ; en pourcentage, il donnerait trois arcs elliptiques(() => { const r = [...document.querySelectorAll('.vl-to__tile')].map((e) => parseFloat(getComputedStyle(e).borderTopLeftRadius)); return (Math.max(...r) - Math.min(...r)) / Math.max(...r); })()≤ 0.003

Le code

fragment — patterns/layout-19-tiles-over-claim.html Télécharger
<!-- layout-19-tiles-over-claim — fragment autonome. Couleurs via --vl-* uniquement, aucune valeur en dur. -->
<div class="vl-tiles-over">

  <div class="vl-to__row">
    <div class="vl-to__tile vl-to__tile--media">
      <!-- Slot libre : une image, une vidéo, ou — ici — un objet reconstruit. Le pattern ne
           porte pas ce dessin, il porte la place qu'il occupe. -->
      <svg viewBox="0 0 708 376" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
        <defs>
          <linearGradient id="vlToScale" x1="0.06" y1="0.1" x2="0.94" y2="0.9">
            <stop offset="0" stop-color="var(--vl-white)"/>
            <stop offset="0.34" stop-color="var(--vl-blob-light)"/>
            <stop offset="1" stop-color="var(--vl-ink-muted)"/>
          </linearGradient>
          <linearGradient id="vlToShade" x1="0" y1="0" x2="1" y2="0.25">
            <stop offset="0" stop-color="var(--vl-white)"/>
            <stop offset="0.52" stop-color="var(--vl-tile)"/>
            <stop offset="1" stop-color="var(--vl-blob-dark)"/>
          </linearGradient>
        </defs>
        <rect width="708" height="376" fill="var(--vl-blob-light)"/>
        <g transform="rotate(-18 354 188)" stroke="var(--vl-white)" stroke-opacity="0.8" stroke-width="2">
          <ellipse cx="55"  cy="186" rx="40" ry="148" fill="url(#vlToScale)"/>
          <ellipse cx="99"  cy="186" rx="42" ry="150" fill="url(#vlToScale)"/>
          <ellipse cx="143" cy="186" rx="43" ry="153" fill="url(#vlToScale)"/>
          <ellipse cx="187" cy="186" rx="45" ry="155" fill="url(#vlToScale)"/>
          <ellipse cx="231" cy="186" rx="46" ry="158" fill="url(#vlToScale)"/>
          <ellipse cx="275" cy="186" rx="48" ry="160" fill="url(#vlToScale)"/>
          <ellipse cx="319" cy="186" rx="50" ry="162" fill="url(#vlToScale)"/>
          <ellipse cx="363" cy="186" rx="51" ry="165" fill="url(#vlToScale)"/>
          <ellipse cx="407" cy="186" rx="53" ry="167" fill="url(#vlToScale)"/>
          <ellipse cx="451" cy="186" rx="54" ry="170" fill="url(#vlToScale)"/>
          <ellipse cx="495" cy="186" rx="56" ry="172" fill="url(#vlToScale)"/>
          <ellipse cx="539" cy="186" rx="58" ry="174" fill="url(#vlToScale)"/>
          <ellipse cx="583" cy="186" rx="59" ry="177" fill="url(#vlToScale)"/>
          <ellipse cx="627" cy="186" rx="61" ry="179" fill="url(#vlToScale)"/>
        </g>
        <rect width="708" height="376" fill="url(#vlToShade)" style="mix-blend-mode: multiply"/>
      </svg>
    </div>

    <div class="vl-to__tile vl-to__tile--brief">
      <span class="vl-to__num">(00)</span>
      <h3 class="vl-to__h">Grow Revenue<br>and Reach</h3>
      <p class="vl-to__p">The European languages are members of the same family. Their separate existence</p>
    </div>

    <div class="vl-to__tile vl-to__tile--brief vl-to__tile--accent">
      <span class="vl-to__num">(00)</span>
      <h3 class="vl-to__h">Work Smarter<br>and Faster</h3>
      <p class="vl-to__p">The European languages are members of the same family. Their separate existence</p>
    </div>
  </div>

  <div class="vl-to__claim">
    <h1 class="vl-to__say">A Vision Backed<br>by Clear Goals</h1>
    <!-- OPTIONNEL — supprimer ce div suffit : l'énoncé garde sa place et ses cotes, et les
         trois benchmarks du marqueur se neutralisent. Ce qu'on perd : l'ancre de droite de
         la zone d'énoncé, et le prolongement de l'accent unique sous la rangée. -->
    <div class="vl-to__mark">
      <svg viewBox="0 0 100 100" aria-hidden="true">
        <g stroke="var(--vl-acid)" stroke-width="13">
          <line x1="50" y1="6" x2="50" y2="94"/>
          <line x1="11.9" y1="28" x2="88.1" y2="72"/>
          <line x1="11.9" y1="72" x2="88.1" y2="28"/>
        </g>
      </svg>
    </div>
  </div>

</div>

<style>
.vl-tiles-over {
  /* Les trois colonnes, en fraction de la largeur du bloc. La grande tuile et le COUPLE de
     brèves se partagent la largeur presque à parts égales (0,489 contre 0,500) : c'est ce
     quasi-équilibre qui empêche la rangée de se lire comme « une image et deux annexes ». */
  --vl-to-w1: 0.489;
  --vl-to-w2: 0.2376;
  --vl-to-w3: 0.2514;
  --vl-to-gap: 0.01105;
  --vl-to-row: 0.2597;
  --vl-to-mark: 0.1050;
  --vl-to-block: 1448px;

  width: var(--vl-to-block);
  display: flex;
  flex-direction: column;
  font-family: var(--vl-font, Poppins, "Century Gothic", Futura, Arial, sans-serif);
  color: var(--vl-ink);
  box-sizing: border-box;
}
.vl-tiles-over * { box-sizing: border-box; }

/* — la rangée : trois tuiles de MÊME hauteur, une gouttière unique, AUCUNE ombre. Sur un
     fond plus clair qu'elles, une tuile ne se détache que par son aplat et son rayon. — */
.vl-to__row {
  display: grid;
  grid-template-columns:
    calc(var(--vl-to-w1) * var(--vl-to-block))
    calc(var(--vl-to-w2) * var(--vl-to-block))
    calc(var(--vl-to-w3) * var(--vl-to-block));
  gap: calc(var(--vl-to-gap) * var(--vl-to-block));
  height: calc(var(--vl-to-row) * var(--vl-to-block));
}
.vl-to__tile {
  border-radius: var(--vl-r-tile, 32px);
  overflow: hidden;
  box-shadow: none;
}
.vl-to__tile--media { background: var(--vl-tile-media); }
.vl-to__tile--brief {
  background: var(--vl-tile);
  padding: 31px 40px 73px;
  display: flex;
  flex-direction: column;
}
/* UN SEUL aplat d'accent dans la rangée — et il est porté par la colonne la plus large des
   deux brèves : la même hiérarchie est dite deux fois, par la couleur ET par la largeur. */
.vl-to__tile--accent { background: var(--vl-acid); }

.vl-to__num { font-size: 18px; line-height: 1.2; font-weight: 500; color: var(--vl-ink-muted); }
.vl-to__tile--accent .vl-to__num { color: var(--vl-ink-on-acid); }
.vl-to__h { margin: 8px 0 0; font-size: 33px; line-height: 1.24; font-weight: 500; letter-spacing: -0.02em; }
/* Le paragraphe est collé au PLANCHER de la tuile et coupé à trois lignes : deux tuiles de
   largeurs différentes portent le même texte, et sans la coupe l'une en fait quatre — les
   deux blocs cessent alors de partir de la même hauteur. La coupe est une hauteur maximale
   et non un `line-clamp`, qui ajouterait des points de suspension. */
.vl-to__p {
  margin: auto 0 0; font-size: 18px; line-height: 1.48; font-weight: 400;
  color: var(--vl-ink-muted); max-height: 80px; overflow: hidden;
}
.vl-to__tile--accent .vl-to__p { color: var(--vl-ink-on-acid); }

/* — l'énoncé : le seul très grand corps du bloc, et un interligne SOUS 1 — c'est lui qui
     fait lire deux lignes comme une masse et non comme deux phrases. Le demi-interligne
     devient alors négatif (−36 px ici) : sans la compensation de 31 px, les descendantes
     de la dernière ligne mordraient la marge de page. — */
.vl-to__claim { display: flex; align-items: flex-end; margin-top: 30px; }
.vl-to__say { margin: 0 0 31px; font-size: 135px; line-height: 0.86; font-weight: 400; letter-spacing: -0.005em; }

/* Le marqueur de marque n'est PAS calé sur la marge : il est centré sur l'AXE de la tuile
   d'accent, et sa base tombe sur la baseline de la dernière ligne de l'énoncé. C'est ce
   double ancrage qui le rattache à l'accent au lieu d'en faire un objet de coin. */
.vl-to__mark {
  margin-left: auto;
  margin-right: calc((var(--vl-to-w3) - var(--vl-to-mark)) / 2 * var(--vl-to-block));
  margin-bottom: 42px;
  width: calc(var(--vl-to-mark) * var(--vl-to-block));
  height: calc(var(--vl-to-mark) * var(--vl-to-block));
  border-radius: var(--vl-r-mark, 32px);
  background: var(--vl-mark);
  display: flex; align-items: center; justify-content: center; flex: none;
}
.vl-to__mark svg { width: 54%; height: 54%; }
</style>
tokens de la charte — systems/ref-22-yellow-proposal-bento.json Télécharger
:root {
  --vl-bg: #F8F8F8;
  --vl-white: #FFFFFF;
  --vl-tile: #ECEAEB;
  --vl-tile-media: #E3E3E4;
  --vl-acid: #F2EF4B;
  --vl-ink: #111111;
  --vl-mark: #232323;
  --vl-ink-muted: #5E5E5E;
  --vl-ink-on-acid: #33330E;
  --vl-hairline: #1111111F;
  --vl-blob-light: #D9DBDD;
  --vl-blob-dark: #1A1A1A;
  --vl-margin: 76px;
  --vl-gap: 16px;
  --vl-gap-block: 30px;
  --vl-r-tile: 32px;
  --vl-r-mark: 32px;
}
les métadonnées — patterns/layout-19-tiles-over-claim.json (télécharger)
{
  "name": "Rangée de tuiles sous énoncé",
  "family": "layout",
  "ref": "ref-22-yellow-proposal-bento",
  "intent": "Ouvrir un écran par une rangée de trois tuiles de largeurs très inégales — une grande visuelle, deux brèves dont UNE SEULE est un aplat d'accent — puis laisser un énoncé prendre toute la parole en dessous, dans le seul très grand corps du bloc. Deux lois tiennent la composition : la grande tuile et le COUPLE de brèves se partagent la largeur presque à parts égales, ce qui interdit à la rangée de se lire comme « une image et deux annexes » ; et le marqueur de marque n'est pas calé sur la marge droite mais centré sur l'AXE de la tuile d'accent, base posée sur la baseline de la dernière ligne. C'est ce second ancrage qui rattache le marqueur à l'accent au lieu d'en faire un objet de coin.",
  "when_to_use": "Quand un écran doit dire une chose forte ET montrer trois entrées de rangs différents sans les hiérarchiser par la taille du texte : une couverture de section, une ouverture de proposition commerciale, un hero de page produit. Vaut particulièrement lorsque l'un des trois blocs est un VISUEL et les deux autres du texte court — la répartition 0,49 / 0,24 / 0,25 est faite pour ce cas et pour lui seul. Vaut aussi comme démonstration du geste « accent unique dit deux fois » : la tuile jaune est à la fois la seule colorée et la plus large des deux brèves.",
  "avoid_when": "Avec trois tuiles de MÊME nature — trois brèves, trois KPI : les largeurs inégales n'ont alors aucun motif et se lisent comme un défaut de calage (prendre `card-12-inverted-kpi-row`, qui traite exactement ce cas). Avec deux aplats d'accent : la rangée perd son point d'entrée, et le marqueur n'a plus d'axe unique à viser. Et sous ~1100 px de largeur, où trois colonnes dont une à 24 % ne portent plus trois lignes de texte — au repli, on empile les brèves sous le visuel et le marqueur revient en fin d'énoncé.",
  "media": [
    "slide",
    "web"
  ],
  "tags": [
    "bento",
    "tuile",
    "rangee",
    "enonce",
    "titre",
    "accent",
    "marqueur",
    "asterisque",
    "media",
    "breve",
    "proposition",
    "couverture",
    "hero"
  ],
  "vars": [
    {
      "name": "--vl-to-block",
      "role": "la largeur du bloc — toutes les cotes en découlent, aucune n'est écrite en pixels ailleurs",
      "default": "1448px"
    },
    {
      "name": "--vl-to-w1",
      "role": "la part de la tuile visuelle ; avec w2 + w3 + gouttière elle doit rester à moins de 2 % de la moitié, sinon la rangée bascule en « image + annexes »",
      "default": "0.489"
    },
    {
      "name": "--vl-to-w2",
      "role": "la part de la brève neutre",
      "default": "0.2376"
    },
    {
      "name": "--vl-to-w3",
      "role": "la part de la brève d'ACCENT — 5,8 % plus large que la neutre : la hiérarchie dite une seconde fois, par la largeur",
      "default": "0.2514"
    },
    {
      "name": "--vl-to-gap",
      "role": "la gouttière unique de la rangée",
      "default": "0.01105"
    },
    {
      "name": "--vl-to-row",
      "role": "la hauteur commune des trois tuiles",
      "default": "0.2597"
    },
    {
      "name": "--vl-to-mark",
      "role": "le côté du marqueur de marque ; il dicte aussi son retrait, puisqu'il est centré sur l'axe de la troisième colonne",
      "default": "0.1050"
    }
  ],
  "slots": [
    {
      "name": "media",
      "selector": ".vl-to__tile--media",
      "role": "le visuel de la rangée — image, vidéo ou objet reconstruit ; le pattern porte sa place, pas son dessin"
    },
    {
      "name": "numero",
      "selector": ".vl-to__num",
      "role": "le rang de la brève, en micro-libellé"
    },
    {
      "name": "titre",
      "selector": ".vl-to__h",
      "role": "le titre de la brève, sur deux lignes écrites"
    },
    {
      "name": "corps",
      "selector": ".vl-to__p",
      "role": "l'explication de la brève, collée au plancher de la tuile et coupée à trois lignes"
    },
    {
      "name": "enonce",
      "selector": ".vl-to__say",
      "role": "la phrase qui porte l'écran, sur deux lignes et dans le seul très grand corps du bloc"
    },
    {
      "name": "marque",
      "selector": ".vl-to__mark",
      "role": "le marqueur de marque, centré sur l'axe de la tuile d'accent — **OPTIONNEL** : retiré, l'énoncé garde toute la largeur et le bloc perd son ancre de droite (cf. notes)"
    }
  ],
  "geometry": {
    "root": ".vl-tiles-over",
    "frame": [
      1448,
      668
    ],
    "ratios": {
      "colonnes_over_block": [
        0.489,
        0.2376,
        0.2514
      ],
      "gouttiere_over_block": 0.01105,
      "hauteur_rangee_over_block": 0.2597,
      "marque_over_block": 0.105,
      "accent_sur_neutre": 1.058,
      "ecart_visuel_vs_couple": 0.011,
      "rayon_over_block": 0.0221,
      "interligne_enonce": 0.86,
      "interligne_titre_tuile": 1.24
    },
    "type_px": {
      "enonce": 135,
      "titre_tuile": 33,
      "corps": 18,
      "numero": 18
    },
    "pad_ratio": {
      "top": 0.0214,
      "x": 0.0276,
      "bottom": 0.0504
    }
  },
  "benchmarks": [
    {
      "name": "TROIS TUILES DE MÊME HAUTEUR — la rangée est une bande, pas trois objets posés côte à côte",
      "measure": "(() => { const h = [...document.querySelectorAll('.vl-to__tile')].map((e) => e.getBoundingClientRect().height); return (Math.max(...h) - Math.min(...h)) / W; })()",
      "max": 0.001
    },
    {
      "name": "QUASI-ÉQUILIBRE — la tuile visuelle et le COUPLE de brèves (gouttière comprise) se partagent la largeur à moins de 2 % près : au-delà, la rangée se lit « une image et deux annexes »",
      "measure": "(() => { const t = [...document.querySelectorAll('.vl-to__tile')].map((e) => e.getBoundingClientRect()); return Math.abs(t[0].width - (t[2].right - t[1].left)) / W; })()",
      "max": 0.02
    },
    {
      "name": "L'ACCENT EST AUSSI LE PLUS LARGE — la brève colorée dépasse la neutre de 3 à 10 % : la même hiérarchie dite deux fois, par la couleur ET par la largeur",
      "measure": "box('.vl-to__tile--accent').w / box('.vl-to__tile--brief:not(.vl-to__tile--accent)').w",
      "min": 1.03,
      "max": 1.1
    },
    {
      "name": "UN SEUL APLAT D'ACCENT — deux et la rangée n'a plus de point d'entrée, ni le marqueur d'axe à viser",
      "measure": "(() => { const a = cs('.vl-to__tile--accent', 'backgroundColor'); return [...document.querySelectorAll('.vl-to__tile')].filter((e) => getComputedStyle(e).backgroundColor === a).length; })()",
      "expect": 1,
      "tol": 0
    },
    {
      "name": "MARQUEUR SUR L'AXE DE L'ACCENT — s'il est là, son centre tombe sur celui de la troisième colonne, pas ailleurs",
      "measure": "(() => { if (!document.querySelector('.vl-to__mark')) return 0; const m = box('.vl-to__mark'), t = box('.vl-to__tile--accent'); return Math.abs((m.left + m.right) / 2 - (t.left + t.right) / 2) / W; })()",
      "max": 0.004
    },
    {
      "name": "ET DONC PAS COLLÉ À LA MARGE — il reste au moins 5 % de la largeur entre son bord droit et celui du bloc : un marqueur aligné sur la marge redevient un objet de coin",
      "measure": "(() => { if (!document.querySelector('.vl-to__mark')) return 1; return (box('.vl-tiles-over').right - box('.vl-to__mark').right) / W; })()",
      "min": 0.05
    },
    {
      "name": "BASE DU MARQUEUR SUR LA DERNIÈRE LIGNE — il ne descend jamais sous la boîte de l'énoncé, et n'en remonte pas de plus de 2 %",
      "measure": "(() => { if (!document.querySelector('.vl-to__mark')) return 0.01; return (box('.vl-to__say').bottom - box('.vl-to__mark').bottom) / W; })()",
      "min": 0,
      "max": 0.02
    },
    {
      "name": "INTERLIGNE DE L'ÉNONCÉ SOUS 1 — c'est ce qui fait lire deux lignes comme UNE masse ; au-dessus, ce sont deux phrases empilées",
      "measure": "num('.vl-to__say', 'lineHeight') / num('.vl-to__say', 'fontSize')",
      "max": 1
    },
    {
      "name": "SAUT DE HIÉRARCHIE — l'énoncé fait au moins 3,2 fois le titre d'une brève : entre les deux, aucune taille intermédiaire n'existe",
      "measure": "num('.vl-to__say', 'fontSize') / num('.vl-to__h', 'fontSize')",
      "min": 3.2
    },
    {
      "name": "PLANCHER DE CORPS — l'explication d'une brève ne descend pas sous 0,0120 de la largeur du bloc (18 px sur 1448) : la source, elle, tombe à ~15 px une fois remise à l'échelle",
      "measure": "num('.vl-to__p', 'fontSize') / W",
      "min": 0.012
    },
    {
      "name": "LES DEUX BRÈVES PARTENT DE LA MÊME HAUTEUR — leurs paragraphes sont collés au plancher ET coupés au même nombre de lignes ; sans la coupe, la plus étroite en fait une de plus et le rail se casse",
      "measure": "(() => { const p = [...document.querySelectorAll('.vl-to__p')].map((e) => e.getBoundingClientRect().top); return (Math.max(...p) - Math.min(...p)) / W; })()",
      "max": 0.004
    },
    {
      "name": "AUCUNE OMBRE — sur un fond plus CLAIR qu'elles, les tuiles ne se détachent que par leur aplat et leur rayon ; une ombre y fabriquerait une troisième couche",
      "measure": "[...document.querySelectorAll('.vl-to__tile')].filter((e) => getComputedStyle(e).boxShadow !== 'none').length",
      "expect": 0,
      "tol": 0
    },
    {
      "name": "RAYON EN LONGUEUR — le même rayon en pixels sur trois tuiles de largeurs DIFFÉRENTES ; en pourcentage, il donnerait trois arcs elliptiques",
      "measure": "(() => { const r = [...document.querySelectorAll('.vl-to__tile')].map((e) => parseFloat(getComputedStyle(e).borderTopLeftRadius)); return (Math.max(...r) - Math.min(...r)) / Math.max(...r); })()",
      "max": 0.003
    }
  ],
  "notes": "**Ce que ce pattern ajoute à `card-12-inverted-kpi-row`.** Les deux emploient le même geste — accentuer UNE carte d'une rangée par le fond ET par une colonne plus large. Mais `card-12` traite trois objets de MÊME nature (trois indicateurs), où l'inégalité de largeur est la seule chose qui hiérarchise ; ici les trois tuiles sont de natures différentes (un visuel, deux brèves), et l'inégalité de largeur sert d'abord à faire tenir un visuel à côté de deux textes. C'est pour ça que le benchmark central n'est pas le même : là-bas on mesure l'écart entre colonnes, ici on mesure le QUASI-ÉQUILIBRE entre la tuile visuelle et le couple de brèves.\n\n**Le marqueur est le vrai apport, et il est contre-intuitif.** Le réflexe est de le caler sur la marge droite, en face du titre. Mesuré sur la source, il ne l'est pas : son centre tombe à 1342 px sur 1600, soit exactement l'axe de la tuile d'accent, à 182 px de la marge. L'effet est net dès qu'on compare les deux : calé sur la marge, il ferme la slide et n'appartient à rien ; calé sur l'axe, il tire une verticale invisible depuis la seule tuile colorée, et l'accent unique se prolonge dans la zone de titre au lieu de s'arrêter à la rangée. Le second benchmark de retrait (≥ 5 % du bloc) existe pour interdire le retour au réflexe.\n\n**L'interligne sous 1 a une contrepartie qu'il faut payer explicitement.** À `line-height: 0.86`, le demi-interligne devient négatif (−36 px pour un corps de 134) : la boîte du titre est plus courte que ses glyphes, et un bloc calé en bas laisse ses descendantes mordre la marge de page. La compensation de 31 px sur le `margin-bottom` de l'énoncé n'est pas un réglage à l'œil — c'est la moitié de l'excédent, et elle se recalcule si le corps ou l'interligne changent.\n\n**La coupe du paragraphe est une hauteur maximale, pas un `line-clamp`.** Le clamp ajouterait des points de suspension que la source n'a pas ; la hauteur maximale coupe net, comme un texte de gabarit tronqué. Sans coupe du tout, la brève la plus étroite passe à quatre lignes et les deux paragraphes cessent de partir de la même hauteur — c'est la seule chose que cette rangée demande à ses brèves, et c'est mesuré.\n\n**Le marqueur est OPTIONNEL, et ce qu'on perd en le retirant est nommé.** Le bloc reste valide sans lui : l'énoncé garde la même position, la même compensation d'interligne, et les trois benchmarks qui le concernent se neutralisent quand il est absent (ils testent une loi conditionnelle — « s'il est là, il vise l'axe » — et non sa présence). Ce qui disparaît est réel : sans lui, la zone d'énoncé n'a plus d'ancre à droite, et l'accent unique s'arrête à la rangée au lieu de se prolonger sous elle. Deux cas où le retirer est le bon geste : quand l'énoncé est plus long et réclame la largeur, et quand le bloc est posé dans une page qui porte déjà la marque ailleurs (un hero sous une barre de navigation). Le retrait tient en une chose — supprimer le `div.vl-to__mark` ; la règle CSS peut rester, elle n'est plus appelée.\n\n**Origine.** Reversé le 24/08/2026 depuis une slide de proposition commerciale relevée à la pipette (`assets/refs/ref-22-yellow-proposal-bento.png`, 586 px de large, donc ×2,7304 pour une slide 1600×900). Les corps de la source tombent à ~15 px une fois remis à l'échelle, sous le plancher du corpus : ils sont remontés à 20, et c'est le seul écart au relevé.",
  "id": "layout-19-tiles-over-claim"
}

À côté