/* ==========================================================================
   Bindex Web — feuille de style.

   Source de vérité : `foundations/tokens.html` du projet Claude Design
   « Bindex — Design System ». Les cinq règles y sont énoncées ; elles sont
   appliquées ici telles quelles :

     1. Interactif = OR. Un seul accent pour tout ce qui se touche.
     2. Chiffres = MONO tabulaire. Le serif Fraunces est réservé au héros.
     3. « — » honnête plutôt qu'un chiffre faux ; aucun repli FR↔EU.
     4. Pills explicites plutôt que routage malin.
     5. Glass plat : aucun flou dans la couche contenu.

   DEUX DIVERGENCES ASSUMÉES, propres au grand écran, et pas des dérives :

   a) Le héros est à 40px, non 30px. Le canon dimensionne pour un écran de
      390px de large ; à 1600px, 30px dans un bandeau de cinq colonnes ne se
      lit plus comme un héros. Tout le reste de l'échelle est conservé.
   b) Les titres de section sont à 17px, non 19px, pour la densité d'un
      tableau de bord bureau. La graisse (Fraunces 500) est celle du canon.

   Aucun hex hors du bloc :root — même discipline que le cliquet
   `hexRatchet` du mobile (plafond 204, aucun hex en dur hors thème).
   ========================================================================== */

/* ---------------------------------------------------------------------------
   TOKENS — recopiés de `foundations/tokens.html` (projet Claude Design
   « Bindex — Design System »). Cette bloc est la SEULE source de couleur du
   projet : aucun hex ne doit exister ailleurs, ni en CSS ni en JS (discipline
   du cliquet `hexRatchet` côté mobile, appliquée ici à l'identique).
   --------------------------------------------------------------------------- */
:root {
  /* Le navigateur doit savoir dans quel monde il peint : barres de
     défilement natives, contrôles de formulaire, `<select>` système. */
  color-scheme: dark;

  /* Rampe warm — élévation par TEINTE, jamais par ombre (HIG dark mode). */
  --bg: #08070d;            /* bgWarm       */
  --bg-soft: #0e0d14;       /* bgWarmSoft   */
  --surface: #15131f;       /* surfaceWarm  */
  --surface2: #1b1929;      /* surfaceWarm2 */
  --surface3: #272535;      /* surfaceWarm3 */
  --border: #251f37;        /* borderWarm   */
  --border-hi: #322a4a;     /* borderWarmHi */

  /* Encre */
  --text: #f4f2fa;   /* textWarm   — 18:1  */
  --text2: #b7b0cc;  /* textWarm2  — 9,6:1 */
  --text3: #89829e;  /* textWarm3  — 4,7:1 minimum, NE PAS assombrir */
  --text4: #3f3d52;  /* text4      — placeholder / désactivé */

  /* Or — la valeur et l'action. Règle 1 : l'interactif est OR. */
  --gold: #f4d582;          /* holoGold       — valeur, CTA, actif */
  --gold-strong: #ffe9a8;   /* holoGoldStrong — survol / actif */
  --gold-dim: #8a7440;      /* holoGoldDimHex */
  --ink-on-gold: #1a0f2e;   /* inkOnGold      — texte sur CTA or */

  /* Sémantiques — DONNÉES et états, jamais décoratifs. */
  --green: #6ee7b7;   /* holoGreen  — gain        */
  --red: #f26b7a;     /* holoRed    — perte, SELL */
  --violet: #a78bfa;  /* holoViolet — confiance, JAPON */
  --cyan: #7fe0f0;    /* holoCyan   — info, indices, ANGLAIS */
  --amber: #f9b872;   /* holoAmber  — WATCH, avertissement */
  --pink: #f472b6;    /* holoPink   — décoratif */

  /* Typographie — trois voix, pas quatre. Le canon ne connaît ni
     Plus Jakarta Sans ni Roboto : serif pour le héros et les titres,
     mono pour TOUS les chiffres, sans système pour le corps. */
  --font-serif: "Fraunces", "Didot", "Bodoni 72", Georgia, serif;
  --font-mono: "JetBrains Mono", "SF Mono", ui-monospace, Menlo, monospace;
  --font-sans: -apple-system, "SF Pro Text", "Inter", system-ui, sans-serif;
  --font-body: var(--font-sans);

  --rail-w: 232px;
  --radius: 10px;
  --radius-lg: 14px;

  /* Deltas bureau (foundations/bureau-deltas.html) — le canon mobile
     dimensionne pour 390 px ; appliqué tel quel à 1440 px il produit une
     application mobile étirée. Une seule cause, six conséquences. */
  --row-h: 30px;    /* 44 px est une cible du DOIGT ; au curseur la densité prime */
  --gut: 20px;      /* séparer des COLONNES demande plus qu'espacer des blocs */

  /* Grille de cartes — piloté par le sélecteur de densité. */
  --tile-w: 132px;
  --tile-gap: var(--sp-6);

  /* ---- LA SIGNATURE HOLOGRAPHIQUE -------------------------------------
     Voir foundations/signature-holo.html dans « Bindex — Bureau (web) ».

     Les jetons s'appellent holoGold, holoViolet, holoCyan depuis l'origine :
     le mot « holo » était dans le système et personne ne s'en servait. Une
     carte Pokémon, c'est du foil qui diffracte — c'est le seul motif que ce
     produit puisse revendiquer, et qu'un terminal financier générique ne
     peut pas copier.

     DISCIPLINE : trois emplois, pas un de plus. Le héros, la nappe du
     graphe, le filet d'état actif. Jamais sur du chrome, jamais derrière du
     texte de lecture, JAMAIS comme couleur de série — elle deviendrait une
     donnée et cesserait d'être une signature. L'interactif reste l'or plat. */
  --holo: linear-gradient(104deg, #f4d582 0%, #f9b872 22%, #f472b6 44%,
                          #a78bfa 66%, #7fe0f0 86%, #6ee7b7 100%);

  /* La lumière vient du HAUT : un filet clair sur l'arête supérieure décolle
     la surface du fond. C'est ce qui sépare une interface sombre CRAFTÉE
     d'un aplat gris — et c'était le vrai défaut du premier jet. */
  --lift: inset 0 1px 0 rgba(255, 255, 255, 0.045);
  /* Un cran au-dessus, pour les surfaces qui FLOTTENT (panneau, palette,
     popover). Au-delà de .10 d'alpha ça ne lit plus comme de la lumière mais
     comme un biseau de plastique. */
  --lift-hi: inset 0 1px 0 rgba(255, 255, 255, 0.07);

  /* =====================================================================
     JETONS DÉRIVÉS — la raison d'être : le THÈME CLAIR (2026-08-14).

     Avant lui, une trentaine de `rgba(8,7,13,…)`, `rgba(244,213,130,…)` et
     ombres noires vivaient EN DUR dans les composants. En sombre elles
     étaient justes ; recolorer les seuls jetons de base aurait laissé des
     voiles noirs sur fond clair et des teintes d'or invisibles. La règle
     « aucun hex hors du bloc de thème » s'étend donc aux alphas : toute
     couleur, même transparente, est un jeton d'ICI.

     Les teintes d'accent passent par `color-mix` sur le jeton de base :
     elles suivent l'or (ou le rouge) du thème SANS seconde définition. */

  /* Le dégradé du chiffre héros (background-clip: text). En clair, l'or
     pâle serait illisible : le bloc clair le redéfinit en or profond. */
  --gold-grad: linear-gradient(180deg, #fff6dc 0%, #f4d582 62%, #d9b65f 100%);

  /* Voiles d'overlay (backdrops) et pastille posée SUR une illustration. */
  --veil: rgba(8, 7, 13, 0.72);       /* palette ⌘K, feuille de position */
  --veil-soft: rgba(8, 7, 13, 0.6);   /* panneau latéral, tiroir de nav  */
  --overimg: rgba(8, 7, 13, 0.84);    /* pastilles lisibles sur une carte */
  --frise-gap: rgba(8, 7, 13, 0.55);  /* le joint entre parts de frise    */
  --sheen: rgba(244, 242, 250, 0.06); /* balayage de squelette            */
  --logo-bed: rgb(255 255 255 / 0.03);/* lit neutre sous un logo de set   */

  /* Le dos de carte du repli sans image N'EST PAS du chrome : c'est un
     objet physique, sombre dans les deux thèmes — jamais redéfini. */
  --cardback: linear-gradient(150deg, #221f33, #191725 55%, #14121d);

  /* Teintes d'état — elles suivent leur jeton de base par construction. */
  --gold-a05: color-mix(in srgb, var(--gold) 5%, transparent);
  --gold-a07: color-mix(in srgb, var(--gold) 7%, transparent);
  --gold-a09: color-mix(in srgb, var(--gold) 9%, transparent);
  --gold-a28: color-mix(in srgb, var(--gold) 28%, transparent);
  --red-a06: color-mix(in srgb, var(--red) 6%, transparent);
  --red-a12: color-mix(in srgb, var(--red) 12%, transparent);
  --red-a45: color-mix(in srgb, var(--red) 45%, transparent);
  --amber-a06: color-mix(in srgb, var(--amber) 6%, transparent);

  /* Ombres — nommées par EMPLOI, parce qu'un thème clair ne peut pas se
     contenter de les éclaircir uniformément : une ombre à .8 d'alpha sur
     fond clair est un trou, pas une élévation. */
  --shadow-mark: 0 2px 10px rgba(0, 0, 0, 0.45);
  --shadow-mark-lg: 0 4px 14px rgba(0, 0, 0, 0.5);
  --shadow-menu: 0 18px 40px -12px rgba(0, 0, 0, 0.8);
  --shadow-tip: 0 10px 26px rgba(0, 0, 0, 0.55);
  --shadow-pop: 0 14px 40px rgba(0, 0, 0, 0.62);
  --shadow-modal: 0 22px 52px rgba(0, 0, 0, 0.55);
  --shadow-cmdk: 0 26px 70px rgba(0, 0, 0, 0.66);
  --shadow-card: 0 24px 48px -24px rgba(0, 0, 0, 0.75);
  --shadow-art: 0 12px 30px rgba(0, 0, 0, 0.5);
  --shadow-drawer: 0 24px 64px rgba(0, 0, 0, 0.6);
  --qty-ring: 0 0 0 3px rgba(0, 0, 0, 0.35);

  /* =====================================================================
     L'ÉCHELLE D'ESPACEMENT — une seule, et rien en dehors.

     Le défaut mesuré avant ce revamp : 27 valeurs d'espacement distinctes
     dans cette feuille (5, 7, 9, 11, 13, 15, 17, 18, 21, 22, 26, 28, 34…).
     Aucune n'était fausse isolément ; ensemble elles ne produisaient aucun
     RYTHME, et c'est le rythme — pas la valeur — qui se voit.

     Quantification : par 2 jusqu'à 12, par 4 ensuite. Une valeur hors
     échelle est un défaut, pas un réglage fin.

     L'INVARIANT DE PROXIMITÉ gouverne le choix : padding intérieur <
     écart entre frères < écart entre groupes, chaque cran au moins ×1,5.
     Concrètement 8 → 12 → 24. Quand le padding d'une carte ÉGALE l'écart
     entre deux cartes, le groupement est détruit — l'œil ne sait plus ce
     qui appartient à quoi. C'était le cas ici : panneaux à 18/20 px de
     padding, séparés de 16/20 px.
     ===================================================================== */
  --sp-1: 2px;    /* correction optique seule (alignement d'icône, pastille) */
  --sp-2: 4px;    /* icône↔libellé ; valeur↔delta */
  --sp-3: 6px;    /* padding vertical d'un contrôle dense (onglet, pastille) */
  --sp-4: 8px;    /* padding de chip ; écart entre frères SERRÉS */
  --sp-5: 12px;   /* padding d'un champ ; écart de ligne à ligne */
  --sp-6: 16px;   /* padding de carte dense ; gouttière de grille de tuiles */
  --sp-7: 20px;   /* gouttière BUREAU — valeur du canon (§3 du skill) */
  --sp-8: 24px;   /* padding intérieur d'un panneau bureau */
  --sp-9: 32px;   /* marge horizontale de page ; écart entre GROUPES */
  --sp-10: 48px;  /* séparation de section */
  --sp-11: 64px;  /* respiration de bas de page */

  /* La gouttière du canon EST un cran de l'échelle — on garde le nom
     historique, il est cité dans le skill et dans les vues. */
  --gut: var(--sp-7);

  /* =====================================================================
     LE RYTHME TYPOGRAPHIQUE — 10 crans, contre 26 avant.

     Le fichier portait 26 tailles distinctes, dont onze au demi-pixel
     (8,5 · 9,5 · 10,5 · 11,5 · 12,5 · 13,5 · 14,5 · 15,5 · 16,5). Un demi-
     pixel ne se voit pas ; VINGT-SIX crans, si — ils suppriment la
     hiérarchie, parce que deux niveaux séparés par 0,5 px se lisent comme
     un seul niveau.

     La forme de la rampe est sous-linéaire en bas (11→12→13→14, ×1,08) et
     géométrique en haut (16→20→26→40, ×1,3) : c'est la forme des systèmes
     denses réels, pas un ratio unique appliqué mécaniquement.

     APPROCHE : la hiérarchie se fait par la TAILLE **et** la GRAISSE, et
     dans une table dense par la COULEUR — qui ne coûte aucune mise en page.
     Trois graisses, pas quatre : 400 corps et données, 500 libellés et
     cellules accentuées, 600 titres. **600 seulement au-dessus de 16 px** ;
     en dessous il épaissit sans hiérarchiser.
     ===================================================================== */
  --t-micro: 9px;   /* mono : tag, pastille de périmètre — RIEN d'autre */
  --t-label: 10px;  /* mono capitales : eyebrow, en-tête de colonne (canon) */
  --t-cap: 11px;    /* métadonnée, note de pied, explication */
  --t-sm: 12px;     /* cellule dense, chip, légende */
  --t-base: 13px;   /* corps par défaut */
  --t-md: 14px;     /* corps accentué, bulle de conversation */
  --t-lg: 16px;     /* titre de panneau (serif) */
  --t-h: 18px;      /* mot-symbole du rail, valeur de bandeau */
  --t-xl: 20px;     /* titre de panneau latéral (serif) */
  --t-2xl: 26px;    /* titre de page (serif, canon) */
  --t-hero: 40px;   /* LE chiffre héros — un seul par écran (canon) */

  /* Interlignes : en PX pour que les lignes tombent sur la grille de 4 ;
     unitless seulement pour la prose qui se LIT vraiment. */
  --lh-tight: 1.15;   /* héros et gros titres */
  --lh-ui: 1.35;      /* libellés, cellules, contrôles */
  --lh-body: 1.55;    /* phrases d'explication — c'est de la lecture */

  /* Interlettrage : la règle est qu'il vaut ZÉRO à 14 px et en dessous.
     Du tracking négatif sur du petit texte est LA signature de l'amateur —
     il ne resserre pas, il abîme. Au-dessus, il se creuse avec la taille.
     Les capitales, elles, prennent du POSITIF : sans ça elles s'agglutinent. */
  --tr-caps: 0.14em;   /* mono capitales 9-10 px (canon) */
  --tr-caps-sm: 0.08em;/* mono capitales au-delà de 10 px */
  --tr-lg: -0.01em;    /* 16-20 px */
  --tr-2xl: -0.02em;   /* 26 px */
  --tr-hero: -0.028em; /* 40 px */

  /* =====================================================================
     LE MOUVEMENT — durées et courbes nommées.

     Avant : 90 · 110 · 111 · 120 · 160 · 190 · 200 · 380 ms au hasard, et
     la courbe `ease` du navigateur presque partout — celle qui n'a été
     choisie par personne.

     Deux principes portent tout le reste :
       · la SORTIE fait ~70 % de l'entrée. Partir doit libérer la place ;
         arriver doit se poser.
       · la durée suit la DISTANCE parcourue, plafonnée à 400 ms. Au-delà,
         un outil de productivité paraît mou.
     ===================================================================== */
  --dur-press: 80ms;      /* enfoncement */
  --dur-fast: 120ms;      /* survol : couleur, bordure, opacité */
  --dur-release: 140ms;   /* relâchement */
  --dur-in: 200ms;        /* entrée d'une couche flottante */
  --dur-out: 150ms;       /* sa sortie */
  --dur-panel: 280ms;     /* entrée du panneau latéral (grande distance) */
  --dur-panel-out: 200ms; /* sa sortie */
  --dur-view: 200ms;      /* transition de vue — fondu SEUL */

  --ease-std: cubic-bezier(0.2, 0, 0, 1);          /* usage général */
  --ease-accel: cubic-bezier(0.3, 0, 1, 1);        /* ce qui PART */
  --ease-decel: cubic-bezier(0, 0, 0, 1);          /* ce qui ARRIVE */
  --ease-emph-in: cubic-bezier(0.05, 0.7, 0.1, 1); /* entrée appuyée */
  --ease-emph-out: cubic-bezier(0.3, 0, 0.8, 0.15);/* sortie appuyée */
  /* La courbe iOS du panneau latéral, conservée : elle était déjà juste. */
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);
}

/* ===========================================================================
   LE THÈME CLAIR (2026-08-14) — une PALETTE, jamais une inversion.

   Le sombre reste le DÉFAUT et la référence : `:root` ci-dessus n'est pas
   touché. Le clair n'existe qu'au niveau des jetons — aucun composant n'est
   réécrit — et il est activé par DEUX portes :

     · `:root[data-theme="light"]` — le choix EXPLICITE de l'utilisateur
       (bouton du bandeau, persisté par lib/prefs.mjs) ;
     · `@media (prefers-color-scheme: light)` sur `:root:not([data-theme])` —
       le mode « auto », qui suit l'OS quand aucun choix n'est posé.
       (`[data-theme="dark"]` échappe à la requête : sombre explicite.)

   LES DEUX BLOCS SONT JUMEAUX, à l'octet près. Les sélecteurs ne peuvent pas
   traverser une requête de média : la duplication est le prix de la porte
   sans JavaScript. Toute retouche se fait DANS LES DEUX, sinon « auto-clair »
   et « clair choisi » divergent en silence.

   Ce qui a été DÉCIDÉ, pas seulement transposé :
     · l'élévation s'inverse : en sombre, s'élever = s'éclaircir ; en clair,
       les surfaces montent vers le blanc et le fond de page reste teinté ;
     · l'or d'interaction devient un or PROFOND (#8a6a12) — l'or pâle du
       sombre mesure 1,4:1 sur blanc, il n'existe pas en clair. Le CTA
       s'inverse donc : fond or profond, encre claire (`--ink-on-gold`) ;
     · les six sémantiques gardent leur TEINTE (violet = Japon partout) mais
       descendent à ≥ 4,5:1 sur `--surface` — mesuré, pas estimé ;
     · les ombres passent d'un noir épais à une encre violette diluée : sur
       fond clair, une ombre à .6 d'alpha est un trou, pas une élévation ;
     · `--cardback` (le dos de carte) n'est PAS redéfini : un objet physique
       ne change pas de couleur avec le thème.
   =========================================================================== */
:root[data-theme="light"] {
  color-scheme: light;

  /* Rampe claire — le fond de page garde la teinte warm-violette de la
     marque ; les surfaces montent vers le blanc (élévation inversée). */
  --bg: #f1eff6;
  --bg-soft: #e9e6f1;
  --surface: #faf9fd;
  --surface2: #efedf6;
  --surface3: #e3e0ed;
  --border: #dcd7e7;
  --border-hi: #c7c0d9;

  /* Encre — mêmes rapports de contraste que la rampe sombre :
     16:1 · 8:1 · 4,6:1 (plancher, NE PAS éclaircir) · placeholder. */
  --text: #1c1726;
  --text2: #4e4762;
  --text3: #655e7c;
  --text4: #b1abc4;

  /* Or profond — 4,8:1 sur surface. Le CTA s'inverse : or foncé, encre
     claire. `--gold-strong` (le survol) FONCE au lieu d'éclaircir. */
  --gold: #8a6a12;
  --gold-strong: #6f550c;
  --gold-dim: #8f741f;
  --ink-on-gold: #fff8e6;

  /* Sémantiques — même teinte, contraste ≥ 4,5:1 sur surface. */
  --green: #0e7a4c;
  --red: #c22c42;
  --violet: #6d4ec8;
  --cyan: #0c7288;
  --amber: #a4590c;
  --pink: #b21e63;

  /* Le héros en or PROFOND : le dégradé pâle du sombre disparaît sur blanc. */
  --gold-grad: linear-gradient(180deg, #a87f16 0%, #8a6a12 55%, #6b510d 100%);

  /* La lumière du haut devient un filet blanc franc — sur des surfaces
     claires il se fond, il ne peut pas gêner. */
  --lift: inset 0 1px 0 rgba(255, 255, 255, 0.7);
  --lift-hi: inset 0 1px 0 rgba(255, 255, 255, 0.9);

  /* Voiles et pastilles sur image : le voile reste une encre (assombrir ce
     qui est derrière), la pastille sur illustration devient un blanc laiteux
     — les accents du thème sont désormais FONCÉS, ils se lisent dessus. */
  --veil: rgba(28, 23, 38, 0.4);
  --veil-soft: rgba(28, 23, 38, 0.32);
  --overimg: rgba(250, 249, 253, 0.9);
  --frise-gap: rgba(255, 255, 255, 0.75);
  --sheen: rgba(28, 23, 38, 0.05);
  --logo-bed: rgb(28 23 38 / 0.03);

  /* Ombres — encre violette diluée, alphas divisés par ~3. */
  --shadow-mark: 0 2px 10px rgba(28, 23, 38, 0.14);
  --shadow-mark-lg: 0 4px 14px rgba(28, 23, 38, 0.16);
  --shadow-menu: 0 18px 40px -12px rgba(28, 23, 38, 0.25);
  --shadow-tip: 0 10px 26px rgba(28, 23, 38, 0.16);
  --shadow-pop: 0 14px 40px rgba(28, 23, 38, 0.18);
  --shadow-modal: 0 22px 52px rgba(28, 23, 38, 0.2);
  --shadow-cmdk: 0 26px 70px rgba(28, 23, 38, 0.22);
  --shadow-card: 0 24px 48px -24px rgba(28, 23, 38, 0.28);
  --shadow-art: 0 12px 30px rgba(28, 23, 38, 0.22);
  --shadow-drawer: 0 24px 64px rgba(28, 23, 38, 0.24);
  --qty-ring: 0 0 0 3px rgba(255, 255, 255, 0.8);
}

/* LE BLOC JUMEAU — mêmes valeurs, porte « auto » (aucun attribut posé).
   Toute retouche se fait dans les deux blocs. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;

    --bg: #f1eff6;
    --bg-soft: #e9e6f1;
    --surface: #faf9fd;
    --surface2: #efedf6;
    --surface3: #e3e0ed;
    --border: #dcd7e7;
    --border-hi: #c7c0d9;

    --text: #1c1726;
    --text2: #4e4762;
    --text3: #655e7c;
    --text4: #b1abc4;

    --gold: #8a6a12;
    --gold-strong: #6f550c;
    --gold-dim: #8f741f;
    --ink-on-gold: #fff8e6;

    --green: #0e7a4c;
    --red: #c22c42;
    --violet: #6d4ec8;
    --cyan: #0c7288;
    --amber: #a4590c;
    --pink: #b21e63;

    --gold-grad: linear-gradient(180deg, #a87f16 0%, #8a6a12 55%, #6b510d 100%);

    --lift: inset 0 1px 0 rgba(255, 255, 255, 0.7);
    --lift-hi: inset 0 1px 0 rgba(255, 255, 255, 0.9);

    --veil: rgba(28, 23, 38, 0.4);
    --veil-soft: rgba(28, 23, 38, 0.32);
    --overimg: rgba(250, 249, 253, 0.9);
    --frise-gap: rgba(255, 255, 255, 0.75);
    --sheen: rgba(28, 23, 38, 0.05);
    --logo-bed: rgb(28 23 38 / 0.03);

    --shadow-mark: 0 2px 10px rgba(28, 23, 38, 0.14);
    --shadow-mark-lg: 0 4px 14px rgba(28, 23, 38, 0.16);
    --shadow-menu: 0 18px 40px -12px rgba(28, 23, 38, 0.25);
    --shadow-tip: 0 10px 26px rgba(28, 23, 38, 0.16);
    --shadow-pop: 0 14px 40px rgba(28, 23, 38, 0.18);
    --shadow-modal: 0 22px 52px rgba(28, 23, 38, 0.2);
    --shadow-cmdk: 0 26px 70px rgba(28, 23, 38, 0.22);
    --shadow-card: 0 24px 48px -24px rgba(28, 23, 38, 0.28);
    --shadow-art: 0 12px 30px rgba(28, 23, 38, 0.22);
    --shadow-drawer: 0 24px 64px rgba(28, 23, 38, 0.24);
    --qty-ring: 0 0 0 3px rgba(255, 255, 255, 0.8);
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--t-md);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Chiffres alignés en colonne partout. */
body { font-variant-numeric: tabular-nums; }

a { color: inherit; text-decoration: none; }
/* `padding: 0` fait partie de la remise à zéro : le navigateur pose 1px 6px
   sur tout `<button>` non habillé. Ce n'est pas un cran de l'échelle, donc
   c'est une valeur que personne n'a décidée — et le contrôle de rendu n° 6
   la signalait sur six boutons de la vue Sets. */
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea, select { font: inherit; }
/* Le navigateur pose 0 1px 2px sur chaque `<option>` : encore une valeur que
   personne n'a décidée. macOS rend ces listes lui-même, donc l'effet visuel
   est nul — mais la valeur, elle, redevient un cran de l'échelle. */
option { padding: var(--sp-1) var(--sp-2); gap: var(--sp-3); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--surface3); border-radius: 6px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--border-hi); }

/* ==========================================================================
   Ossature — rail fixe à gauche (convention Koyfin / Ghostfolio / Sharesight)
   ========================================================================== */

/* La colonne du rail est peinte par la COQUILLE, pas par le rail.
   Le rail fait 100 vh (il est collant) : sur une page longue, sa couleur
   s'arrêtait net au bas du premier écran et le fond de page reprenait — une
   couture horizontale en plein milieu de la fenêtre, très visible sur le
   portefeuille. Deux dégradés figés peignent la bande et son filet sur TOUTE
   la hauteur du document, quelle qu'elle soit. */
.shell {
  display: flex;
  min-height: 100vh;
  background:
    linear-gradient(var(--border), var(--border)) left var(--rail-w) top / 1px 100% no-repeat,
    linear-gradient(var(--bg-soft), var(--bg-soft)) left top / var(--rail-w) 100% no-repeat;
}

.rail {
  width: var(--rail-w);
  flex: 0 0 var(--rail-w);
  background: var(--bg-soft);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.rail__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-7) var(--sp-6) var(--sp-6);
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
}
/* LA MARQUE — le fichier officiel, pas un dessin d'ici.
   `brand-icon.svg` porte déjà son fond sombre arrondi et sa nappe holo ; le
   CSS ne fait que le poser et l'ancrer dans le rail (bordure + lumière du
   haut). Toute retouche de couleur ici recréerait une SECONDE marque, ce
   qu'on vient précisément de supprimer. */
