/* ── POLICES ────────────────────────────────────────────────────────────────
   Roboto et IBM Plex Mono, les deux familles de la référence, SERVIES DEPUIS CE
   SITE et non depuis Google. Ce n'est pas une préférence : la page « mentions
   légales » affirme que le site « n'envoie rien à un serveur » et n'utilise
   « aucun traceur tiers ». Une balise vers fonts.gstatic.com enverrait l'adresse
   IP de chaque visiteur à Google et rendrait cette phrase fausse — un défaut de
   la même famille que tous ceux corrigés cette semaine, à ceci près qu'il
   toucherait la seule page qui engage juridiquement l'éditeur.

   Roboto est distribué en fichier VARIABLE : les trois graisses (400, 700, 900)
   sortaient du même octet près — vérifié par empreinte, les trois fichiers
   téléchargés étaient identiques. Un seul est donc servi, avec une plage de
   graisses, ce qui épargne 75 Ko.

   `font-display: swap` : le texte s'affiche immédiatement dans la police
   système et bascule à l'arrivée du fichier. Sans cela, un réseau lent laisse
   la page muette. */
@font-face {
  font-family: 'Roboto var'; src: url('/fonts/roboto-var.woff2') format('woff2-variations');
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono'; src: url('/fonts/plexmono-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono'; src: url('/fonts/plexmono-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}

/* ============================================================
   Coût Réel — design system
   Dark-first (convention du secteur), thème clair supporté.
   Tokens uniquement : aucune couleur en dur hors de :root.
   ============================================================ */

:root {
  --ground:      #0B0E11;
  --surface:     #12171B;
  --raised:      #191F25;
  --sunken:      #080A0D;
  --ink:         #E9EDEF;
  --ink-2:       #A8B3BB;
  --ink-3:       #798893;
  --rule:        #222A31;
  --rule-strong: #333E47;
  /* Accent repris du bleu-gris désaturé de la référence (#a0afc8). Il est CLAIR,
     là où notre vert était sombre : le texte posé dessus passe donc en foncé,
     sans quoi un bouton primaire deviendrait illisible. */
  --accent:      #A0AFC8;
  /* L'accent en composantes, pour le doser en transparence. La référence ne
     dessine jamais un gris neutre : ses bords sont son accent à 14-18 %, ses
     fonds le même à 6-12 %. Un bord teinté se lit comme appartenant à la
     palette, là où un gris posé à côté se lit comme une bordure de tableau. */
  --accent-rgb:  160 175 200;
  --accent-ink:  #0A0B0D;
  --accent-soft: #1A1F2B;
  --warn:        #E0A93B;
  --warn-soft:   #2E2410;
  --crit:        #E8635A;
  --crit-soft:   #331817;
  --info:        #5AA9E8;

  --sans: 'Roboto var', Roboto, system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  /* 94rem = 1 504 px. Mesuré : c'est la première valeur à laquelle le tableau
     du comparateur (1 139 px) cesse de déborder à côté de la colonne de filtres
     (264 px) sur un écran large — à 76rem il manquait 253 px, à 90rem encore 29.
     En dessous de ~1 550 px de fenêtre la limite redevient l'écran lui-même, et
     ce sont les colonnes secondaires qui se retirent. La prose, elle, garde
     --wrap-narrow : élargir un tableau aide, élargir un paragraphe nuit. */
  --wrap: 94rem;
  --nav-h: 3.5rem;
}

/* --ink-3 assombri de #6C7880 à #616C73 le 2026-08-17. L'ancienne valeur tombait
   sous le seuil AA de 4,5:1 sur deux fonds réellement utilisés — 4,18 sur
   --ground et 3,81 sur --sunken, ce dernier étant le fond des en-têtes du
   comparateur. Aucun texte concerné n'atteint la taille qui donnerait droit au
   seuil assoupli de 3:1 (le plus grand fait 12,8 px en graisse 400).
   La teinte bleu-gris est conservée ; seule la luminosité change.
   Le thème SOMBRE échouait aussi : #6E7C86 donnait 4,20 sur --surface et 3,87
   sur --raised, éclairci à #798893 (pire cas 4,55). Une première mesure avait
   conclu que le sombre passait — elle portait sur une valeur qui n'était pas
   celle du fichier. Les ratios sont désormais calculés par un test, à partir
   des tokens LUS dans ce fichier, pour que l'instrument ne puisse plus mentir. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --ground:      #F4F6F7;
    --surface:     #FFFFFF;
    --raised:      #FFFFFF;
    --sunken:      #E8ECEE;
    --ink:         #12171B;
    --ink-2:       #414C54;
    --ink-3:       #616C73;
    --rule:        #DCE2E6;
    --rule-strong: #B4BEC5;
    /* Même teinte, assombrie : #A0AFC8 sur blanc ne donne que 2,0 de contraste,
       ce que le test de contraste refuse. La référence n'a pas ce problème —
       elle n'a pas de thème clair. */
    --accent:      #4A5A78;
  --accent-rgb:  74 90 120;
    --accent-rgb:  74 90 120;
    --accent-ink:  #FFFFFF;
    --accent-soft: #EAEEF4;
    --warn:        #8A5A0C;
    --warn-soft:   #F8EFDB;
    --crit:        #A22E25;
    --crit-soft:   #F8E4E2;
    --info:        #1D5F91;
  }
}

:root[data-theme="light"] {
  --ground:      #F4F6F7;
  --surface:     #FFFFFF;
  --raised:      #FFFFFF;
  --sunken:      #E8ECEE;
  --ink:         #12171B;
  --ink-2:       #414C54;
  --ink-3:       #616C73;
  --rule:        #DCE2E6;
  --rule-strong: #B4BEC5;
  --accent:      #4A5A78;
  --accent-rgb:  74 90 120;
  --accent-ink:  #FFFFFF;
  --accent-soft: #EAEEF4;
  --warn:        #8A5A0C;
  --warn-soft:   #F8EFDB;
  --crit:        #A22E25;
  --crit-soft:   #F8E4E2;
  --info:        #1D5F91;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: calc(var(--nav-h) + 1rem); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 1.25rem; }
.wrap-narrow { max-width: 46rem; margin: 0 auto; padding: 0 1.25rem; }

a { color: var(--accent); text-underline-offset: 2px; }
a:focus-visible, button:focus-visible, select:focus-visible, input:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px;
}

h1, h2, h3, h4 { text-wrap: balance; }

/* ---------------- nav ---------------- */

.nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--rule);
  height: var(--nav-h);
}
.nav-in { display: flex; align-items: center; gap: 1.5rem; height: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 1.25rem; }
.logo {
  font-family: var(--mono); font-weight: 700; font-size: 0.86rem;
  letter-spacing: 0.06em; color: var(--ink); text-decoration: none; white-space: nowrap;
}
.logo b { color: var(--accent); }
.nav-groups { display: flex; gap: 1.4rem; flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.nav-groups::-webkit-scrollbar { display: none; }
.nav-group { display: flex; flex-direction: column; gap: 0.1rem; }
.nav-group-label {
  font-family: var(--mono); font-size: 0.54rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-3);
}
.nav-links { display: flex; gap: 0.85rem; }
.nav-links a {
  font-size: 0.82rem; color: var(--ink-2); text-decoration: none; white-space: nowrap;
  padding-bottom: 2px; border-bottom: 1.5px solid transparent;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); font-weight: 600; }
.nav-right { display: flex; align-items: center; gap: 0.6rem; margin-left: auto; }
.btn-discord {
  background: var(--accent); color: var(--accent-ink); font-weight: 700; font-size: 0.78rem;
  padding: 0.42rem 0.9rem; border-radius: 999px; text-decoration: none; white-space: nowrap;
}
.theme-toggle {
  background: none; border: 1px solid rgb(var(--accent-rgb) / 0.28); color: var(--ink-2);
  border-radius: 999px; width: 2rem; height: 2rem; cursor: pointer; font-size: 0.8rem; line-height: 1;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--accent); }

@media (max-width: 60rem) {
  .nav-group-label { display: none; }
  .nav-groups { gap: 0.9rem; }
  .nav-group { flex-direction: row; }
}

/* ---------------- hero ---------------- */

.hero { padding: 3.5rem 0 2.5rem; border-bottom: 1px solid var(--rule); }
.hero-updated {
  font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-3); margin: 0 0 1.25rem;
  display: flex; align-items: center; gap: 0.7rem;
}
.hero-updated::before { content: ""; width: 2rem; height: 1px; background: var(--rule-strong); }
.hero h1 {
  font-size: clamp(2.1rem, 6vw, 3.6rem); line-height: 1.02; letter-spacing: -0.03em;
  font-weight: 900; margin: 0 0 1.1rem; max-width: 16ch;
}
.hero h1 em { font-style: normal; color: var(--accent); }
.hero-sub { color: var(--ink-2); max-width: 46ch; font-size: 1.02rem; margin: 0 0 1.75rem; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-bottom: 2.25rem; }
.btn {
  display: inline-block; padding: 0.7rem 1.3rem; border-radius: 12px; font-size: 0.9rem;
  font-weight: 650; text-decoration: none; border: 1px solid rgb(var(--accent-rgb) / 0.28);
  color: var(--ink); background: var(--raised); cursor: pointer;
}
.btn:hover { border-color: var(--accent); }
.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn-primary:hover { filter: brightness(1.08); }

.hero-stats { display: flex; gap: 0.65rem; flex-wrap: wrap; }
.hero-stat {
  background: rgb(var(--accent-rgb) / 0.05); border: 1px solid rgb(var(--accent-rgb) / 0.16);
  border-radius: 14px; padding: 0.7rem 1.15rem; min-width: 6.5rem;
}
.hero-stat-value {
  font-family: var(--mono); font-size: 1.3rem; font-weight: 700;
  font-variant-numeric: tabular-nums; display: block; line-height: 1.1;
}
.hero-stat-value.is-green { color: var(--accent); }
.hero-stat-label {
  font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-3); display: block; margin-top: 0.3rem;
}

/* ---------------- generic page head ---------------- */

.page-head { padding: 2.75rem 0 1.5rem; border-bottom: 1px solid var(--rule); margin-bottom: 2rem; }
.eyebrow {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.17em;
  text-transform: uppercase; color: var(--accent); margin: 0 0 0.8rem; font-weight: 600;
}
.page-head h1 { font-size: clamp(1.7rem, 4vw, 2.5rem); letter-spacing: -0.025em; margin: 0 0 0.6rem; font-weight: 750; }
.page-head p { color: var(--ink-2); max-width: 60ch; margin: 0; }

/* ---------------- comparateur layout ---------------- */

.cmp { display: grid; grid-template-columns: 16.5rem 1fr; gap: 1.5rem; padding: 2rem 0 4rem; align-items: start; }
/* Sans min-width:0, un élément de grille garde min-width:auto et refuse de
   passer sous la min-content de son contenu — ici la table à min-width 56rem.
   La colonne restait donc à ~1020 px sur mobile et toute la page défilait
   horizontalement, alors même que la media query passait bien en une colonne. */
.cmp > * { min-width: 0; }
@media (max-width: 62rem) { .cmp { grid-template-columns: 1fr; } }

.filters {
  background: rgb(var(--accent-rgb) / 0.05); border: 1px solid rgb(var(--accent-rgb) / 0.16); border-radius: 18px;
  padding: 1rem; position: sticky; top: calc(var(--nav-h) + 1rem);
  max-height: calc(100vh - var(--nav-h) - 2rem); overflow-y: auto;
}
@media (max-width: 62rem) { .filters { position: static; max-height: none; } }

.filters-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.9rem; }
.filters-head h2 { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.15em; text-transform: uppercase; color: var(--ink-3); margin: 0; }

.mode-tabs { display: flex; gap: 0.3rem; background: var(--sunken); padding: 0.22rem; border-radius: 12px; margin-bottom: 1rem; }
.mode-tabs button {
  flex: 1; background: none; border: none; color: var(--ink-3); font-family: var(--mono);
  font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; padding: 0.42rem;
  border-radius: 8px; cursor: pointer; font-weight: 600;
}
.mode-tabs button[aria-selected="true"] { background: var(--raised); color: var(--ink); }

.fg { margin-bottom: 1.05rem; }
.fg-label {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 0.45rem; display: block; font-weight: 600;
}
.fr-row { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.fr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem; }

.chipbtn {
  background: var(--raised); border: 1px solid var(--rule); color: var(--ink-2);
  font-size: 0.73rem; padding: 0.32rem 0.6rem; border-radius: 8px; cursor: pointer;
  font-family: var(--sans); white-space: nowrap; text-align: center;
}
.chipbtn:hover { border-color: var(--rule-strong); color: var(--ink); }
.chipbtn[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 650; }

.fcheck-list { display: flex; flex-direction: column; gap: 0.2rem; max-height: 8.5rem; overflow-y: auto; }
.fcheck { display: flex; align-items: center; gap: 0.45rem; font-size: 0.78rem; color: var(--ink-2); cursor: pointer; }
.fcheck input { accent-color: var(--accent); width: 0.9rem; height: 0.9rem; flex: none; }

.ftoggle { display: flex; align-items: center; gap: 0.45rem; font-size: 0.76rem; color: var(--ink-2); cursor: pointer; margin-bottom: 0.3rem; }
.ftoggle input { accent-color: var(--accent); width: 0.9rem; height: 0.9rem; flex: none; }

.range-row { display: flex; align-items: center; gap: 0.6rem; }
.range-row input[type="range"] { flex: 1; accent-color: var(--accent); }
.range-val { font-family: var(--mono); font-size: 0.76rem; color: var(--accent); min-width: 3.6rem; text-align: right; font-variant-numeric: tabular-nums; }