.rail__brand:hover .rail__mark { transform: translateY(-1px); }
.rail__mark {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 9px;
  border: 1px solid var(--border-hi);
  box-shadow: var(--lift), var(--shadow-mark);
  display: block;
  transition: transform var(--dur-fast) var(--ease-std);
}
.rail__brandtxt { min-width: 0; }
.rail__wordmark {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-h);
  letter-spacing: var(--tr-lg);
  line-height: 1.15;
  color: var(--text);
}
.rail__tag {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text3);
  margin-top: var(--sp-1);
  white-space: nowrap;
}

/* La porte de la palette a migré dans le bandeau (.topbar__search) : elle y
   voisine le relevé et l'identité, comme dans tout terminal. */

.rail__section {
  padding: var(--sp-6) var(--sp-5) var(--sp-2) var(--sp-7);
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text3);
}

.rail__nav { padding: var(--sp-3) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-1); }

.rail__link {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-radius: 8px;
  color: var(--text2);
  font-size: var(--t-md);
  font-weight: 500;
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std);
  position: relative;
}
.rail__link:hover { background: var(--surface); color: var(--text); }
.rail__link.is-active { background: var(--surface2); color: var(--text); }
/* EMPLOI 3 de la signature — le filet d'état actif. Seul endroit du chrome où
   elle est autorisée, parce qu'elle y encode un ÉTAT et non une décoration. */
.rail__link.is-active::before {
  content: "";
  position: absolute;
  left: -10px; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 17px;
  border-radius: 0 3px 3px 0;
  background: var(--holo);
}
.rail__link svg { width: 16px; height: 16px; flex: none; opacity: 0.85; }
/* L'état actif a trois voix concordantes : fond, filet holo, icône or. */
.rail__link.is-active svg { opacity: 1; color: var(--gold); }
.rail__count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--text3);
}

/* ---- sets : liens de pilier dans le segmented + vue Blocs ---- */
.segbtn { display: inline-block; padding: var(--sp-3) var(--sp-5); font-size: var(--t-base);
          color: var(--text3); text-decoration: none; border-radius: 7px; }
.segbtn:hover { color: var(--text2); }
.segbtn.is-on { background: var(--surface2); color: var(--gold); }

.sblock { margin-bottom: var(--sp-8); }
.sblock__t {
  font-family: var(--font-serif); font-size: var(--t-lg); font-weight: 600;
  margin: 0 0 var(--sp-5); display: flex; align-items: baseline; gap: var(--sp-5);
}
.sblock__n { font-family: var(--font-mono); font-size: var(--t-label); color: var(--text3);
             letter-spacing: 0.08em; }
.sgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); gap: var(--sp-5); }
.stile {
  background: var(--bg-soft); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--lift); padding: var(--sp-5); text-decoration: none; color: var(--text);
  display: flex; flex-direction: column; gap: var(--sp-3);
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.stile:hover { border-color: var(--gold-dim); transform: translateY(-1px); }
.stile__logo { height: 48px; display: grid; place-items: center; }
.stile__logo img { max-width: 100%; max-height: 46px; object-fit: contain; }
.stile__name { font-size: var(--t-base); font-weight: 600; line-height: 1.25;
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stile__meta { display: flex; justify-content: space-between; font-size: var(--t-cap); color: var(--text3); }
.stile__compl { display: flex; align-items: center; gap: var(--sp-4); font-size: var(--t-cap); color: var(--text2); }
.stile__compl .bar { flex: 1; }

/* ---- vintage JP : UN bloc d'ère (`.sblock`, même gabarit que les autres
   ères du pilier Japon — retour Nathan 2026-08-13, ce n'est pas un 4ᵉ
   marché) sous-groupé par FAMILLE (Carddass, Meiji, Topsun…) → séries.
   `.vfamily` est le niveau intermédiaire — plus petit que `.sblock__t`
   (c'est un sous-titre, pas un titre d'ère) ; la carte de série elle-même
   est plus dense que `.stile` (pas de logo, deux lignes de couverture
   réelle prix/image en plus de la possession) donc elle a sa propre
   classe. */
.vfamilies { display: flex; flex-direction: column; gap: var(--sp-8); }
.vfamily__t {
  font-size: var(--t-sm); font-weight: 600; text-transform: uppercase;
  letter-spacing: var(--tr-caps-sm); color: var(--text2);
  margin: 0 0 var(--sp-4); display: flex; align-items: baseline; gap: var(--sp-4);
}
.vfamily__n {
  font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text3);
  font-weight: 400; text-transform: none; letter-spacing: 0;
}
.vgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(236px, 1fr)); gap: var(--sp-5); }
.vseries {
  background: var(--bg-soft); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--lift); padding: var(--sp-6); text-decoration: none; color: var(--text);
  display: flex; flex-direction: column; gap: var(--sp-3);
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.vseries:hover { border-color: var(--gold-dim); transform: translateY(-1px); }
.vseries__top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-4); }
.vseries__name { font-size: var(--t-base); font-weight: 600; line-height: var(--lh-ui); }
.vseries__year { font-size: var(--t-cap); color: var(--text3); flex: none; }
.vseries__meta { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); font-size: var(--t-cap); }
.vseries__q {
  color: var(--text3); font-size: var(--t-micro); text-transform: uppercase;
  letter-spacing: var(--tr-caps-sm); border: 1px solid var(--border-hi); border-radius: 4px;
  padding: 1px var(--sp-2); white-space: nowrap; flex: none;
}
.vseries__cov { display: flex; justify-content: space-between; font-size: var(--t-cap); color: var(--text2); }
.vseries__owned { display: flex; align-items: center; gap: var(--sp-4); font-size: var(--t-cap); color: var(--text2); }
.vseries__owned .bar { flex: 1; }

/* Grille de pièces LECTURE SEULE (fiche de série vintage) : pas d'ajout ni
   d'édition depuis le web pour ce pilier — le curseur en pointeur de
   `.tile` mentirait sur ce qui se passe au clic (règle « ce qui se clique
   le dit au repos »). Le survol reste actif (popover d'information). */
.grid--readonly .tile { cursor: default; }

/* ---- recherche v2 : grille visuelle ---- */
.rbar { display: flex; justify-content: flex-end; margin-bottom: var(--sp-5); }
.seg--mini { display: flex; border: 1px solid var(--border-hi); border-radius: 8px; overflow: hidden; }
.seg--mini button { font-size: var(--t-sm); padding: var(--sp-2) var(--sp-5); color: var(--text3); }
.seg--mini button.is-on { background: var(--surface2); color: var(--gold); }

.rgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: var(--sp-6); }
.rcard {
  background: var(--bg-soft); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--lift); padding: var(--sp-5); cursor: pointer; position: relative;
  display: flex; flex-direction: column; gap: var(--sp-3);
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.rcard:hover { border-color: var(--gold-dim); transform: translateY(-1px); }
.rcard__art {
  aspect-ratio: 63/88; border-radius: 6px; background: var(--surface);
  display: grid; place-items: center; color: var(--text4); font-size: var(--t-cap); overflow: hidden;
}
.rcard__art img { width: 100%; height: 100%; object-fit: contain; }
.rcard__name { font-size: var(--t-base); font-weight: 600; line-height: 1.25;
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rcard__set { font-size: var(--t-cap); color: var(--text3);
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rcard__foot { display: flex; align-items: baseline; gap: var(--sp-3); margin-top: auto; }
.rcard__price { font-size: var(--t-base); }
.rcard__foot .scope { margin-left: auto; }
.rcard__lang {
  position: absolute; top: 16px; left: 16px; z-index: 1;
  font-family: var(--font-mono); font-size: var(--t-micro); padding: var(--sp-1) var(--sp-3); border-radius: 999px;
  background: var(--overimg); border: 1px solid var(--border-hi);
}
/* la couleur d'un pilier ne change JAMAIS d'un écran à l'autre */
.rcard__lang--jp { color: var(--violet); }
.rcard__lang--fr { color: var(--gold); }
.rcard__lang--cn { color: var(--red); }
.rcard__lang--en { color: var(--cyan); }
.rcard__print {
  position: absolute; top: 16px; right: 16px; z-index: 1;
  font-family: var(--font-mono); font-size: var(--t-micro); color: var(--gold);
  border: 1px solid var(--gold-dim); border-radius: 999px; padding: var(--sp-1) var(--sp-3);
  background: var(--overimg);
}
/* possédée : le filet OR au pied — le fil du portefeuille */
.rcard--owned::after {
  content: ""; position: absolute; left: 10px; right: 10px; bottom: 5px;
  height: 2px; border-radius: 2px; background: var(--gold); opacity: 0.65;
}

/* ---- panneau détail v2 : faits, sources séparées, gradées atténuées ---- */
.sheet__facts { display: flex; gap: var(--sp-3); margin-top: var(--sp-4); flex-wrap: wrap; }
.sheet__fact {
  font-family: var(--font-mono); font-size: var(--t-label); padding: var(--sp-1) var(--sp-4);
  border: 1px solid var(--border-hi); border-radius: 999px; color: var(--text2);
}
.sheet__fact b { color: var(--gold); font-weight: 500; }
.sheet__h3sub { font-weight: 400; font-size: var(--t-cap); color: var(--text3); }

.srctabs { display: flex; gap: var(--sp-1); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-5); }
.srctab {
  font-size: var(--t-sm); padding: var(--sp-3) var(--sp-5); color: var(--text3); position: relative;
  transition: color var(--dur-fast) var(--ease-std);
}
.srctab:hover { color: var(--text2); }
.srctab.is-on { color: var(--text); }
.srctab.is-on::after {
  content: ""; position: absolute; left: 8px; right: 8px; bottom: -1px;
  height: 2px; background: var(--gold); border-radius: 2px 2px 0 0;
}
.srctab__age { font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text4); margin-left: var(--sp-2); }

.srccard { border: 1px solid var(--border); border-radius: 10px; padding: var(--sp-5) var(--sp-6); }
.srccard__head {
  display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--sp-4);
}
.srccard__name { font-size: var(--t-sm); color: var(--text2); font-weight: 600; }
.srccard__link { font-size: var(--t-cap); color: var(--gold-dim); }
.srccard__link:hover { color: var(--gold); }
.kv__n { font-family: var(--font-mono); font-size: var(--t-label); color: var(--text4); margin-left: var(--sp-3); }

.grade { display: flex; justify-content: space-between; align-items: baseline; padding: var(--sp-2) 0; gap: var(--sp-5); }
.grade__lbl { font-size: var(--t-base); color: var(--text2); }
.grade__val { font-size: var(--t-md); margin-left: auto; }
/* `text4` est un jeton de PLACEHOLDER (~1,9:1). Cette cellule porte
   « 2 ventes — échantillon insuffisant » : c'est l'ATTÉNUATION elle-même,
   c'est-à-dire l'information qui dit à quel point croire le chiffre d'à
   côté. La rendre presque invisible enlève la seule chose qui rend le
   chiffre honnête. Retour au plancher `text3` (4,7:1), et un cran de taille
   de plus pour une phrase qui se lit vraiment. */
.grade__meta { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); line-height: var(--lh-ui); }
/* échantillon mince : un chiffre existe mais ne porte pas une tendance */
.grade--thin .grade__val { color: var(--text3); }
.grade--thin .grade__val::before { content: "~"; color: var(--text4); margin-right: var(--sp-1); }

/* Le pied du rail (identité, relevé, sortie) a MIGRÉ dans le bandeau. Ne
   restent ici que les deux pastilles d'état, que le menu de session réemploie
   telles quelles — une seule définition pour un seul objet. */
.rail__dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: var(--sp-3); }
.rail__dot--ok { background: var(--green); }
.rail__dot--off { background: var(--text4); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/* ==========================================================================
   LE BANDEAU — barre supérieure permanente

   Hauteur 52 px : au-dessus, il mange la couche de lecture sans rien dire de
   plus ; en dessous, l'avatar (28 px) et le champ de recherche n'ont plus
   leur air de rembourrage. Il est COLLANT — c'est tout l'intérêt : le relevé
   et le fil restent vrais après 2 000 px de défilement.
   ========================================================================== */

.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--sp-7);
  height: 52px;
  flex: none;
  padding: 0 var(--sp-9);
  /* Opaque, jamais translucide : la règle « pas de flou dans la couche
     contenu » vaut aussi pour ce qui la surplombe — un tableau de chiffres
     qui transparaît sous la barre se lit comme une donnée. */
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--lift);
}

.topbar__crumb {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  min-width: 0;
  overflow: hidden;
}
/* Le fil est une COORDONNÉE, pas un titre : mono capitales, comme les
   en-têtes de colonne et les eyebrows qu'il remplace. */
.topbar__trail {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
  white-space: nowrap;
}
.topbar__sep { color: var(--text4); font-size: var(--t-md); }
.topbar__title {
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--text);
  letter-spacing: var(--tr-lg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar__right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-width: 0;
}

/* ---- Le relevé du jour ----
   Un montant NU obligerait à deviner de quoi il parle : il porte donc son
   libellé à gauche et sa pastille de périmètre à droite, comme partout
   ailleurs dans le produit. */
.topbar__quote {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  box-shadow: var(--lift);
  white-space: nowrap;
}
.topbar__qlab {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
}
.topbar__qval {
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.topbar__delta {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  padding-left: var(--sp-4);
  border-left: 1px solid var(--border);
}
.topbar__dlab {
  font-size: var(--t-micro);
  color: var(--text3);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
}

/* ---- La porte de la palette ---- */
.topbar__search {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  color: var(--text3);
  font-size: var(--t-sm);
  min-width: 172px;
  transition: color var(--dur-fast) var(--ease-std), border-color var(--dur-fast) var(--ease-std);
}
.topbar__search:hover { color: var(--text); border-color: var(--gold-dim); }
.topbar__search svg { width: 14px; height: 14px; flex: none; }
.topbar__search kbd {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  color: var(--text3);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: var(--sp-1) var(--sp-2);
}

/* ---- Le commutateur de thème ----
   Un réglage d'AFFICHAGE, pas une décision sur les données : il porte donc
   la forme discrète des réglages d'affichage (canon §8 — l'or marque ce qui
   se touche, il ne hiérarchise pas les gestes). Trois états cyclés
   auto → clair → sombre ; l'icône DIT l'état courant, le `title` le nomme. */
.topbar__theme {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  color: var(--text3);
  transition: color var(--dur-fast) var(--ease-std), border-color var(--dur-fast) var(--ease-std);
}
.topbar__theme:hover { color: var(--text); border-color: var(--gold-dim); }
.topbar__theme svg { width: 15px; height: 15px; }

/* ---- Identité de session ---- */
.topbar__session { position: relative; flex: none; }
.topbar__avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--surface2);
  border: 1px solid var(--border-hi);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--gold);
  transition: border-color var(--dur-fast) var(--ease-std);
}
.topbar__avatar:hover { border-color: var(--gold-dim); }
.topbar__signin {
  font-size: var(--t-sm);
  color: var(--ink-on-gold);
  background: var(--gold);
  border-radius: 7px;
  padding: var(--sp-3) var(--sp-5);
  text-decoration: none;
  font-weight: 600;
}
.topbar__signin:hover { background: var(--gold-strong); }

.usermenu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 244px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  box-shadow: var(--lift), var(--shadow-menu);
  padding: var(--sp-4);
  z-index: 70;
}
.usermenu[hidden] { display: none; }
.usermenu__id { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-3) var(--sp-4) var(--sp-5); }
.usermenu__avatar {
  width: 32px; height: 32px; flex: none; border-radius: 50%;
  background: var(--surface2); border: 1px solid var(--border-hi);
  display: grid; place-items: center;
  font-family: var(--font-mono); font-size: var(--t-sm); color: var(--gold);
}
.usermenu__who { min-width: 0; }
.usermenu__mail {
  font-size: var(--t-sm); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.usermenu__meta { font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text3); margin-top: var(--sp-1); }
.usermenu__sep { height: 1px; background: var(--border); margin: var(--sp-2) 0; }
.usermenu__item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--sp-4) var(--sp-4);
  border-radius: 7px;
  font-size: var(--t-base);
  color: var(--text2);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std);
}
.usermenu__item:hover { background: var(--surface2); color: var(--text); }
.usermenu__item--danger:hover { color: var(--red); }

/* Sous 1200 px, le libellé de la recherche part avant le relevé : c'est la
   seule des trois données du bandeau dont l'icône suffit. */
@media (max-width: 1200px) {
  .topbar__searchtxt { display: none; }
  .topbar__search { min-width: 0; }
}

/* ==========================================================================
   Couche de page
   ========================================================================== */

.page { padding: var(--sp-8) var(--sp-9) var(--sp-11); max-width: 1720px; width: 100%; }

/* La métadonnée de page — ce qui reste de l'ancien en-tête : « relevé du
   12/08 », « 40 sur 1 133 résultats », « calculé le … ». Une ligne discrète
   au-dessus du contenu, alignée à droite comme dans l'en-tête d'origine, et
   non un bloc de 90 px pour porter un titre que la barre dit déjà. */
.page__meta {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--sp-5);
  min-height: 22px;
  margin-bottom: var(--sp-6);
}

/* Le fondu d'entrée d'une vue. Opacité SEULE — voir la raison dans app.mjs.
   Sous mouvement réduit, la règle générale ramène la durée à 0,01 ms : le
   changement reste instantané, ce qui est exactement le comportement voulu. */
.page.is-entering { animation: view-in var(--dur-view) var(--ease-decel) both; }
@keyframes view-in { from { opacity: 0; } to { opacity: 1; } }

/* L'en-tête de page (eyebrow + titre 26 px serif) a été ABSORBÉ par le
   bandeau — voir topbar.mjs. `.eyebrow` reste : c'est un style de LIBELLÉ,
   employé par le panneau de forage et la feuille de carte, pas seulement par
   l'ancien en-tête. */

/* Eyebrow — 10px, .14em, capitales, textWarm3. Spécifié au canon. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text3);
}

/* L'AFFIRMATION de la page — ce qui remplace le titre-étiquette.
   Elle n'est légitime que si elle CONCLUT quelque chose (« Le marché est
   avide, mais les indices reculent ») ; répéter le nom de la vue ici
   reproduirait exactement le défaut qu'on vient de retirer. */
.page__claim {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-2xl);
  letter-spacing: var(--tr-2xl);
  line-height: var(--lh-tight);
  margin: 0 0 var(--sp-6);
  color: var(--text);
  max-width: 46ch;
}
.page__claim--err { color: var(--text); }

/* Le nombre héros : UN SEUL par écran, serif, celui qui répond à la question.
   Fraunces 600 / −.02em (canon) ; 40px au lieu de 30px — voir l'en-tête. */
.hero-value {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-hero);
  line-height: 1.06;
  letter-spacing: var(--tr-hero);
  color: var(--gold);
  display: block;
}
.hero-value--ink { color: var(--text); }
.hero-value sub {
  /* Le dénominateur reste un CHIFFRE : mono, comme tous les autres. */
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text3);
  vertical-align: baseline;
  bottom: 0;
}

/* ==========================================================================
   Bandeau FILETÉ — une grandeur et ses qualificatifs, pas six choses égales.

   Six cartes identiques côte à côte se lisent comme six éléments de MÊME
   rang. Des filets verticaux dans une seule surface rétablissent la
   hiérarchie : un patrimoine, et ce qui le qualifie.
   ========================================================================== */

/* ---- LE HÉROS — un objet à part, pas la première case d'une grille ----

   Le patrimoine occupait la cellule 1 d'un bandeau de six à 1,5 fr : il était
   un peu plus large que ses voisins, ce qui n'est pas dominer. Il a maintenant
   sa surface, sa nappe (emploi 1 de la signature) et son affirmation ; les
   cinq qualificatifs passent EN DESSOUS, dans une bande dont la taille dit
   qu'ils qualifient. Trois échelles au lieu d'une seule (canon §5). */
.hero {
  position: relative;
  overflow: hidden;
  display: grid;
  /* Le total et sa décomposition, côte à côte : la question qui suit
     immédiatement « combien » est « de quoi ». 340 px suffisent aux quatre
     poches et laissent la lecture à l'affirmation. */
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--sp-9);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  border-bottom: 0;
  padding: var(--sp-8);
  box-shadow: var(--lift-hi);
}
.hero__main { min-width: 0; }
.hero__pockets {
  min-width: 0;
  align-self: center;
  padding-left: var(--sp-8);
  border-left: 1px solid var(--border);
}
.hero__pockets .stackbar { margin-top: var(--sp-5); }
.hero__pockets .legend { margin-top: var(--sp-5); }

@media (max-width: 1100px) {
  .hero { grid-template-columns: minmax(0, 1fr); gap: var(--sp-7); }
  .hero__pockets { padding-left: 0; border-left: 0; border-top: 1px solid var(--border); padding-top: var(--sp-6); }
}
/* EMPLOI 1 de la signature — la nappe SOUS le chiffre : 13 % d'opacité,
   floutée à 38 px, arrêtée avant le bas. Une lueur, pas un fond coloré. */
.hero::before {
  content: "";
  position: absolute;
  inset: -60% 30% 30% -10%;
  background: var(--holo);
  opacity: 0.13;
  filter: blur(38px);
  pointer-events: none;
}
.hero > *, .hero__main > *, .hero__pockets > * { position: relative; }
.hero__k {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
}
/* Fraunces à 40 px avec son dégradé vertical : c'est le seul chiffre de
   l'écran qui répond à la question posée. */
.hero__v {
  font-family: var(--font-serif);
  font-size: var(--t-hero);
  font-weight: 600;
  letter-spacing: var(--tr-hero);
  line-height: 1.04;
  margin-top: var(--sp-3);
  background: var(--gold-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* Le compteur remplace le texte à chaque image : sans largeur minimale
     réservée, la ligne qui suit sauterait de quelques pixels pendant
     l'animation. */
  min-height: 44px;
}
/* Le héros hors session : le « — » AVOUE, il ne chante pas. Même recette que
   `.setband__v--none` — mono, encre text3, sans le dégradé d'or : un dash
   peint en or aurait l'air d'une valeur. La min-height du héros est gardée,
   l'état vide occupe la place de l'objet qu'il remplace (§9 ter). */
.hero__v--none {
  font-family: var(--font-mono);
  font-size: var(--t-xl);
  font-weight: 500;
  letter-spacing: var(--tr-lg);
  background: none;
  -webkit-text-fill-color: var(--text3);
  color: var(--text3);
}
.hero__s { font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-3); }
/* L'affirmation — c'est de la LECTURE, donc interligne de lecture, largeur
   de lecture, et un filet qui la sépare des chiffres qui la précèdent. */
.hero__claim {
  margin: var(--sp-7) 0 0;
  padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  max-width: 68ch;
  font-size: var(--t-md);
  line-height: var(--lh-body);
  color: var(--text2);
}
.hero__claim b { color: var(--text); font-weight: 600; }
.hero__claim b.pos { color: var(--green); }
.hero__claim b.neg { color: var(--red); }
/* Une entité citée dans l'affirmation S'OUVRE — même signal au repos que
   `.sheet__illus` : le filet pointillé or dit « ça se clique » sans un mot. */
.hero__claim a { border-bottom: 1px dotted var(--gold-dim); transition: color var(--dur-fast) var(--ease-std); }
.hero__claim a:hover { color: var(--gold-strong); }

.strip {
  display: grid;
  grid-template-columns: 1.5fr repeat(5, 1fr);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: var(--gut);
  box-shadow: var(--lift);
}
/* La bande de qualificatifs se raccorde AU héros : elle en est la suite,
   pas un second objet. D'où le haut carré et l'absence de filet partagé. */
.strip--qual {
  grid-template-columns: repeat(5, 1fr);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  box-shadow: none;
}
.strip--qual .strip__cell:first-child { background: transparent; }
.strip__cell { padding: var(--sp-5) var(--sp-6); border-left: 1px solid var(--border); min-width: 0; }
/* EMPLOI 1 de la signature — la cellule héros.
   La nappe est SOUS le chiffre, à 13 % d'opacité, floutée à 38 px et arrêtée
   avant le bas : c'est une lueur, pas un fond coloré. */