.filters-actions { display: flex; gap: 0.4rem; margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid var(--rule); }
.filters-actions button { flex: 1; font-size: 0.72rem; padding: 0.5rem; }
.linkish { background: none; border: none; color: var(--ink-3); font-size: 0.68rem; cursor: pointer; text-decoration: underline; padding: 0; font-family: var(--sans); }
.linkish:hover { color: var(--accent); }

.hint-link { font-size: 0.68rem; color: var(--ink-3); text-decoration: none; display: inline-block; margin-top: 0.3rem; }
.hint-link:hover { color: var(--accent); }

/* ---------------- results ---------------- */

.results-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap; margin-bottom: 0.9rem;
}
.results-count { font-family: var(--mono); font-size: 0.74rem; color: var(--ink-3); letter-spacing: 0.06em; }
.results-count b { color: var(--accent); font-size: 1rem; }
.sorts { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.sorts button {
  background: none; border: 1px solid var(--rule); color: var(--ink-3);
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.3rem 0.55rem; border-radius: 8px; cursor: pointer;
}
.sorts button[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }

.table-scroll { overflow-x: auto; border: 1px solid rgb(var(--accent-rgb) / 0.16); border-radius: 18px; background: var(--surface); }
table.cmp-table { width: 100%; border-collapse: collapse; font-size: 0.83rem; min-width: 56rem; }
.cmp-table thead th {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 600; text-align: left; padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--rule-strong); white-space: nowrap; background: var(--sunken);
  position: sticky; top: 0; z-index: 2;
}
.cmp-table td { padding: 0.7rem 0.85rem; border-bottom: 1px solid var(--rule); vertical-align: middle; }

/* Première colonne figée pendant le défilement horizontal. Le tableau fait
   1 106 px pour 333 px de conteneur sur mobile : sans cela, dès qu'on va lire
   le drawdown ou l'objectif, la firme et la formule sont sorties de l'écran et
   la ligne devient anonyme — sur un tableau dont un tiers des groupes porte
   plusieurs formules au même palier, c'est précisément l'information qu'il ne
   faut pas perdre de vue. Le fond est opaque, sans quoi les colonnes défilantes
   se liraient au travers. */
.cmp-table thead th:first-child,
.cmp-table tbody th[scope="row"] { position: sticky; left: 0; z-index: 1; }
.cmp-table tbody th[scope="row"] {
  background: var(--surface); text-align: left; font-weight: 400;
  padding: 0.7rem 0.85rem; border-bottom: 1px solid var(--rule);
}
.cmp-table thead th:first-child { background: var(--sunken); z-index: 3; }
.cmp-table tbody tr:hover th[scope="row"] { background: var(--raised); }
/* Le liseré ne se montre qu'une fois le tableau réellement décalé : à l'arrêt
   il ajouterait une barre verticale que rien ne justifie. */
.table-scroll.is-decale .cmp-table thead th:first-child,
.table-scroll.is-decale .cmp-table tbody th[scope="row"] { box-shadow: 1px 0 0 var(--rule-strong); }

/* La zone défilante est atteignable au clavier. Sans `tabindex`, trois colonnes
   — drawdown, objectif, transparence — n'étaient joignables par AUCUNE commande
   pour qui n'a pas de souris : échec WCAG 2.1.1, sur les colonnes mêmes qui
   justifient le site. */