.strip__cell:first-child {
  border-left: 0;
  background: var(--surface2);
  position: relative;
  overflow: hidden;
}
.strip__cell--hero::before {
  content: "";
  position: absolute;
  inset: -50% -15% 45% -15%;
  background: var(--holo);
  opacity: 0.13;
  filter: blur(38px);
  pointer-events: none;
}
.strip__cell--hero .strip__k,
.strip__cell--hero .strip__s { position: relative; }
.strip__k {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text3);
  display: flex; align-items: center; gap: var(--sp-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.strip__v {
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  font-weight: 600;
  margin-top: var(--sp-3);
  color: var(--text);
}
/* Héros à 31 px : il doit dominer son VOISINAGE, pas la page — à 1440 px il
   a des voisins, ce que le canon mobile ne prévoit pas. */
/* Fraunces à 40 px avec un dégradé vertical. La police a du caractère : elle
   n'en montrait aucun à 31 px en aplat d'or. C'est ICI qu'elle doit chanter —
   c'est le seul chiffre de l'écran qui répond à la question posée. */
.strip__cell--hero .strip__v {
  position: relative;
  font-family: var(--font-serif);
  font-size: var(--t-hero);
  font-weight: 600;
  letter-spacing: -0.028em;
  line-height: 1.04;
  background: var(--gold-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.strip__s { font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-2); }
/* Une cellule qui cite un INDICE est un lien vers sa fiche : même survol
   que les cellules d'indices du Marché (.sm__cell), même anneau interne. */
a.strip__cell { display: block; transition: background var(--dur-fast) var(--ease-std); }
a.strip__cell:hover { background: var(--surface2); }

/* Marque d'information — le bureau a la place d'expliquer une métrique. */
.info {
  width: 12px; height: 12px; border-radius: 50%;
  border: 1px solid var(--text4); color: var(--text3);
  font-size: var(--t-micro); font-family: var(--font-sans);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: help; flex: none;
}

/* Un panneau qui doit tenir la HAUTEUR de sa rangée. `.cols` aligne ses
   éléments sur le haut (`align-items: start`) — c'est juste dans le cas
   général : deux panneaux de contenus différents ne s'étirent pas à vide.
   Mais quand le panneau PORTE l'objet principal de l'écran (le graphe), son
   effondrement laisse un trou visible à côté du panneau voisin. Il s'étire
   alors, et c'est la zone de graphe qui absorbe la hauteur. */
.panel--fill { align-self: stretch; display: flex; flex-direction: column; }
/* La zone de graphe grandit et CENTRE ce qu'elle contient — un état vide y
   est composé comme un état plein, à la même place et dans la même boîte. */
.chartbox {
  flex: 1;
  display: flex; align-items: center; justify-content: center;
  min-height: 236px;
  padding: var(--sp-6);
}

/* ---- Rail de risque : une métrique par ligne, avec sa raison ---- */
.metric { padding: var(--sp-5) var(--sp-6); border-bottom: 1px solid var(--border); }
/* La piste qui lèverait la métrique — voir `unlock()` dans dashboard.mjs pour
   la raison. Un filet à gauche la sépare de la RAISON qui précède : ce sont
   deux informations différentes (pourquoi c'est absent / comment l'obtenir). */
.metric__unlock {
  margin-top: var(--sp-3);
  padding-left: var(--sp-4);
  border-left: 2px solid var(--border-hi);
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: var(--lh-ui);
}
.metric__unlock b {
  display: block;
  margin-bottom: var(--sp-1);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: var(--tr-caps-sm);
  text-transform: uppercase;
  color: var(--text2);
}
.metric:last-child { border-bottom: 0; }
.metric__top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-5); }
.metric__lab { font-size: var(--t-sm); color: var(--text2); }
.metric__val { font-size: var(--t-md); font-weight: 600; font-family: var(--font-mono); }
.metric__note { font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-2);
                line-height: var(--lh-body); text-wrap: pretty; }
.metric__bar { height: 3px; background: var(--border); border-radius: 2px; margin-top: var(--sp-4); display: flex; overflow: hidden; }
.metric__bar i { display: block; height: 100%; }

/* ---- Barre de contribution SIGNÉE, ancrée au zéro central ----
   Le négatif part à gauche, le positif à droite : l'œil trouve le SIGNE
   avant de lire le chiffre. */
/* Une barre signée sans son zéro VISIBLE ne se lit pas : l'œil n'a pas
   d'origine à laquelle rapporter la longueur. Le filet central résout ça pour
   un pixel. Et 78 px pour deux demi-barres donnait des slivers illisibles. */
.contrib {
  position: relative;
  width: 150px; height: 17px;
  display: flex; align-items: center; margin-left: auto;
}
.contrib::before {
  content: "";
  position: absolute;
  left: 50%; top: 1px; bottom: 1px;
  width: 1px;
  background: var(--border-hi);
}
.contrib__half { width: 50%; display: flex; }
.contrib__half--neg { justify-content: flex-end; }
.contrib i { height: 11px; border-radius: 2px; display: block; }

/* ---- Paire clé/valeur des panneaux d'analyse ----
   RAPPEL DE PORTÉE (canon §5 ter) : `.kv` est un composant de panneau
   LATÉRAL. Au-delà de ~420 px, son `space-between` met le vide entre la clé
   et sa valeur — 299 px mesurés sur les 523 px d'une colonne du tableau de
   bord — et l'œil ne relie plus les deux. Passé cette largeur, on emploie
   `.srow` juste dessous : des COLONNES alignées. */
.kv {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: var(--sp-3) 0; border-bottom: 1px solid var(--border); font-size: var(--t-sm);
}
.kv:last-child { border-bottom: 0; }
.kv__k { color: var(--text2); }
.kv__v { font-family: var(--font-mono); font-weight: 600; }

/* ---- La ligne d'un CLASSEMENT SIGNÉ : nom / barre ancrée au zéro / valeur.
   (2026-08-14.) Trois colonnes alignées, jamais un `space-between`. La barre
   n'est pas un ornement : c'est elle qui donne l'AMPLEUR relative d'un coup
   d'œil et le SIGNE avant le chiffre — l'œil compare mal des nombres et très
   bien des longueurs (canon §6). ---- */
.srow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 150px 108px;
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--t-sm);
}
.srow:last-child { border-bottom: 0; }
.srow--pair { grid-template-columns: minmax(0, 1fr) auto; }
/* L'étiquette du zéro : elle rend l'ORIGINE visible avant la première barre.
   Une barre signée sans son zéro ne se lit pas — l'œil n'a rien à quoi
   rapporter la longueur. `text4` est légitime ici : c'est une graduation,
   pas un texte à lire. */
.srow--zero {
  border-bottom: 0;
  padding-bottom: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  color: var(--text4);
}
.srow--zero span:nth-child(2) { text-align: center; }
.srow__n { color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srow__n em {
  font-style: normal; font-family: var(--font-mono); font-size: var(--t-cap);
  color: var(--text3); margin-left: var(--sp-3);
}
.srow__v { font-family: var(--font-mono); font-weight: 600; text-align: right; white-space: nowrap; }
.srow__v span { display: block; font-size: var(--t-cap); color: var(--text3); font-weight: 400; }
/* Une ligne de set FORE sa sous-population (tableau de bord, « Plus-value
   par set »). `--drill` réserve la colonne du chevron sur TOUTES les lignes
   — graduation du zéro comprise, sinon les barres perdent leur origine —
   et `--live` porte le geste. Le chevron est visible AU REPOS (règle 8 :
   une affordance qu'il faut découvrir n'existe pas). */
.srow--drill { grid-template-columns: minmax(0, 1fr) 150px 108px 12px; }
.srow--live {
  width: 100%; text-align: left; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-std);
}
.srow--live:hover { background: var(--surface2); }
.srow--live:focus-visible { outline-offset: -2px; }
.srow__chev {
  color: var(--text4); font-size: var(--t-md); line-height: 1; text-align: right;
  transition: color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.srow--live:hover .srow__chev { color: var(--gold); transform: translateX(2px); }
/* ==========================================================================
   LA PROSE N'EST PAS DE LA DONNÉE

   Un panneau contient deux registres qui ne se lisent pas de la même façon :
   des CHIFFRES (mono, tabulaires, alignés, denses) et des PHRASES qui
   expliquent d'où ils viennent. Ils avaient la même graisse, la même encre et
   surtout la même LARGEUR : une explication de 11 px courant sur les 904 px
   d'un panneau fait ~150 caractères par ligne, quand la lecture confortable
   s'arrête vers 75. L'œil perdait la ligne suivante, donc ces phrases — qui
   portent l'honnêteté du produit, « pourquoi ce chiffre est absent », « ce
   que ce total ne compte pas » — ne se lisaient tout simplement pas.

   Trois marqueurs, appliqués partout où il y a de la prose :
     · une MESURE bornée (la marque la plus forte : la prose ne remplit jamais
       la largeur d'un tableau) ;
     · l'interligne de LECTURE (--lh-body, 1,55), pas celui des contrôles ;
     · `text-wrap: pretty`, qui interdit le mot orphelin en fin de paragraphe.
   ========================================================================== */

.panel__foot {
  font-size: var(--t-cap); color: var(--text3); line-height: var(--lh-body);
  padding: var(--sp-5) var(--sp-6) var(--sp-5); border-top: 1px solid var(--border);
  max-width: 96ch;
  text-wrap: pretty;
}

/* ==========================================================================
   Bandeau de statistiques
   ========================================================================== */

.statbar {
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--sp-7);
  box-shadow: var(--lift);
}
.stat {
  padding: var(--sp-6) var(--sp-7);
  border-right: 1px solid var(--border);
  min-width: 0;
  flex: 1;
}
.stat:last-child { border-right: 0; }
.stat--hero { flex: 1.5; background: var(--surface2); }
.stat__label {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Règle 2 : TOUS les chiffres sont en mono tabulaire. Une valeur de bandeau
   est un chiffre — elle n'a donc rien à faire dans la voix du corps. */
.stat__value {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--t-h);
  letter-spacing: var(--tr-lg);
  margin-top: var(--sp-3);
  color: var(--text);
}
.stat__sub { font-size: var(--t-sm); color: var(--text3); margin-top: var(--sp-1); }

.pos { color: var(--green); }
.neg { color: var(--red); }
.muted { color: var(--text3); }
.num { font-family: var(--font-mono); }

/* Un trou de donnée assumé : « — » et pourquoi.
   Le token `text4` (#3F3D52) mesure ~1,9:1 sur la surface : c'est un token de
   PLACEHOLDER, et « — » n'est pas un placeholder — c'est une information
   («  nous n'avons pas cette cote  »). On reste donc sur `text3`, qui tient le
   plancher HIG 4,5:1, et on distingue par la graisse plutôt que par le gris.
   Le 1,17:1 a déjà été retoqué une fois sur ce projet ; on ne le refait pas. */
.nodata { color: var(--text3); font-family: var(--font-mono); }

/* ==========================================================================
   Collections nommées — la bande en tête du portefeuille
   Chips riches (nom + compte + valeur), pas de simples pastilles de filtre :
   une collection porte plus d'information qu'une facette langue/pilier.
   ========================================================================== */
.collstrip { margin-bottom: var(--sp-7); }
.collstrip__label {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.13em;
  text-transform: uppercase; margin-bottom: var(--sp-4);
}
.collstrip__chips { display: flex; gap: var(--sp-4); flex-wrap: wrap; }
.collchip {
  display: flex; align-items: baseline; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5); border-radius: 10px;
  border: 1px solid var(--border-hi); background: var(--surface);
  box-shadow: var(--lift);
  transition: border-color var(--dur-fast) var(--ease-std), background var(--dur-fast) var(--ease-std);
}
.collchip:hover { border-color: var(--gold-dim); }
.collchip.is-on {
  border-color: var(--gold); background: var(--gold-a09);
  box-shadow: var(--lift);
}
.collchip__smart { color: var(--gold); font-size: var(--t-cap); }
.collchip__name { font-size: var(--t-base); font-weight: 600; color: var(--text); }
.collchip__n {
  font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3);
}
.collchip__v { font-family: var(--font-mono); font-size: var(--t-sm); color: var(--text2); }
.collchip.is-on .collchip__v { color: var(--gold); }

/* ==========================================================================
   Barre d'outils / filtres
   ========================================================================== */

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-6);
}
.toolbar__spacer { flex: 1; }

/* Quand la barre passe à la ligne, elle doit se casser ENTRE les groupes, pas
   au milieu de l'un d'eux. Sans ça, le commutateur de densité et l'interrupteur
   d'estompage se retrouvaient orphelins sur une seconde ligne, l'un à gauche et
   l'autre à droite — deux réglages du même axe séparés par 1 000 px de vide,
   alors que la barre est justement censée dire par sa FORME ce que chaque axe
   fait (canon §8). */
.tgroup { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; min-width: 0; }
.tgroup--view { margin-left: auto; flex-wrap: nowrap; }

/* Un réglage d'AFFICHAGE porte la forme des réglages d'affichage : rectangle
   discret, même hauteur que le commutateur de densité auquel il est collé, et
   surtout PAS la pastille d'un filtre — un filtre promet de retrancher des
   lignes, l'estompage n'en retranche aucune. L'or marque ce qui se touche, il
   ne hiérarchise pas les gestes : d'où `surface2`, pas l'aplat. */
.vtog {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  height: 30px;
  padding: 0 var(--sp-5);
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  color: var(--text3);
  font-size: var(--t-sm);
  white-space: nowrap;
  flex: none;
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std);
}
.vtog svg { width: 14px; height: 14px; flex: none; }
.vtog:hover { color: var(--text); }
.vtog.is-on { background: var(--surface2); color: var(--gold); }

.chip {
  padding: var(--sp-3) var(--sp-5);
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  background: transparent;
  color: var(--text2);
  font-size: var(--t-base);
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std), border-color var(--dur-fast) var(--ease-std);
}
.chip:hover { background: var(--surface2); color: var(--text); }
.chip.is-on {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--ink-on-gold);
  font-weight: 600;
}
.chip__n { font-family: var(--font-mono); font-size: var(--t-cap); opacity: 0.72; margin-left: var(--sp-2); }

/* ==========================================================================
   TROIS AXES, TROIS FORMES

   Trois `segmented` identiques côte à côte disaient trois fois « choisis
   parmi » — sans jamais dire QUOI. La forme porte désormais la nature de
   l'action : on la reconnaît avant d'avoir lu le libellé.
   ========================================================================== */

/* 1 — LE PILIER : une NAVIGATION (elle change le corpus ET l'URL).
   Des onglets soulignés, la grammaire universelle du changement de section.
   Le soulignement prend la couleur du pilier : le violet est le Japon partout
   dans le produit, le cyan l'international/anglais, le rouge la Chine. */
.ptabs {
  display: flex;
  gap: var(--sp-8);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--sp-6);
}
.ptab {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-1) var(--sp-5);
  font-size: var(--t-md);
  font-weight: 500;
  color: var(--text3);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-std);
}
.ptab:hover { color: var(--text2); }
.ptab.is-on { color: var(--text); font-weight: 600; }
.ptab.is-on::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--tab-accent, var(--gold));
}
.ptab--intl { --tab-accent: var(--cyan); }
.ptab--jp { --tab-accent: var(--violet); }
.ptab--cn { --tab-accent: var(--red); }
.ptab__n {
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--text3);
}

/* Catalogue Scellé (2026-08-13) — pastille de LANGUE par ligne de table.
   Mêmes couleurs que les onglets de pilier (une langue garde sa couleur
   d'un écran à l'autre, règle 1 du canon) : cyan=anglais, violet=japon,
   rouge=chine. Le français (seeds 1ʳᵉ Édition, hors des trois piliers)
   prend le rose — explicitement décoratif ailleurs, donc libre ici. */
.langtag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: var(--sp-1) var(--sp-2);
  border-radius: 4px;
  border: 1px solid var(--tag-accent, var(--border-hi));
  color: var(--tag-accent, var(--text2));
}
.langtag--en { --tag-accent: var(--cyan); }
.langtag--ja { --tag-accent: var(--violet); }
.langtag--zh { --tag-accent: var(--red); }
.langtag--fr { --tag-accent: var(--pink); }

/* Une carte déjà possédée montre SA QUANTITÉ (règle 8 du canon) — jamais
   un bouton mort. Or : c'est un fait qui touche au patrimoine réel. */
.ownedqty {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--gold);
}

/* Lien sortant vers la fiche source (Cardmarket/TCGGO) — pas de fiche
   produit interne pour le scellé aujourd'hui (2026-08-13). Discret au
   repos, visible au survol de la ligne : ce qui se clique le dit AU
   REPOS (règle 8), mais un lien SECONDAIRE sur une ligne dense n'a pas
   besoin d'un accent or permanent. */
.ext-link {
  color: var(--text3);
  text-decoration: none;
  margin-left: var(--sp-2);
}
.ext-link:hover { color: var(--gold); }

/* 2 — LE FILTRE : il RETRANCHE des lignes du corpus courant.
   Des pastilles, chacune avec son effectif — c'est le nombre qui rend le
   filtre honnête : on sait ce qu'on va perdre avant de cliquer. L'état actif
   est un contour or, pas un aplat : un filtre reste une lecture, il ne
   mérite pas le poids d'un bouton d'action. */
.filters { display: flex; gap: var(--sp-4); flex-wrap: wrap; }
.fchip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-5);
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  color: var(--text2);
  font-size: var(--t-base);
  white-space: nowrap;
  transition: border-color var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std),
    background var(--dur-fast) var(--ease-std);
}
.fchip:hover { color: var(--text); border-color: var(--text4); }
.fchip.is-on {
  color: var(--gold);
  border-color: var(--gold-dim);
  background: var(--gold-a07);
  font-weight: 600;
}
.fchip__n { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); }
/* La DEVISE que la pastille fait lire (catalogue Scellé, 2026-08-14).
   Là-bas la langue n'est pas un filtre comme un autre : elle décide de la
   source de cote, donc de la devise. Le canon interdit qu'un périmètre de
   prix se lise comme un filtre sans le dire ; on garde la forme du filtre
   (c'est bien une population qu'on retranche) et on ÉCRIT la conséquence
   monétaire, en `text4` pour qu'elle qualifie sans concurrencer l'effectif. */
.fchip__cur {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  color: var(--text4);
}
.fchip.is-on .fchip__cur { color: var(--gold-dim); }
/* L'interrupteur d'estompage est un réglage d'AFFICHAGE : quand la barre
   passe à la ligne, il rejoint la droite, sous les autres réglages
   d'affichage, plutôt que de s'aligner sous les filtres de données. */
#btn-dim { margin-left: auto; }
.fchip.is-on .fchip__n { color: var(--gold-dim); }

/* 3 — LA VUE : les MÊMES lignes, dessinées autrement.
   Un commutateur à icônes, compact, collé à la table qu'il gouverne. Rien à
   lire : c'est un réglage d'affichage, pas une décision sur les données. */
.vswitch {
  display: inline-flex;
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  overflow: hidden;
  flex: none;
}
.vswitch button {
  display: grid;
  place-items: center;
  width: 32px;
  height: 30px;
  color: var(--text3);
  border-right: 1px solid var(--border-hi);
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std);
}
.vswitch button:last-child { border-right: 0; }
.vswitch button svg { width: 15px; height: 15px; }
.vswitch button:hover { color: var(--text); }
.vswitch button.is-on { background: var(--surface2); color: var(--gold); }

/* Le PÉRIMÈTRE DE PRIX porte son libellé. « FR / EU / US » nu, posé à côté
   d'un filtre, se lit comme un autre filtre — alors qu'il ne change pas la
   population affichée mais la GRANDEUR affichée, et qu'aucun repli n'existe
   entre les trois marchés. */
.mkt { display: inline-flex; align-items: center; gap: var(--sp-4); }
.mkt__k {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
}

/* Une liste déroulante n'est ni une pastille ni un bouton : elle se donne sa
   propre forme, rectangulaire, avec le chevron que le système lui dessine. */
.selector {
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text2);
  font-size: var(--t-base);
  box-shadow: var(--lift);
}

.segmented {
  display: inline-flex;
  border: 1px solid var(--border-hi);
  border-radius: 999px;
  overflow: hidden;
}
/* Un réglage d'AFFICHAGE (ordre, densité) ne mérite pas l'aplat or d'une
   décision sur les données : il se signale sans crier. */
.seg--quiet button.is-on { background: var(--surface2); color: var(--gold); font-weight: 600; }
.segmented button {
  padding: var(--sp-3) var(--sp-5);
  font-size: var(--t-base);
  color: var(--text2);
  border-right: 1px solid var(--border-hi);
}
.segmented button:last-child { border-right: 0; }
.segmented button:hover { background: var(--surface2); color: var(--text); }
.segmented button.is-on { background: var(--gold); color: var(--ink-on-gold); font-weight: 600; }

.search {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: 999px;
  padding: var(--sp-3) var(--sp-6);
  min-width: 230px;
  box-shadow: var(--lift);
}
.search input {
  background: none; border: 0; outline: 0;
  color: var(--text);
  font: inherit;
  font-size: var(--t-base);
  width: 100%;
  /* Le rembourrage appartient au `label` qui dessine le champ ; celui que le
     navigateur pose d'office (1px 2px) désalignerait le texte de son icône. */
  padding: 0;
}
.search input::placeholder { color: var(--text4); }
.search svg { width: 14px; height: 14px; color: var(--text3); flex: none; }

/* Fourchette numérique (Screener, Marché) — même langue visuelle que
   `.search`/`.selector` : surface, bordure, lift. Les chiffres restent en
   mono comme partout ailleurs (règle 2 du canon). */
.rangebox {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  padding: var(--sp-2) var(--sp-4);
  box-shadow: var(--lift);
}
.rangebox__k {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
  white-space: nowrap;
}
.rangebox__sep { color: var(--text4); }
.rangebox input {
  background: none; border: 0; outline: 0;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  width: 52px;
}
.rangebox input::placeholder { color: var(--text4); }

/* ==========================================================================
   Grille de complétion — LA raison d'être de la version desktop.
   ========================================================================== */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--tile-w), 1fr));
  gap: var(--tile-gap);
  align-items: start;
}

.tile {
  position: relative;
  border-radius: 8px;
  overflow: visible;
  cursor: pointer;
  /* pas de transform au survol : 200 tuiles qui bougent = bruit */
}

.tile__frame {
  position: relative;
  /* 5/7 = 0,71429 contre 0,71591 pour le vrai 63×88 mm : 0,23 % d'écart,
     invisible — mais toutes les tailles tombent sur des entiers
     (120→168, 169→236), donc aucune vignette ne rend sur un demi-pixel. */
  aspect-ratio: 5 / 7;
  border-radius: 7px;
  overflow: hidden;
  background: var(--surface2);
  border: 1px solid var(--border);
}

/* Le numéro de la carte sert de fond pendant le chargement du visuel.
   Une grille de 207 vignettes met plusieurs secondes à se peupler : sans
   repère, l'écran est une nappe de rectangles vides. Le numéro reste
   lisible et disparaît sous l'image dès qu'elle arrive (règle 5 : pas de
   spinner nu, un squelette qui INFORME). */
.tile__frame::before {
  content: attr(data-no);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  color: var(--text4);
  letter-spacing: 0.06em;
}
.tile__img { position: relative; z-index: 1; }

.tile__img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  /* Le visuel arrive en fondu sur le numéro qui tenait sa place — pas de
     clignotement quand 200 vignettes se peuplent au défilement. */
  opacity: 0;
  transition: opacity var(--dur-in) var(--ease-std);
}
.tile__img.is-loaded { opacity: 1; }
.tile__img.is-failed { display: none; }        /* le numéro de fond reprend la main */
.tile--missing .tile__img.is-loaded { opacity: 0.45; }
.grid--nodim .tile--missing .tile__img.is-loaded { opacity: 1; }

/* (Le voile des manquantes est appliqué sur `.tile__img.is-loaded` plus haut,
   pour ne pas entrer en conflit avec le fondu d'apparition.)

   Manquante : ESTOMPÉE, jamais désaturée.
   Un collectionneur retrouve une carte à sa COULEUR — Dracaufeu est orange.
   Passer les manquantes en niveaux de gris détruit exactement l'indice de
   recherche sur lequel repose la lecture d'une grille. C'est la convention de
   TCG Collector (opacity .5, filter: none), et c'est un réglage, pas un dogme :
   le bouton « Estomper les manquantes » le coupe.
   La bordure en tirets porte le sens en plus de l'opacité — l'information
   n'est jamais dans la seule couleur. */
.tile--missing .tile__frame { border-style: dashed; border-color: var(--border-hi); }
.tile:hover.tile--missing .tile__img.is-loaded { opacity: 0.92; }

/* Possédée : anneau or. L'or = la valeur, donc la possession. */
.tile--owned .tile__frame {
  border-color: var(--gold-dim);
  box-shadow: 0 0 0 1px var(--gold-a28);
}

.tile__qty {
  position: absolute;
  top: 5px; right: 5px;
  min-width: 19px; height: 19px;
  padding: 0 var(--sp-2);
  border-radius: 999px;
  background: var(--gold);
  color: var(--ink-on-gold);
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}

/* Le compteur de possession devient cliquable : mêmes pixels, il gagne
   seulement un curseur et une réaction au survol. Il ne change pas de
   taille en devenant un bouton — sinon la grille tressauterait. */
.tile__qty--act {
  border: 0;
  cursor: pointer;
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  font-weight: 700;
  transition: transform var(--dur-fast) var(--ease-std), box-shadow var(--dur-fast) var(--ease-std);
}
.tile__qty--act:hover { transform: scale(1.15); box-shadow: var(--qty-ring); }
.tile__qty--act:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* Le « + » d'une carte manquante : absent au repos pour ne pas noyer un set
   de 900 vignettes, révélé au survol ET au focus clavier — sans le focus,
   la fonction n'existerait tout simplement pas sans souris. */
.tile__add {
  position: absolute;
  top: 5px; right: 5px;
  width: 19px; height: 19px;
  border: 0;
  border-radius: 999px;
  background: var(--gold);
  color: var(--ink-on-gold);
  font-family: var(--font-mono);
  font-size: var(--t-base);
  font-weight: 700;
  line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  opacity: 0;
  transform: scale(.7);
  transition: opacity var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.tile:hover .tile__add,
.tile:focus-within .tile__add,
.tile__add:focus-visible { opacity: 1; transform: scale(1); }
.tile__add:hover { transform: scale(1.15); }
.tile__add:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* Sans pointeur fin (tactile), le survol n'existe pas : le bouton reste
   visible, sinon il serait inatteignable. */
@media (hover: none) {
  .tile__add { opacity: 1; transform: scale(1); }
}

.tile__meta { margin-top: var(--sp-3); padding: 0 var(--sp-1); }
.tile__no {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  color: var(--text3);
  letter-spacing: 0.04em;
}
.tile__name {
  font-size: var(--t-sm);
  color: var(--text2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: var(--sp-1);
}
.tile__price {
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--text);
  margin-top: var(--sp-1);
  font-weight: 500;
}
.tile--missing .tile__name { color: var(--text3); }

/* Densité « compacte » : la vignette seule, un set entier à l'écran. */
.grid--dense .tile__meta { display: none; }
.grid--dense { gap: var(--sp-3); }

/* Bandeau de rareté / signal discret sous la vignette */
.tile__flag {
  position: absolute;
  bottom: 5px; left: 5px;
  padding: var(--sp-1) var(--sp-2);
  border-radius: 4px;
  background: var(--overimg);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.05em;
  color: var(--text2);
  border: 1px solid var(--border);
}

/* ==========================================================================
   Popover de carte au survol
   ========================================================================== */

.pop {
  position: fixed;
  z-index: 80;
  width: 292px;
  background: var(--surface3);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  padding: var(--sp-5) var(--sp-6);
  /* Overlay flottant : l'ombre reste légitime ici (canon §Élévation). */
  box-shadow: var(--shadow-pop);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-std);
}
.pop.is-open { opacity: 1; }
.pop__name { font-family: var(--font-sans); font-weight: 600; font-size: var(--t-md); }
.pop__sub { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-1); }
.pop__row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-5); padding: var(--sp-2) 0;
  border-top: 1px solid var(--border);
  margin-top: var(--sp-4);
}
.pop__row:first-of-type { margin-top: var(--sp-5); }
.pop__k { font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.08em; color: var(--text3); text-transform: uppercase; }
.pop__v { font-family: var(--font-mono); font-size: var(--t-base); font-weight: 600; }
.pop__note { font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-4); line-height: 1.4; }

/* Marqueurs de vignette — ne signalent QUE ce qui doit alerter. */
.tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  padding: 0 var(--sp-2);
  border-radius: 3px;
  border: 1px solid currentColor;
  vertical-align: 1px;
}
.tag--est { color: var(--text3); }
.tag--stale { color: var(--amber); }
/* « récent » n'est pas une alerte : c'est l'exception favorable dans un set
   où l'ancienneté est la norme. Vert de DONNÉE, conformément à la règle 3. */
.tag--fresh { color: var(--green); }

/* Puce de périmètre : un prix ne voyage JAMAIS sans son scope. */
.scope {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.08em;
  padding: var(--sp-1) var(--sp-2);
  border-radius: 4px;
  border: 1px solid var(--border-hi);
  color: var(--text2);
  background: var(--surface);
  white-space: nowrap;
  vertical-align: middle;
}
.scope--stale { border-color: var(--amber); color: var(--amber); }
.scope--est { border-style: dashed; }

/* ==========================================================================
   Tableau dense — convention terminal financier
   ========================================================================== */

.tablewrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--lift);
}
.tablescroll { overflow-x: auto; max-height: calc(100vh - 300px); overflow-y: auto; }

table.data { width: 100%; border-collapse: collapse; }
table.data thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface2);
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text3);
  font-weight: 500;
  text-align: left;
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
table.data thead th:hover { color: var(--text); }
table.data thead th.is-sorted { color: var(--gold); }
table.data th.r, table.data td.r { text-align: right; }

table.data tbody td {
  padding: var(--sp-3) var(--sp-5);                    /* densité ~34px/ligne */
  border-bottom: 1px solid var(--border);
  font-size: var(--t-base);
  white-space: nowrap;
}

/* 1 131 lignes : on laisse le navigateur sauter la mise en page de tout ce
   qui est hors écran. `contain-intrinsic-size` lui donne la hauteur à
   réserver, donc l'ascenseur reste juste et le saut de défilement nul.
   Coût : deux déclarations, aucune virtualisation en JS à maintenir. */
table.data tbody tr {
  content-visibility: auto;
  contain-intrinsic-size: auto 34px;
}
table.data tbody tr:hover { background: var(--surface2); }
table.data tbody tr { cursor: pointer; }
table.data .cell-num { font-family: var(--font-mono); }
table.data .cell-name { max-width: 300px; overflow: hidden; text-overflow: ellipsis; }

/* La vignette de carte — l'actif le plus BEAU du produit.
   Un portefeuille de cartes qui n'affiche pas de cartes se prive de sa seule
   matière visuelle. À 26 px une illustration n'est plus une image, c'est une
   tache : on passe à 42×58. Le liseré holographique dit « foil » sans jamais
   concurrencer l'illustration réelle qui la remplit. */
.thumb {
  width: 42px; height: 58px;
  border-radius: 4px;
  object-fit: cover;
  background: var(--surface2);
  display: block;
  flex: none;
  border: 1px solid var(--border-hi);
}

/* Le repli SANS image : au lieu d'un rectangle gris, un dos de carte. La
   signature holographique y est chez elle — c'est littéralement du foil, et
   ça rend l'absence d'illustration présentable plutôt que cassée. */
div.thumb {
  position: relative;
  overflow: hidden;
  background: var(--cardback);
}
div.thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--holo);
  opacity: 0.17;
  mix-blend-mode: screen;
  pointer-events: none;
}
/* Une vignette de carte qui échoue s'efface : le fond tient la place — ici le
   liseré holographique — et on ne montre jamais l'icône brisée du navigateur. */
.thumb.is-failed { visibility: hidden; }

/* ---- Logo de set, avec son état d'absence ---- */
.logowrap { position: relative; display: inline-block; vertical-align: middle; }
.logoimg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  z-index: 1;
}
.logowrap.is-failed .logoimg { display: none; }
/* Le monogramme vit sous l'image : il apparaît si elle ne vient pas. */
.logofb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%; height: 100%;
  border: 1px dashed var(--border-hi);
  border-radius: 5px;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  color: var(--text3);
  background: var(--surface2);
  vertical-align: middle;
}

/* ==========================================================================
   Cartes de contenu / panneaux
   ========================================================================== */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-8);
  /* Lumière du haut : sans elle, huit panneaux identiques forment un aplat.
     C'est un pixel qui décolle chaque surface de son fond. */
  box-shadow: var(--lift);
}

/* ==========================================================================
   Table de RECHERCHE — largeurs contraintes et en-têtes triables.

   2026-08-09 — la table faisait 940 px dans un conteneur de 882 : la colonne
   de cote était coupée en deux et une barre de défilement horizontale
   apparaissait. La cause n'était pas une colonne trop large mais DEUX :
   `.cell-name` porte `max-width: 300px`, appliqué au nom ET au set, soit
   600 px à eux seuls avant les quatre colonnes fixes.

   `table-layout: fixed` fait respecter les largeurs déclarées au lieu de les
   traiter comme des suggestions — c'est le seul moyen qu'une table à contenu
   `nowrap` tienne dans sa boîte.
   ========================================================================== */

.data--search { table-layout: fixed; width: 100%; }
.data--search th,
.data--search td { overflow: hidden; text-overflow: ellipsis; }
/* Le nom et le set se partagent ce qui reste ; le nom en prend le double,
   parce que c'est lui qu'on lit pour identifier la carte. */
/* Nathan, 09/08 : « on voit pas les images, les proportions sont clairement
   mauvaises ». 335 px de colonne pour écrire « Mew » pendant que
   l'illustration tenait dans 42 px — c'est l'inverse de ce que ce produit
   devrait montrer. La vignette passe à 52×72 et le nom rend la largeur qu'il
   n'utilisait pas ; la rareté récupère la place et devient triable. */
/* 09/08 — RÉGRESSION : la colonne du bouton d'ajout a écrasé la cote, qui
   affichait « 6… » sous un en-tête « C… ».
   La cause n'est pas la nouvelle colonne, c'est que la COTE était la seule
   sans largeur déclarée : en `table-layout: fixed`, elle absorbait tout le
   reste, donc elle a payé seule les 44 px ajoutés. Et rien ne débordait —
   le layout fixe REDISTRIBUE au lieu de déborder, ce qui rend le défaut
   invisible à un contrôle de débordement.

   Deux règles en sortent :
     • toute colonne a une largeur déclarée, sinon l'une d'elles sert de
       variable d'ajustement à l'insu de tout le monde ;
     • la cote ne rétrécit JAMAIS pour faire de la place. Un prix tronqué à
       « 6… » est pire qu'un prix absent : il a l'air d'être là. C'est la
       seule colonne dont la troncature change le SENS.
   Les pourcentages du texte baissent (30/21/14 → 24/16/11) pour payer les
   44 px du bouton. */
/* Les colonnes se rangent en deux familles, et c'est ce qui décide de tout :

   BORNÉES — la vignette, la langue, le tirage, la cote, la rareté, le
   bouton. Leur contenu a une taille maximale connue, donc une largeur juste
   existe. Les tronquer est un DÉFAUT : « Shiny Ultra… » perd une catégorie,
   « 6… » perd un prix. Elles sont déclarées en pixels et ne bougent plus.

   NON BORNÉES — le nom de carte (85 caractères observés) et le set (40).
   Aucune largeur ne les contient tous ; l'ellipse est le comportement
   CORRECT, à condition que la valeur complète reste atteignable — d'où le
   `title` sur ces deux cellules, et la fiche de détail derrière.

   Le nom prend le reste : c'est la seule colonne qui doit servir de variable
   d'ajustement, parce que c'est la seule que l'ellipse n'abîme pas. */
/* Largeurs MESURÉES à l'écran, pas estimées : « Langue » et « Tirage » ne
   savent pas se comprimer (`nowrap`) et se servaient au-delà de ce qu'on
   leur déclarait, aux dépens de la vignette. Une valeur déclarée en dessous
   du besoin réel n'est pas une contrainte, c'est une fiction. */
.data--search th:nth-child(1) { width: 72px; }   /* vignette 52 + 2×8, + marge */
.data--search th:nth-child(3) { width: 150px; }  /* set */
.data--search th:nth-child(4) { width: 132px; }  /* « Special Illustration Rare » */
.data--search th:nth-child(5) { width: 78px; }   /* langue — drapeau + code */
.data--search th:nth-child(6) { width: 98px; }   /* tirage — « 1ʳᵉ Édition » */
.data--search th:nth-child(7) { width: 150px; }  /* cote + pastille de périmètre */
.data--search th:nth-child(8) { width: 52px; }   /* bouton, « ×12 » compris */
/* nth-child(2), le nom : volontairement SANS largeur — il absorbe le reste. */

/* Gouttière resserrée : 12 px de chaque côté sur huit colonnes coûtaient
   64 px, soit exactement ce qui manquait à la rareté et à la vignette.
   Sélecteur aligné sur `table.data tbody td` (0,1,3) : écrit en
   `.data--search td` (0,1,1), la règle était muette. */
table.data--search thead th,
table.data--search tbody td { padding-left: var(--sp-4); padding-right: var(--sp-4); }

/* En dessous de cette largeur, le conteneur DÉFILE au lieu de comprimer.
   Sans ce plancher, une fenêtre étroite reproduirait exactement le défaut
   qu'on vient de corriger, en rabotant les colonnes une à une. */
.data--search { min-width: 860px; }

.data--search .cell-name { max-width: none; }
.data--search .thumb { width: 52px; height: 72px; }

/* ==========================================================================
   Table du PORTEFEUILLE — mêmes deux familles de colonnes que la recherche.

   Défaut mesuré avant ce revamp : les dix colonnes portaient des largeurs EN
   LIGNE, la table n'était pas en `table-layout: fixed`, et tout le contenu
   est en `nowrap`. La table dépassait donc son conteneur et `.tablescroll`
   la faisait défiler — ce qui poussait **P&L et %** hors de l'écran.

   Ce sont les deux colonnes pour lesquelles on ouvre un portefeuille. Elles
   étaient invisibles sans défilement horizontal, sur la vue la plus
   consultée du produit.

   La règle est celle déjà écrite pour la recherche, appliquée ici :
     · toute colonne BORNÉE a une largeur déclarée, sinon l'une d'elles sert
       de variable d'ajustement à l'insu de tout le monde ;
     · le NOM est le seul absorbeur — c'est la seule que l'ellipse n'abîme
       pas, et sa valeur complète reste atteignable par `title` ;
     · un plancher de largeur fait DÉFILER le conteneur au lieu de raboter
       les colonnes une à une quand la fenêtre rétrécit.

   La cote reçoit 216 px parce qu'elle ne porte pas qu'un prix : « 1 438,25 €
   FR_NM_low · EST. » est une seule information — un prix qui voyage sans son
   périmètre oblige à deviner, et deviner prudemment c'est jeter.

   Les largeurs sont MESURÉES, pas estimées : on relâche `table-layout` sur
   les 400 premières lignes réelles et on lit le besoin de chaque colonne.
   Une valeur déclarée en dessous du besoin n'est pas une contrainte, c'est
   une fiction — et en layout fixe elle ne déborde pas, elle TRONQUE. Deux
   colonnes l'ont prouvé au premier jet : « 1 438,25 € FR_NM_low · EST. »
   réclame 214 px (176 déclarés) et « +160,1 % » en réclame 94 (72 déclarés).
   Une cote et une performance rognées sont exactement les deux chiffres
   qu'on ne peut pas se permettre d'abîmer. */
.data--pf { table-layout: fixed; width: 100%; min-width: 1220px; }
.data--pf th, .data--pf td { overflow: hidden; text-overflow: ellipsis; }
.data--pf th:nth-child(1) { width: 68px; }   /* vignette 42 + 2×12 de gouttière */
/* nth-child(2), le nom : volontairement SANS largeur — c'est l'absorbeur. */
.data--pf th:nth-child(3) { width: 170px; }  /* set — non borné, ellipse correcte */
.data--pf th:nth-child(4) { width: 68px; }   /* langue — drapeau + code */
.data--pf th:nth-child(5) { width: 48px; }   /* qté */
.data--pf th:nth-child(6) { width: 216px; }  /* cote + périmètre + « EST. » (214 mesuré) */
.data--pf th:nth-child(7) { width: 100px; }  /* valeur */
.data--pf th:nth-child(8) { width: 100px; }  /* investi */
.data--pf th:nth-child(9) { width: 108px; }  /* P&L */
.data--pf th:nth-child(10) { width: 96px; }  /* % — « +160,1 % » (94 mesuré) */
.data--pf .cell-name { max-width: none; }

/* ==========================================================================
   Table du CLASSEMENT gradé (#/gradees) — 2026-08-13. Même discipline que
   .data--pf : table-layout fixe, largeurs déclarées, le NOM seul absorbe. La
   colonne « PSA 9/10 » porte le montant ET le compte de ventes (« 412,00 € ·
   8 ventes ») — deux faits d'un coup, comme la cote du portefeuille.

   ELLE S'APPELAIT `.data--graded`, ET CE NOM ÉTAIT DÉJÀ PRIS (2026-08-14).
   La poche gradée du Portefeuille le déclare aussi, ~2 000 lignes plus bas
   (bloc « Les trois autres tables ») : la cascade donnait donc au classement
   les largeurs d'une AUTRE table de neuf colonnes. Conséquences mesurées à
   l'écran, aucune détectée par un test :
     · « POPULATION… » et « PORTEFEUI… » tronqués en EN-TÊTE — une colonne
       bornée tronquée est un défaut (contrôle de rendu n° 2) ;
     · `min-width` 1080 au lieu de 1192, donc trois colonnes comprimées ;
     · `cursor: default` hérité de la poche — or CHAQUE ligne d'ici ouvre une
       fiche carte. « Ce qui se clique le dit au repos » (§8) : l'affordance
       était niée par une règle écrite pour une autre table.
   C'est la règle §9-6 du canon (espace de noms réservé) appliquée aux
   variantes de table : deux tables différentes ne partagent jamais un nom.
   ========================================================================== */
/* Largeurs RE-MESURÉES le 2026-08-14, `table-layout` relâché sur les lignes
   réelles (méthode §9). Elles ne l'avaient jamais été DANS ce sélecteur : la
   cascade servait celles d'une autre table, donc personne ne pouvait voir que
   « Brute » à 96 px tronquait « 1 642,50 € » (besoin mesuré : 99 px). Somme
   des colonnes bornées 960 px ; « Carte » absorbe le reste (167 px mesurés,
   172 disponibles à 1 132 px de conteneur). */
.data--gradelb { table-layout: fixed; width: 100%; min-width: 1128px; }
.data--gradelb th, .data--gradelb td { overflow: hidden; text-overflow: ellipsis; }
.data--gradelb th:nth-child(1) { width: 68px; }   /* vignette 42 + 2×12 de gouttière */
/* nth-child(2), le nom : volontairement SANS largeur — c'est l'absorbeur. */
.data--gradelb th:nth-child(3) { width: 148px; }  /* série — 128 mesurés */
.data--gradelb th:nth-child(4) { width: 112px; }  /* brute EU_NM — 99 mesurés, marge à 5 chiffres */
.data--gradelb th:nth-child(5) { width: 132px; }  /* PSA 9 + ventes — 99 mesurés */
.data--gradelb th:nth-child(6) { width: 132px; }  /* PSA 10 + ventes — 114 mesurés (~23 400,00 €) */
.data--gradelb th:nth-child(7) { width: 96px; }   /* écart actif — 83 mesurés */
.data--gradelb th:nth-child(8) { width: 156px; }  /* population US, $ — 154 mesurés */
.data--gradelb th:nth-child(9) { width: 116px; }  /* portefeuille — l'en-tête « PORTEFEUILLE »
                                                     capitalisé + interlettré mesure 110px (104
                                                     déclarés au premier jet -> tronqué, mesuré
                                                     à l'écran plutôt qu'estimé, règle §9) */
.data--gradelb .cell-name { max-width: none; }
/* L'échantillon mince s'ATTÉNUE (règle §4 bis) : encre affaiblie plutôt
   qu'un chiffre plein qui prétend à une tendance sur 2 ventes. */
.data--gradelb .thin { color: var(--text3); }
/* Une ligne du classement OUVRE la fiche de la carte : elle le dit au repos. */
.data--gradelb tbody tr { cursor: pointer; }

/* Un en-tête triable doit se dire cliquable AU REPOS, pas au survol.
   2026-08-09 — première version fautive : le chevron n'apparaissait qu'au
   survol, donc rien ne signalait que la colonne se triait. Nathan a conclu
   « on peut toujours pas trier » alors que tout était câblé. Une affordance
   qu'il faut découvrir n'existe pas — c'est la règle que j'avais posée pour
   le forage, et que j'avais enfreinte ici. */
.data--search th.sortable { cursor: pointer; user-select: none; transition: color var(--dur-fast) var(--ease-std); }
.data--search th.sortable:hover { color: var(--text2); }
.data--search th.is-sorted { color: var(--gold); }
.sortcaret { display: inline-block; width: 11px; margin-left: var(--sp-2); font-size: var(--t-label); }
/* Au repos : chevron double, discret mais VISIBLE. Au survol il s'éclaire.
   Sur la colonne active, la flèche de sens a déjà pris la place. */
.data--search th.sortable:not(.is-sorted) .sortcaret::after {
  content: "⇅";
  color: var(--text4);
  transition: color var(--dur-fast) var(--ease-std);
}
.data--search th.sortable:not(.is-sorted):hover .sortcaret::after { color: var(--gold-dim); }
/* Titre de section — Fraunces 500 (canon), 17px pour la densité bureau. */
.panel__title {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--t-lg);
  letter-spacing: var(--tr-lg);
  margin: 0 0 var(--sp-2);
}
.panel__hint {
  font-size: var(--t-sm);
  color: var(--text3);
  line-height: var(--lh-body);
  margin-bottom: var(--sp-6);
  max-width: 78ch;
  text-wrap: pretty;
}

.cols { display: grid; gap: var(--sp-6); }
.cols--2 { grid-template-columns: 1fr 1fr; }
.cols--set { grid-template-columns: minmax(0, 1fr) 320px; }
/* CONTENU + RAIL — la grammaire des rangées du tableau de bord : un objet
   dense qui absorbe la largeur, et une colonne de faits à droite. Elle vivait
   en `style=` dans la vue, donc AUCUNE requête de média ne pouvait
   l'atteindre : sous 1 100 px la colonne de droite gardait ses 340 px fixes
   pendant que la gauche s'effondrait. Même leçon que les largeurs de colonne
   du portefeuille — une mise en page en ligne rend la feuille muette. */
.cols--rail { grid-template-columns: minmax(0, 1fr) 316px; gap: var(--gut); }
.cols--rail-wide { grid-template-columns: minmax(0, 1fr) 340px; }
.cols--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gut); }
/* Trois piliers côte à côte (Pokédex) — même grammaire que `.cols--pair`,
   une colonne de plus. Sous 1180 px elle passe à DEUX avant de s'empiler :
   trois piliers tiennent encore à deux par ligne là où `.cols--pair` n'en
   a que deux à retomber d'un coup. */
.cols--triple { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gut); }
@media (max-width: 1180px) {
  .cols--rail, .cols--rail-wide, .cols--pair { grid-template-columns: minmax(0, 1fr); }
  .cols--triple { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 780px) {
  .cols--triple { grid-template-columns: minmax(0, 1fr); }
}

/* Panneaux côte à côte : chacun sa hauteur, pas d'étirement à vide. */
.cols { align-items: start; }

/* ---- SAUF quand les deux panneaux répondent à la MÊME question ----
   (2026-08-14, lot 2 de la passe de direction artistique.)

   `align-items: start` est juste par défaut, et faux pour une rangée dont les
   deux moitiés sont deux angles d'un même sujet : « Top/Flop » (par position)
   et « Plus-value par set » (par set) mesuraient 510 px et 362 px — soit
   148 px de noir sous le second, exactement ce que le contrôle de rendu n° 4
   signale. Idem pour les trois piliers du Pokédex (430 / 360 / 200 px).

   `--knit` étire, et c'est le PIED qui absorbe (`margin-top: auto`, la même
   mécanique que `.acard__foot` sur la fiche de set) : la prose d'honnêteté se
   colle au bas plutôt que de laisser un trou sous le panneau le plus court. */
.cols--knit { align-items: stretch; }
.cols--knit > .panel { display: flex; flex-direction: column; }
.cols--knit > .panel > .panel__foot { margin-top: auto; }

/* ---- Courbe de valeur ---- */
.chart { position: relative; margin-top: var(--sp-2); }
.chart svg { display: block; overflow: visible; }
.chart-tip {
  position: absolute;
  top: 2px;
  transform: translateX(-50%);
  background: var(--surface3);
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--t-sm);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-std);
  white-space: nowrap;
  box-shadow: var(--shadow-tip);
}
.chart-tip.is-on { opacity: 1; }
.chart-tip__d {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text3);
  margin-bottom: var(--sp-2);
}
.chart-tip__r { display: flex; align-items: center; gap: var(--sp-3); color: var(--text2); }
.chart-tip__r b { font-family: var(--font-mono); color: var(--text); margin-left: auto; padding-left: var(--sp-6); }
.chart-tip__sw { width: 8px; height: 8px; border-radius: 2px; flex: none; }

/* Légende de graphique — l'identité ne repose jamais sur la seule couleur. */
.chart-legend { display: flex; gap: var(--sp-6); font-size: var(--t-sm); color: var(--text2); margin-top: var(--sp-5); }
.chart-legend__i { display: flex; align-items: center; gap: var(--sp-3); }
.chart-legend__line { width: 16px; height: 0; border-top: 2px solid; }

/* Barre de progression de complétion */
.bar { height: 7px; border-radius: 999px; background: var(--surface3); overflow: hidden; }
.bar__fill { height: 100%; background: var(--gold); border-radius: 999px; transition: width var(--dur-panel) var(--ease-std); }

/* Répartition en barre empilée */
.stackbar { display: flex; height: 10px; border-radius: 999px; overflow: hidden; background: var(--surface3); }
.stackbar > span { display: block; height: 100%; }
.legend { display: flex; flex-direction: column; gap: var(--sp-3); margin-top: var(--sp-6); }
.legend__row { display: flex; align-items: center; gap: var(--sp-4); font-size: var(--t-base); }
.legend__sw { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.legend__label { color: var(--text2); }
.legend__val { margin-left: auto; font-family: var(--font-mono); font-weight: 600; }

/* ---- L'identité d'une fiche de set ----
   Le logo est le SUJET, pas un ornement de coin : à 132×56 il se reconnaît
   d'un coup d'œil, ce qui est exactement la façon dont un collectionneur
   identifie un set. Le nom en serif à côté, la série et l'effectif en
   dessous — trois échelles dans un objet de 76 px de haut. */
.setid {
  display: flex;
  align-items: center;
  gap: var(--sp-7);
  margin-bottom: var(--sp-7);
}
.setid__logo {
  display: grid;
  place-items: center;
  width: 132px;
  height: 56px;
  flex: none;
}
.setid__logo img { max-width: 100%; max-height: 56px; object-fit: contain; }
.setid__txt { min-width: 0; }
.setid__name {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-2xl);
  letter-spacing: var(--tr-2xl);
  line-height: var(--lh-tight);
  margin: 0;
  color: var(--text);
}
.setid__meta {
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--text3);
  margin-top: var(--sp-3);
}

/* ==========================================================================
   LA FICHE DE SET — trois échelles, pas six panneaux de même rang
   (2026-08-13, passe de direction artistique.)

   Le défaut mesuré : `setid` → bandeau hors-session → `statbar` de six tuiles
   → panneau « EV de rip » (qui contenait une SECONDE `statbar`) → panneau
   « Analyse du set » → barre d'outils, soit ~1 090 px AVANT la première carte.
   Or la grille EST le sujet de cet écran : c'est la seule chose qu'un grand
   écran apporte au mobile. Tout ce qui la précède doit tenir en deux objets,
   et ces deux objets ne doivent pas se ressembler.

   Trois fautes de composition se cumulaient :
     · DEUX bandeaux de tuiles identiques pour deux grandeurs sans rapport
       (ma position dans le set / le modèle économique d'une box scellée) —
       la répétition n'est légitime que sur la MÊME grandeur (canon §5) ;
     · des lignes clé-valeur étirées sur 1 050 px, où ~950 px de vide
       séparaient « Dracaufeu » de « 1 600,00 € » : `.kv` est un composant de
       panneau LATÉRAL (320 px), il ne survit pas à la pleine largeur ;
     · la prose d'explication en 13 px AU-DESSUS des chiffres, sur toute la
       largeur du panneau — elle pesait plus lourd que ce qu'elle explique.
   ========================================================================== */

/* ---- 1. LA BANDE DE TÊTE : identité + héros + affirmation, en UN objet ----
   Même grammaire que le héros du tableau de bord (surface élevée, nappe holo,
   bande de qualificatifs soudée dessous) : une langue visuelle qui change
   d'un écran à l'autre n'est pas une langue. Le nom du set reste en grand
   avec son illustration — c'est l'exception « FICHE » de la règle de partage
   barre/page : ici le nom n'est pas une étiquette de navigation, c'est le
   SUJET. */