.table-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cmp-table tbody tr:hover { background: var(--raised); }
.cmp-table .num { font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.cmp-table .price { font-weight: 700; font-size: 0.95rem; }
.cmp-table .price small { display: block; font-weight: 400; font-size: 0.64rem; color: var(--ink-3); }
.firm-cell { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
/* Toute la ligne déplie : le chevron n'est plus la seule cible, il n'en est que
   le signe. Le curseur le dit avant le clic. */
.cmp-table tbody tr { cursor: pointer; }
.cmp-table tbody tr .firm-cell a { cursor: pointer; }
.firm-mark {
  width: 1.6rem; height: 1.6rem; border-radius: 8px; flex: none;
  background: var(--accent-soft); color: var(--accent); font-family: var(--mono);
  font-size: 0.62rem; font-weight: 700; display: grid; place-items: center;
}
.firm-cell a { color: var(--ink); text-decoration: none; font-weight: 650; white-space: nowrap; }
.firm-cell a:hover { color: var(--accent); }
/* Le nom de la formule sous celui de la firme : c'est lui qui rend lisibles
   trois lignes « Topstep 50K » à trois prix différents. Il s'efface
   typographiquement — c'est une précision, pas un titre — mais il ne se coupe
   qu'en dernier recours, et l'attribut title rend alors le nom entier. */
.firm-id { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.firm-plan {
  font-size: 0.66rem; line-height: 1.25; color: var(--ink-3); font-weight: 500;
  max-width: 13rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.tag {
  display: inline-block; font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.07em;
  text-transform: uppercase; font-weight: 600; padding: 0.2rem 0.55rem; border-radius: 999px;
  border: 1px solid rgb(var(--accent-rgb) / 0.28); background: rgb(var(--accent-rgb) / 0.07);
  color: var(--ink-2); white-space: nowrap;
}
.tag.ok   { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.tag.warn { background: var(--warn-soft);   color: var(--warn);   border-color: var(--warn); }
.tag.crit { background: var(--crit-soft);   color: var(--crit);   border-color: var(--crit); }

.promo-code {
  font-family: var(--mono); font-size: 0.7rem; font-weight: 700; color: var(--accent);
  background: var(--accent-soft); border: 1px dashed var(--accent); border-radius: 8px;
  padding: 0.2rem 0.45rem; cursor: pointer;
}
.promo-code:hover { filter: brightness(1.15); }
.no-promo { color: var(--ink-3); font-size: 0.72rem; }

.score { display: flex; align-items: center; gap: 0.4rem; }
.score-bar { width: 2.6rem; height: 4px; background: var(--sunken); border-radius: 2px; overflow: hidden; }
.score-fill { height: 100%; background: var(--accent); display: block; }
.score-val { font-family: var(--mono); font-size: 0.72rem; font-variant-numeric: tabular-nums; color: var(--ink-2); }

.empty-state { padding: 3rem 1rem; text-align: center; color: var(--ink-3); }
.empty-state p { margin: 0 0 0.8rem; }

/* ---------------- cards / grids ---------------- */

.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 0.9rem; }
.card {
  background: rgb(var(--accent-rgb) / 0.05); border: 1px solid rgb(var(--accent-rgb) / 0.16); border-radius: 18px;
  padding: 1.1rem; text-decoration: none; color: inherit; display: block;
}
a.card:hover { border-color: var(--accent); }
.card h3 { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 700; }
.card p { margin: 0; color: var(--ink-2); font-size: 0.85rem; }
.card-meta { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.7rem; }

/* ---------------- prose ---------------- */

.prose { max-width: 44rem; padding-bottom: 4rem; }
.prose h2 {
  font-size: 1.35rem; margin: 2.5rem 0 0.8rem; font-weight: 700; letter-spacing: -0.015em;
  padding-top: 0.9rem; border-top: 1px solid var(--rule);
}
.prose h3 { font-size: 1.02rem; margin: 1.8rem 0 0.5rem; font-weight: 650; }

/* Un titre ancré atterrit collé au haut de la fenêtre après un saut, et le bord
   supérieur de la section passe sous le bord de l'écran. La marge de défilement
   est ce qui fait qu'un lien profond montre le titre ET la phrase qui le suit —
   c'est-à-dire la réponse, la seule raison d'avoir posé l'ancre. */
.prose h2[id], .prose h3[id] { scroll-margin-top: 1.5rem; }

/* LA PHRASE-RÉPONSE : le premier élément après chaque titre-question, et la
   seule ligne de la section qu'un extrait de recherche ou un modèle de langue
   recopiera telle quelle. Elle porte le chiffre, son état et sa date ; elle se
   lit AVANT le tableau, et se distingue du corps de texte pour cette raison. */
.prose p.reponse {
  color: var(--ink); font-size: 0.98rem; line-height: 1.55;
  border-left: 2px solid var(--accent); padding-left: 0.9rem; margin-bottom: 1.2rem;
}

/* Sommaire à liens de saut. Les ancres existent maintenant sur chaque section ;
   sans cette liste, elles ne sont atteignables que par qui les connaît déjà. */
.toc {
  margin: 0 0 2rem; padding: 0.9rem 1rem;
  background: var(--sunken); border: 1px solid var(--rule); border-radius: 8px;
}
.toc-titre {
  margin: 0 0 0.5rem; font-family: var(--mono); font-size: 0.62rem;
  letter-spacing: 0.05em; color: var(--ink-3); text-transform: uppercase;
}
.toc ul { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; margin: 0; padding: 0; list-style: none; }
.toc li { margin: 0; }
.toc a { font-size: 0.85rem; color: var(--ink-2); }
.toc a:hover { color: var(--accent); }

.prose p, .prose li { color: var(--ink-2); }
.prose p { margin: 0 0 1rem; }
.prose ul, .prose ol { margin: 0 0 1rem; padding-left: 1.25rem; }
.prose li { margin-bottom: 0.4rem; }
.prose li::marker { color: var(--ink-3); }
.prose strong { color: var(--ink); }
.prose code {
  font-family: var(--mono); font-size: 0.85em; background: var(--sunken);
  border: 1px solid var(--rule); padding: 0.08em 0.3em; border-radius: 3px;
}

/* Conteneur défilant appliqué automatiquement à chaque .datatable par build.mjs.
   Sans cette règle, l'enveloppe existe mais ne contient rien : le tableau
   déborde et c'est le body entier qui défile latéralement sur mobile. */
.scroll { overflow-x: auto; max-width: 100%; margin: 1.2rem 0; }
.scroll .datatable { margin: 0; }

.datatable { width: 100%; border-collapse: collapse; font-size: 0.85rem; margin: 1.2rem 0; }
.datatable th, .datatable td { text-align: left; padding: 0.6rem 0.8rem 0.6rem 0; border-bottom: 1px solid rgb(var(--accent-rgb) / 0.16); }
.datatable thead th {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-3); border-bottom-color: var(--rule-strong);
}
.datatable td.num, .datatable th.num { text-align: right; padding-right: 0; font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.datatable tbody th { font-weight: 600; color: var(--ink); }

.callout {
  background: rgb(var(--accent-rgb) / 0.05); border: 1px solid rgb(var(--accent-rgb) / 0.16); border-left: 3px solid var(--accent);
  padding: 0.9rem 1.1rem; margin: 1.4rem 0; border-radius: 0 12px 12px 0;
}
.callout.warn { border-left-color: var(--warn); }
.callout.crit { border-left-color: var(--crit); }
.callout p { margin: 0 0 0.6rem; }
.callout p:last-child { margin: 0; }
.callout-label {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); font-weight: 700; display: block; margin-bottom: 0.4rem;
}
.callout.warn .callout-label { color: var(--warn); }
.callout.crit .callout-label { color: var(--crit); }

/* ---------------- FAQ ---------------- */

.faq { border-top: 1px solid var(--rule); padding: 2.5rem 0 4rem; }
.faq h2 { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 1.2rem; }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  cursor: pointer; padding: 0.9rem 0; font-weight: 600; font-size: 0.95rem;
  list-style: none; display: flex; justify-content: space-between; gap: 1rem; align-items: center;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--accent); font-family: var(--mono); font-size: 1.1rem; flex: none; }
.faq details[open] summary::after { content: "−"; }
.faq details > div { padding: 0 0 1.1rem; color: var(--ink-2); max-width: 62ch; font-size: 0.92rem; }
.faq details > div p { margin: 0 0 0.7rem; }
.faq details > div p:last-child { margin: 0; }

/* ---------------- footer ---------------- */

.site-footer { border-top: 1px solid var(--rule); background: var(--sunken); padding: 2.5rem 0 3rem; margin-top: 3rem; }
.footer-grid { display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: 2rem; margin-bottom: 2rem; }
@media (max-width: 52rem) { .footer-grid { grid-template-columns: 1fr 1fr; } }
.footer-col h4 {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 0.7rem; font-weight: 600;
}
.footer-col ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.footer-col a { color: var(--ink-2); text-decoration: none; font-size: 0.83rem; }
.footer-col a:hover { color: var(--accent); }
.footer-about p { color: var(--ink-3); font-size: 0.8rem; margin: 0 0 0.8rem; max-width: 34ch; }
.footer-legal {
  border-top: 1px solid var(--rule); padding-top: 1.25rem;
  font-size: 0.74rem; color: var(--ink-3); display: flex; flex-wrap: wrap; gap: 0.5rem 1.2rem;
}
.footer-disclaimer { max-width: 100%; margin-top: 0.9rem; line-height: 1.6; }

/* ---------------- misc ---------------- */

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 0.5rem; top: -3rem; background: var(--accent); color: var(--accent-ink);
  padding: 0.5rem 0.9rem; border-radius: 8px; z-index: 100; transition: top 0.15s;
}
.skip-link:focus { top: 0.5rem; }

.verif-note { font-family: var(--mono); font-size: 0.62rem; color: var(--ink-3); letter-spacing: 0.05em; }
.verif-note .ok { color: var(--accent); }
.verif-note .todo { color: var(--warn); }

.aff-disclosure {
  background: var(--warn-soft); border: 1px solid var(--warn); color: var(--warn);
  font-size: 0.8rem; padding: 0.65rem 0.9rem; border-radius: 12px; margin: 1rem 0;
}
.aff-disclosure strong { color: inherit; }

/* ---------------- calculateur ---------------- */

.controls {
  background: rgb(var(--accent-rgb) / 0.05); border: 1px solid rgb(var(--accent-rgb) / 0.16); border-radius: 18px;
  padding: 1.1rem 1.25rem 1.25rem; margin: 1.5rem 0 2rem;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); gap: 1rem 1.5rem;
}
.field { display: flex; flex-direction: column; gap: 0.35rem; }
.field label {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3); font-weight: 600;
}
.field select, .field input[type="number"] {
  font-family: var(--mono); font-size: 0.95rem; padding: 0.5rem 0.6rem;
  border: 1px solid rgb(var(--accent-rgb) / 0.28); border-radius: 8px;
  background: var(--ground); color: var(--ink); width: 100%;
}
.field .hint { font-size: 0.7rem; color: var(--ink-3); }
.check { display: flex; align-items: center; gap: 0.5rem; align-self: end; padding-bottom: 0.45rem; }
.check input { width: 1.05rem; height: 1.05rem; accent-color: var(--accent); }
.check label { font-size: 0.88rem; color: var(--ink-2); }

#results .card { margin-bottom: 0.75rem; padding: 0; overflow: hidden; }
#results details.card summary {
  list-style: none; cursor: pointer; display: grid;
  grid-template-columns: 2rem 1fr auto; align-items: center; gap: 0.9rem; padding: 0.85rem 1.1rem;
}
#results details.card summary::-webkit-details-marker { display: none; }
#results details.card summary:hover { background: var(--raised); }
.rank { font-family: var(--mono); font-size: 0.8rem; color: var(--ink-3); font-weight: 600; }
.card.best .rank { color: var(--accent); }
.firm-id { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.firm-name { font-weight: 650; font-size: 1rem; }
.firm-account { font-size: 0.78rem; color: var(--ink-3); }
.chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.1rem; }
.chips .chip {
  display: inline-block; font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.07em;
  text-transform: uppercase; font-weight: 600; padding: 0.2rem 0.55rem; border-radius: 999px;
  border: 1px solid rgb(var(--accent-rgb) / 0.28); color: var(--ink-2); white-space: nowrap;
}
.chips .chip.warn  { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
.chips .chip.promo { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.total {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 1.25rem; font-weight: 700; text-align: right; white-space: nowrap;
}
.total .lbl {
  display: block; font-size: 0.55rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
.bill { border-top: 1px solid var(--rule); padding: 0.4rem 1.1rem 1rem; overflow-x: auto; }
.bill table {
  width: 100%; border-collapse: collapse; font-size: 0.86rem;
  font-variant-numeric: tabular-nums; min-width: 32rem;
}
.bill th, .bill td { text-align: left; padding: 0.5rem 0.9rem 0.5rem 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
.bill thead th {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3); font-weight: 600;
}
.bill td.amt, .bill th.amt { text-align: right; padding-right: 0; font-family: var(--mono); white-space: nowrap; }
.bill td.amt.negative { color: var(--accent); }
.bill td.amt.unpriced { color: var(--warn); font-size: 0.74rem; font-weight: 600; }
.bill td.amt.optional { color: var(--ink-3); }
.bill tr.is-optional td, .bill tr.is-optional th { opacity: 0.72; }
.bill .calc { color: var(--ink-3); font-size: 0.76rem; font-family: var(--mono); }
.bill .note { color: var(--ink-3); font-size: 0.74rem; font-weight: 400; margin-top: 3px; white-space: normal; max-width: 26rem; }
.bill tfoot td { font-weight: 700; border-bottom: none; padding-top: 0.65rem; }
.bill tfoot td.amt { font-size: 1rem; }
.bill tfoot tr.is-optional td { font-weight: 400; font-size: 0.78rem; padding-top: 0.2rem; color: var(--ink-3); }
.bill .go {
  display: inline-block; margin-top: 0.85rem; font-size: 0.85rem; font-weight: 650;
  color: var(--accent); text-decoration: none; border: 1px solid var(--accent);
  border-radius: 8px; padding: 0.45rem 0.9rem;
}
.bill .go:hover { background: var(--accent-soft); }
.excluded { color: var(--ink-3); font-size: 0.85rem; margin: 1.25rem 0 0; }
.excluded strong { color: var(--ink-2); }

@media print { .nav, .filters, .site-footer { display: none; } }

/* Sens du tri : la flèche est le seul indice VISUEL, l'aria-label porte la même
   information pour les lecteurs d'écran. Sans elle, ascendant et descendant
   étaient rigoureusement indiscernables alors que l'état existait bel et bien. */
.sorts button .sort-dir { font-weight: 700; letter-spacing: 0; }

/* ---------------- marques de firme ---------------- */

/* Deux teintes posées en ligne par le JS, l'active choisie ICI : le script n'a
   donc pas à connaître le thème courant ni à se réexécuter quand il change.
   Les deux valeurs sont calculées pour tenir le seuil AA sur les fonds de leur
   thème respectif — une pastille illisible n'identifie rien. */
.firm-mark {
  color: var(--mark-dark, var(--accent));
  background: color-mix(in srgb, var(--mark-dark, var(--accent)) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--mark-dark, var(--accent)) 38%, transparent);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .firm-mark {
    color: var(--mark-light, var(--accent));
    background: color-mix(in srgb, var(--mark-light, var(--accent)) 12%, transparent);
    border-color: color-mix(in srgb, var(--mark-light, var(--accent)) 34%, transparent);
  }
}
:root[data-theme="light"] .firm-mark {
  color: var(--mark-light, var(--accent));
  background: color-mix(in srgb, var(--mark-light, var(--accent)) 12%, transparent);
  border-color: color-mix(in srgb, var(--mark-light, var(--accent)) 34%, transparent);
}

/* Plateformes : monogramme sobre, sans couleur. Ce sont des noms de produits
   tiers ; une pastille typographique les distingue sans emprunter leur identité. */
.plat-list { display: flex; flex-wrap: wrap; gap: 0.25rem; align-items: center; }
.plat-mark {
  font-family: var(--mono); font-size: 0.58rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 0.16rem 0.45rem; border-radius: 999px;
  border: 1px solid rgb(var(--accent-rgb) / 0.28); color: var(--ink-2); white-space: nowrap;
}
.plat-more { font-family: var(--mono); font-size: 0.58rem; color: var(--ink-3); }

/* La pastille sert aussi bien dans une cellule de tableau que dans un titre de
   carte : dimension fixe et alignement sur la ligne de base du texte voisin. */
.card h3 .firm-mark {
  display: inline-grid; place-items: center;
  width: 1.35rem; height: 1.35rem; border-radius: 8px;
  font-family: var(--mono); font-size: 0.55rem; font-weight: 700;
  vertical-align: -0.22rem; margin-right: 0.15rem;
}


/* Logo de firme : le fichier vient du site de la firme, pas du nôtre, et il est
   dessiné pour SON fond. Les quatre que ce commentaire donnait autrefois pour
   perdus sur notre fond sombre — apex, tradeday, yrm, phidias — sont en réalité
   des vignettes opaques à monogramme clair, et se lisent partout : voir le bloc
   PLAQUES plus bas, qui explique pourquoi la luminance moyenne les avait
   condamnés à tort. Le padding de 2 px ci-dessous n'est donc plus là pour
   dégager une plaque, mais pour que la marque ne touche pas le bord arrondi.
   Une firme sans logo garde son monogramme. */
/* LA TUILE. Huit des treize logos de firme sont des vignettes OPAQUES : ils
   peignent la case entière et se lisent comme un carré de couleur. Trois ont un
   fond TRANSPARENT et une encre qui ne couvre que 29 à 42 % de la surface —
   mff, lucid, ffn — si bien qu'ils flottent seuls sur la carte pendant que
   leurs voisins montrent une tuile pleine. Sur la grille des guides, qui aligne
   les treize, la rangée qui les porte se lit comme deux cases vides.
   La tuile ne s'adresse donc qu'à ces trois-là : derrière une vignette opaque
   elle est invisible, ce que la mesure du bloc PLAQUES avait déjà établi pour
   les plaques. Elle vaut l'accent à 10 %, l'idiome de fond de cette palette,
   et non un gris neutre qui se lirait comme une bordure de tableau.
   `--tuile` existe pour que les règles de plaque puissent y REVENIR quand le
   thème courant n'a pas besoin d'elles : sans elle, `background: none` rendait
   sa tuile à tpt en thème clair.

   .PLAT-LOGO A LE MÊME DÉFAUT ET NE PEUT PAS PRENDRE LE MÊME REMÈDE, et la
   raison est mesurée. Le déséquilibre y est même inverse : sur les vingt-six
   marques de plateforme, treize flottent et cinq seulement remplissent leur
   pastille. Mais un FOND, quel qu'il soit, assombrit le thème clair — on ne
   peint pas plus clair que du blanc — et coûte donc du contraste à toute encre
   moyenne. Or MotiveWave est déjà à 3,12:1 sur blanc nu, pour un seuil à 3 :
   la tuile le fait passer dessous DÈS 4 %, et à 2 % — invisible — la pire marge
   est encore de 3,03. Il n'existe pas d'opacité qui tienne. La payer, ce serait
   lui coller un `ton: clair`, c'est-à-dire la plaque noire qu'on vient de
   retirer à ffn pour cette raison exacte.
   D'où un CONTOUR et non un fond : il borne la pastille sans rien changer au
   fond sur lequel l'encre se lit, donc il ne déplace aucun verdict et n'oblige
   aucune plaque. Cinq autres marques passeraient à moins de 0,15 du seuil avec
   un fond — NinjaTrader 3,04, DxFeed 3,05, AgenaTrader 3,13, Bookmap 3,15 —
   et le contour les laisse où elles sont. */
.firm-logo {
  --tuile: rgb(var(--accent-rgb) / .10);
  width: 1.6rem; height: 1.6rem; border-radius: 8px; flex: none;
  display: grid; place-items: center; overflow: hidden;
  background: var(--tuile);
}
.firm-logo img {
  width: 100%; height: 100%; object-fit: contain; padding: 2px; display: block;
}

/* Un champ légal non renseigné se montre comme un trou NOMMÉ. Il ne doit
   ressembler ni à du texte ordinaire — qu'on relirait sans le voir — ni à une
   valeur : c'est la seule mention de la page dont l'absence engage. */
.manque {
  font-family: var(--mono); font-size: 0.85em; color: var(--warn, #B45309);
  background: var(--accent-soft); border: 1px dashed var(--rule-strong);
  border-radius: 3px; padding: 0.05em 0.35em; white-space: nowrap;
}

/* ── Lignes dépliables ──────────────────────────────────────────────────────
   Depuis que deux formules d'un même palier peuvent coûter le même prix et ne
   différer que par leurs règles — les deux Bulenox 50K à 175 $US, l'une en
   drawdown suiveur sans limite journalière, l'autre en fin de journée avec
   1 100 $ — le tableau ne se lit plus seul. Ce qui les sépare était à deux
   clics, sur la fiche de la firme. */
.row-toggle-btn {
  display: grid; place-items: center; width: 1.6rem; height: 1.6rem; padding: 0;
  background: none; border: 1px solid transparent; border-radius: 8px;
  color: var(--ink-3); cursor: pointer;
}
.row-toggle-btn svg { width: 0.9rem; height: 0.9rem; transition: transform 0.15s ease; }
.row-toggle-btn:hover { color: var(--ink); border-color: var(--rule-strong); }
.row-toggle-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cmp-table tr.is-ouvert .row-toggle-btn { color: var(--accent); }
.cmp-table tr.is-ouvert .row-toggle-btn svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .row-toggle-btn svg { transition: none; } }

/* Le panneau déplié ne suit PAS le défilement horizontal du tableau : son
   contenu est collé au bord gauche de la fenêtre visible, dont la largeur est
   publiée en `--vue` par le seul code qui observe déjà ce conteneur. Sans cela,
   à 390 px et défilé à fond, le lecteur ouvrait la ligne et voyait 2 champs
   sur 8 — le panneau ajouté pour rendre le tableau lisible était lui-même
   illisible. */
.detail-row > td { background: var(--sunken); padding: 0; }
.detail-enveloppe {
  position: sticky; left: 0; width: var(--vue, 100%); box-sizing: border-box;
  padding: 0.9rem 0.85rem 1rem;
}
.detail-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: 0.5rem; margin-bottom: 0.7rem;
  /* Sans cela, une carte haute impose sa hauteur à TOUTE sa rangée : la carte
     « Plateformes », qui listait six noms les uns sous les autres, laissait sept
     cartes voisines aux trois quarts vides. Les logos règlent la cause, ceci
     règle la conséquence — une carte ne s'étire plus au-delà de son contenu. */
  align-items: start;
}
/* Chaque règle est une CARTE et non une ligne d'un tableau de plus. Douze
   paires libellé/valeur alignées en grille se lisaient comme un second tableau
   sous le premier, au moment précis où le lecteur vient d'en sortir. Une carte
   par règle donne à chacune un bord, un fond et une surface propres : on les
   compare d'un coup d'œil au lieu de suivre une colonne. */
.detail-item {
  display: flex; flex-direction: column; gap: 0.15rem; min-width: 0;
  background: rgb(var(--accent-rgb) / 0.05); border: 1px solid rgb(var(--accent-rgb) / 0.16);
  border-radius: 12px; padding: 0.55rem 0.7rem;
  /* Hauteur commune : sans elle, une valeur qui passe à la ligne — « 3 mini /
     30 micro » — rend sa carte plus haute que ses voisines, et douze cartes de
     hauteurs légèrement différentes se lisent comme un défaut d'alignement
     plutôt que comme une grille. La valeur couvre deux lignes de valeur, ce qui
     est le pire cas observé hors plateformes. */
  min-height: 3.9rem; justify-content: flex-start;
}
/* La carte des plateformes porte une rangée de marques : elle prend deux
   colonnes plutôt que d'empiler ses logos sur trois lignes. */
.detail-item-large { grid-column: span 2; }
.detail-label {
  font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--ink-3);
}
.detail-value { font-size: 0.85rem; color: var(--ink); font-weight: 600; line-height: 1.3; }
.detail-lien { font-size: 0.78rem; color: var(--accent); text-decoration: none; }
.detail-lien:hover { text-decoration: underline; }