.setband {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--sp-9);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  border-bottom: 0;
  padding: var(--sp-8);
  box-shadow: var(--lift-hi);
}
/* EMPLOI 1 de la signature — la nappe SOUS le chiffre héros : 13 %
   d'opacité, floutée à 38 px. Une lueur, pas un fond coloré. */
.setband::before {
  content: "";
  position: absolute;
  inset: -60% 26% 34% -8%;
  background: var(--holo);
  opacity: 0.13;
  filter: blur(38px);
  pointer-events: none;
}
.setband > * { position: relative; }
.setband__id { display: flex; align-items: flex-start; gap: var(--sp-7); min-width: 0; }
.setband__logo {
  width: 148px;
  height: 62px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--radius);
  background: var(--logo-bed);
  border: 1px solid var(--border);
  box-shadow: var(--lift);
}
.setband__logo img { max-width: 132px; max-height: 54px; object-fit: contain; }
.setband__txt { min-width: 0; }
.setband__name {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-2xl);
  letter-spacing: var(--tr-2xl);
  line-height: var(--lh-tight);
  margin: 0;
  color: var(--text);
}
.setband__meta {
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--text3);
  margin-top: var(--sp-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}
.setband__meta i { font-style: normal; color: var(--text4); }
/* L'AFFIRMATION de la page — calculée, jamais écrite en dur. C'est de la
   LECTURE : mesure bornée, interligne de lecture, `text-wrap: pretty`. */
.setband__claim {
  margin: var(--sp-7) 0 0;
  padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  max-width: 64ch;
  font-size: var(--t-md);
  line-height: var(--lh-body);
  color: var(--text2);
  text-wrap: pretty;
}
.setband__claim b { color: var(--text); font-weight: 600; }
.setband__claim b.pos { color: var(--green); }
.setband__claim b.neg { color: var(--red); }
.setband__claim--warn b { color: var(--amber); }
/* Le héros de cet écran est la COMPLÉTION : c'est la question que la fiche
   pose. Il domine son VOISINAGE, pas la page. */
.setband__hero {
  align-self: center;
  padding-left: var(--sp-8);
  border-left: 1px solid var(--border);
  min-width: 0;
}
.setband__k {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
}
.setband__v {
  font-family: var(--font-serif);
  font-size: var(--t-hero);
  font-weight: 600;
  letter-spacing: var(--tr-hero);
  line-height: 1.04;
  margin-top: var(--sp-3);
  min-height: 44px;
  background: var(--gold-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.setband__v sub {
  font-family: var(--font-mono);
  font-size: var(--t-xl);
  font-weight: 400;
  color: var(--text3);
  -webkit-text-fill-color: var(--text3);
  vertical-align: baseline;
  bottom: 0;
}
/* Hors session, la complétion n'est pas « zéro », elle est INCONNUE. Un
   « — » de 40 px en serif doré serait un bloc gris massif qui crie une
   absence ; à la voix mono, en text3, il DIT l'absence sans occuper le rang
   d'un chiffre qu'on n'a pas. */
.setband__v--none {
  font-family: var(--font-mono);
  font-size: var(--t-xl);
  font-weight: 500;
  letter-spacing: var(--tr-lg);
  background: none;
  -webkit-text-fill-color: var(--text3);
  color: var(--text3);
  min-height: 0;
}
.setband__hero .bar { margin-top: var(--sp-5); }
.setband__s { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-3); }

/* ---- La MÊME bande, sur une vue de CATALOGUE (2026-08-14, lot 2) ----
   Gradées, Scellé et Pokédex n'ont pas de SUJET à nommer : leur nom vit dans
   le bandeau (§5 bis, la barre porte l'identité, la page porte l'affirmation).
   La colonne large porte donc directement l'affirmation calculée — en serif,
   à la place qu'occupait le nom du set — et la provenance descend en méta.
   Aucune autre différence : même surface, même nappe, même héros, mêmes
   qualificatifs soudés. Une langue qui change d'un écran à l'autre n'en est
   pas une. */
.setband--claim { grid-template-columns: minmax(0, 1fr) 300px; }
.setband__say { min-width: 0; }
.setband__claim--lead {
  margin-top: var(--sp-5);
  padding-top: 0;
  border-top: 0;
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--t-xl);
  line-height: 1.32;
  color: var(--text);
  max-width: 60ch;
}
.setband__claim--lead b { color: var(--gold-strong); font-weight: 600; }
.setband__claim--lead b.pos { color: var(--green); }
.setband__claim--lead b.neg { color: var(--red); }
/* L'affirmation d'EMPÊCHEMENT (hors session) reste ambre : elle ne dit pas
   une valeur, elle dit qu'on ne la connaît pas. Écrite APRÈS `--lead b`, qui
   a la même spécificité — sans ça l'ordre de source la repeindrait en or,
   c'est-à-dire en « donnée ». */
.setband__claim--lead.setband__claim--warn b { color: var(--amber); }
.setband--claim .setband__meta {
  margin-top: var(--sp-6);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  max-width: 78ch;
  line-height: var(--lh-body);
}

/* La bande de qualificatifs SOUDÉE au héros : elle en est la suite, pas un
   second objet — d'où le haut carré et le filet partagé. Cinq tuiles de même
   taille sont ici LÉGITIMES : elles qualifient toutes la même chose (ma
   position dans ce set), et aucune ne prétend au rang du héros. */
.qualbar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--sp-7);
}
.qual { padding: var(--sp-5) var(--sp-7); border-left: 1px solid var(--border); min-width: 0; }
.qual:first-child { border-left: 0; }
.qual__k {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.qual__v {
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  font-weight: 600;
  margin-top: var(--sp-3);
  color: var(--text);
}
.qual__s {
  font-size: var(--t-cap);
  color: var(--text3);
  margin-top: var(--sp-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ---- 2. LA BANDE D'ANALYSE : deux objets qu'on ne peut pas confondre ---- */
.setcut {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: var(--gut);
  /* `stretch`, pas `start` : deux cartes côte à côte dont l'une s'arrête
     200 px plus haut laissent un trou noir que le contrôle de rendu n° 4
     signale déjà ailleurs dans ce produit. */
  align-items: stretch;
  margin-bottom: var(--sp-7);
}
.setcut--solo { grid-template-columns: minmax(0, 1fr); }
.acard {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--lift);
  padding: var(--sp-7) var(--sp-8) var(--sp-6);
  display: flex;
  flex-direction: column;
}
.movers { display: grid; grid-template-columns: repeat(auto-fit, minmax(226px, 1fr)); gap: 0 var(--sp-7); }
.mvcol { min-width: 0; }
.acard__h {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-5); margin-bottom: var(--sp-5);
}
.acard__t {
  font-family: var(--font-serif);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--tr-lg);
  margin: 0;
}
.acard__tag {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
  flex: none;
}
/* La prose DESCEND sous les chiffres : elle explique d'où ils viennent, elle
   ne les précède plus. Bornée, interligne de lecture, filet de séparation. */
.acard__foot {
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: var(--lh-body);
  /* `margin-top: auto` : le pied se colle au BAS de la carte et absorbe la
     différence de hauteur avec sa voisine, plutôt que de laisser un trou noir
     sous la plus courte des deux. */
  margin: auto calc(var(--sp-8) * -1) 0;
  padding: var(--sp-6) var(--sp-8) 0;
  border-top: 1px solid var(--border);
  max-width: 92ch;
  text-wrap: pretty;
}
.acard__foot strong { color: var(--text2); font-weight: 600; }
.acard .banner:last-child { margin-bottom: 0; }

/* L'EV de rip est un VERDICT, pas trois tuiles : ouvrir, ou garder scellé.
   L'œil compare mal des angles et très bien des longueurs (canon §6) — deux
   barres sur une échelle PARTAGÉE répondent en un coup d'œil. */
.verdict {
  font-size: var(--t-md);
  line-height: var(--lh-body);
  color: var(--text2);
  margin: 0 0 var(--sp-6);
  max-width: 58ch;
  text-wrap: pretty;
}
.verdict b {
  font-family: var(--font-mono);
  font-size: var(--t-xl);
  font-weight: 600;
  letter-spacing: var(--tr-lg);
}
/* Le nom cité par un verdict ouvre sa fiche — le filet pointillé or est le
   signal au repos (le même que `.sheet__illus`), le bouton hérite de la
   typographie du <b> qui l'entoure. */
.verdict__link {
  border-bottom: 1px dotted var(--gold-dim);
  transition: color var(--dur-fast) var(--ease-std);
}
.verdict__link:hover { color: var(--gold-strong); }
.evbars { display: flex; flex-direction: column; gap: var(--sp-5); }
.evbar {
  display: grid;
  grid-template-columns: 76px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-5);
}
.evbar__k {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
}
.evbar__track { height: 12px; border-radius: 3px; background: var(--surface3); overflow: hidden; }
.evbar__track i { display: block; height: 100%; border-radius: 3px; }
.evbar__v { font-family: var(--font-mono); font-size: var(--t-md); font-weight: 600; text-align: right; white-space: nowrap; }
/* La référence (le prix payé) est NEUTRE ; seule l'EV porte le signe. */
.evbar--ref .evbar__track i { background: var(--text4); }
.evbar--ev .evbar__track i { background: var(--red); }
.evbar--ev.is-up .evbar__track i { background: var(--green); }
.evbar__sub {
  grid-column: 2 / -1;
  font-family: var(--font-mono);
  font-size: var(--t-cap);
  color: var(--text3);
  margin-top: calc(var(--sp-2) * -1);
}

/* Une ligne d'analyse : nom / qualificatif / valeur, en COLONNES alignées.
   `justify-content: space-between` sur 1 050 px met 950 px de vide entre le
   nom et son prix — l'œil ne relie plus les deux. */
.arows { display: flex; flex-direction: column; }
.arow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 84px;
  align-items: baseline;
  gap: var(--sp-5);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--t-sm);
}
.arow:last-child { border-bottom: 0; }
.arow__n { color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arow__m { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); white-space: nowrap; }
.arow__v { font-family: var(--font-mono); font-weight: 600; text-align: right; white-space: nowrap; }
.subhead {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
  margin: var(--sp-8) 0 var(--sp-4);
}
.subhead:first-child { margin-top: 0; }
.mvcol .subhead { margin-top: var(--sp-6); }
/* La liste des chases est un GROUPE distinct des barres : l'invariant de
   proximité veut que l'écart entre groupes dépasse d'au moins ×1,5 celui
   entre frères. Sans cette marge, « CARTES QUI PORTENT L'EV » venait coller
   la légende de la barre juste au-dessus. */
.boxev__chase { margin-top: var(--sp-8); }

/* Les faits du set en PILULES. Quatre grandeurs différentes ne se rangent pas
   dans quatre tuiles identiques : l'œil croirait lire quatre choses
   comparables — c'est la faute des quatre camemberts (canon §5). */
.facts { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-bottom: var(--sp-6); }
.fact {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface2);
  font-size: var(--t-sm);
  color: var(--text3);
}
.fact b { font-family: var(--font-mono); font-weight: 600; color: var(--text); }
.fact em { font-style: normal; font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); }

/* Les mouvements de la semaine : nom / variation / cote, en colonnes. */
.mv {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 62px 74px;
  align-items: baseline;
  gap: var(--sp-4);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--t-sm);
}
.mv:last-child { border-bottom: 0; }
.mv__n { color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Le numéro DÉSAMBIGUÏSE deux homonymes du même set (« Carapuce » commune vs
   illustration rare) : il se lit, il ne se compare pas — donc atténué, à la
   même encre que `.tile__no` de la grille juste au-dessus. */
.mv__no { font-family: var(--font-mono); font-size: var(--t-label); color: var(--text3); letter-spacing: 0.04em; }
.mv__c { font-family: var(--font-mono); font-weight: 600; text-align: right; }
.mv__p { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); text-align: right; }

/* Sous 1180 px la bande de tête se replie : le héros passe SOUS l'identité
   plutôt que de rétrécir le nom du set. */
@media (max-width: 1180px) {
  .setband { grid-template-columns: minmax(0, 1fr); gap: var(--sp-7); }
  .setband__hero { padding-left: 0; border-left: 0; border-top: 1px solid var(--border); padding-top: var(--sp-6); }
  .setcut { grid-template-columns: minmax(0, 1fr); }
}

/* Huit sets EN DEUX COLONNES.
   Empilés, ils faisaient 604 px de haut contre 249 px au panneau voisin :
   355 px de noir sous celui-ci, et le contrôle de rendu n° 4 le signalait.
   Deux colonnes de quatre tiennent la largeur disponible (le panneau fait
   904 px) et ramènent l'écart sous le seuil — sans rien retirer. */
.setgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--sp-6);
}
@media (max-width: 1200px) { .setgrid { grid-template-columns: minmax(0, 1fr); } }

/* Liste set / marché */
.setrow {
  display: flex; align-items: center; gap: var(--sp-5);
  padding: var(--sp-5) var(--sp-5);
  border-radius: 9px;
  transition: background var(--dur-fast) var(--ease-std);
  cursor: pointer;
}
.setrow:hover { background: var(--surface2); }
.setrow__logo { width: 52px; height: 30px; object-fit: contain; flex: none; }
.setrow__body { min-width: 0; flex: 1; }
.setrow__name { font-size: var(--t-base); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.setrow__meta { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-1); }
.setrow__val { text-align: right; font-family: var(--font-mono); font-size: var(--t-base); font-weight: 600; }

/* ==========================================================================
   POKÉDEX — le hub de complétion (#/pokedex).

   Trois familles de tuile, trois natures différentes :
     · `.pkx-pillar`  — un pilier/langue entier (3 tuiles, `.cols--triple`) ;
     · `.pkx-region`  — une région/génération du Pokédex national (grille) ;
     · `.pkx-species` — une espèce, possédée ou non, dans une fiche de région.
   Chacune reprend `.bar`/`.bar__fill` (la même barre que les sets) : UNE
   seule grammaire de complétion dans toute l'application, jamais une
   deuxième barre de progression réinventée pour ce hub.
   ========================================================================== */

/* ---- UN PILIER = UNE COLONNE (2026-08-14, lot 2) ----
   La tuile de pilier et « ses sets les plus avancés » étaient DEUX panneaux,
   dans deux rangées différentes : l'œil devait relier « Japon 1,3 % » à une
   liste posée 90 px plus bas, et la rangée du bas laissait 230 px de noir
   sous la colonne chinoise (2 sets) pendant que l'internationale en faisait
   430. Un seul objet par pilier : en-tête (identité + barre + effectifs),
   corps (les sets, ou l'état vide qui occupe leur place), pied (le chemin
   vers le catalogue de CE pilier). `.cols--knit` les étire à la même hauteur
   et le pied absorbe l'écart. */
.dexcol { padding: 0; overflow: hidden; }
.dexcol__h {
  padding: var(--sp-7) var(--sp-7) var(--sp-6);
  border-bottom: 1px solid var(--border);
  background: var(--surface2);
}
.dexcol__b { padding: var(--sp-5) var(--sp-6); flex: 1; min-width: 0; }
.dexcol__f {
  border-top: 1px solid var(--border);
  padding: var(--sp-5) var(--sp-6);
  font-size: var(--t-cap);
  margin-top: auto;
}
.dexcol__f a { color: var(--gold); }
.dexcol__f a:hover { color: var(--gold-strong); }
/* Le corps d'une colonne de pilier ne pousse pas la boîte : c'est la liste
   qui défile si un pilier a beaucoup plus de sets entamés que ses voisins. */
.dexcol .setrow { padding: var(--sp-4); }

/* ---- Tuile de pilier — sa couleur EST son identité (règle du canon : une
   langue garde sa couleur d'un écran à l'autre — violet Japon, cyan
   international, rouge Chine, comme les onglets `.ptab--*`). ---- */
.pkx-pillar {
  display: block;
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.pkx-pillar:hover { border-color: var(--pillar-accent, var(--border-hi)); transform: translateY(-1px); }
.dexcol:hover { border-color: var(--pillar-accent, var(--border-hi)); }
.pkx-pillar__top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--sp-4); }
.pkx-pillar__lab { font-family: var(--font-serif); font-weight: 500; font-size: var(--t-lg); letter-spacing: var(--tr-lg); color: var(--pillar-accent, var(--text)); }
.pkx-pillar__pct { font-size: var(--t-md); font-weight: 600; color: var(--text2); }
.pkx-pillar__meta { display: flex; justify-content: space-between; gap: var(--sp-4); margin-top: var(--sp-4); font-size: var(--t-cap); }
.pkx-pillar__meta .muted { color: var(--text3); }

/* ---- Grille des régions — même largeur de tuile que `.sgrid`, sans logo :
   la région se reconnaît à son nom, pas à une illustration. ---- */
.pkx-regions { display: grid; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); gap: var(--sp-5); }
.pkx-region {
  display: block;
  padding: var(--sp-5);
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface2);
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.pkx-region:hover { border-color: var(--gold-dim); transform: translateY(-1px); }
.pkx-region__top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.pkx-region__name { font-size: var(--t-base); font-weight: 600; }
.pkx-region__gen { font-size: var(--t-micro); letter-spacing: 0.06em; text-transform: uppercase; }
.pkx-region__meta { margin-top: var(--sp-3); font-size: var(--t-cap); color: var(--text2); }

/* ---- Grille des espèces (fiche de région) — l'or au pied dit « possédée »,
   exactement la règle du canon (« une carte déjà possédée montre sa
   quantité » — ici une espèce n'a pas de quantité, la présence suffit). ---- */
.pkx-species-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: var(--sp-3); }
.pkx-species {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  opacity: 0.5;
}
.pkx-species.is-owned {
  opacity: 1;
  border-color: var(--gold-dim);
  background: var(--surface2);
  box-shadow: inset 2px 0 0 var(--gold);
}
.pkx-species__no { font-size: var(--t-cap); color: var(--text3); flex: none; }
.pkx-species__name { font-size: var(--t-sm); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pkx-species__alt { font-size: var(--t-micro); }

/* ==========================================================================
   Bannières — règle 5 : jamais de spinner nu
   ========================================================================== */

.banner {
  display: flex; align-items: flex-start; gap: var(--sp-5);
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--radius);
  border: 1px solid var(--border-hi);
  background: var(--surface);
  font-size: var(--t-base);
  color: var(--text2);
  margin-bottom: var(--sp-6);
  box-shadow: var(--lift);
}
.banner strong { color: var(--text); font-weight: 600; }
.banner--warn { border-color: var(--amber); }
.banner--err { border-color: var(--red); }
/* Succès / message neutre — filet vert (le vert du gain), même sobriété que
   les deux autres variantes. Sert le « code généré » et le « mot de passe mis
   à jour » du flux de réinitialisation : un fait, pas un cri. */
.banner--ok { border-color: var(--green); }
.banner__icon { flex: none; margin-top: var(--sp-1); }

/* ==========================================================================
   SQUELETTES — ils imitent la MISE EN PAGE, sinon ils valent moins qu'un
   spinner.

   Le défaut mesuré : le même empilement de dix barres identiques servait au
   tableau de bord, au marché et au portefeuille. Aucun des trois écrans ne
   ressemble à ça. Un squelette qui ment sur la forme à venir déplace la page
   au moment du remplacement, et il fait attendre pour rien — le bénéfice
   perçu vient de la RESSEMBLANCE, pas de l'animation.

   Trois règles :
     · même boîte que le vrai contenu (hauteur de ligne, largeur de colonne,
       rayon) — le remplacement ne doit décaler AUCUN pixel ;
     · jamais de squelette sur le CHROME. Les en-têtes, les titres de colonne,
       les filtres se peignent tout de suite, en vrai. C'est le partage le
       plus net entre une interface soignée et une interface pressée ;
     · jamais plus de lignes que l'écran n'en montre.
   ========================================================================== */

.skel {
  background: var(--surface2);
  border-radius: var(--sp-3);
  position: relative;
  overflow: hidden;
}

/* Une pulsation d'opacité plutôt qu'un balayage : sur une grille dense, huit
   balayages synchrones sont du bruit — et un coût de repeint réel. */
.skel { animation: skel-pulse 1.8s ease-in-out infinite; }
@keyframes skel-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }

/* Le balayage reste disponible pour les surfaces ISOLÉES (un héros, un
   graphe) où il n'y en a qu'un à l'écran. */
.skel--sweep { animation: none; }
.skel--sweep::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--sheen), transparent);
  animation: sweep 1.5s linear infinite;
}
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

.skel-tile { aspect-ratio: 63/88; border-radius: 7px; }
.skel-line { height: 9px; border-radius: var(--sp-2); }

/* La ligne de squelette a la hauteur RÉELLE d'une ligne de table (34 px) et
   ses colonnes aux vraies places : le remplacement ne décale rien. */
.skelrow {
  display: flex; align-items: center; gap: var(--sp-6);
  height: 34px; padding: 0 var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.skelrow:last-child { border-bottom: 0; }
/* L'en-tête n'est pas un squelette : c'est du CHROME, il se peint en vrai. */
.skelhead {
  height: 32px; background: var(--surface2);
  border-bottom: 1px solid var(--border);
}
/* Les largeurs VARIENT : quatre barres de même longueur se lisent comme un
   code-barres, pas comme du texte à venir. */
.skel-line--80 { width: 80%; }
.skel-line--60 { width: 60%; }
.skel-line--40 { width: 40%; }
.skel-stack { display: flex; flex-direction: column; gap: var(--sp-3); }

/* Squelette de BANDEAU : la vraie grille du bandeau, pas des barres. */
.skel-strip {
  display: grid;
  grid-template-columns: 1.5fr repeat(5, 1fr);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: var(--gut);
  box-shadow: var(--lift);
}
.skel-strip > div { padding: var(--sp-5) var(--sp-6); border-left: 1px solid var(--border); }
.skel-strip > div:first-child { border-left: 0; background: var(--surface2); }

/* ==========================================================================
   MOUVEMENT RÉDUIT — « réduit » ne veut pas dire « aucun ».

   La consigne du système d'exploitation vise le mouvement VESTIBULAIRE :
   glissements, parallaxes, zooms, rotations. Elle ne demande pas de
   supprimer les fondus : une interface dont tous les changements d'état
   deviennent instantanés paraît CASSÉE, pas calme.

   D'où l'inversion : le mouvement de TRANSFORMATION est déclaré en
   `no-preference`, donc la variante sobre est le défaut et un navigateur qui
   ne connaît pas la requête reçoit le chemin sûr. Les fondus et les
   changements de couleur, eux, vivent toujours.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  /* Filet de sécurité pour les animations décoratives et le CSS qu'on
     n'écrit pas soi-même. 0,01 ms plutôt que 0 s pour que `animationend`
     continue de se déclencher. */
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  /* Ce qui se DÉPLACE cesse de se déplacer — mais garde son fondu. Le tiroir
     de navigation en fait partie : sous mouvement réduit il apparaît au lieu
     de glisser (le `transform` n'est plus transité, seul le voile fond). */
  .sheet__panel, .psheet__sheet, .cmdk__panel, .rail { transition-property: opacity !important; }
  .stile:hover, .rcard:hover, .comp__row:hover .comp__chev,
  .donut__row.is-drillable:hover .donut__chev,
  .srow--live:hover .srow__chev { transform: none !important; }
}

/* ==========================================================================
   FOCUS CLAVIER — visible, et surtout jamais ROGNÉ.

   Le défaut mesuré : `:focus-visible` posait un `outline` à 2 px de décalage
   POSITIF, donc dessiné à l'EXTÉRIEUR de l'élément. Or `.strip`, `.statbar`,
   `.tablewrap`, `.sm`, `.segmented` et `.frise` portent tous
   `overflow: hidden` — l'anneau était donc coupé, parfois entièrement, sur
   les contrôles les plus utilisés du produit. Un anneau rogné est un anneau
   absent : au clavier, on ne sait plus où on est.

   Deux anneaux, pas un : un liseré sombre à l'intérieur garantit la
   séparation d'avec un fond OR (un anneau or sur un bouton or est invisible),
   l'or à l'extérieur garantit la séparation d'avec la surface. L'anneau ne
   s'anime JAMAIS — un anneau qui apparaît en fondu perd l'utilisateur au
   milieu de sa tabulation.

   Le rayon est HÉRITÉ : un anneau à 4 px de rayon autour d'une pastille
   ronde était un rectangle arrondi posé sur un cercle.
   ========================================================================== */
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-radius: inherit;
  transition: none;
}

/* Dans un conteneur qui rogne, l'anneau se dessine à l'INTÉRIEUR. Même
   épaisseur, même couleur — seule l'origine change. */
.strip__cell:focus-visible,
.stat:focus-visible,
.segmented button:focus-visible,
.seg--mini button:focus-visible,
.vswitch button:focus-visible,
.sm__cell:focus-visible,
.frise i:focus-visible,
.psheet__seg button:focus-visible,
table.data tbody tr:focus-visible,
table.data thead th:focus-visible,
.qt:focus-visible,
.nw:focus-visible {
  outline-offset: -2px;
}

/* Sous un en-tête collant, l'élément focalisé doit rester VISIBLE quand la
   tabulation le fait défiler (WCAG 2.2 — Focus Not Obscured). Sans cette
   marge, tabuler dans une table de 1 100 lignes cache la ligne active sous
   son propre en-tête. */
table.data tbody tr, .setrow, .comp__row, .donut__row { scroll-margin-top: 44px; }

/* Le BANDEAU est collant : tout ce que la tabulation fait défiler dans la
   page peut passer dessous. `scroll-padding-top` sur le défileur du document
   réserve sa hauteur pour TOUT `scrollIntoView` — ancre, focus clavier,
   ouverture de panneau — sans avoir à l'écrire élément par élément.
   52 px de barre + une respiration. */
html { scroll-padding-top: 76px; }

/* ==========================================================================
   La composition — une frise + un classement, pas quatre camemberts.

   L'œil compare mal des angles et très bien des longueurs. Et quatre
   graphiques identiques côte à côte annoncent « quatre choses », alors que
   ce sont quatre découpes du même total.
   ========================================================================== */

.comp__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-7); margin-bottom: var(--sp-6);
}
.comp__axes { flex: none; }

/* ---- La frise : la forme du patrimoine d'un coup d'œil ---- */
.frise {
  display: flex; height: 46px;
  border-radius: 9px; overflow: hidden;
  margin-bottom: var(--sp-2); box-shadow: var(--lift);
}
.frise i {
  position: relative; display: flex; align-items: center;
  padding-left: var(--sp-5); cursor: pointer; min-width: 0;
  border: 0; border-right: 1px solid var(--frise-gap);
  transition: filter var(--dur-fast) var(--ease-std);
}
.frise i:last-child { border-right: 0; }
.frise i:hover { filter: brightness(1.18); }
.frise span {
  font-family: var(--font-mono); font-size: var(--t-cap); font-weight: 600;
  color: var(--bg-soft); white-space: nowrap; overflow: hidden;
}
.frise__ax {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: var(--t-label);
  color: var(--text3); margin-bottom: var(--sp-6);
}

/* ---- Le classement ---- */
.comp__rowhead, .comp__row {
  display: grid;
  grid-template-columns: 13px minmax(0, 1fr) minmax(120px, 2fr) 92px 56px 96px 14px;
  align-items: center; gap: var(--sp-5);
}
.comp__rowhead {
  padding: 0 var(--sp-4) var(--sp-4);
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text3);
  border-bottom: 1px solid var(--border-hi); margin-bottom: var(--sp-2);
}
.comp__rowhead .r, .comp__v, .comp__p, .comp__g { text-align: right; }

.comp__rows { display: flex; flex-direction: column; }
.comp__row {
  width: 100%; text-align: left;
  padding: var(--sp-4); border-radius: 8px; cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast) var(--ease-std);
}
.comp__row:last-child { border-bottom: 0; }
.comp__row:hover { background: var(--surface2); }
.comp__dot { width: 9px; height: 9px; border-radius: 3px; }
.comp__name {
  font-size: var(--t-base); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--text);
}
.comp__name em { font-style: normal; color: var(--text3); font-size: var(--t-cap); margin-left: var(--sp-3); }

.comp__track {
  position: relative; height: 15px;
  background: var(--bg-soft); border-radius: 3px;
}
.comp__track i { display: block; height: 100%; border-radius: 3px; }
/* Le repère de prix de revient. À GAUCHE du bout de barre la part gagne,
   à DROITE elle perd — lisible sans légende, parce que la barre est la
   valeur et le filet ce qu'elle a coûté. */
.comp__track u {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: var(--text2); opacity: 0.85; border-radius: 1px;
}
.comp__v { font-family: var(--font-mono); font-size: var(--t-sm); }
.comp__p { font-family: var(--font-mono); font-size: var(--t-sm); color: var(--text3); }
.comp__g { font-family: var(--font-mono); font-size: var(--t-sm); }
.comp__chev {
  color: var(--text4); font-size: var(--t-lg); text-align: right;
  transition: color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.comp__row:hover .comp__chev { color: var(--gold); transform: translateX(2px); }
.comp__note {
  font-size: var(--t-cap); color: var(--text3); line-height: 1.55;
  margin-top: var(--sp-5); padding-top: var(--sp-5); border-top: 1px solid var(--border);
}

/* ==========================================================================
   Anneaux de répartition (conservés : utilisés hors tableau de bord)
   ========================================================================== */

.donut { display: flex; flex-direction: column; gap: var(--sp-6); }
.donut__ring { position: relative; align-self: center; }
.donut__ring svg { display: block; }
.donut__arc { transition: opacity var(--dur-fast) var(--ease-std); }
.donut:hover .donut__arc { opacity: 0.55; }
.donut__arc:hover { opacity: 1; }

.donut__center {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  pointer-events: none; text-align: center;
}
.donut__ck {
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text3);
}
/* Le centre porte le TOTAL — c'est la raison d'être de l'anneau. */
.donut__cv {
  font-family: var(--font-mono); font-size: var(--t-lg); font-weight: 600;
  color: var(--text); margin-top: var(--sp-2); letter-spacing: var(--tr-lg);
}
.donut__cs { font-size: var(--t-label); color: var(--text3); margin-top: var(--sp-1); }

.donut__legend { display: flex; flex-direction: column; }
.donut__row {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4); border-radius: 7px;
  font-size: var(--t-sm); text-align: left; width: 100%;
  border-bottom: 1px solid var(--border);
  position: relative;
}
.donut__row:last-child { border-bottom: 0; }
.donut__row.is-drillable { cursor: pointer; padding-right: var(--sp-7); }
.donut__row.is-drillable:hover { background: var(--surface2); }
.donut__sw { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.donut__lab { color: var(--text2); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.donut__val { font-family: var(--font-mono); font-weight: 600; }
.donut__pct { font-family: var(--font-mono); color: var(--text3); min-width: 46px; text-align: right; }

/* Le chevron est visible AU REPOS : une zone cliquable qui ne s'annonce
   qu'au survol n'existe pas pour qui ne survole pas. */
.donut__chev {
  position: absolute; right: 6px;
  color: var(--text4); font-size: var(--t-md); line-height: 1;
  transition: color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.donut__row.is-drillable:hover .donut__chev { color: var(--gold); transform: translateX(2px); }
.donut__foot { font-size: var(--t-cap); color: var(--text3); line-height: 1.45; }
.donut__empty { padding: var(--sp-8) 0; text-align: center; font-size: var(--t-sm); }

/* ---- Panneau de forage ---- */
.sheet__panel--wide { width: min(560px, 95vw); }
.drill__stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  border-bottom: 1px solid var(--border);
}
.drill__stats > div { padding: var(--sp-5) var(--sp-6); border-left: 1px solid var(--border); }
.drill__stats > div:first-child { border-left: 0; }
.drill__note {
  font-size: var(--t-cap); color: var(--text3);
  padding: var(--sp-4) var(--sp-6); border-bottom: 1px solid var(--border);
}

/* ==========================================================================
   Marché — quatre échelles, quatre densités
   ========================================================================== */

/* ---- 1. Le héros : un état affirmé une fois ---- */
.mk-hero {
  display: grid; grid-template-columns: 240px minmax(0, 1fr);
  gap: var(--sp-8); align-items: center;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--lift);
  padding: var(--sp-8) var(--sp-8); margin-bottom: var(--gut);
  position: relative; overflow: hidden;
}
/* La nappe holo — emploi 1 sur 3 : le héros. */
.mk-hero::before {
  content: ""; position: absolute; inset: -50% 40% 50% -10%;
  background: var(--holo); opacity: 0.1; filter: blur(44px); pointer-events: none;
}
.mk-hero__gauge { position: relative; text-align: center; }
.mk-hero__score {
  font-family: var(--font-serif); font-size: var(--t-hero); font-weight: 600;
  line-height: 1; letter-spacing: -0.03em; margin-top: -14px;
}
.tone-greed-x, .tone-greed { color: var(--green); }
.tone-neutral { color: var(--amber); }
.tone-fear, .tone-fear-x { color: var(--red); }
.mk-hero__lab {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text3); margin-top: var(--sp-3);
}
.mk-hero__say { position: relative; min-width: 0; }
.mk-hero__sentence {
  font-family: var(--font-serif); font-size: var(--t-xl); font-weight: 500;
  letter-spacing: -0.015em; line-height: 1.25; margin: 0;
}
.mk-hero__note { font-size: var(--t-sm); color: var(--text3); margin: var(--sp-4) 0 0; line-height: 1.5; }
.mk-hero__facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-5); margin-top: var(--sp-6); padding-top: var(--sp-6); border-top: 1px solid var(--border);
}
.mk-hero__fv { font-family: var(--font-mono); font-size: var(--t-base); display: block; margin-top: var(--sp-2); }

.mk-factors { display: flex; gap: var(--sp-6); margin-top: var(--sp-6); flex-wrap: wrap; }
.mk-factor { display: flex; align-items: center; gap: var(--sp-3); font-size: var(--t-cap); color: var(--text3); }
.mk-factor__k { min-width: 58px; }
.mk-factor__bar { width: 54px; height: 3px; background: var(--border); border-radius: 2px; overflow: hidden; }
.mk-factor__bar i { display: block; height: 100%; background: var(--text3); }
.mk-factor__v { color: var(--text2); }

/* ---- 2. Petits multiples ---- */
.mk-sec { margin-bottom: var(--gut); }
.mk-sec__head { margin-bottom: var(--sp-5); }
/* Le fond du conteneur est la SURFACE, pas la bordure : sinon la cellule
   laissée vide par 9 indices sur 5 colonnes s'affiche comme un bloc plus
   clair. Ce sont les CELLULES qui portent les séparateurs. */
.sm {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  background: var(--surface);
  border: 1px solid var(--border); border-radius: 12px;
  overflow: hidden; box-shadow: var(--lift);
}
.sm__cell {
  padding: var(--sp-5) var(--sp-6) var(--sp-5); text-align: left; width: 100%;
  border-right: 1px solid var(--border); border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast) var(--ease-std);
}
.sm__cell:hover { background: var(--surface2); }
.sm__name {
  font-size: var(--t-sm); color: var(--text2); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sm__spark { height: 30px; margin: var(--sp-4) 0 var(--sp-3); }
.sm__svg, .sm__flat { width: 100%; height: 30px; display: block; }
.sm__loading { height: 30px; border-radius: 3px; background: var(--surface2); }
.sm__foot { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); }
.sm__val { font-size: var(--t-base); font-weight: 600; }
.sm__chg { font-size: var(--t-sm); }
.sm__none {
  font-family: var(--font-mono); font-size: var(--t-micro); color: var(--text4);
  margin-top: var(--sp-2); letter-spacing: 0.06em;
}

/* ---- 3. Actualité ---- */
.nw__list { display: flex; flex-direction: column; }
.nw {
  display: block; padding: var(--sp-5) var(--sp-6); border-top: 1px solid var(--border);
  transition: background var(--dur-fast) var(--ease-std);
}
.nw:hover { background: var(--surface2); }
.nw__flag {
  display: inline-block; font-family: var(--font-mono); font-size: var(--t-micro);
  letter-spacing: 0.09em; color: var(--amber); border: 1px solid var(--amber);
  padding: var(--sp-1) var(--sp-2); border-radius: 3px; margin-right: var(--sp-3); vertical-align: 1px;
}
.nw__t { font-size: var(--t-base); color: var(--text); }
.nw__m {
  display: block; font-family: var(--font-mono); font-size: var(--t-label);
  color: var(--text3); margin-top: var(--sp-2);
}
.nw__sep {
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text3);
  padding: var(--sp-5) var(--sp-6) var(--sp-3); border-top: 1px solid var(--border-hi);
}

/* ---- 4. Bande de cotation ---- */
/* Hausses et baisses EMPILÉES, pas côte à côte.
   Deux colonnes dans un panneau de demi-largeur laissaient ~60 px au nom :
   « Noctali VMAX » devenait « Nocta… ». Une bande de cotation dont on ne
   peut pas lire les noms ne cote rien. Empilées, chaque ligne dispose de la
   largeur du panneau, et la densité est conservée. */
.qt__cols { display: flex; flex-direction: column; }
.qt__cols > div { border-top: 1px solid var(--border); }
.qt__h {
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text3); padding: var(--sp-4) var(--sp-6) var(--sp-3);
}
.qt {
  display: grid; grid-template-columns: 20px minmax(0, 1fr) 74px 66px;
  align-items: center; gap: var(--sp-4); padding: var(--sp-2) var(--sp-6); height: var(--row-h);
}
.qt:hover { background: var(--surface2); }
/* Sur le tableau de bord (Top / Flop), la ligne OUVRE la fiche de sa carte :
   la variante `--live` porte le curseur — la bande du Marché, elle, n'ouvre
   rien et ne le promet pas. */
.qt--live { cursor: pointer; }
.qt__i { width: 20px; height: 28px; object-fit: cover; border-radius: 2px; background: var(--surface2); }
.qt__n { font-size: var(--t-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qt__v { font-size: var(--t-cap); text-align: right; color: var(--text2); }
.qt__c { font-size: var(--t-cap); text-align: right; }

/* ==========================================================================
   Recherche
   ========================================================================== */

.search--big { min-width: 0; width: 100%; padding: var(--sp-5) var(--sp-6); margin-bottom: var(--sp-4); }
.search--big input { font-size: var(--t-lg); }
.search__hint { font-size: var(--t-cap); color: var(--text3); margin-bottom: var(--sp-7); }
.search__hint code {
  font-family: var(--font-mono); font-size: var(--t-cap);
  color: var(--gold); background: var(--surface2);
  padding: var(--sp-1) var(--sp-2); border-radius: 3px;
}

.facets { display: flex; flex-direction: column; gap: var(--sp-6); }
.facet {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--sp-5) var(--sp-5);
}
.facet__t {
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text3); margin-bottom: var(--sp-3);
}
.facet__row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  width: 100%; padding: var(--sp-2) var(--sp-3); border-radius: 6px;
  font-size: var(--t-sm); color: var(--text2); text-align: left;
  transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std);
}
.facet__row:hover { background: var(--surface2); color: var(--text); }
.facet__row.is-on { background: var(--gold); color: var(--ink-on-gold); font-weight: 600; }
/* Une facette à 0 reste VISIBLE mais éteinte : la faire disparaître
   empêcherait de comprendre pourquoi un résultat manque. */
.facet__row.is-empty { opacity: 0.4; }
.facet__row.is-empty:hover { background: none; }
.facet__lab { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.facet__n { font-family: var(--font-mono); font-size: var(--t-cap); opacity: 0.75; flex: none; }
.facet__note { font-size: var(--t-cap); color: var(--text3); line-height: 1.5; padding: 0 var(--sp-1); }

/* Le tirage est un chip DORÉ, pas une mention discrète : « 1ʳᵉ Édition »
   vaut six fois « Unlimited » sur la même carte — c'est l'information la
   plus chère de la ligne. */
.print {
  font-family: var(--font-mono); font-size: var(--t-label);
  color: var(--gold); border: 1px solid var(--gold-dim);
  padding: var(--sp-1) var(--sp-2); border-radius: 4px; white-space: nowrap;
}

/* ==========================================================================
   Ajouter / corriger une position
   ========================================================================== */

.psheet { position: fixed; inset: 0; z-index: 200; display: none; }
.psheet.is-open { display: block; }
.psheet__backdrop { position: absolute; inset: 0; background: var(--veil); }
.psheet__sheet {
  position: relative;
  width: min(430px, calc(100vw - 32px));
  margin: 9vh auto 0;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: 14px;
  box-shadow: var(--shadow-modal), var(--lift);
  overflow: hidden;
  outline: 0;
}

.psheet__head { display: flex; gap: var(--sp-5); padding: var(--sp-6) var(--sp-6); border-bottom: 1px solid var(--border); }
.psheet__art {
  width: 52px; height: 72px; border-radius: 5px; object-fit: cover; flex: none;
  background: var(--surface2); border: 1px solid var(--border-hi);
}
.psheet__title { font-family: var(--font-serif); font-size: var(--t-lg); font-weight: 600; line-height: 1.2; margin: 0; }
.psheet__sb { font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-1); }
.psheet__chips { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); flex-wrap: wrap; }
.chip--n {
  font-family: var(--font-mono); font-size: var(--t-micro); padding: var(--sp-1) var(--sp-2);
  border-radius: 3px; border: 1px solid var(--border-hi); color: var(--text3);
}

.psheet__body { padding: var(--sp-6) var(--sp-6) var(--sp-2); }
.psheet__duo { display: grid; grid-template-columns: 1.5fr 1fr; gap: var(--sp-5); }
.psheet__f { margin-bottom: var(--sp-5); }
.psheet__l {
  display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text3); margin-bottom: var(--sp-3);
}
.psheet__opt { font-family: var(--font-sans); font-size: var(--t-label); letter-spacing: 0; text-transform: none; color: var(--text4); }
.psheet__f input {
  width: 100%; background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 8px; padding: var(--sp-4) var(--sp-5); color: var(--text);
  font-family: var(--font-mono); font-size: var(--t-base); outline: none;
  transition: border-color var(--dur-fast) var(--ease-std);
}
.psheet__f input:focus { border-color: var(--gold); }
.psheet__f input::placeholder { color: var(--text4); }

/* Le repère de cote — jamais une valeur pré-remplie. Le report est un
   GESTE explicite : pré-remplir enregistrerait une cote comme prix d'achat
   et annulerait le P&L de tout le portefeuille, sans erreur visible. */
.psheet__hint {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: var(--sp-3); font-size: var(--t-cap); color: var(--text3); gap: var(--sp-5);
}
.psheet__hint button {
  font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text2);
  border-bottom: 1px dashed var(--border-hi); padding: 0; flex: none;
}
.psheet__hint button:hover { color: var(--gold); border-color: var(--gold-dim); }
.psheet__err { font-size: var(--t-cap); color: var(--red); margin-top: var(--sp-3); line-height: 1.4; }

/* Cinq pastilles, pas une liste déroulante : il n'y en a que cinq, et
   l'API rejette tout le reste par un 400. */
.psheet__seg { display: flex; gap: var(--sp-1); background: var(--border); border-radius: 8px; overflow: hidden; }
.psheet__seg button {
  flex: 1; text-align: center; background: var(--surface2); color: var(--text3);
  font-family: var(--font-mono); font-size: var(--t-cap); font-weight: 600;
  padding: var(--sp-4) 0; transition: background var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std);
}
.psheet__seg button:hover { color: var(--text); }
.psheet__seg button.is-on { background: var(--gold); color: var(--ink-on-gold); }

.psheet__foot {
  display: flex; gap: var(--sp-4); padding: var(--sp-5) var(--sp-6) var(--sp-6);
  border-top: 1px solid var(--border);
}
.psheet__btn { flex: 1; text-align: center; padding: var(--sp-5); border-radius: 9px; font-size: var(--t-base); font-weight: 600; }
.psheet__btn--p { background: var(--gold); color: var(--ink-on-gold); }
.psheet__btn--p:hover:not([disabled]) { background: var(--gold-strong); }
.psheet__btn--s { border: 1px solid var(--border-hi); color: var(--text2); }
/* La suppression : étroite, à gauche, jamais collée au bouton principal. */
.psheet__btn--d { border: 1px solid var(--border-hi); color: var(--red); flex: none; padding: var(--sp-5) var(--sp-5); }
.psheet__btn--d:hover:not([disabled]) { border-color: var(--red); }
.psheet__btn[disabled] { opacity: 0.5; cursor: default; }

/* Le point de départ — visible AU REPOS. Une action qu'il faut découvrir
   n'existe pas. Une carte possédée montre sa quantité, pas un bouton mort. */
.addbtn {
  border: 1px solid var(--border-hi); color: var(--text3);
  border-radius: 7px; padding: var(--sp-2) var(--sp-4); font-size: var(--t-sm); font-weight: 600;
  font-family: var(--font-mono); flex: none;
  transition: border-color var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std), background var(--dur-fast) var(--ease-std);
}
.addbtn:hover { border-color: var(--gold); color: var(--gold); background: var(--gold-a07); }
.addbtn.is-owned { border-color: var(--gold-dim); color: var(--gold); }

/* ==========================================================================
   Collections nommées — le sélecteur « + Collection »
   Discret au repos (bouton secondaire, largeur fixe) ; le picker réutilise
   la coquille .psheet (même overlay, même comportement Échap/backdrop).
   ========================================================================== */
.cpicker__trigger { flex: none; padding: var(--sp-5) var(--sp-6); white-space: nowrap; }
.cpicker__sheet { width: min(380px, calc(100vw - 32px)); }
.cpicker__head {
  display: flex; justify-content: space-between; gap: var(--sp-5);
  padding: var(--sp-6) var(--sp-6); border-bottom: 1px solid var(--border);
}
.cpicker__body { padding: var(--sp-5) var(--sp-6) var(--sp-6); }
.cpicker__list { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-5); }

/* Une ligne se clique ENTIÈRE — pas seulement une case à cocher minuscule.
   L'état « déjà dedans » se lit aux TROIS mêmes voix qu'ailleurs dans ce
   produit : fond, filet, encre (règle « trois voix concordantes »). */
.cpicker__row {
  display: flex; align-items: center; gap: var(--sp-4); width: 100%;
  padding: var(--sp-4) var(--sp-5); border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg-soft); text-align: left;
  transition: border-color var(--dur-fast) var(--ease-std), background var(--dur-fast) var(--ease-std);
}
.cpicker__row:hover:not([disabled]) { border-color: var(--gold-dim); }
.cpicker__row.is-in { border-color: var(--gold-dim); background: var(--gold-a07); }
.cpicker__row[disabled] { opacity: 0.55; cursor: default; }
.cpicker__mark {
  width: 16px; height: 16px; flex: none; border-radius: 4px;
  border: 1px solid var(--border-hi); display: flex; align-items: center;
  justify-content: center; font-size: var(--t-cap); color: var(--ink-on-gold);
}
.cpicker__row.is-in .cpicker__mark { background: var(--gold); border-color: var(--gold); }
.cpicker__name { flex: 1; min-width: 0; font-size: var(--t-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cpicker__meta { font-family: var(--font-mono); font-size: var(--t-cap); flex: none; }
.cpicker__hint { font-size: var(--t-cap); line-height: 1.5; margin: 0 0 var(--sp-5); }
.cpicker__new { display: flex; gap: var(--sp-3); }
.cpicker__new input {
  flex: 1; min-width: 0; background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 8px; padding: var(--sp-4) var(--sp-5); color: var(--text); font-size: var(--t-base); outline: none;
  transition: border-color var(--dur-fast) var(--ease-std);
}
.cpicker__new input:focus { border-color: var(--gold); }
.cpicker__new input::placeholder { color: var(--text4); }

/* ==========================================================================
   Connexion
   ========================================================================== */

.auth { display: flex; align-items: center; justify-content: center; min-height: 88vh; padding: var(--sp-7); }
/* La carte se DÉCOLLE : c'est le seul objet de l'écran, et un rectangle posé
   à plat sur le fond ne dit pas qu'il est le sujet. Ombre portée + lumière du
   haut, et un filet holo sur l'arête supérieure — l'écran de connexion ne
   porte ni héros ni graphe, la signature y a donc son emploi libre. */
.auth__card {
  width: min(408px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-9) var(--sp-9) var(--sp-8);
  box-shadow: var(--lift), var(--shadow-card);
  position: relative;
  overflow: hidden;
}
.auth__card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: var(--holo);
  opacity: 0.85;
}
/* Aligné à GAUCHE, pas centré : la marque annonce le produit, elle ne fait
   pas une page de titre. Le regard part du même bord que les champs. */
.auth__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin-bottom: var(--sp-8);
}
.auth__mark {
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 12px;
  border: 1px solid var(--border-hi);
  box-shadow: var(--lift), var(--shadow-mark-lg);
}
.auth__wordmark {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-2xl);
  letter-spacing: var(--tr-2xl);
  line-height: 1.1;
  color: var(--text);
}
.auth__title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-xl);
  letter-spacing: var(--tr-lg);
  margin: 0 0 var(--sp-3);
}
.auth__sub { font-size: var(--t-base); color: var(--text3); line-height: 1.5; margin: 0 0 var(--sp-7); }

.auth__label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text3);
  margin: var(--sp-6) 0 var(--sp-2);
}
.auth__input {
  width: 100%;
  padding: var(--sp-5) var(--sp-5);
  background: var(--bg-soft);
  border: 1px solid var(--border-hi);
  border-radius: 8px;
  color: var(--text);
  font: inherit;
  font-size: var(--t-md);
  outline: 0;
  transition: border-color var(--dur-fast) var(--ease-std);
}
/* Règle 1 : l'affordance d'un contrôle actif est portée par l'OR, pas par un
   gris — aucune nuance de la rampe n'atteint 3:1 sur cette surface. */
.auth__input:focus { border-color: var(--gold); }
.auth__hint { font-size: var(--t-cap); color: var(--text3); margin: var(--sp-3) 0 0; }

.auth__submit {
  width: 100%;
  margin-top: var(--sp-7);
  padding: var(--sp-5);
  background: var(--gold);
  color: var(--ink-on-gold);
  border-radius: 9px;
  font-weight: 600;
  font-size: var(--t-md);
  transition: background var(--dur-fast) var(--ease-std);
}
.auth__submit:hover:not([disabled]) { background: var(--gold-strong); }
.auth__submit[disabled] { opacity: 0.55; cursor: default; }

.auth__sep {
  display: flex; align-items: center; gap: var(--sp-5);
  margin: var(--sp-7) 0 var(--sp-6);
  color: var(--text3); font-size: var(--t-cap);
}
.auth__sep::before, .auth__sep::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}

.auth__social {
  width: 100%;
  display: flex; align-items: center; justify-content: center; gap: var(--sp-4);
  padding: var(--sp-5);
  border: 1px solid var(--border-hi);
  border-radius: 9px;
  color: var(--text2);
  font-size: var(--t-md);
}
.auth__social[disabled] { opacity: 0.4; cursor: not-allowed; }

/* Le bouton Apple ACTIF — style « blanc », l'un des trois que les Human
   Interface Guidelines autorisent (noir, blanc, blanc cerclé). Sur une
   interface sombre — et celle-ci n'a qu'un thème — c'est le seul des trois
   qui garde le contraste exigé.

   Ce qui n'est PAS négociable côté Apple, et qu'on respecte ici : la marque
   n'est pas redessinée, le libellé est la formule officielle (« Se connecter
   avec Apple »), la hauteur reste ≥ 44 px, et rien d'autre n'entre dans le
   bouton. C'est pour ça qu'il ne porte pas la teinte or de la maison :
   l'identité de ce bouton-là appartient à Apple, pas à Bindex. */