/* ── Visuel du hero ─────────────────────────────────────────────────────────
   Il ne décore pas : il montre le cas qui justifie tout le site — trois lignes
   au même prix dont seules les règles diffèrent. Il passe sous le texte en
   dessous de 60rem plutôt que de rétrécir jusqu'à l'illisible, et disparaît
   sur mobile, où il ne ferait que repousser le comparateur. */
.hero .wrap { position: relative; }
.hero-visuel { display: none; }
@media (min-width: 60rem) {
  .hero .wrap { display: grid; grid-template-columns: minmax(0, 1fr) 22rem; column-gap: 3rem; align-items: start; }
  .hero-updated, .hero h1, .hero-sub, .hero-cta { grid-column: 1; }
  .hero-visuel { display: block; grid-column: 2; grid-row: 1 / span 4; align-self: center; }
  .hero-visuel svg { width: 100%; height: auto; }
  .hero-stats { grid-column: 1 / -1; }
}

/* ── MOUVEMENT ──────────────────────────────────────────────────────────────
   Jetons de mouvement, au même titre que les couleurs : une durée et une courbe
   nommées valent mieux que trente valeurs recopiées. Règles suivies, tirées des
   guides d'animation de référence (emilkowalski/skills, vercel-labs/open-agents) :

     · on n'anime QUE `transform` et `opacity` — jamais une propriété de mise en
       page, qui force un recalcul à chaque image ;
     · rien au-dessus de 300 ms sur de l'interface ;
     · `ease-out` pour ce qui entre, jamais `ease-in`, qui donne un départ mou ;
     · la FRÉQUENCE décide : ce qu'on voit cent fois par session ne s'anime pas.
       C'est pourquoi les 144 lignes du tableau, leurs filtres et leurs barres de
       score n'ont aucune animation d'entrée — seuls le hero, vu une fois, et le
       panneau déplié, ouvert à la demande, en ont une. */