.auth__social--apple {
  background: #fff;
  color: #000;
  border-color: #fff;
  min-height: 44px;
  font-weight: 500;
  cursor: pointer;
}
.auth__social--apple:hover { background: #e8e8ed; border-color: #e8e8ed; }
/* En THÈME CLAIR, le blanc disparaît sur la carte blanche : on passe au
   style « noir », le deuxième des trois que les HIG autorisent. Toujours
   les couleurs d'Apple, jamais les nôtres — même exception, même raison.
   Deux portes, comme pour les jetons : choix explicite et « auto ». */
:root[data-theme="light"] .auth__social--apple { background: #000; color: #fff; border-color: #000; }
:root[data-theme="light"] .auth__social--apple:hover { background: #1d1d1f; border-color: #1d1d1f; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .auth__social--apple { background: #000; color: #fff; border-color: #000; }
  :root:not([data-theme]) .auth__social--apple:hover { background: #1d1d1f; border-color: #1d1d1f; }
}
.auth__pending {
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: 1.5;
  margin: var(--sp-4) 0 0;
}
.auth__pending strong { color: var(--text2); }

/* Empilés : côte à côte, les deux libellés se coupaient en milieu de phrase
   dans une carte de 408 px. */
.auth__foot {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-4);
  margin-top: var(--sp-7); padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  text-align: center;
}
.auth__link { font-size: var(--t-base); color: var(--gold); }
.auth__link:hover { color: var(--gold-strong); }

/* « Mot de passe oublié ? » — discret par demande : aligné à droite sous le
   champ mot de passe, encre atténuée (text3, le plancher de contraste), or au
   survol comme tout ce qui se touche. Une rangée à part pour ne pas coller au
   champ ni au bouton de soumission. */
.auth__aux { display: flex; justify-content: flex-end; margin-top: var(--sp-4); }
.auth__link--muted { font-size: var(--t-cap); color: var(--text3); }
.auth__link--muted:hover { color: var(--gold); }

/* ==========================================================================
   Panneau latéral de carte
   ========================================================================== */

.sheet { position: fixed; inset: 0; z-index: 150; visibility: hidden; }
.sheet.is-open { visibility: visible; }

.sheet__backdrop {
  position: absolute; inset: 0;
  background: var(--veil-soft);
  opacity: 0;
  transition: opacity var(--dur-out) var(--ease-std);
}
.sheet.is-open .sheet__backdrop { opacity: 1; }

.sheet__panel {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  /* 2026-08-13 — 430 → 486 px. Le panneau porte désormais neuf sections,
     dont un graphe dont l'axe des ordonnées mange 62 px : à 430 px il
     restait ~280 px de tracé, et « 1 438,25 € FR_NM » ne tenait pas sur une
     ligne dans les tables de sources. 486 px laisse le tracé respirer sans
     couvrir la grille de cartes derrière — c'est elle qui donne le contexte
     de la navigation ↑↓. */
  width: min(486px, 92vw);
  background: var(--surface);
  border-left: 1px solid var(--border-hi);
  display: flex;
  flex-direction: column;
  outline: 0;
  /* Entrée 200 ms / sortie 160 ms, courbe iOS : la sortie est plus rapide que
     l'entrée, sinon fermer paraît lent. Aucun rebond (canon : 0 overshoot). */
  transform: translateX(100%);
  transition: transform var(--dur-out) cubic-bezier(0.32, 0.72, 0, 1);
}
.sheet.is-open .sheet__panel {
  transform: translateX(0);
  transition-duration: var(--dur-in);
}

.sheet__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-5);
  padding: var(--sp-7) var(--sp-7) var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.sheet__title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-xl);
  letter-spacing: var(--tr-lg);
  margin: var(--sp-2) 0 0;
}
.sheet__alt { font-size: var(--t-sm); color: var(--text3); margin-top: var(--sp-1); }
.sheet__close {
  font-size: var(--t-lg); color: var(--text3);
  padding: var(--sp-2) var(--sp-4); border-radius: 7px; flex: none;
}
.sheet__close:hover { background: var(--surface2); color: var(--text); }

.sheet__body { flex: 1; overflow-y: auto; padding: var(--sp-6) var(--sp-7) var(--sp-7); }

.sheet__art {
  width: 190px;
  aspect-ratio: 5 / 7;
  object-fit: cover;
  border-radius: 9px;
  display: block;
  margin: 0 auto var(--sp-7);
  /* Ombre légitime : la profondeur de l'objet physique fait partie de
     l'identité de la carte (canon §Élévation). */
  box-shadow: var(--shadow-art);
}
.sheet__art--none {
  display: flex; align-items: center; justify-content: center;
  background: var(--surface2);
  border: 1px dashed var(--border-hi);
  color: var(--text3); font-size: var(--t-sm);
  box-shadow: none;
}

.sheet__owned {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--radius);
  background: var(--surface2);
  border: 1px solid var(--gold-dim);
  margin-bottom: var(--sp-7);
}
.sheet__owned--no { border-color: var(--border); justify-content: center; }
.sheet__ownqty {
  font-family: var(--font-serif);
  font-size: var(--t-xl);
  color: var(--gold);
  margin-top: var(--sp-1);
}

.sheet__h3 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--t-lg);
  margin: 0 0 var(--sp-2);
}
/* ---- LE RYTHME DU PANNEAU (2026-08-13) ----
   Sept sections se suivaient à 20 px d'écart, toutes avec un titre serif de
   même taille et un paragraphe d'explication de même graisse que les
   données : un mur uniforme où rien ne dit où une section finit. Le filet
   + l'écart de groupe (32 px, deux crans au-dessus de l'espacement
   intérieur) rétablissent l'invariant de proximité. */
.sheet__sec { margin-top: var(--sp-9); padding-top: var(--sp-7); border-top: 1px solid var(--border); }
.sheet__sec--first { margin-top: var(--sp-7); }
/* Une SOUS-partie : elle appartient à la section, elle ne la concurrence pas.
   D'où un titre en mono capitales (`.subhead`) et non un second serif. */
.sheet__sub { margin-top: var(--sp-7); }
.sheet__sub:first-of-type { margin-top: var(--sp-5); }
.subhead__sc {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  color: var(--gold-dim);
  margin-left: var(--sp-3);
}
/* LA PROSE N'EST PAS DE LA DONNÉE. Dans le panneau elle portait `t-sm` et
   `text2` — la graisse et l'encre des chiffres qu'elle explique. Elle passe
   en 11 px / `text3`, à l'interligne de LECTURE, bornée. */
.sheet__hint {
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: var(--lh-body);
  margin: 0 0 var(--sp-5);
  max-width: 62ch;
  text-wrap: pretty;
}
.sheet__hint strong { color: var(--text2); font-weight: 600; }
/* L'état « interrogé, rien à tracer » — il occupe la place du graphe qu'il
   remplace et DATE son propos, au lieu de disparaître en silence. */
.sheet__void {
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: var(--lh-body);
  margin: 0;
  padding: var(--sp-6);
  border: 1px dashed var(--border-hi);
  border-radius: var(--radius);
  background: var(--bg-soft);
  max-width: 62ch;
  text-wrap: pretty;
}
.sheet__void b { color: var(--text2); font-family: var(--font-mono); font-weight: 500; }
.sheet__sb { font-size: var(--t-sm); color: var(--text3); margin-top: var(--sp-2); }
.sheet__illus {
  color: var(--gold); font-size: var(--t-sm); padding: 0;
  border-bottom: 1px dotted var(--gold-dim);
}
.sheet__illus:hover { color: var(--gold-strong); }

.sheet__stage { position: relative; }
.sheet__chips {
  position: absolute; left: 0; right: 0; bottom: 20px;
  display: flex; gap: var(--sp-3); justify-content: center; flex-wrap: wrap;
}
/* Le tirage est un chip DORÉ — « 1ʳᵉ Édition » vaut six fois « Unlimited »
   sur la même carte : c'est l'information la plus chère du panneau. */
.chip--print {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.06em;
  color: var(--gold); border: 1px solid var(--gold-dim);
  background: var(--overimg);
  padding: var(--sp-2) var(--sp-3); border-radius: 5px;
}

/* ---- Les marchés superposés ---- */
.mk { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-5); }
.mkr {
  display: grid; grid-template-columns: 34px minmax(0, 1fr) 84px 62px;
  align-items: center; gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-3); border-radius: 6px;
}
.mkr:hover { background: var(--surface2); }
.mkr.is-current { background: var(--gold-a07); box-shadow: inset 2px 0 0 var(--gold); }
.mkr__l { font-family: var(--font-mono); font-size: var(--t-label); color: var(--text3); }
.mkr__b { height: 13px; background: var(--bg-soft); border-radius: 3px; overflow: hidden; }
.mkr__b i { display: block; height: 100%; border-radius: 3px; }
/* Pas de cote sur ce marché : la piste PLEINE se lisait comme une barre à
   zéro, donc comme un prix de 0 € — la confusion exacte que « — » existe
   pour éviter. Un filet pointillé tient la colonne sans rien affirmer. */
.mkr--none .mkr__b {
  background: transparent;
  border-bottom: 1px dashed var(--border-hi);
  height: 7px;
  border-radius: 0;
}
.mkr__v { font-family: var(--font-mono); font-size: var(--t-sm); text-align: right; }
.mkr__a { font-family: var(--font-mono); font-size: var(--t-label); color: var(--text3); text-align: right; }
.sheet__scopes { margin-top: var(--sp-5); }
.sheet__scopes td { padding: var(--sp-3) var(--sp-3); font-size: var(--t-sm); }
.sheet__scopes tr.is-current td { background: var(--gold-a07); }
.sheet__scopes tr.is-current td:first-child { box-shadow: inset 2px 0 0 var(--gold); }

.sheet__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-5);
  padding: var(--sp-5) var(--sp-7);
  border-top: 1px solid var(--border);
}
.sheet__foot .chip[disabled] { opacity: 0.35; cursor: default; }

/* La tuile ouverte reste repérable derrière le panneau. */
.tile.is-active .tile__frame { box-shadow: 0 0 0 2px var(--gold); }

/* ==========================================================================
   Palette de commandes (⌘K)
   ========================================================================== */

.cmdk { position: fixed; inset: 0; z-index: 200; display: none; }
.cmdk.is-open { display: block; }
.cmdk__backdrop { position: absolute; inset: 0; background: var(--veil); }
.cmdk__panel {
  position: relative;
  width: min(620px, calc(100vw - 40px));
  margin: 11vh auto 0;
  background: var(--surface2);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-cmdk);
}
.cmdk__input {
  width: 100%;
  padding: var(--sp-6) var(--sp-7);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--border);
  outline: 0;
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--t-lg);
}
.cmdk__input::placeholder { color: var(--text3); }
.cmdk__list { max-height: 52vh; overflow-y: auto; padding: var(--sp-3); }
.cmdk__row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-radius: 8px;
  cursor: pointer;
}
.cmdk__row.is-cur { background: var(--surface3); }
.cmdk__row.is-cur .cmdk__label { color: var(--gold); }
.cmdk__kind {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text3);
  min-width: 66px;
  flex: none;
}
.cmdk__label { font-size: var(--t-md); font-weight: 500; }
.cmdk__sub { font-size: var(--t-sm); color: var(--text3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdk__n { margin-left: auto; font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); flex: none; }
.cmdk__empty { padding: var(--sp-8); text-align: center; color: var(--text3); font-size: var(--t-base); }
.cmdk__foot {
  display: flex; gap: var(--sp-6);
  padding: var(--sp-4) var(--sp-6);
  border-top: 1px solid var(--border);
  font-size: var(--t-cap);
  color: var(--text3);
}
.cmdk__foot kbd {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  border: 1px solid var(--border-hi);
  border-radius: 4px;
  padding: var(--sp-1) var(--sp-2);
  margin-right: var(--sp-2);
}

/* ==========================================================================
   LES ÉTATS VIDES — quatre objets distincts, pas un « aucune donnée ».

   Le défaut mesuré : un seul `.empty` — un titre gris centré dans 1 000 px
   de noir — servait à la fois de « tapez pour chercher », de « aucun
   résultat » et de « rien ici ». Fondre ces cas dans un seul composant est le
   signe de qualité le plus visible qu'on puisse rater, parce que les quatre
   n'appellent PAS la même action :

     · PREMIÈRE FOIS   il n'y a jamais rien eu   → une action qui amorce
     · AUCUN RÉSULTAT  un filtre a tout exclu    → RAPPELER la requête + sortie
     · INVITE          on attend une saisie      → montrer la SYNTAXE
     · EMPÊCHÉ         ça a échoué               → dire quoi, et REJOUER

   Structure commune, cinq éléments au plus : glyphe monochrome discret (pas
   d'illustration colorée — dans un outil qui cote de l'argent, ça décrédibilise),
   titre qui énonce le FAIT, une phrase qui dit pourquoi et ce qui remplira,
   une action, et rien d'autre.

   PIÈGE, celui qui se produit vraiment : ne jamais peindre un état vide
   pendant que la requête est encore en vol. La condition est
   `!chargement && !erreur && n === 0` — sinon on annonce « aucun résultat »
   sur une page qui va s'en remplir.
   ========================================================================== */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--sp-10) var(--sp-7);
  max-width: 420px;
  margin: 0 auto;
  color: var(--text3);
}
/* Le glyphe : un trait, dans un carré à peine plus clair. Il occupe la place
   d'une illustration sans en prendre le ton. */
.empty__glyph {
  width: 44px; height: 44px;
  border-radius: 12px;
  background: var(--surface2);
  border: 1px solid var(--border);
  box-shadow: var(--lift);
  display: grid; place-items: center;
  margin-bottom: var(--sp-6);
  color: var(--text3);
}
.empty__glyph svg { width: 20px; height: 20px; }
.empty__title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--t-lg);
  letter-spacing: var(--tr-lg);
  color: var(--text);
  margin: 0 0 var(--sp-3);
}
.empty__body {
  font-size: var(--t-base);
  line-height: var(--lh-body);
  color: var(--text3);
  margin: 0;
  max-width: 58ch;
  text-wrap: pretty;
}
/* La requête est CITÉE : sans elle, « aucun résultat » ne dit pas de quoi. */
.empty__q { font-family: var(--font-mono); color: var(--text2); }
.empty__actions { display: flex; gap: var(--sp-4); margin-top: var(--sp-6); flex-wrap: wrap; justify-content: center; }
/* L'aperçu FANTÔME de l'état plein : une ligne d'exemple à 40 %. Elle
   convertit mieux que n'importe quelle illustration et ne coûte rien —
   surtout, elle montre ce qu'on obtiendra. */
.empty__ghost { width: 100%; margin-top: var(--sp-8); opacity: 0.4; pointer-events: none; }
.empty--blocked .empty__glyph { border-color: var(--amber); color: var(--amber); }
/* Variante PLEINE PAGE : quand l'état vide est TOUT l'écran (l'invite de la
   recherche), il se centre dans ce qui reste sous l'en-tête. Posé en haut
   d'un viewport de 1 000 px, il laissait 700 px de noir sous lui et se lisait
   comme un chargement inachevé plutôt que comme une invitation. */
.empty--page { min-height: min(46vh, 420px); justify-content: center; }
/* Variante EN LIGNE : pour un panneau, où le vide n'a pas 400 px à occuper. */
.empty--inline { padding: var(--sp-8) var(--sp-6); }
.empty--inline .empty__glyph { width: 34px; height: 34px; border-radius: 9px; margin-bottom: var(--sp-5); }
.empty--inline .empty__glyph svg { width: 16px; height: 16px; }
.empty--inline .empty__title { font-size: var(--t-md); }
.empty--inline .empty__body { font-size: var(--t-cap); }

/* Écrans étroits : le rail se réduit, la grille s'adapte toute seule. */
@media (max-width: 1180px) {
  .cols--set { grid-template-columns: 1fr; }
  .cols--2 { grid-template-columns: 1fr; }
}
/* ==========================================================================
   RESPONSIVE — du bureau (1440+) au mobile (375).

   Le canon (§3) : le mobile n'est pas le bureau rétréci. Deux modes de chrome,
   UN seul point de bascule à 900 px, pour que le geste soit le même partout :

     · ≥ 901 px — le rail fixe habituel (convention Koyfin / Linear) ;
     · ≤ 900 px — le rail devient un TIROIR escamotable, ouvert par le burger
       du bandeau. Tablette (768) et téléphone (375) partagent EXACTEMENT le
       même geste : un utilisateur qui apprend un écran connaît les autres.

   Les tables denses ne rétrécissent jamais leurs colonnes sous un seuil : elles
   défilent DANS `.tablescroll`, jamais le corps de page. Les grilles fluides
   retombent en une colonne. Aucune donnée n'est tronquée sans que sa valeur
   complète reste atteignable (contrôle de rendu n°2).
   ========================================================================== */

/* Le burger et le voile n'existent QU'en mode tiroir : masqués sur le bureau,
   ils ne prennent aucune place et ne sont pas atteignables au clavier. */
.topbar__burger { display: none; }
.nav-backdrop { display: none; }

@media (max-width: 900px) {
  /* ---- Le chrome bascule en mode tiroir ---- */

  /* La coquille ne peint plus la colonne du rail (le rail quitte le flux) ;
     le corps prend toute la largeur. */
  .shell { display: block; background: var(--bg); }

  /* Le burger prend la place du rail permanent. 44 px = la cible du DOIGT. Le
     retrait négatif aligne le glyphe sur la marge gauche de la page. */
  .topbar__burger {
    display: inline-grid;
    place-items: center;
    width: 44px; height: 44px;
    flex: none;
    margin-left: calc(var(--sp-5) * -1);
    border-radius: 10px;
    color: var(--text2);
    transition: color var(--dur-fast) var(--ease-std), background var(--dur-fast) var(--ease-std);
  }
  .topbar__burger:hover { color: var(--text); background: var(--surface2); }
  .topbar__burger svg { width: 22px; height: 22px; }

  /* Le rail devient un tiroir hors-champ à gauche. `visibility:hidden` fermé le
     sort de l'ordre de tabulation : un menu invisible ne doit pas être
     atteignable au clavier (WCAG). La bascule de visibilité est différée à la
     fermeture (le tiroir reste visible le temps de glisser dehors), immédiate à
     l'ouverture. */
  .rail {
    position: fixed;
    top: 0; left: 0;
    width: min(86vw, 300px);
    height: 100dvh;
    z-index: 200;
    border-right: 1px solid var(--border-hi);
    box-shadow: var(--shadow-drawer);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform var(--dur-panel) var(--ease-sheet),
                visibility 0s linear var(--dur-panel);
  }
  body.nav-open .rail {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--dur-panel) var(--ease-sheet), visibility 0s;
  }

  /* Le voile — sous le tiroir, au-dessus du contenu. Il ferme au toucher. */
  .nav-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 190;
    background: var(--veil-soft);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-out) var(--ease-std),
                visibility 0s linear var(--dur-out);
  }
  body.nav-open .nav-backdrop {
    opacity: 1;
    visibility: visible;
    transition: opacity var(--dur-in) var(--ease-std), visibility 0s;
  }
  /* Tiroir ouvert : le fond ne défile plus sous le voile. */
  body.nav-open { overflow: hidden; }

  /* Dans un tiroir on a la largeur : les libellés du rail sont pleins (rien
     n'est masqué comme sur l'ancien rail-icône), et chaque lien porte la cible
     du doigt. */
  .rail__link { min-height: 44px; }

  /* ---- Le bandeau ---- */
  .topbar { padding: 0 var(--sp-6); gap: var(--sp-4); }
  .page { padding: var(--sp-7) var(--sp-6) var(--sp-10); }

  /* La porte de recherche devient un bouton-icône carré (le raccourci ⌘K n'a
     pas de sens au doigt) ; 44 px de cible. */
  .topbar__search {
    min-width: 0; width: 44px; height: 44px;
    padding: 0; justify-content: center;
  }
  .topbar__search kbd { display: none; }

  /* Le relevé perd son libellé avant son périmètre : un montant sans son marché
     oblige à deviner, un montant sans son mot ne coûte qu'un instant. */
  .topbar__qlab { display: none; }

  /* Cibles tactiles des contrôles denses : la densité 30 px du bureau vise le
     CURSEUR ; au doigt on remonte vers 44 (canon §3). */
  table.data tbody td { padding-top: var(--sp-5); padding-bottom: var(--sp-5); }
  table.data tbody tr { contain-intrinsic-size: auto 44px; }
  .chip, .segmented button, .seg--mini button, .vtog {
    min-height: 40px; display: inline-flex; align-items: center;
  }

  /* ---- Objets denses du tableau de bord et du marché ---- */

  /* La bande de KPI (héros + 5 mesures) ne peut pas garder 6 colonnes : elles
     deviennent illisibles. Elle s'enroule en cases d'au moins 148 px, le héros
     occupe la largeur, et les séparateurs passent en filet horizontal. */
  .strip, .strip--qual { grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); }
  .strip .strip__cell:first-child,
  .strip--qual .strip__cell:first-child { grid-column: 1 / -1; }
  .strip__cell { border-left: 0; border-top: 1px solid var(--border); }
  .strip .strip__cell:first-child { border-top: 0; }

  /* Le héros du marché : la jauge passe AU-DESSUS de la phrase. */
  .mk-hero { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
  .mk-hero__gauge { justify-self: start; }
}

/* Sous 640 px : le relevé du bandeau s'efface — burger + titre + recherche +
   session tiennent alors sans se chevaucher. Le patrimoine reste à un geste (le
   tableau de bord), et un bandeau lisible vaut mieux qu'un relevé écrasé. On ne
   montre JAMAIS le montant sans sa pastille de périmètre : c'est tout ou rien,
   jamais un chiffre nu (canon §7). */
@media (max-width: 640px) {
  .topbar__quote { display: none; }

  /* Le classement signé : la barre rétrécit pour rendre de la place au nom. */
  .srow { grid-template-columns: minmax(0, 1fr) 88px 78px; gap: var(--sp-4); }

  /* Le classement de composition : le repère de coût et le chevron cèdent la
     place ; la ligne reste cliquable en entier, le tap ouvre le forage. */
  .comp__rowhead, .comp__row {
    grid-template-columns: 10px minmax(0, 1.3fr) minmax(48px, 1fr) 64px 48px;
    gap: var(--sp-3);
  }
  .comp__rowhead > :nth-child(6), .comp__row > :nth-child(6),
  .comp__rowhead > :nth-child(7), .comp__row > :nth-child(7) { display: none; }
}

/* Sous 480 px : la marge de page se resserre pour rendre chaque pixel aux
   tables et grilles, et le fil d'Ariane garde le seul titre. */
@media (max-width: 480px) {
  .page { padding: var(--sp-6) var(--sp-5) var(--sp-9); }
  .topbar { padding: 0 var(--sp-5); }
  .topbar__trail, .topbar__sep { display: none; }
  .pockets { grid-template-columns: minmax(0, 1fr); }
}

/* ===========================================================================
   ASSISTANT — la collection en langage naturel.

   Trois règles portées par le style, pas seulement par le code :
     · l'OR ne marque que ce qui se touche — ici, UN seul geste compte :
       « Confirmer » un brouillon (et l'envoi). Tout le reste est encre.
     · la carte de brouillon est le point focal de l'écran : c'est la seule
       surface élevée (surface2 + lift), parce que c'est la seule décision.
     · les montants sont en mono tabulaire, comme partout.
   =========================================================================== */
/* La hauteur suit le chrome : 52 px de bandeau + 24 px de marge haute +
   64 px de respiration basse. L'ancien 175 px comptait l'en-tête de page de
   90 px qui n'existe plus — le panneau perdait 27 px pour rien. */
.asst { display: flex; flex-direction: column; height: calc(100vh - 148px); min-height: 420px; padding: 0; overflow: hidden; }
/* La garantie, sous le champ : elle occupe la largeur de la barre et se lit
   au moment d'écrire. `flex-basis: 100%` la fait passer à la ligne SOUS le
   champ et le bouton, dans la même boîte — elle appartient à ce geste. */
.asst__hint {
  flex-basis: 100%;
  margin: var(--sp-4) 0 0;
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: var(--lh-body);
}
.asst__scroll { flex: 1; overflow-y: auto; padding: var(--sp-7) var(--sp-7) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-6); }

.asst__turn { display: flex; flex-direction: column; gap: var(--sp-4); }
.asst__turn--user { align-items: flex-end; }
.asst__turn--assistant { align-items: flex-start; }
.asst__bubble {
  max-width: 68ch; padding: var(--sp-5) var(--sp-6); border-radius: 12px;
  background: var(--surface2); border: 1px solid var(--border);
  box-shadow: var(--lift); font-size: var(--t-md); line-height: 1.55; color: var(--text);
}
.asst__bubble--user { background: var(--surface3); border-color: var(--border-hi); color: var(--text2); }

/* Le markdown rendu : des blocs respirés, pas un pavé. */
.asst__bubble--md { line-height: 1.62; }
.asst__bubble--md p { margin: 0 0 var(--sp-5); }
.asst__bubble--md p:last-child { margin-bottom: 0; }
.asst__bubble--md strong { color: var(--text); font-weight: 600; }
.asst__bubble--md ul { margin: 0 0 var(--sp-5); padding-left: var(--sp-7); }
.asst__bubble--md li { margin: var(--sp-2) 0; }
.asst__bubble--md li::marker { color: var(--gold-dim); }
.asst__md-h {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text3);
  margin: var(--sp-6) 0 var(--sp-3) !important;
}
.asst__tablewrap { overflow-x: auto; margin: var(--sp-2) 0 var(--sp-5); }
.asst__table { border-collapse: collapse; font-size: var(--t-base); min-width: 60%; }
.asst__table th {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text3); font-weight: 500;
  text-align: left; padding: var(--sp-2) var(--sp-6) var(--sp-2) 0;
  border-bottom: 1px solid var(--border-hi);
}
.asst__table td {
  padding: var(--sp-2) var(--sp-6) var(--sp-2) 0; border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.asst__table tr:last-child td { border-bottom: 0; }
.asst__table td:first-child { color: var(--text); }

/* La trace d'outils : ce que l'assistant a réellement consulté — de
   l'encre discrète, jamais une bulle. Un refus d'outil reste VISIBLE :
   c'est une information (« j'ai refusé de deviner »), pas une honte. */
.asst__trace { display: flex; flex-direction: column; gap: var(--sp-1); padding-left: var(--sp-2); }
.asst__trace-line { font-size: var(--t-cap); color: var(--text3); }
.asst__trace-line.is-refused { color: var(--amber); }

/* La carte de brouillon — LE point focal. */
.asst__draft {
  width: min(480px, 100%); border: 1px solid var(--border-hi); border-radius: 12px;
  background: var(--surface2); box-shadow: var(--lift); padding: var(--sp-6) var(--sp-6);
}
.asst__draft.is-done { opacity: 0.72; }
.asst__draft-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--sp-4); }
.asst__draft-kind {
  font-size: var(--t-cap); letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--gold); font-weight: 600;
}
.asst__done { font-size: var(--t-sm); color: var(--text3); }
.asst__done--ok { color: var(--green); }
.asst__draft-name { font-size: var(--t-md); color: var(--text); display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
.asst__namefr { color: var(--text3); font-size: var(--t-base); }
.asst__draft-note { font-size: var(--t-sm); color: var(--text3); margin-top: var(--sp-2); }
.asst__draft-grid { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-7); margin-top: var(--sp-5); }
.asst__k { display: block; font-size: var(--t-label); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text3); }
.asst__v { font-size: var(--t-base); color: var(--text); }
.asst__v--money { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.asst__draft-lines { margin-top: var(--sp-5); border-top: 1px solid var(--border); padding-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); }
.asst__draft-line { display: flex; justify-content: space-between; font-size: var(--t-sm); color: var(--text2); }
.asst__draft-err { margin-top: var(--sp-4); font-size: var(--t-sm); color: var(--red); }
.asst__draft-actions { display: flex; gap: var(--sp-4); margin-top: var(--sp-5); }

.asst__btn { padding: var(--sp-4) var(--sp-6); border-radius: 9px; font-size: var(--t-base); font-weight: 600; }
.asst__btn--p { background: var(--gold); color: var(--ink-on-gold); }
.asst__btn--p:hover:not([disabled]) { background: var(--gold-strong); }
.asst__btn--s { border: 1px solid var(--border-hi); color: var(--text2); }
.asst__btn[disabled] { opacity: 0.5; cursor: default; }

/* Barre d'entrée */
.asst__bar { display: flex; flex-wrap: wrap; gap: var(--sp-5); padding: var(--sp-6) var(--sp-7); border-top: 1px solid var(--border); background: var(--surface); }
.asst__input {
  /* `--bg2` n'a JAMAIS existé dans le bloc de thème : le champ de saisie de
     l'assistant était donc transparent sur sa barre, sans erreur ni signal.
     Le jeton s'appelle `--bg-soft`. */
  flex: 1; background: var(--bg-soft); border: 1px solid var(--border-hi); border-radius: 10px;
  padding: var(--sp-5) var(--sp-6); font-size: var(--t-md); color: var(--text);
}
.asst__input::placeholder { color: var(--text4); }
.asst__input:focus { outline: none; border-color: var(--gold-dim); }

/* ---- L'INTRODUCTION — ce que l'assistant SAIT FAIRE ----

   L'état vide était un titre, une phrase et trois pastilles empilées au
   centre. Trois longueurs différentes centrées font un DRAPEAU : une forme
   qui n'encode rien et que l'œil lit comme un défaut d'alignement. Et le
   tout flottait au milieu d'une boîte de 900 px, si bien que l'écran d'accueil
   de l'assistant était surtout du noir.

   Trois cartes de largeur ÉGALE, alignées à gauche, chacune portant sa
   capacité (Retrouver / Ajouter / Mesurer) : la réponse à « qu'est-ce que ça
   sait faire ? », qui est la vraie question du premier écran. */
.asst__intro {
  /* Centré VERTICALEMENT, aligné à gauche : le bloc s'appuie sur le même
     bord que les bulles de conversation qui le remplaceront. */
  margin: auto 0;
  width: 100%;
  max-width: 940px;
  padding: var(--sp-8) 0;
}
.asst__intro-title {
  font-family: var(--font-serif);
  font-size: var(--t-2xl);
  letter-spacing: var(--tr-2xl);
  color: var(--text);
  margin-bottom: var(--sp-5);
}
.asst__intro-sub {
  font-size: var(--t-md);
  color: var(--text2);
  line-height: var(--lh-body);
  margin: 0 0 var(--sp-9);
  max-width: 66ch;
  text-wrap: pretty;
}
.asst__k {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text3);
  margin-bottom: var(--sp-5);
}
.asst__suggests {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-6);
  align-items: stretch;
}
@media (max-width: 1000px) { .asst__suggests { grid-template-columns: minmax(0, 1fr); } }
.sugg {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  text-align: left;
  padding: var(--sp-6);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface2);
  box-shadow: var(--lift);
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.sugg:hover { border-color: var(--gold-dim); transform: translateY(-1px); }
.sugg__kind {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--gold);
}
.sugg__text { font-size: var(--t-base); color: var(--text); line-height: var(--lh-ui); }
.sugg__note {
  font-size: var(--t-cap);
  color: var(--text3);
  line-height: var(--lh-body);
  margin-top: auto;
  padding-top: var(--sp-3);
  text-wrap: pretty;
}

/* États honnêtes : quota / budget / session — un bandeau, pas une erreur brute. */
.asst__gate {
  border: 1px solid var(--amber); border-radius: 10px; padding: var(--sp-5) var(--sp-6);
  font-size: var(--t-base); color: var(--text2); background: var(--amber-a06);
}
.asst__gate--error { border-color: var(--red); background: var(--red-a06); }
.asst__gate-link { color: var(--gold); }

/* Attente */
.asst__thinking { font-size: var(--t-sm); color: var(--text3); display: flex; align-items: center; gap: var(--sp-3); }
.asst__dots i {
  display: inline-block; width: 4px; height: 4px; border-radius: 50%;
  background: var(--text3); margin-right: var(--sp-2); animation: asst-blink 1.2s infinite;
}
.asst__dots i:nth-child(2) { animation-delay: 0.2s; }
.asst__dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes asst-blink { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }

/* ==========================================================================
   LES QUATRE POCHES DU PATRIMOINE (portefeuille)

   Le défaut corrigé n'était pas esthétique : le scellé et les gradées
   n'existaient nulle part à l'écran, alors que le héros les comptait.
   Nathan : « dans le portefeuille de la partie web on peut pas voir scellé
   et gradée, c'est pas clair. »

   La forme suit donc une seule contrainte : la pastille doit dire ce qu'elle
   contient SANS être cliquée — nom, compte, valeur, et le nombre de lignes
   sans cote. Une pastille muette aurait laissé le même trou avec un onglet
   de plus. Ce n'est pas un `.segmented` (qui filtre une même population) :
   ce sont QUATRE populations différentes, avec quatre schémas, et la forme
   doit le dire — d'où la carte, pas le bouton de barre.
   ========================================================================== */
.pockets {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-4); margin-bottom: var(--sp-6);
}
.pocket {
  display: flex; align-items: flex-start; gap: var(--sp-4); text-align: left;
  padding: var(--sp-5) var(--sp-5);
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); box-shadow: var(--lift);
  transition: border-color var(--dur-fast) var(--ease-std), background var(--dur-fast) var(--ease-std);
}
.pocket:hover { border-color: var(--border-hi); background: var(--surface2); }
/* L'état actif se lit aux TROIS voix — filet, fond, encre du montant —
   parce que le seul filet doré ne se distingue pas d'un survol. */
.pocket.is-on { border-color: var(--gold-dim); background: var(--gold-a05); }
.pocket.is-on .pocket__v { color: var(--gold); }
/* Le swatch reprend EXACTEMENT la couleur de la frise de répartition : les
   deux objets décrivent la même partition, et deux couleurs différentes
   pour la même poche les feraient lire comme deux découpes distinctes. */
.pocket__sw { width: 3px; align-self: stretch; border-radius: 2px; flex: none; min-height: 34px; }
.pocket__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pocket__label {
  font-size: var(--t-sm); color: var(--text2); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.pocket.is-on .pocket__label { color: var(--text); }
.pocket__n { font-family: var(--font-mono); font-size: var(--t-cap); color: var(--text3); margin-left: var(--sp-2); }
.pocket__v { font-family: var(--font-mono); font-size: var(--t-md); font-weight: 600; color: var(--text); }
.pocket__miss { font-size: var(--t-cap); font-family: var(--font-mono); }

@media (max-width: 1120px) {
  .pockets { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* La légende de répartition devient un CHEMIN : « tout ce qui porte une
   sous-population se clique ». Le bouton garde la grille de `.legend__row`
   pour que les deux formes restent alignées au pixel. */
.legend__row--btn {
  width: 100%; background: transparent; border: 0; border-radius: 6px;
  padding: var(--sp-2) var(--sp-3); margin-left: calc(var(--sp-3) * -1);
  text-align: left; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-std);
}
.legend__row--btn:hover { background: var(--surface2); }
.legend__row--btn.is-on { background: var(--gold-a07); }
.legend__row--btn.is-on .legend__label { color: var(--text); }

/* La preuve que les quatre parts composent le tout. Discrète quand elle est
   vraie, ALARMANTE quand elle ne l'est pas : c'est le seul cas où une note
   de bas de panneau mérite la couleur d'alerte. */
.panel__recon { margin: var(--sp-5) 0 0; font-family: var(--font-mono); font-size: var(--t-cap); }
.panel__recon--off { color: var(--red); font-family: inherit; font-size: var(--t-sm); line-height: var(--lh-body); }

/* ---- Les trois autres tables ----
   Mêmes principes que `.data--pf` : layout FIXE, largeurs mesurées sur les
   lignes réelles, colonne « nom » sans largeur (l'absorbeur). Une colonne
   bornée tronquée est un défaut — « 6… » au lieu d'un prix a l'air d'être là. */
.data--graded, .data--sealed, .data--vintage { table-layout: fixed; width: 100%; }
.data--graded th, .data--graded td,
.data--sealed th, .data--sealed td,
.data--vintage th, .data--vintage td { overflow: hidden; text-overflow: ellipsis; }

.data--graded { min-width: 1080px; }
.data--graded th:nth-child(1) { width: 68px; }
.data--graded th:nth-child(3) { width: 190px; }  /* set */
.data--graded th:nth-child(4) { width: 132px; }  /* grade — « PURE GRADING 10 » */
.data--graded th:nth-child(5) { width: 120px; }  /* certificat */
.data--graded th:nth-child(6) { width: 210px; }  /* cote gradée + provenance */
.data--graded th:nth-child(7) { width: 100px; }
.data--graded th:nth-child(8) { width: 108px; }
.data--graded th:nth-child(9) { width: 96px; }
.data--graded .cell-name { max-width: none; }

.data--sealed { min-width: 1160px; }
.data--sealed th:nth-child(1) { width: 68px; }
.data--sealed th:nth-child(3) { width: 168px; }  /* type — « Display de mini-tins » */
.data--sealed th:nth-child(4) { width: 68px; }
.data--sealed th:nth-child(5) { width: 48px; }
.data--sealed th:nth-child(6) { width: 200px; }  /* cote + « EBAY 30 J » */
.data--sealed th:nth-child(7) { width: 100px; }
.data--sealed th:nth-child(8) { width: 100px; }
.data--sealed th:nth-child(9) { width: 108px; }
.data--sealed th:nth-child(10) { width: 96px; }
.data--sealed .cell-name { max-width: none; }

.data--vintage { min-width: 1120px; }
.data--vintage th:nth-child(1) { width: 68px; }
.data--vintage th:nth-child(3) { width: 200px; }  /* série */
.data--vintage th:nth-child(4) { width: 68px; }   /* année */
.data--vintage th:nth-child(5) { width: 48px; }
.data--vintage th:nth-child(6) { width: 176px; }
.data--vintage th:nth-child(7) { width: 100px; }
.data--vintage th:nth-child(8) { width: 100px; }
.data--vintage th:nth-child(9) { width: 108px; }
.data--vintage th:nth-child(10) { width: 96px; }
.data--vintage .cell-name { max-width: none; }

/* Une ligne qui n'ouvre rien ne fait PAS semblant : pas de curseur pointeur,
   pas de surbrillance de survol appuyée. Une affordance qui ne mène nulle
   part coûte autant qu'une affordance manquante. */
.data--graded tbody tr, .data--sealed tbody tr, .data--vintage tbody tr { cursor: default; }

/* ==========================================================================
   GESTION DES COLLECTIONS — renommer, supprimer

   La suppression est le seul geste destructeur de cette surface. Elle porte
   donc sa propre couleur (rouge = perte, jamais l'or de l'interactif), et sa
   confirmation DIT ce qui reste avant de dire ce qui part : une collection
   est une étiquette sur des positions, pas un conteneur — supprimer
   « Full Set Rocket Gang JP » ne supprime aucune des 65 cartes.
   ========================================================================== */
.cmanager__sheet { width: min(460px, calc(100vw - 32px)); }
.cmanager .cpicker__sheet { width: min(460px, calc(100vw - 32px)); }
.cmanager__row {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5); border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg-soft);
}
.cmanager__row--danger { border-color: var(--red); background: var(--red-a06); }
.cmanager__id { display: flex; align-items: center; gap: var(--sp-3); flex: 1; min-width: 0; }
.cmanager__btns { display: flex; gap: var(--sp-2); flex: none; }
.chip--mini { padding: var(--sp-2) var(--sp-4); font-size: var(--t-cap); }
.chip--danger { border-color: var(--red-a45); color: var(--red); }
.chip--danger:hover { background: var(--red-a12); color: var(--red); border-color: var(--red); }
.cmanager__confirm { display: flex; flex-direction: column; gap: var(--sp-3); width: 100%; }
.cmanager__note { margin: 0; font-size: var(--t-sm); line-height: var(--lh-body); color: var(--text2); }
.cmanager__actions { display: flex; gap: var(--sp-3); justify-content: flex-end; }

/* Le point d'entrée dans la bande de collections. Il ne PORTE pas de
   valeur — c'est un geste, pas une sous-population — donc il se distingue
   des chips de collection par son encre et son filet pointillé. */
.collchip--manage {
  border-style: dashed; color: var(--text3);
  font-size: var(--t-sm); font-weight: 500;
}
.collchip--manage:hover { color: var(--gold); border-color: var(--gold-dim); }

/* ==========================================================================
   PANNEAU DE CARTE — la bande de tête et la ligne d'ouverture

   Nathan, 13/08 : « il y a toujours quasiment rien quand on ouvre. » Les
   quatre blocs de métriques rendaient bel et bien — mais l'illustration
   pleine largeur (190 px × 5/7 = 266 px, plus 28 px de marge) suivie de la
   possession et de deux boutons occupait TOUT le premier écran, et « Les
   marchés » commençait à la ligne de flottaison.

   Livrer une donnée sous la ligne de flottaison, c'est ne pas la livrer.
   L'illustration reste la plus belle matière du produit, mais elle n'est pas
   la RÉPONSE de ce panneau : elle passe donc à 128 px et partage sa ligne
   avec la cote et la possession.
   ========================================================================== */
/* La bande de qualificatifs. Elle a la MÊME grammaire que `.qualbar` de la
   fiche de set — un k mono capitales, une valeur mono, un sous-titre — parce
   que c'est la même chose : ce qui qualifie le héros au-dessus. Une langue
   qui change d'un écran à l'autre n'est pas une langue.
   Le `--lift` en plus : sans le filet clair sur l'arête haute, quatre cases
   de même surface forment un aplat gris (canon §4). */
.sheet__kpis {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; margin-bottom: var(--sp-7);
  box-shadow: var(--lift);
}
.sheet__kpi { background: var(--surface2); padding: var(--sp-5) var(--sp-6); min-width: 0; }
.sheet__kpi--wide { grid-column: 1 / -1; }
.sheet__kpi-k {
  font-family: var(--font-mono); font-size: var(--t-micro); letter-spacing: var(--tr-caps);
  text-transform: uppercase; color: var(--text3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sheet__kpi-v {
  font-family: var(--font-mono); font-size: var(--t-lg); font-weight: 600;
  color: var(--text); margin-top: var(--sp-3);
}
.sheet__kpi-s {
  font-size: var(--t-cap); color: var(--text3); margin-top: var(--sp-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* La ligne d'ouverture : illustration + cote + possession. */
.sheet__stage {
  position: relative;
  display: grid; grid-template-columns: 128px minmax(0, 1fr);
  gap: var(--sp-6); align-items: start; margin-bottom: var(--sp-6);
}
.sheet__art { width: 128px; margin: 0; }
.sheet__art--none { font-size: var(--t-cap); text-align: center; padding: var(--sp-4); }
.sheet__lede { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }

/* La cote — un prix voyage AVEC son marché et AVEC son âge. */
/* LE HÉROS DE CE PANNEAU. On ouvre une fiche de carte pour savoir ce qu'elle
   VAUT : c'est donc ce chiffre-là qui répond à la question de l'écran, et le
   seul qui ait droit au serif et au dégradé (canon, règle 2 — Fraunces est
   réservé au héros, un par écran). En mono 26 px il avait exactement le même
   poids que les quatre cases de métriques posées au-dessus. */
.sheet__price-v {
  font-family: var(--font-serif);
  font-size: var(--t-2xl);
  font-weight: 600;
  letter-spacing: var(--tr-2xl);
  line-height: 1.06;
  background: var(--gold-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.sheet__price--none .sheet__price-v, .sheet__price--none > .nodata { font-size: var(--t-xl); }
.sheet__price-sub { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; font-size: var(--t-cap); margin-top: var(--sp-2); }

/* Les chips reprennent leur place dans le flux : superposées à
   l'illustration, elles masquaient le bas de l'image à 128 px. */
.sheet__chips { position: static; justify-content: flex-start; }

/* La possession est COMPACTE dans la ligne d'ouverture : elle occupait
   auparavant une bande pleine largeur de 62 px pour deux nombres.

   Elle S'EMPILE au lieu de se juxtaposer : « DANS MA COLLECTION » et
   « PRIX DE REVIENT » côte à côte réclament 241 px pour 235 disponibles,
   et le débordement TRONQUAIT le montant — « 830,00 € » rogné à droite est
   pire qu'un montant absent, parce qu'il a l'air d'être là (contrôle de
   rendu n° 2 : aucune cellule BORNÉE tronquée). Mesuré, pas supposé. */
.sheet__stage .sheet__owned {
  margin-bottom: 0; padding: var(--sp-4) var(--sp-5);
  flex-direction: column; align-items: stretch; gap: var(--sp-2);
}
.sheet__stage .sheet__owned > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-4); text-align: left;
}
.sheet__stage .sheet__owned--no { padding: var(--sp-3) var(--sp-5); font-size: var(--t-sm); }
.sheet__stage .sheet__ownqty { font-size: var(--t-lg); margin-top: 0; }

/* La première section n'a pas besoin du grand écart vertical NI du filet :
   ce qui la précède est déjà un bloc bordé, pas un titre. */
.sheet__sec--first { margin-top: var(--sp-6); padding-top: 0; border-top: 0; }

/* Pourquoi ce panneau est plus maigre — dit une fois, en bas, en petit. */
.sheet__notice {
  margin: var(--sp-7) 0 0; font-size: var(--t-cap); line-height: var(--lh-body);
  border-top: 1px solid var(--border); padding-top: var(--sp-5);
}

/* ==========================================================================
   RESPONSIVE ÉTROIT (téléphone, 375-430 px) — SECTION DÉDIÉE, layout seul.

   Ajoutée le 2026-08-14 après les captures iPhone de Nathan (~390 px).
   AUCUN jeton de couleur n'est défini ni redéfini ici : uniquement des
   grilles, des retours à la ligne, des tailles et des débordements. Les
   règles vivent EN FIN de feuille pour gagner à source égale sur les blocs
   qu'elles adaptent — c'est voulu, ne pas les remonter.
   ========================================================================== */

/* ---- 1. Le bandeau de stats (`.statbar`) ne survit pas en 4 colonnes ----

   À 390 px, quatre cellules flex de ~90 px : « 69 326,38 € » en Fraunces 40
   DÉBORDAIT sur « +9 738,33 € », le « € » entrait dans « couverture », et
   les libellés tronquaient (« VALEUR TO… »). Sous 780 px le héros passe en
   rangée pleine, les qualificatifs en 2 colonnes, et le DERNIER d'un nombre
   impair prend la rangée entière plutôt que de laisser une demi-case vide. */
@media (max-width: 780px) {
  .statbar { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .statbar .stat { border-right: 0; border-top: 1px solid var(--border); }
  .statbar .stat--hero:first-child { grid-column: 1 / -1; border-top: 0; }
  /* Le filet vertical ne sépare que deux voisins d'une MÊME rangée. */
  .statbar .stat--hero:first-child ~ .stat:nth-child(even) { border-right: 1px solid var(--border); }
  .statbar .stat--hero:first-child ~ .stat:last-child:nth-child(even) {
    grid-column: 1 / -1;
    border-right: 0;
  }
  /* Libellés COMPLETS : l'ellipse cachait le mot qui distingue deux tuiles
     jumelles (« VARIATION 24 H » / « VARIATION 7 J » → « VARIA… » les deux). */
  .statbar .stat__label { white-space: normal; overflow: visible; text-overflow: clip; }
  /* Une valeur ne sort JAMAIS de sa tuile : plafond fluide sous le cran
     nominal — à 390 px rien ne change, à 320 px le chiffre cède avant la
     tuile. `min()`, pas `clamp()` : on ne grossit jamais au-delà du jeton. */
  .statbar .stat__value { font-size: min(var(--t-h), 4.6vw); }
  .statbar .hero-value { font-size: min(var(--t-hero), 10.2vw); }
}

/* ---- 2. Le classement signé foré (`.srow--drill`) perdait son chevron ----

   Sous 640 px, la règle générique `.srow { grid-template-columns: … 88px
   78px }` (plus bas dans la feuille que `.srow--drill`, donc gagnante à
   spécificité égale) réduisait la grille à TROIS colonnes : le chevron — le
   4ᵉ enfant — retombait SEUL sur une rangée implicite sous chaque ligne, et
   chaque rangée doublait de hauteur. Et `.contrib` gardait ses 150 px FIXES
   dans une colonne de 88 px : la barre traversait la colonne des montants
   (« +2 884,18 € » barré de vert sur la capture). */
@media (max-width: 640px) {
  .srow--drill { grid-template-columns: minmax(0, 1fr) 64px auto 12px; gap: var(--sp-4); }
  /* La barre remplit SA colonne, quelle qu'en soit la largeur. */
  .contrib { width: 100%; }
  /* La même règle générique cassait aussi `.srow--pair` (clé/valeur) : la
     valeur tombait dans une colonne CENTRALE de 88 px et une 3ᵉ colonne
     vide de 78 px tronquait la clé (« Positions marquées à la … »). */
  .srow--pair { grid-template-columns: minmax(0, 1fr) auto; }
}

/* ---- 3. La tête du panneau Composition s'empile en étroit ----

   `.comp__head` est un flex ligne : titre + hint À CÔTÉ du segmenté d'axes.
   À 390 px le segmenté (Langue/Pilier/Ère/Rareté) débordait et se faisait
   ROGNER — « Rareté » devenait inatteignable, une affordance amputée — et le
   hint se pliait mot à mot dans les ~180 px restants. */
@media (max-width: 640px) {
  .comp__head { flex-direction: column; align-items: stretch; }
  /* Filet de sécurité sous 340 px : le segmenté défile dans SA boîte,
     le corps de page ne défile jamais latéralement. */
  .comp__axes { align-self: flex-start; max-width: 100%; overflow-x: auto; }
}

/* ---- 4. Les `.cols` à colonnes INLINE s'empilent en étroit ----

   Trois vues posent leurs colonnes en `style="grid-template-columns:…"`
   (Marché : actus + hausses/baisses ; Recherche : facettes 216 px + grille ;
   fiche d'indice : graphe + constituants). Un style inline gagne sur toute
   feuille — d'où le `!important`, employé UNIQUEMENT ici : à 390 px les
   « Hausses / Baisses » vivaient dans deux panneaux de 159 px et la colonne
   des variations sortait de l'écran (mesuré : right 418 pour 390). */
@media (max-width: 780px) {
  .cols[style] { grid-template-columns: minmax(0, 1fr) !important; }
  /* Empilée, la Recherche mettait ses TROIS panneaux de facettes AVANT le
     premier résultat : l'ordre visuel suit l'usage, pas le DOM — les cartes
     d'abord, les facettes après. */
  .cols[style] > .facets { order: 2; }
}

/* ---- 4 bis. Le groupe d'affichage d'une barre d'outils REDEVIENT sécable --

   `.tgroup--view { flex-wrap: nowrap }` est juste sur le bureau (deux
   réglages du même axe ne doivent pas se retrouver à 1 000 px l'un de
   l'autre) et faux au téléphone : sur la fiche de set, recherche + tri +
   densité + « Estomper les manquantes » réclamaient 558 px sur 390 — le
   bouton sortait de l'écran SANS être atteignable. En étroit le groupe se
   casse, et la recherche prend sa rangée entière. */
@media (max-width: 780px) {
  .tgroup--view { flex-wrap: wrap; }
  .tgroup--view .search { flex: 1 1 100%; min-width: 0; }
}

/* ---- 5. La bande de qualificatifs (`.qualbar`) passe en 2 colonnes ----

   `repeat(auto-fit, minmax(0,1fr))` tient TOUT sur une rangée quel que soit
   l'effectif : à 390 px, quatre cases de ~90 px tronquaient jusqu'aux
   LIBELLÉS (« ANGLAI… », « SANS C… », « DANS V… » sur Scellé) — le titre
   d'une case est une cellule bornée, l'ellipse y est un défaut (canon §9.2). */
@media (max-width: 640px) {
  .qualbar { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .qualbar .qual { border-left: 0; border-top: 1px solid var(--border); }
  .qualbar .qual:nth-child(-n + 2) { border-top: 0; }
  .qualbar .qual:nth-child(2n) { border-left: 1px solid var(--border); }
  /* Un qualificatif ORPHELIN (effectif impair) prend la rangée entière
     plutôt que de laisser une demi-case de noir à sa droite. */
  .qualbar .qual:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .qualbar .qual__k, .qualbar .qual__s { white-space: normal; overflow: visible; text-overflow: clip; }
}

/* ---- 6. Les stats du panneau de forage se serrent au contenu ----

   Trois colonnes de 1fr dans un panneau de ~370 px : la case « Plus-value »
   (+3 473,15 €, onze glyphes mono) débordait de sa colonne de ~123 px.
   `auto` + `space-between` donne à chaque case SA largeur, et le plafond
   fluide fait céder le chiffre avant la case sur les écrans de 320 px. */
@media (max-width: 640px) {
  .drill__stats { grid-template-columns: repeat(3, minmax(0, auto)); justify-content: space-between; }
  .drill__stats > div { padding: var(--sp-5) var(--sp-5); }
  .drill__stats .strip__v { font-size: min(var(--t-lg), 4.2vw); }
}