:root {
  --t-micro: 120ms;
  --t-ui: 200ms;
  --t-entree: 260ms;
  --e-sortie: cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes apparition-bas {
  from { opacity: 0; transform: translate3d(0, 10px, 0); }
  to   { opacity: 1; transform: none; }
}

/* Le hero est vu une fois par visite : c'est le seul endroit où un décalage
   entre les éléments se justifie. Les retards restent courts — au-delà, on
   attend la page au lieu de la lire. */
.hero-updated, .hero h1, .hero-sub, .hero-cta, .hero-visuel, .hero-stats {
  animation: apparition-bas var(--t-entree) var(--e-sortie) both;
}
.hero h1 { animation-delay: 40ms; }
.hero-sub { animation-delay: 80ms; }
.hero-cta { animation-delay: 120ms; }
.hero-visuel { animation-delay: 160ms; }
.hero-stats { animation-delay: 200ms; }

/* Le panneau déplié : il vient de sous sa ligne, ce qui dit d'où il sort. On
   n'anime pas la hauteur — la ligne apparaît d'un coup, son CONTENU glisse. */
.detail-row .detail-enveloppe > * { animation: apparition-bas var(--t-ui) var(--e-sortie) both; }
.detail-row .detail-lien { animation-delay: 60ms; }

/* Le survol ne s'anime que là où il existe vraiment : au doigt, il n'y a pas de
   survol, et la transition ne fait que retarder le retour visuel du toucher. */
@media (hover: hover) and (pointer: fine) {
  .cmp-table tbody tr { transition: background var(--t-micro) var(--e-sortie); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-updated, .hero h1, .hero-sub, .hero-cta, .hero-visuel, .hero-stats,
  .detail-row .detail-enveloppe > * { animation: none; }
  .cmp-table tbody tr { transition: none; }
}

/* ── Priorité des colonnes ──────────────────────────────────────────────────
   Mesuré à 390 px : la colonne Prix était à 0 % visible au repos, sur un site
   dont c'est la raison d'être. Le premier geste demandé au visiteur mobile
   n'était pas de comparer, c'était de chercher.

   Les colonnes se retirent donc par ordre d'utilité décroissante plutôt que de
   toutes se serrer. Rien n'est perdu : le panneau déplié porte ce qui sort, et
   la fiche de la firme porte tout. Les classes sont EXPLICITES et non
   positionnelles — c'est une position supposée (`td:first-child`) qui a figé
   une colonne vide pendant tout un commit. */
/* 96rem = 1 536 px : mesuré, c'est en dessous de cette largeur de FENÊTRE que
   les neuf colonnes cessent de tenir à côté des filtres. Le seuil était à
   64rem, ce qui laissait une bande de 1 024 à 1 536 px — la plupart des
   portables — où rien ne se retirait et où le tableau débordait quand même de
   224 px. Le seuil suit maintenant la mesure et non une valeur ronde. */
@media (max-width: 96rem) {
  .cmp-table .col-p3 { display: none; }
}
@media (max-width: 46rem) {
  .cmp-table .col-p2 { display: none; }
  table.cmp-table { min-width: 0; }
  /* La colonne figée prenait 302 px sur 390 : elle laissait 0 px au prix. Elle
     est bornée, le nom de la firme passe à la ligne plutôt que d'imposer sa
     largeur, et le nom de la formule se coupe — il reste lisible en entier dans
     le panneau déplié, où il n'a plus à tenir sur une ligne. */
  .cmp-table tbody th[scope="row"] { max-width: 11.5rem; }
  .firm-cell a { white-space: normal; }
  .firm-plan { max-width: 7.5rem; }
  .cmp-table th, .cmp-table td { padding-left: 0.55rem; padding-right: 0.55rem; }
}

/* Logo de plateforme. Ces fichiers sont dessinés pour le site de LEUR éditeur,
   et une seule des vingt-six marques disparaît vraiment sur notre fond sombre :
   AgenaTrader, dont aucun pixel n'atteint 1,2:1. Noter que l'img n'a ici AUCUN
   padding, contrairement à .firm-logo : sur une image opaque, la plaque posée
   derrière est donc entièrement recouverte. C'est ce qui a révélé que sept des
   neuf plaques d'alors ne peignaient rien. */
.plat-logo {
  --tuile: transparent;
  width: 1.15rem; height: 1.15rem; flex: none; border-radius: 5px;
  display: grid; place-items: center; overflow: hidden;
  background: var(--tuile);
  /* Ombre INTERNE et non `border` : une bordure s'ajouterait à la boîte, et ces
     18,4 px sont mesurés — la colonne en aligne trois par ligne. 0,28 est
     l'intensité que cette palette donne déjà à ses bords de pastille.
     Il faut dire ce que ce contour ne fait pas : à 18,4 px il borne la pastille
     sans jamais la remplir, et l'effet reste discret là où la tuile de
     .firm-logo, à 25,6 px, se voit franchement. C'est le prix de ne pas toucher
     au fond — et à cette taille, aucune autre option ne laissait les vingt-six
     marques où elles sont. */
  box-shadow: inset 0 0 0 1px rgb(var(--accent-rgb) / .28);
}
.plat-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* Les marques de plateforme dans le panneau déplié : elles s'enroulent sur
   plusieurs lignes au lieu d'allonger la carte, et restent toutes visibles —
   le panneau n'a pas la contrainte de largeur de la colonne, qui en coupe à
   trois. */
.plat-list-detail { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.1rem; }
.plat-list-detail .plat-logo { width: 1.3rem; height: 1.3rem; }

/* ── PLAQUES ────────────────────────────────────────────────────────────────
   Un logo est dessiné pour le site de son éditeur, pas pour le nôtre, et
   quelques-uns disparaîtraient sur l'un de nos deux fonds. Ceux-là seulement
   reçoivent une plaque, et seulement dans le thème où ils s'effacent.

   IL N'EN RESTE QUE TROIS SUR TRENTE-NEUF, et le tri s'est fait sur une mesure
   qui a dû être refaite. La règle était la luminance moyenne de l'encre, sous
   un seuil brut : elle condamnait neuf logos. Elle avait deux angles morts,
   constatés sur les fichiers eux-mêmes. Aveugle à la TEINTE, elle plaquait
   cqg.ico, rouge vif, comme s'il était noir. Aveugle à la RÉPARTITION, elle
   plaquait huit vignettes OPAQUES à monogramme clair — apex, tradeday, yrm,
   topstep, fn, phidias, projectx, volfix — dont la moyenne est basse mais qui
   portent toutes des pixels quasi blancs.

   Sur ces huit, la plaque ne pouvait de toute façon rien faire, et c'est ce qui
   a mis la puce à l'oreille : une image OPAQUE couvre le fond qu'on met
   derrière elle. Pour .plat-logo, dont l'img n'a aucun padding, la plaque était
   invisible à cent pour cent — mesuré dans le navigateur, boîte 18,4 px et
   image 18,4 px. Pour .firm-logo, dont l'img a 2 px de padding, elle ne
   paraissait qu'en anneau de 2 px : une bordure décorative, pas un fond.

   La règle est maintenant le CONTRASTE WCAG du pixel qui tranche le mieux sur
   chaque fond réel, avec le seuil 3:1 de la règle 1.4.11. Un logo n'a besoin
   d'une plaque que si AUCUNE de ses parties n'atteint ce seuil. Le seuil tombe
   dans un vide mesuré — AgenaTrader 1,18, CQG 2,65, puis 4,00 pour le suivant —
   donc il ne coupe pas au ras d'un cas limite. tpt.svg s'y ajoute pour une
   raison différente : sans aplat de fond, sa forme DOMINANTE est un quasi noir
   à 1,05:1, et un accent orange qui, lui, ressort ne suffit pas à sauver le
   corps du logo.

   Un test recalcule tout cela depuis les FICHIERS et refuse un classement qui
   ne correspond plus — remplacer un logo par une version plus claire ne peut
   donc pas le rendre invisible en silence.

   TPT N'EN A PLUS BESOIN : la marque publie ses DEUX encres, et poser la bonne
   vaut mieux que poser la sombre sur un carré blanc. Voir le bloc suivant. */
.firm-logo.ton-sombre, .plat-logo.ton-sombre { background: #FFF; }
.firm-logo.ton-clair,  .plat-logo.ton-clair  { background: #12171B; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .firm-logo.ton-sombre,
  :root:not([data-theme="dark"]) .plat-logo.ton-sombre { background: var(--tuile); }
}
:root[data-theme="light"] .firm-logo.ton-sombre,
:root[data-theme="light"] .plat-logo.ton-sombre { background: var(--tuile); }
:root[data-theme="dark"] .firm-logo.ton-clair,
:root[data-theme="dark"] .plat-logo.ton-clair { background: var(--tuile); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .firm-logo.ton-clair,
  :root:not([data-theme="light"]) .plat-logo.ton-clair { background: var(--tuile); }
}

/* ── DEUX ENCRES ────────────────────────────────────────────────────────────
   Quand la marque publie elle-même sa version claire, le HTML porte LES DEUX
   images et c'est ici qu'on choisit. Même raison que --mark-dark/--mark-light
   pour les monogrammes : le JS ne connaît pas le thème et n'a pas à se
   réexécuter quand le visiteur en change — un rendu statique ne le pourrait
   pas de toute façon.

   Le défaut est le thème SOMBRE, comme partout dans cette feuille, d'où
   `.sur-sombre` visible sans condition. Les trois états du visiteur sont
   couverts : préférence système, choix explicite clair, choix explicite sombre.
   Ce dernier n'est pas décoratif — sans lui, un visiteur qui force le thème
   sombre sur un système clair verrait l'encre noire sur notre fond noir.

   `<picture>` aurait été plus court et aurait été FAUX : son `media` ne lit que
   prefers-color-scheme, donc il ignore le sélecteur de thème du site. */
.firm-logo img.sur-clair { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .firm-logo img.sur-clair { display: block; }
  :root:not([data-theme="dark"]) .firm-logo img.sur-sombre { display: none; }
}
:root[data-theme="light"] .firm-logo img.sur-clair { display: block; }
:root[data-theme="light"] .firm-logo img.sur-sombre { display: none; }
:root[data-theme="dark"] .firm-logo img.sur-clair { display: none; }
:root[data-theme="dark"] .firm-logo img.sur-sombre { display: block; }

/* LA LISTE DES FIRMES RENDUE PAR LE SERVEUR (build/pages.mjs, `rosterFirmes`).
   Elle existe pour qu'un lecteur sans JavaScript — un agent de recherche
   générative, par exemple — trouve les treize firmes et leurs liens sur les
   quatre pages qui portent les mots-clés du site. Deux colonnes dès qu'il y a
   la place : treize lignes en colonne unique poussent le reste de la page loin
   sous la ligne de flottaison, alors que le tableau interactif est juste
   au-dessus et fait déjà le travail pour qui a JavaScript. */
.roster {
  columns: 2;
  column-gap: 2rem;
  margin: 0.6rem 0 0;
  padding-left: 1.1rem;
}
.roster li { break-inside: avoid; margin: 0.18rem 0; }
@media (max-width: 560px) { .roster { columns: 1; } }
