/* ════════════════════════════════════════════════════════════════════════════
   JETINVITE — DESIGN SYSTEM v1.0
   jetinvite.fr · jetinvite.re · les tunnels · la page invitée · le backoffice

   Écrit le 10/09/2026. Remplace le DS « Nuit » v0.1 (~/jetinvite-sombre/assets/jet.css)
   et unifie les quatre palettes qui vivaient en parallèle le 10/09 au matin :
     · DS Nuit v0.1 .................... pages d'attente .fr/.re
     · palette « papier chaud » ........ maquettes tunnels (8925, 8930)
     · palette « grille » .............. 8940 (déjà clair + sombre)
     · palette Velinis --vl-* .......... 8950 (héritage, à retirer)

   ⛔ CE FICHIER EST LA SOURCE. On n'écrit plus de :root ailleurs.

   ────────────────────────────────────────────────────────────────────────────
   TROIS ÉTAGES — l'architecture reprise de higgsfield.ai (relevée le 10/09)

     ① PRIMITIVES   --jt-*        les valeurs brutes. Une couleur EXISTE ici.
                                  ⛔ jamais appelées par un composant.
     ② SÉMANTIQUE   --fond-* --encre-* --accent-*   ce que la valeur SIGNIFIE.
                                  C'est le SEUL étage qui change avec le thème.
     ③ COMPOSANT    --bouton-* --carte-* --champ-*  ce qu'un objet CONSOMME.

   🔑 La conséquence, et c'est tout l'intérêt : un composant écrit une fois
      fonctionne dans les deux thèmes, parce qu'il ne nomme jamais une couleur.
      Si vous écrivez `color:#F5F2FA` dans un composant, vous cassez le thème
      clair sans qu'aucune erreur ne le dise.

   ────────────────────────────────────────────────────────────────────────────
   DEUX THÈMES, DEUX MÉTIERS — ce ne sont pas deux peaux du même écran

     « NUIT »   (défaut)  l'écran de JETINVITE. La marque, la vitrine, le
                          catalogue. Le chrome s'efface, les cartes sont la
                          seule lumière. C'est là que vit le logo.
     « PAPIER »           l'écran du FAIRE-PART. Le tunnel, la page invitée,
                          l'aperçu. On y imite le papier parce qu'on y vend
                          du papier.

     <html data-theme="nuit">    ← défaut, inutile de l'écrire
     <html data-theme="papier">
     <html data-theme="auto">    ← suit le réglage du téléphone

   ⭐ L'attribut vaut sur N'IMPORTE QUEL élément, pas seulement sur <html> :
      <div data-theme="papier"> bascule tout son sous-arbre. C'est ce qui permet
      de montrer un APERÇU du faire-part (papier) à l'intérieur de la vitrine
      (nuit) sans dupliquer une seule règle.

   ────────────────────────────────────────────────────────────────────────────
   LES SIX RÈGLES DURES

   1. JAMAIS DE NOIR PUR, JAMAIS DE BLANC PUR EN FOND DE PAGE.
      #000 fait flotter les cartes dans le vide et traîne au scroll sur OLED.
      Le sol de nuit est un noir teinté violet (#0E0C15) ; le sol de papier est
      un blanc cassé chaud (#FAF8F5). Le blanc pur est réservé aux CARTES.

   2. ON SÉPARE PAR LA LUMIÈRE, PAS PAR LE TRAIT.
      En nuit, ce qui avance s'ÉCLAIRCIT (une ombre n'existe pas sur du noir).
      En papier, ce qui avance BLANCHIT et prend une ombre courte.
      Les filets sont un dernier recours, jamais la hiérarchie principale.

   3. LE CONTRASTE SE CALIBRE SUR LA SURFACE LA PLUS DÉFAVORABLE.
      Nuit → la plus CLAIRE (--jt-nuit-400, #211C2E).
      Papier → la plus SOMBRE (--jt-papier-200, #FAF8F5), pas la carte blanche.
      ⚠️ C'est l'inverse d'un thème à l'autre, et c'est le piège classique :
         calibrer le clair sur le blanc donne des valeurs qui échouent sur la page.
      Toutes les valeurs ci-dessous sont MESURÉES, pas estimées. Le tableau est
      dans la page de documentation.

   4. ⛔ LE VERT DE MARQUE N'EST JAMAIS UNE ENCRE SUR FOND CLAIR — JAMAIS.
      Mesuré : #A9D42C fait 11,22:1 sur la nuit et 1,63:1 sur le papier.
      Il n'y a QU'UN SEUL VERT (décision Luigi du 09/09, second vert écarté).
      ⇒ En thème papier, l'accent n'existe que comme PASTILLE PLEINE avec du
        texte sombre dessus (#0E0C15 sur #A9D42C = 11,22:1 ✅). Un lien vert,
        une icône verte, un libellé vert sur du papier sont INTERDITS.
      C'est la règle 2 du mémo logo, généralisée à toute l'interface.

   5. L'ACCENT NE SE POSE QUE SUR L'ACTION. Il ne décore rien.
      Un écran a UNE pastille acide. Deux, c'est qu'on a perdu la hiérarchie.
      (Relevé chez higgsfield : sur toute la page d'accueil, un seul bouton lime.)

   6. LA COULEUR VIENT DES CARTES, JAMAIS DU CHROME.
      Doctrine héritée de Velinis (S14) et confirmée par higgsfield : leur
      interface est muette pour que les visuels portent toute la couleur.
      Le catalogue jetinvite compte 430 cartes ; le chrome n'a pas à concourir.

   ⭐ L'accent est logé dans un TROU MESURÉ du catalogue : les 333 dominantes
      relevées saturent 10-49° (ambres, ors, cires) et 180-230° (bleus), et
      laissent 50-149° presque vide (4 cartes sur 333). Le vert acide (~75°)
      y vit seul ⇒ il ne peut structurellement jamais concurrencer une carte.

   ════════════════════════════════════════════════════════════════════════════ */


/* ───────────────────────────────────────────────────────────────────────────
   POLICES — auto-hébergées, licence OFL (auto-hébergement autorisé).
   ⛔ Aucun appel au CDN Google Fonts : règle S11 (RGPD) + un aller-retour
      réseau bloquant en moins en tête de page.
   Les deux fichiers sont VARIABLES (100→900) : une seule requête par famille.
   ─────────────────────────────────────────────────────────────────────────── */
@font-face{
  font-family:'Jost';
  src:url('polices/jost-latin.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Montserrat';
  src:url('polices/montserrat-latin.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉTAGE ① — PRIMITIVES
   Une valeur brute existe ICI et nulle part ailleurs.
   ⛔ Un composant qui appelle --jt-* directement est un bug : il ne suivra
      pas le thème. Passez toujours par l'étage ②.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{

  /* ── La nuit : le sol et ses étages (plus c'est haut, plus c'est clair) ── */
  --jt-nuit-100:#07060B;   /* le puits — derrière tout, jamais une surface */
  --jt-nuit-200:#0E0C15;   /* la page — c'est aussi le fond du logo */
  --jt-nuit-300:#161320;   /* rail, barre, surface posée */
  --jt-nuit-400:#211C2E;   /* la surface la plus haute — étalon de contraste */
  --jt-nuit-500:#2C2540;   /* survol d'une surface haute */

  /* ── Les craies : les encres de la nuit (mesurées sur --jt-nuit-400) ── */
  --jt-craie-100:#F5F2FA;  /* 14,93:1 — le texte courant */
  --jt-craie-200:#B9B2C8;  /*  8,08:1 — le texte secondaire */
  --jt-craie-300:#8E849A;  /*  4,66:1 — ⚠️ corrigé le 10/09, valait #847C97 (4,18:1 ❌) */

  /* ── Le papier : le sol clair et ses étages (plus c'est haut, plus c'est blanc) ── */
  --jt-papier-100:#F2EFEA;  /* creux, champ enfoncé, bande */
  --jt-papier-150:#F5F2ED;  /* le survol — voir la note ci-dessous */
  --jt-papier-200:#FAF8F5;  /* la page — étalon de contraste du thème clair */
  --jt-papier-300:#FFFFFF;  /* la carte, la feuille posée */
  /* ⚠️ Sur du clair, le SURVOL ASSOMBRIT — il ne peut pas éclaircir, le blanc
     est déjà le plafond. C'est le pendant exact de la règle 2 : en nuit ce qui
     réagit s'éclaircit, en papier ce qui réagit se teinte. Sans ce cran, les
     trois surfaces hautes valent toutes #FFFFFF et le survol ne se voit pas —
     défaut constaté au rendu le 10/09, pas déduit. */

  /* ── Les encres du papier (mesurées sur --jt-papier-200, pas sur le blanc) ── */
  --jt-encre-100:#181614;  /* 17,02:1 — le texte courant */
  --jt-encre-200:#777069;  /*  4,60:1 — ⚠️ corrigé, valait #8b857d (3,45:1 ❌) */
  --jt-encre-300:#968F88;  /*  3,01:1 — ⚠️ corrigé, valait #b8b1a8 (2,00:1 ❌) */
                           /*     réservé au non-essentiel : jamais un libellé seul */

  /* ── Le vert de marque. UN SEUL. Acté le 09/09 avec le logo. ── */
  --jt-vif-400:#D6FA63;    /* survol seulement */
  --jt-vif-500:#C9F24D;    /* la pastille d'action */
  --jt-vif-600:#A9D42C;    /* le « t » du logo · les bordures actives */
  --jt-vif-700:#8EB520;    /* pression */
  --jt-vif-a10:rgba(169,212,44,.10);
  --jt-vif-a20:rgba(169,212,44,.20);
  --jt-vif-a35:rgba(169,212,44,.35);

  /* ── Les couleurs d'ÉTAT. Elles ne sont pas la marque : elles préviennent.
        Deux jeux, parce qu'une valeur lisible sur nuit ne l'est pas sur papier.
        ⭐ Quatre des sept valeurs des maquettes tunnels passaient déjà : elles sont
        GARDÉES telles quelles. On ne remplace que ce qui échoue, mesuré. ── */
  --jt-succes-nuit:#4FB98A;   --jt-succes-papier:#0F7B4F;   /* 4,99:1 — valeur d'origine gardée */
  --jt-alerte-nuit:#F3C977;   --jt-alerte-papier:#AD5A1F;   /* 4,65:1 — neuve, distincte de l'or */
  --jt-erreur-nuit:#E0787F;   --jt-erreur-papier:#A5303A;   /* 6,42:1 — valeur d'origine gardée */

  /* ── L'or. Il vient du catalogue (cires, dorures), pas du chrome.
        Réservé aux mentions de faire-part, jamais à une action. ── */
  --jt-or-nuit:#D3A44A;       --jt-or-papier:#8F691E;       /* 4,71:1 — corrigé, valait #a8802b (3,42:1 ❌) */

  /* ── Blancs et noirs transparents : la matière des filets et des voiles ── */
  --jt-clair-04:rgba(245,242,250,.04);
  --jt-clair-08:rgba(245,242,250,.08);
  --jt-clair-14:rgba(245,242,250,.14);
  --jt-clair-24:rgba(245,242,250,.24);
  --jt-sombre-06:rgba(24,22,20,.06);
  --jt-sombre-10:rgba(24,22,20,.10);
  --jt-sombre-16:rgba(24,22,20,.16);
  --jt-sombre-28:rgba(24,22,20,.28);

  /* ── Espacement — échelle de 4, reprise de higgsfield (--hf-space-*) ── */
  --jt-espace-050:2px;  --jt-espace-100:4px;  --jt-espace-150:6px;
  --jt-espace-200:8px;  --jt-espace-300:12px; --jt-espace-400:16px;
  --jt-espace-500:20px; --jt-espace-600:24px; --jt-espace-800:32px;
  --jt-espace-1000:40px; --jt-espace-1200:48px; --jt-espace-1600:64px;
  --jt-espace-2000:80px; --jt-espace-2400:96px;

  /* ── Rayons ── */
  --jt-rayon-100:4px;  --jt-rayon-200:8px;  --jt-rayon-300:12px;
  --jt-rayon-400:16px; --jt-rayon-500:20px; --jt-rayon-600:24px;
  --jt-rayon-plein:999px;

  /* ── Typographie ──
     Jost = le DISPLAY. C'est la police du logo (Jost 300), donc tout titre
     est de la même famille que la marque. Montserrat = le TEXTE courant.
     ⛔ Aucune troisième famille. Un serif « faire-part » se pose sur la CARTE,
        jamais dans le chrome. */
  --jt-police-display:'Jost',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --jt-police-texte:'Montserrat',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;

  --jt-taille-100:11px;  --jt-taille-200:12.5px; --jt-taille-300:14px;
  --jt-taille-400:15px;  --jt-taille-500:16px;   --jt-taille-600:18px;
  --jt-taille-700:21px;  --jt-taille-800:26px;   --jt-taille-900:32px;
  --jt-taille-1000:40px; --jt-taille-1100:52px;

  --jt-graisse-legere:300;  /* la graisse du logo — celle des grands titres */
  --jt-graisse-normale:400;
  --jt-graisse-moyenne:500;
  --jt-graisse-forte:700;

  /* Interlettrage : les deux premières valeurs viennent du logo, mesurées. */
  --jt-lettre-mot:-.018em;    /* le mot « jetinvite » */
  --jt-lettre-mono:-.03em;    /* le monogramme « jt » */
  --jt-lettre-titre:-.02em;
  --jt-lettre-sur:.26em;      /* les surtitres capitales */

  /* ── Mouvement ── */
  --jt-duree-instant:0s;   --jt-duree-vive:.12s;  --jt-duree-normale:.2s;
  --jt-duree-posee:.32s;   --jt-duree-lente:.7s;
  --jt-courbe:cubic-bezier(.2,.8,.3,1);

  /* ── Verre — la surface flottante (barre, panneau, menu) ── */
  --jt-flou-verre:blur(14px) saturate(1.3);
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉTAGE ② — SÉMANTIQUE · THÈME « NUIT » (le défaut)
   C'est le seul bloc qui se réécrit d'un thème à l'autre.
   Les NOMS ne changent jamais. Seules les valeurs changent.
   ═══════════════════════════════════════════════════════════════════════════ */
:root,
[data-theme="nuit"]{
  color-scheme:dark;

  --fond-puits:var(--jt-nuit-100);
  --fond-page:var(--jt-nuit-200);
  --fond-surface:var(--jt-nuit-300);
  --fond-haute:var(--jt-nuit-400);
  --fond-survol:var(--jt-nuit-500);
  --fond-creux:var(--jt-nuit-100);
  --fond-verre:color-mix(in srgb, var(--jt-nuit-200) 82%, transparent);
  --fond-voile:rgba(7,6,11,.72);          /* derrière une modale */
  --fond-inverse:var(--jt-papier-300);

  --encre-1:var(--jt-craie-100);
  --encre-2:var(--jt-craie-200);
  --encre-3:var(--jt-craie-300);
  --encre-inverse:var(--jt-nuit-200);      /* texte posé sur --fond-inverse */

  --filet:var(--jt-clair-08);
  --filet-fort:var(--jt-clair-14);
  --filet-vif:var(--jt-clair-24);

  --accent:var(--jt-vif-500);
  --accent-survol:var(--jt-vif-400);
  --accent-presse:var(--jt-vif-700);
  --accent-trait:var(--jt-vif-600);        /* bordure active, focus */
  --sur-accent:var(--jt-nuit-200);         /* 15,06:1 — le texte SUR la pastille */
  --accent-voile:var(--jt-vif-a20);
  --accent-halo:rgba(201,242,77,.22);

  /* ⭐ En NUIT seulement, l'accent peut aussi être une ENCRE (12,83:1 mesuré).
     En PAPIER cette variable retombe sur l'encre normale — voir plus bas. */
  --accent-encre:var(--jt-vif-500);

  --succes:var(--jt-succes-nuit);
  --alerte:var(--jt-alerte-nuit);
  --erreur:var(--jt-erreur-nuit);
  --or:var(--jt-or-nuit);

  --ombre-posee:none;                      /* règle 2 : pas d'ombre sur la nuit */
  --ombre-haute:none;
  --ombre-accent:0 8px 34px var(--accent-halo);

  /* L'ambiance : le halo de lumière qui remplace les traits. En nuit il est
     visible ; en papier il s'éteint (une lueur sur du blanc est une salissure). */
  --ambiance-force:.06;
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉTAGE ② — SÉMANTIQUE · THÈME « PAPIER »
   ⚠️ Mêmes noms. Un composant n'a pas une ligne à changer.
   ═══════════════════════════════════════════════════════════════════════════ */
[data-theme="papier"]{
  color-scheme:light;

  --fond-puits:var(--jt-papier-100);
  --fond-page:var(--jt-papier-200);
  --fond-surface:var(--jt-papier-300);
  --fond-haute:var(--jt-papier-300);
  --fond-survol:var(--jt-papier-150);
  --fond-creux:var(--jt-papier-100);
  --fond-verre:color-mix(in srgb, var(--jt-papier-200) 84%, transparent);
  --fond-voile:rgba(24,22,20,.42);
  --fond-inverse:var(--jt-nuit-200);

  --encre-1:var(--jt-encre-100);
  --encre-2:var(--jt-encre-200);
  --encre-3:var(--jt-encre-300);
  --encre-inverse:var(--jt-craie-100);

  --filet:var(--jt-sombre-10);
  --filet-fort:var(--jt-sombre-16);
  --filet-vif:var(--jt-sombre-28);

  /* L'accent NE CHANGE PAS de valeur : il n'y a qu'un vert (règle 4).
     Ce qui change, c'est ce qu'on a le droit d'en faire. */
  --accent:var(--jt-vif-600);              /* la pastille, plus dense sur du clair */
  --accent-survol:var(--jt-vif-500);
  --accent-presse:var(--jt-vif-700);
  --accent-trait:var(--jt-vif-700);        /* 🟠 un filet vert sur papier reste
                                              faible : ne jamais le laisser SEUL
                                              porter l'information de focus —
                                              d'où le double anneau, plus bas. */
  --sur-accent:var(--jt-nuit-200);         /* 11,22:1 — mesuré */
  --accent-voile:var(--jt-vif-a20);
  --accent-halo:rgba(169,212,44,.30);

  /* ⛔ RÈGLE 4 APPLIQUÉE : sur du papier, l'accent n'est PAS une encre.
        Toute écriture qui aurait été verte redevient de l'encre normale. */
  --accent-encre:var(--jt-encre-100);

  --succes:var(--jt-succes-papier);
  --alerte:var(--jt-alerte-papier);
  --erreur:var(--jt-erreur-papier);
  --or:var(--jt-or-papier);

  /* Règle 2, versant clair : ici l'ombre existe, et elle est COURTE.
     Une ombre longue sur du papier fait « bouton web », pas « feuille posée ». */
  --ombre-posee:0 1px 2px var(--jt-sombre-06);
  --ombre-haute:0 6px 20px var(--jt-sombre-10);
  --ombre-accent:0 6px 20px var(--accent-halo);

  --ambiance-force:0;                      /* pas de halo sur du papier */
}

/* « auto » — suit le réglage du téléphone. À ne poser que sur le PRODUIT :
   la vitrine de marque reste en nuit, c'est le geste de marque. */
[data-theme="auto"]{ color-scheme:light dark; }
@media (prefers-color-scheme:light){
  [data-theme="auto"]{
    color-scheme:light;
    --fond-puits:var(--jt-papier-100);   --fond-page:var(--jt-papier-200);
    --fond-surface:var(--jt-papier-300); --fond-haute:var(--jt-papier-300);
    --fond-survol:var(--jt-papier-150);  --fond-creux:var(--jt-papier-100);
    --fond-verre:color-mix(in srgb, var(--jt-papier-200) 84%, transparent);
    --fond-voile:rgba(24,22,20,.42);     --fond-inverse:var(--jt-nuit-200);
    --encre-1:var(--jt-encre-100); --encre-2:var(--jt-encre-200);
    --encre-3:var(--jt-encre-300); --encre-inverse:var(--jt-craie-100);
    --filet:var(--jt-sombre-10); --filet-fort:var(--jt-sombre-16);
    --filet-vif:var(--jt-sombre-28);
    --accent:var(--jt-vif-600); --accent-survol:var(--jt-vif-500);
    --accent-trait:var(--jt-vif-700); --accent-encre:var(--jt-encre-100);
    --accent-halo:rgba(169,212,44,.30);
    --succes:var(--jt-succes-papier); --alerte:var(--jt-alerte-papier);
    --erreur:var(--jt-erreur-papier); --or:var(--jt-or-papier);
    --ombre-posee:0 1px 2px var(--jt-sombre-06);
    --ombre-haute:0 6px 20px var(--jt-sombre-10);
    --ombre-accent:0 6px 20px var(--accent-halo);
    --ambiance-force:0;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   ÉTAGE ③ — COMPOSANT
   Ce que chaque objet consomme. Toujours en var(--…) de l'étage ②.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{
  --barre-h:64px;
  --barre-fond:var(--fond-verre);

  --bouton-h:44px;
  --bouton-h-petit:34px;
  --bouton-rayon:var(--jt-rayon-plein);
  --bouton-padding:0 var(--jt-espace-600);

  --champ-h:48px;
  --champ-rayon:var(--jt-rayon-plein);
  --champ-fond:var(--fond-surface);
  --champ-filet:var(--filet-fort);

  --carte-rayon:var(--jt-rayon-400);
  --carte-fond:var(--fond-surface);
  --carte-filet:var(--filet);
  --carte-ratio:9/16;          /* le format d'une carte du catalogue */

  --pastille-h:22px;
  --pastille-rayon:var(--jt-rayon-100);

  --panneau-rayon:var(--jt-rayon-500);
  --modale-rayon:var(--jt-rayon-600);
  --largeur-lecture:68ch;
  --largeur-page:1200px;
}


/* ═══════════════════════════════════════════════════════════════════════════
   COMPATIBILITÉ — les noms du DS v0.1
   ⚠️ TRANSITOIRE. Ils existent pour que jet.css, les pages d'attente et les
   maquettes continuent de fonctionner pendant la bascule. À RETIRER une fois
   les quatre palettes migrées. Ne PAS écrire de code neuf sur ces noms.
   ⚠️ Ce bloc porte AUSSI sur [data-theme], pas seulement sur :root — sinon un
   sous-arbre thémé garderait les valeurs de la racine sur les anciens noms, en
   silence (mesuré au banc le 10/09 : --doux restait en craie dans un
   <div data-theme="papier">).
   ═══════════════════════════════════════════════════════════════════════════ */
:root, [data-theme]{
  --nuit-0:var(--jt-nuit-100); --nuit-1:var(--jt-nuit-200);
  --nuit-2:var(--jt-nuit-300); --nuit-3:var(--jt-nuit-400);
  --craie:var(--encre-1); --craie-2:var(--encre-2); --craie-3:var(--encre-3);
  --vif:var(--accent); --vif-d:var(--accent-trait); --vif-ombre:var(--accent-halo);
  --r-1:var(--jt-rayon-100); --r-2:var(--jt-rayon-300);
  --r-3:var(--jt-rayon-500); --r-4:var(--jt-rayon-600);
  --e-1:var(--jt-espace-100); --e-2:var(--jt-espace-200); --e-3:var(--jt-espace-400);
  --e-4:var(--jt-espace-600); --e-5:var(--jt-espace-1000);
  --e-6:var(--jt-espace-1600); --e-7:var(--jt-espace-2400);
  --display:var(--jt-police-display); --texte:var(--jt-police-texte);
  /* maquettes tunnels (8925 · 8930 · 8940) — palette papier.
     ⚠️ --doux et --tres-doux pointent désormais vers des valeurs CORRIGÉES :
        les originales échouaient (3,45:1 et 2,00:1). Le rendu changera
        légèrement, et c'est le but. */
  --papier:var(--fond-page); --encre:var(--encre-1); --doux:var(--encre-2);
  --tres-doux:var(--encre-3); --trait:var(--filet-fort); --blanc:var(--fond-surface);
  --nuit:var(--fond-inverse); --carte:var(--fond-surface);
  --vert:var(--succes); --rouge:var(--erreur);
  /* ⚠️ --or n'a PAS d'alias : le nom existe déjà dans la couche sémantique
     (étage ②), et `--or:var(--or)` serait un cycle — la variable deviendrait
     invalide, silencieusement. Les maquettes qui écrivent --or tombent donc
     directement sur la bonne valeur, corrigée par thème. */
}


/* ═══════════════════════════════════════════════════════════════════════════
   SOCLE
   ═══════════════════════════════════════════════════════════════════════════ */
*,*::before,*::after{box-sizing:border-box}

/* ⛔ `hidden` PERD CONTRE UN `display` D'AUTEUR — et l'échec est SILENCIEUX.
   `[hidden]` ne vaut qu'un display:none de feuille utilisateur : la moindre
   règle d'auteur qui pose un display le bat. Piège déjà payé deux fois
   (demo.css du 17/08, puis jet.css) — un garde-fou non transporté est perdu. */
[hidden]{display:none !important}

html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--fond-page);
  color:var(--encre-1);
  font-family:var(--jt-police-texte);
  font-weight:var(--jt-graisse-legere);
  font-size:var(--jt-taille-500);
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  transition:background var(--jt-duree-posee) ease, color var(--jt-duree-posee) ease;
}
img,svg,video{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
a{color:var(--accent-encre);text-underline-offset:.18em}

h1,h2,h3,h4{
  font-family:var(--jt-police-display);
  font-weight:var(--jt-graisse-legere);
  letter-spacing:var(--jt-lettre-titre);
  line-height:1.1; margin:0;
  text-wrap:balance;
}
h1{font-size:clamp(var(--jt-taille-900), 9vw, var(--jt-taille-1100))}
h2{font-size:clamp(var(--jt-taille-800), 5vw, var(--jt-taille-1000))}
h3{font-size:var(--jt-taille-700)}
h4{font-size:var(--jt-taille-600); font-weight:var(--jt-graisse-moyenne)}

/* Le surtitre — capitales espacées. Il situe, il ne titre pas. */
.jt-surtitre{
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-100); letter-spacing:var(--jt-lettre-sur);
  text-transform:uppercase; color:var(--encre-3); margin:0;
}

/* ⛔ FOCUS — jamais retiré, et visible dans LES DEUX thèmes.
   Double anneau : l'anneau accent porte la couleur, l'anneau de fond porte le
   contraste. Sur papier l'accent seul serait trop faible (règle 4) — c'est
   l'anneau sombre extérieur qui garantit les 3:1 exigés par WCAG 1.4.11. */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--accent-trait);
  outline-offset:2px;
  box-shadow:0 0 0 4px var(--fond-page), 0 0 0 6px var(--filet-vif);
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important; animation-iteration-count:1!important;
    transition-duration:.01ms!important; scroll-behavior:auto!important;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   L'AMBIANCE — le calque de lumière qui remplace les filets (règle 2)
   La couleur est EMPRUNTÉE à la carte regardée : c'est ce qui fait que l'écran
   « prend la teinte » du modèle sans qu'aucun élément d'interface ne change.
   ⭐ Le halo d'une carte est sa propre couleur, et son intensité est l'INVERSE
      de sa clarté (catalogue mesuré de 0,11 à 0,82 : un halo uniforme raterait
      les deux bouts). --vive et --clarte viennent de outils/preparer.py.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-ambiance{
  position:fixed; inset:-20vh -20vw; z-index:0; pointer-events:none;
  background:
    radial-gradient(60% 45% at 50% 0%,
      color-mix(in srgb, var(--ambiance, var(--accent)) calc(var(--ambiance-force) * 100%), transparent) 0%,
      transparent 70%),
    radial-gradient(46% 38% at 82% 104%,
      color-mix(in srgb, var(--ambiance, var(--accent)) calc(var(--ambiance-force) * 60%), transparent) 0%,
      transparent 66%);
  transition:background var(--jt-duree-lente) ease, opacity var(--jt-duree-lente) ease;
}
.jt-page{position:relative; z-index:1}
.jt-contenu{max-width:var(--largeur-page); margin-inline:auto; padding-inline:var(--jt-espace-600)}


/* ═══════════════════════════════════════════════════════════════════════════
   LE LOGO
   ⛔ NE JAMAIS RECOMPOSER LE MONOGRAMME EN CSS. Le jambage du « j » décale
      l'encre de 258/1000 vers le bas : place-items:center centre la boîte de
      ligne, pas l'encre. Les SVG sont centrés sur les bornes réelles des
      courbes (mesuré 50,00 % / 49,95 %). On POSE un fichier, on n'en refait pas.
   ⛔ Pas d'apostrophe. La marque déposée à l'INPI le 06/09 est JETINVITE.
      (⚠️ jet.css v0.1 disait l'inverse — commentaire périmé, corrigé ici.)
   ⛔ Pas de ™ dans le logo : aucun effet juridique en France. Il vit dans le
      TEXTE des mentions — « jetinvite™ est une marque de la SAS Knotvillage ».
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-logo{display:inline-block; line-height:0}
.jt-logo img{height:var(--logo-h, 28px); width:auto}

/* Quel fichier poser, selon le thème et le fond :
     thème nuit ................ logo/logo-mot-fond-sombre.svg
     thème papier .............. logo/logo-mot-sur-nuit.svg   ⚠️ AVEC son bloc nuit
     monogramme (avatar/icône) . logo/logo-jt-icone.svg
     favicons 16 et 32 px ...... logo/favicon-16.png · favicon-32.png (Jost 500)
   ⚠️ En 300, la barre du « t » passe sous le pixel en dessous de 48 px et vire
      à l'olive (#6F8922 mesuré à 16 px). Les deux petits favicons sont en
      Jost 500 : c'est de la compensation optique, pas une entorse. */
[data-theme="papier"] .jt-logo{ --logo-fond:var(--jt-nuit-200); }


/* ═══════════════════════════════════════════════════════════════════════════
   LA BARRE — verre flottant
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-barre{
  position:sticky; top:0; z-index:40;
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--jt-espace-400);
  min-height:var(--barre-h);
  padding:var(--jt-espace-300) var(--jt-espace-600);
  background:var(--barre-fond);
  backdrop-filter:var(--jt-flou-verre);
  -webkit-backdrop-filter:var(--jt-flou-verre);
  border-bottom:1px solid var(--filet);
}


/* ═══════════════════════════════════════════════════════════════════════════
   BOUTONS
   Règle 5 : UNE pastille acide par écran. Le reste est secondaire ou fantôme.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-bouton{
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--jt-espace-200);
  min-height:var(--bouton-h); padding:var(--bouton-padding);
  border-radius:var(--bouton-rayon);
  font-family:var(--jt-police-display);
  font-weight:var(--jt-graisse-forte);
  font-size:var(--jt-taille-400);
  letter-spacing:.01em; text-decoration:none; white-space:nowrap;
  transition:transform var(--jt-duree-vive) var(--jt-courbe),
             box-shadow var(--jt-duree-posee), background var(--jt-duree-normale),
             border-color var(--jt-duree-normale);
}
.jt-bouton:active{transform:translateY(0) scale(.985)}
.jt-bouton[disabled],.jt-bouton[aria-disabled="true"]{
  opacity:.45; pointer-events:none;
}
.jt-bouton--petit{min-height:var(--bouton-h-petit); padding:0 var(--jt-espace-400); font-size:var(--jt-taille-300)}
.jt-bouton--large{width:100%}

/* ① L'ACTION — la pastille acide. Une seule par écran.
   Le texte dessus est TOUJOURS --sur-accent : 15,06:1 en nuit, 11,22:1 en
   papier. ⛔ Du blanc sur le vert fait 1,29:1 — c'est illisible, jamais. */
.jt-bouton--action{
  background:var(--accent); color:var(--sur-accent);
  box-shadow:0 0 0 0 var(--accent-halo);
}
.jt-bouton--action:hover{
  background:var(--accent-survol); transform:translateY(-1px);
  box-shadow:var(--ombre-accent);
}
.jt-bouton--action:active{background:var(--accent-presse)}

/* ② LE SECOND CHOIX — une surface, pas une couleur */
.jt-bouton--second{
  background:var(--fond-haute); color:var(--encre-1);
  border:1px solid var(--filet-fort);
}
.jt-bouton--second:hover{background:var(--fond-survol); border-color:var(--filet-vif)}

/* ③ LE FANTÔME — pour ce qui doit être là sans être proposé */
.jt-bouton--fantome{background:transparent; color:var(--encre-2)}
.jt-bouton--fantome:hover{background:var(--fond-haute); color:var(--encre-1)}

/* ④ LE CONTRAIRE — inverse du fond. Le « Créer mon compte » des maquettes
     tunnels est exactement ça : noir plein sur papier. */
.jt-bouton--contraire{background:var(--fond-inverse); color:var(--encre-inverse)}
.jt-bouton--contraire:hover{opacity:.88; transform:translateY(-1px)}

/* ⑤ LE DANGER — jamais acide. L'accent est la marque, pas une alarme. */
.jt-bouton--danger{background:transparent; color:var(--erreur); border:1px solid var(--erreur)}


/* ═══════════════════════════════════════════════════════════════════════════
   CHAMPS
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-champ{
  width:100%; min-height:var(--champ-h);
  padding:0 var(--jt-espace-500);
  background:var(--champ-fond);
  border:1px solid var(--champ-filet);
  border-radius:var(--champ-rayon);
  color:var(--encre-1);
  font-family:var(--jt-police-texte);
  font-weight:var(--jt-graisse-legere);
  font-size:var(--jt-taille-400);
  transition:border-color var(--jt-duree-normale), background var(--jt-duree-normale);
}
.jt-champ::placeholder{color:var(--encre-3)}
.jt-champ:focus{outline:none; border-color:var(--accent-trait); background:var(--fond-haute)}
.jt-champ:focus-visible{box-shadow:0 0 0 4px var(--accent-voile)}
.jt-champ[aria-invalid="true"]{border-color:var(--erreur)}
textarea.jt-champ{padding-block:var(--jt-espace-300); min-height:120px; line-height:1.6; resize:vertical}

/* L'étiquette invisible mais lue — jamais un placeholder seul comme étiquette. */
.jt-etiquette-lue{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
.jt-aide{margin:var(--jt-espace-200) 0 0; font-size:var(--jt-taille-200); color:var(--encre-3)}
.jt-erreur{margin:var(--jt-espace-200) 0 0; font-size:var(--jt-taille-200); color:var(--erreur)}


/* ═══════════════════════════════════════════════════════════════════════════
   CARTES — règle 6 : c'est ELLES qui portent la couleur
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-carte{
  background:var(--carte-fond);
  border:1px solid var(--carte-filet);
  border-radius:var(--carte-rayon);
  box-shadow:var(--ombre-posee);
  overflow:hidden;
  transition:transform var(--jt-duree-normale) var(--jt-courbe),
             box-shadow var(--jt-duree-posee), border-color var(--jt-duree-normale);
}
.jt-carte:hover{transform:translateY(-2px); box-shadow:var(--ombre-haute); border-color:var(--filet-fort)}
.jt-carte__media{aspect-ratio:var(--carte-ratio); width:100%; object-fit:cover; background:var(--fond-creux)}
.jt-carte__corps{padding:var(--jt-espace-400)}
.jt-carte__titre{font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne); font-size:var(--jt-taille-500); margin:0}
.jt-carte__note{margin:var(--jt-espace-100) 0 0; font-size:var(--jt-taille-300); color:var(--encre-2)}

/* La grille du catalogue */
.jt-grille{
  display:grid; gap:var(--jt-espace-600);
  grid-template-columns:repeat(auto-fill, minmax(210px, 1fr));
}


/* ═══════════════════════════════════════════════════════════════════════════
   PASTILLES D'INFORMATION
   ⛔ Ce ne sont PAS des boutons. Une pastille acide sur une carte volerait
      l'accent à l'action de l'écran (règle 5). Le neutre est le défaut.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-pastille{
  display:inline-flex; align-items:center; gap:var(--jt-espace-100);
  height:var(--pastille-h); padding:0 var(--jt-espace-200);
  border-radius:var(--pastille-rayon);
  background:var(--fond-haute); color:var(--encre-2);
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-100); letter-spacing:.06em; text-transform:uppercase;
}
.jt-pastille--accent{background:var(--accent-voile); color:var(--accent-encre)}
.jt-pastille--succes{background:color-mix(in srgb, var(--succes) 16%, transparent); color:var(--succes)}
.jt-pastille--alerte{background:color-mix(in srgb, var(--alerte) 16%, transparent); color:var(--alerte)}
.jt-pastille--erreur{background:color-mix(in srgb, var(--erreur) 16%, transparent); color:var(--erreur)}


/* ═══════════════════════════════════════════════════════════════════════════
   SURFACES POSÉES — panneau, verre, modale
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-panneau{
  background:var(--fond-surface); border:1px solid var(--filet);
  border-radius:var(--panneau-rayon); padding:var(--jt-espace-600);
  box-shadow:var(--ombre-posee);
}
.jt-verre{
  background:var(--fond-verre);
  backdrop-filter:var(--jt-flou-verre); -webkit-backdrop-filter:var(--jt-flou-verre);
  border:1px solid var(--filet-fort); border-radius:var(--panneau-rayon);
}
.jt-voile{position:fixed; inset:0; background:var(--fond-voile); z-index:90}
.jt-modale{
  background:var(--fond-surface); border:1px solid var(--filet-fort);
  border-radius:var(--modale-rayon); box-shadow:var(--ombre-haute);
  max-width:560px; width:calc(100% - var(--jt-espace-800)); padding:var(--jt-espace-800);
}

/* Un filet ne porte JAMAIS seul la hiérarchie (règle 2) : il ferme une zone
   déjà séparée par la lumière. */
.jt-filet{height:1px; background:var(--filet); border:0; margin:var(--jt-espace-600) 0}


/* ═══════════════════════════════════════════════════════════════════════════
   LA BASCULE DE THÈME
   À poser sur le PRODUIT (tunnel, page invitée, backoffice) — pas sur la
   vitrine de marque, qui reste en nuit.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-bascule{
  display:inline-flex; padding:var(--jt-espace-050);
  background:var(--fond-haute); border:1px solid var(--filet);
  border-radius:var(--jt-rayon-plein);
}
.jt-bascule button{
  min-height:var(--bouton-h-petit); padding:0 var(--jt-espace-400);
  border-radius:var(--jt-rayon-plein);
  font-family:var(--jt-police-display); font-size:var(--jt-taille-200);
  font-weight:var(--jt-graisse-moyenne); color:var(--encre-3);
  transition:background var(--jt-duree-normale), color var(--jt-duree-normale);
}
.jt-bascule button[aria-pressed="true"]{background:var(--fond-page); color:var(--encre-1)}


/* ═══════════════════════════════════════════════════════════════════════════
   UTILITAIRES — le strict minimum. Ce n'est pas Tailwind.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-pile{display:flex; flex-direction:column; gap:var(--jt-espace-400)}
.jt-rang{display:flex; align-items:center; gap:var(--jt-espace-300); flex-wrap:wrap}
.jt-centre{text-align:center; margin-inline:auto}
.jt-lecture{max-width:var(--largeur-lecture)}
.jt-discret{color:var(--encre-3); font-size:var(--jt-taille-200)}
.jt-second{color:var(--encre-2)}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  PARTIE II — L'OBJET, LE RAIL, L'ÉTIQUETTE                                ║
   ║  Ajoutée le 10/09/2026. C'est la partie qui MONTRE le produit.            ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   ────────────────────────────────────────────────────────────────────────────
   ⛔ MOBILE D'ABORD — ce bloc se lit dans ce sens, pas l'inverse

   Tout ce qui suit est écrit POUR LE TÉLÉPHONE, puis élargi. Les paliers :
     (rien)              ← le téléphone. 360-430 px. Le cas par défaut.
     min-width: 48rem    ← 768 px, tablette et petit portable
     min-width: 64rem    ← 1024 px, bureau
   ⚠️ Une media query ne lit pas les variables CSS : les valeurs sont écrites
      en clair. Ne pas essayer d'y mettre var(--bp-…), ça ne marchera pas.

   🔑 Pourquoi le téléphone décide : une invitation s'OUVRE sur un téléphone.
      Le poster, le rail, l'enveloppe, le RSVP — tout est vu au pouce, souvent
      d'une main, souvent dehors. Le bureau est la vue secondaire.

   ────────────────────────────────────────────────────────────────────────────
   LES QUATRE RÈGLES DE L'OBJET (mesurées sur les 333 cartes du catalogue)

   7. ⭐ LE HALO D'UNE CARTE EST SA PROPRE COULEUR, ET SON INTENSITÉ EST
      L'INVERSE DE SA CLARTÉ. Mesuré : le catalogue va de 0,11 à 0,82 de clarté —
      200 cartes sombres, 133 claires. Un halo uniforme raterait les deux bouts :
      les sombres se noieraient, les claires brûleraient.
      ⇒ Chaque carte s'éclaire ELLE-MÊME, avec ce qu'elle est. `--vive` et
        `--clarte` viennent de `outils/preparer.py`, jamais d'un humain :
        personne ne pose 333 palettes à la main.

   8. ⭐ LA RETENUE — le pendant du halo pour les 45 cartes pâles (clarté ≥ 0,70).
      Une ombre INTERNE qui les tient dans leur cadre. Une carte crème qui
      rayonne ne lit pas comme un objet précieux, elle lit comme un bug.
      🔑 UNE SEULE MESURE gouverne les deux — quand le halo descend, la tenue
        monte — donc aucune carte ne tombe entre les deux régimes.

   9. ⛔ LA 3D EST UN PRIVILÈGE, JAMAIS UN ATTRIBUT. Un seul objet en relief par
      écran. C'est « une seule masse d'accent par écran » transposée au volume.
      Le wow n'est pas « des cartes 3D partout » : c'est le CONTRASTE entre une
      grille plate et un objet qui se lève quand on le touche. Et c'est ce qui
      permet de tenir 333 modèles sans chauffer un téléphone.

   10. ⛔ UNE ŒUVRE NE PORTE PAS D'ÉTIQUETTE SUR LE VISAGE.
      Relevé chez higgsfield, et c'est leur distinction la plus utile : sur leurs
      TUILES d'interface, les pastilles sont DEDANS (« Video », « Image ») ; sur
      leurs AFFICHES de film, elles sont DESSOUS, dans une barre de pied.
      ⇒ Ici : l'enveloppe est une œuvre. Son nom, sa catégorie, sa durée vivent
        dans `.jt-objet__pied`, SOUS l'image. La seule chose qui a le droit de se
        poser SUR elle est une **marque d'état** que l'acheteur doit voir avant
        de cliquer (« Offert », « Nouveau ») — jamais une catégorie, jamais un
        ornement. Une enveloppe à 19 € dont on masque le cachet de cire par un
        badge, c'est le produit qu'on abîme pour décorer l'interface.

   ────────────────────────────────────────────────────────────────────────────
   ⚠️ CE QUE LE CATALOGUE A DÉJÀ COÛTÉ — à ne pas repayer

   Mesuré sur la maquette sombre, avant/après correction :
     · 243 images demandées au 1ᵉʳ écran, ~19 Mo   →  26 images, ~0,96 Mo
     · une vignette servie en 420 px pour 154 px d'affichage → 324 px, q72, 37 Ko
   ⛔ `loading="lazy"` NE SUFFIT PAS : son seuil est généreux et 45 rails font
      19 000 px de page — il laissait partir 243 images. La seule vraie économie
      est de N'ÉCRIRE LA BALISE qu'à l'approche du rail (IntersectionObserver,
      900 px d'avance).
   🔑 Et il faut RÉSERVER LA HAUTEUR du rail avant de le remplir — sinon la page
      se replie, les 45 rails se retrouvent tous « près du viewport », et
      l'économie s'annule d'elle-même. D'où `--rail-h` et le `min-height`
      ci-dessous : ce n'est pas de la mise en page, c'est le garde-fou.

   ⛔ 45 RAILS, C'EST 45 RAILS. La vraie réponse à 333 modèles n'est pas 45
      titres empilés, c'est une ÉDITORIALISATION (« ce qu'on envoie ce mois-ci »,
      « anniversaire 5-10 ans »). Les 45 catégories vivent en URL, pas en menu.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── L'objet. Il se dimensionne par la HAUTEUR, jamais par la largeur :
        un 9:16 mesuré en vw fait 132vh de haut et sort le bouton de l'écran. ── */
  --objet-ratio:9/16;              /* mesuré : les vignettes font 324×576 */

  /* ── LE CADRAGE — les deux seuls réglages, et ils sont GLOBAUX ───────────
     ⛔ Ne PAS chercher à corriger carte par carte automatiquement. Mesuré et
     démontré le 10/09 : deux détecteurs, deux échecs.
       · par la TEXTURE — une enveloppe unie (velours, aplat) a la même activité
         qu'un fond : la sonde coupait EN PLEIN DANS LE DESSIN.
       · par le DÉGRADÉ (la méthode de preparer.py, pourtant éprouvée à 309/333)
         — sur une vignette DÉJÀ recadrée, les 4 coins sont sur l'enveloppe,
         la nappe modélise l'enveloppe, l'écart tombe à zéro : 91 % des cartes
         dans la même classe. Un classement qui ne classe pas.
     🔑 Une enveloppe crème sur un fond crème est indiscernable au bord. Aucun
        seuil ne les sépare — le chercher revient à casser du sain.
     ⇒ La réponse n'est pas dans le traitement d'image, elle est dans LE CADRE.

     Distribution mesurée sur 333 vignettes — occupation du cadre :
       largeur  min 67,6 % · p10 96,6 % · MÉDIANE 100 %
       hauteur  min 60,9 % · p10 92,0 % · MÉDIANE 97,2 %
     ⇒ la plupart vont bien, une trentaine est franchement hors-format. Celles-là
       se recadrent à la source ou s'écartent — À L'ŒIL, pas au seuil. */
  --objet-passe:0px;   /* ⛔ AUCUN PASSE-PARTOUT SUR TÉLÉPHONE — décision Luigi, 10/09.
     Mesuré : la carte fait ≈ 142 px de large sur téléphone contre ≈ 191 px au
     bureau. Un même 9 px y mange donc 12,7 % de la largeur contre 9,4 % — ~35 %
     de plus, exactement là où chaque pixel d'enveloppe compte le plus. À cette
     taille on ne perçoit plus un cadre : on perçoit ce qu'il vous prend.
     ⇒ Sur téléphone la carte va au bord. Le contour commun y est tenu par le
       RAYON et le filet interne, pas par une marge.

     ⚠️ CE CHOIX A UNE CONDITION, ET ELLE N'EST PAS OPTIONNELLE.
     Sans passe-partout, plus rien ne masque un fond résiduel : c'est L'IMAGE qui
     redéfinit la silhouette. Le téléphone n'est donc tenable QUE si les sources
     sont propres — d'où le recadrage à la source, qui passe du confort à la
     dépendance. Tant que les hors-format traînent, ils se verront sur mobile.

     🔑 Le passe-partout n'est plus une réparation, c'est un choix de style qui
        n'a de sens que là où il y a de la place. */
  --objet-sur:1.05;    /* le surbalayage : mord dans le bord de l'image pour
                          manger les liserés fins (médiane à 97 % ⇒ ~3 % à
                          rattraper). ⛔ Au-delà de 1,10 on ampute les motifs
                          de coin et parfois le cachet de cire. */
  --objet-h:clamp(184px, 30vh, 268px);
  --objet-l:calc(var(--objet-h) * 9 / 16);
  --objet-rayon:var(--jt-rayon-300);

  /* ── Le rail ── */
  --rail-gap:var(--jt-espace-300);
  --rail-marge:var(--jt-espace-400);   /* le retrait des bords sur téléphone */
  --rail-h:calc(var(--objet-h) + 62px);/* objet + pied — RÉSERVÉ, cf ci-dessus */

  /* ── Le pouce ── */
  --cible-min:44px;                    /* rien de cliquable en dessous */
  --marge-sûre-bas:env(safe-area-inset-bottom, 0px);
  --marge-sûre-haut:env(safe-area-inset-top, 0px);
}

@media (min-width:48rem){
  :root{
    --objet-h:clamp(220px, 32vh, 300px);
    --objet-passe:6px;                 /* carte ≈ 169 px ⇒ 7,1 % de la largeur */
    --rail-gap:var(--jt-espace-400);
    --rail-marge:var(--jt-espace-600);
  }
}
@media (min-width:64rem){
  :root{
    --objet-h:clamp(260px, 34vh, 340px);
    --objet-passe:9px;                 /* carte ≈ 191 px ⇒ 9,4 % de la largeur */
    --rail-marge:0px;                  /* au bureau, le rail rentre dans la colonne */
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   L'OBJET — une enveloppe, une carte de vœux, un modèle
   L'appelant ne pose QUE deux valeurs, et elles sont MESURÉES, pas choisies :
     <article class="jt-objet" style="--vive:#834B2B; --clarte:.22">
   Tout le reste — halo, tenue, bordure — en découle par calcul.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-objet{
  --vive:var(--accent);              /* repli si l'appelant oublie */
  --clarte:.5;

  /* Règle 7 : le halo est l'inverse de la clarté. Une carte à 0,11 rayonne à
     46 %, une carte à 0,82 à 9 %. Le plancher évite qu'une pâle soit posée à plat. */
  --halo:clamp(6%, calc((1 - var(--clarte)) * 52%), 46%);
  /* Règle 8 : la tenue ne s'allume qu'au-delà de 0,45 — donc jamais sur une
     sombre, toujours sur les 45 pâles. Même mesure, deux régimes, zéro trou. */
  --tenue:clamp(0%, calc((var(--clarte) - .45) * 90%), 22%);

  display:flex; flex-direction:column; gap:var(--jt-espace-300);
  min-width:0;
}

.jt-objet__cadre{
  position:relative; display:block;
  aspect-ratio:var(--objet-ratio);
  border-radius:var(--objet-rayon);
  background:var(--fond-creux);
  overflow:hidden;
  text-decoration:none;
  padding:var(--objet-passe);      /* le passe-partout, cf ci-dessus */
  /* Le halo, dehors — et la tenue, dedans. Les deux dans la même déclaration
     parce qu'ils sont les deux faces d'une seule mesure. */
  box-shadow:
    0 14px 38px -10px color-mix(in srgb, var(--vive) var(--halo), transparent),
    0 2px 10px -4px color-mix(in srgb, var(--vive) calc(var(--halo) / 2), transparent),
    inset 0 0 0 1px color-mix(in srgb, var(--encre-1) var(--tenue), transparent);
  transition:transform var(--jt-duree-normale) var(--jt-courbe),
             box-shadow var(--jt-duree-posee);
}
/* La vitre : l'image vit DEDANS, jamais au contact du bord de la carte.
   C'est ce qui fait que plus aucune image ne définit la silhouette. */
.jt-objet__vitre{
  position:absolute; inset:var(--objet-passe);
  border-radius:calc(var(--objet-rayon) - var(--objet-passe) / 1.6);
  overflow:hidden; background:var(--fond-creux);
}
.jt-objet__image{
  width:100%; height:100%; object-fit:cover;
  transform:scale(var(--objet-sur)); transform-origin:center;
  /* ⚠️ Ces deux lignes valent le poids de la page : sans dimensions, le
     navigateur ne réserve rien, la page se replie et le chargement paresseux
     part en vrille (mesuré : 243 images au lieu de 26). */
  aspect-ratio:var(--objet-ratio);
}

/* ═══ L'APERÇU ANIMÉ — la tuile joue son film au survol ═══
   ⭐ L'enveloppe QUI S'OUVRE est la promesse du produit : une vitrine qui ne
      montre que des images fixes la raconte sans la tenir.
   ⛔ La vidéo se superpose à l'image, elle ne la REMPLACE pas : l'image reste
      dessous, affichée, et porte toujours le poids de la mise en page. Si le
      film ne charge pas — réseau lent, format refusé, données économisées —
      la tuile garde exactement l'allure qu'elle a sans lui. Rien ne saute,
      rien ne clignote, rien ne se vide.
   ⛔ Et elle n'apparaît QUE quand elle a de quoi jouer (`.a-joue`, posé par le
      script au premier `playing`) : un cadre noir qui s'allume avant la
      première image serait pire que pas d'effet du tout. */
.jt-objet__film{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; transform:scale(var(--objet-sur)); transform-origin:center;
  opacity:0; transition:opacity var(--jt-duree-normale) ease; pointer-events:none;
}
.jt-objet__film.a-joue{opacity:1}
/* ⚠️ `prefers-reduced-motion` n'est pas une politesse : un mouvement
   involontaire déclenche des troubles vestibulaires chez certaines personnes.
   On ne charge alors rien — le script lit la même préférence. */
@media (prefers-reduced-motion:reduce){
  .jt-objet__film{display:none}
}

/* Le survol NE grossit pas l'image — il lève l'objet et rallume son halo.
   Grossir recadrerait une œuvre dont chaque bord a été mesuré au détourage. */
@media (hover:hover){
  .jt-objet__cadre:hover{
    transform:translateY(-4px);
    box-shadow:
      0 22px 54px -10px color-mix(in srgb, var(--vive) calc(var(--halo) * 1.35), transparent),
      0 3px 12px -4px color-mix(in srgb, var(--vive) var(--halo), transparent),
      inset 0 0 0 1px color-mix(in srgb, var(--encre-1) var(--tenue), transparent);
  }
}
.jt-objet__cadre:active{transform:translateY(-1px) scale(.99)}
.jt-objet__cadre:focus-visible{outline:2px solid var(--accent-trait); outline-offset:3px}

/* RÈGLE 10 — le pied. C'est ICI que vivent le nom et la catégorie. */
.jt-objet__pied{display:flex; flex-direction:column; gap:2px; padding-inline:2px}
.jt-objet__nom{
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-300); color:var(--encre-1); margin:0;
  /* ⛔ PAS de `white-space:nowrap`. Mesuré le 11/09 sur la landing, à 390 px :
     « Quatre-vingts printemps » sortait en « Quatre-vingts printe… ». Un titre
     de carte n'est pas une métadonnée qu'on peut tronquer — c'est le NOM du
     produit, et la moitié d'un nom ne nomme rien. Deux lignes, puis on coupe :
     la hauteur du pied reste prévisible (le rail ne se déforme pas), et aucun
     des 99 titres de la home ne dépasse deux lignes à cette largeur. */
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2;
  overflow:hidden; text-wrap:pretty;
  /* ⭐ La hauteur est RÉSERVÉE pour deux lignes, même quand le titre n'en fait
     qu'une. Sinon un nom court et un nom long donnent deux pieds de hauteurs
     différentes, et la ligne « Vidéo · à leur nom » des tuiles voisines ne
     s'aligne plus — la bande entière paraît de travers. C'est le même défaut
     que celui des enveloppes mal cadrées, une octave plus bas. */
  min-height:calc(2 * 1.3em); line-height:1.3;
}
.jt-objet__meta{
  margin:0; font-size:var(--jt-taille-200); color:var(--encre-3);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* LA MARQUE D'ÉTAT — la seule chose autorisée SUR l'œuvre (règle 10).
   En verre, jamais en aplat de marque : un aplat acide sur une enveloppe
   volerait l'accent à l'action de l'écran (règle 5). */
.jt-objet__marque{
  position:absolute; top:var(--jt-espace-200); left:var(--jt-espace-200);
  display:inline-flex; align-items:center; gap:4px;
  height:20px; padding:0 8px; border-radius:var(--jt-rayon-100);
  background:rgba(14,12,21,.62);
  backdrop-filter:blur(10px) saturate(1.2);
  -webkit-backdrop-filter:blur(10px) saturate(1.2);
  color:var(--jt-craie-100);          /* ⚠️ fixe : le voile est sombre dans LES DEUX
                                         thèmes, donc l'encre l'est aussi. C'est
                                         l'exception qui confirme l'étage ②. */
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-forte);
  font-size:10.5px; letter-spacing:.08em; text-transform:uppercase;
  box-shadow:inset 0 0 0 1px rgba(245,242,250,.14);
}
.jt-objet__marque--accent{background:var(--accent); color:var(--sur-accent); box-shadow:none}

/* La durée, posée en bas à droite — l'information qu'on cherche en parcourant. */
.jt-objet__duree{
  position:absolute; right:var(--jt-espace-200); bottom:var(--jt-espace-200);
  padding:2px 7px; border-radius:var(--jt-rayon-100);
  background:rgba(14,12,21,.62);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
  color:var(--jt-craie-100); font-size:11px;
  font-variant-numeric:tabular-nums;
}

/* RÈGLE 9 — le privilège. Une seule par écran, jamais dans un rail. */
.jt-objet--relief .jt-objet__cadre{
  transform:perspective(1400px) rotateY(var(--incline, -9deg)) rotateX(3deg);
  transform-style:preserve-3d;
  transition:transform var(--jt-duree-posee) var(--jt-courbe);
}
.jt-objet--relief .jt-objet__cadre::after{
  /* Le reflet. ⛔ `screen` ne vaut RIEN sur fond clair — screen(blanc,x)=blanc.
     C'est la première chose que la nuit rend possible et que le papier interdit :
     en thème papier on le coupe. */
  content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(115deg, transparent 38%, rgba(255,255,255,.22) 50%, transparent 62%);
  mix-blend-mode:screen;
}
[data-theme="papier"] .jt-objet--relief .jt-objet__cadre::after{display:none}


/* ═══════════════════════════════════════════════════════════════════════════
   LE RAIL — une bande éditorialisée, pas un vidage de catégorie
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-rail{ margin-block:var(--jt-espace-800) }

.jt-rail__tete{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--jt-espace-400);
  margin:0 var(--rail-marge) var(--jt-espace-400);
}
.jt-rail__titre{
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-600); letter-spacing:-.01em; margin:0; color:var(--encre-1);
}
.jt-rail__compte{
  font-family:var(--jt-police-display); font-size:var(--jt-taille-100);
  letter-spacing:.18em; text-transform:uppercase; color:var(--encre-3);
  white-space:nowrap; font-variant-numeric:tabular-nums;
}
.jt-rail__lien{
  font-family:var(--jt-police-display); font-size:var(--jt-taille-300);
  color:var(--accent-encre); text-decoration:none; white-space:nowrap;
  min-height:var(--cible-min); display:inline-flex; align-items:center;
}

.jt-rail__piste{
  display:grid; grid-auto-flow:column;
  grid-auto-columns:var(--objet-l);
  gap:var(--rail-gap);
  /* ⭐ RÉSERVÉ AVANT D'ÊTRE REMPLI — sans ça la page se replie et le chargement
     paresseux relâche tout le catalogue d'un coup (mesuré : ×9,3 de requêtes). */
  min-height:var(--rail-h);

  overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;
  /* Le rail SAIGNE jusqu'aux bords sur téléphone, mais les cartes commencent
     alignées sur le texte : c'est ce qui donne le « il y en a d'autres » sans
     rogner la première. */
  padding:2px var(--rail-marge) var(--jt-espace-200);
  scroll-padding-inline:var(--rail-marge);
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.jt-rail__piste::-webkit-scrollbar{display:none}
.jt-rail__piste > *{scroll-snap-align:start}

/* La dernière carte doit pouvoir venir se coller à gauche : sans ce fantôme,
   le rail bute avant d'avoir fini de la montrer. */
.jt-rail__piste::after{content:""; inline-size:1px}

/* Les flèches n'existent qu'au pointeur. Au doigt, on pousse. */
.jt-rail__fleches{display:none}
@media (hover:hover) and (min-width:64rem){
  .jt-rail__fleches{display:inline-flex; gap:var(--jt-espace-200)}
  .jt-rail__fleche{
    width:36px; height:36px; border-radius:var(--jt-rayon-plein);
    display:inline-flex; align-items:center; justify-content:center;
    background:var(--fond-haute); color:var(--encre-2);
    border:1px solid var(--filet);
    transition:background var(--jt-duree-normale), color var(--jt-duree-normale);
  }
  .jt-rail__fleche:hover{background:var(--fond-survol); color:var(--encre-1)}
  .jt-rail__fleche[disabled]{opacity:.35; pointer-events:none}
}


/* ═══════════════════════════════════════════════════════════════════════════
   LA TUILE — l'objet d'INTERFACE (pas une œuvre)
   C'est ici, et seulement ici, que la pastille se pose DEDANS (règle 10).
   Sert aux entrées de catalogue, aux étapes, aux occasions.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-tuile{
  position:relative; display:flex; flex-direction:column; gap:var(--jt-espace-200);
  min-height:var(--cible-min);
  padding:var(--jt-espace-400);
  background:var(--fond-surface); border:1px solid var(--filet);
  border-radius:var(--jt-rayon-400);
  text-decoration:none; color:inherit;
  box-shadow:var(--ombre-posee);
  transition:background var(--jt-duree-normale), border-color var(--jt-duree-normale),
             transform var(--jt-duree-normale) var(--jt-courbe);
}
@media (hover:hover){
  .jt-tuile:hover{background:var(--fond-haute); border-color:var(--filet-fort); transform:translateY(-2px)}
}
.jt-tuile__haut{display:flex; align-items:center; justify-content:space-between; gap:var(--jt-espace-300)}
.jt-tuile__icone{width:22px; height:22px; color:var(--encre-2); flex:0 0 auto}
.jt-tuile__titre{
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-500); margin:0; color:var(--encre-1);
  display:flex; align-items:center; gap:var(--jt-espace-200); flex-wrap:wrap;
}
.jt-tuile__note{margin:0; font-size:var(--jt-taille-300); color:var(--encre-2)}

/* L'étiquette DANS la tuile — en verre, discrète, jamais accentuée. */
.jt-etiquette{
  display:inline-flex; align-items:center; gap:5px;
  height:24px; padding:0 9px; border-radius:var(--jt-rayon-200);
  background:var(--fond-haute); color:var(--encre-2);
  border:1px solid var(--filet);
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-200); white-space:nowrap;
}


/* ═══════════════════════════════════════════════════════════════════════════
   L'AFFICHE — la bande de mise en avant (une par page, en haut)
   ⚠️ Chez higgsfield, son dégradé est rose de marque. Ici il est INTERDIT :
      la couleur vient de la carte mise en avant (règle 6). Le voile emprunte
      donc `--vive` de l'œuvre — l'affiche change de couleur avec elle.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-affiche{
  --vive:var(--accent);
  position:relative; isolation:isolate; overflow:hidden;
  border-radius:var(--jt-rayon-500);
  min-height:340px;
  display:flex; flex-direction:column; justify-content:flex-end;
  padding:var(--jt-espace-600);
  color:var(--jt-craie-100);          /* l'affiche est toujours sombre : elle
                                         porte une image, pas une surface */
  background:var(--jt-nuit-200);
}
.jt-affiche__media{position:absolute; inset:0; z-index:-2; width:100%; height:100%; object-fit:cover}
.jt-affiche__voile{
  position:absolute; inset:0; z-index:-1;
  background:
    linear-gradient(to top, rgba(7,6,11,.92) 0%, rgba(7,6,11,.55) 42%, transparent 78%),
    linear-gradient(to right, color-mix(in srgb, var(--vive) 55%, transparent) 0%, transparent 70%);
}
.jt-affiche__sur{
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-moyenne);
  font-size:var(--jt-taille-100); letter-spacing:var(--jt-lettre-sur);
  text-transform:uppercase; color:rgba(245,242,250,.72); margin:0 0 var(--jt-espace-300);
}
.jt-affiche__titre{
  font-family:var(--jt-police-display); font-weight:var(--jt-graisse-legere);
  font-size:clamp(28px, 7vw, 46px); line-height:1.04; letter-spacing:-.02em;
  margin:0 0 var(--jt-espace-400); max-width:18ch; text-wrap:balance;
}
@media (min-width:48rem){
  .jt-affiche{min-height:400px; padding:var(--jt-espace-1000)}
}


/* ═══════════════════════════════════════════════════════════════════════════
   LA BARRE D'ACTION DU POUCE — téléphone seulement
   L'action principale ne se cherche pas en haut d'un écran qu'on tient d'une
   main : elle attend sous le pouce. Elle disparaît au-delà de la tablette,
   où le bouton du haut redevient atteignable.
   ═══════════════════════════════════════════════════════════════════════════ */
.jt-pouce{
  position:fixed; left:0; right:0; bottom:0; z-index:60;
  padding:var(--jt-espace-300) var(--rail-marge)
          calc(var(--jt-espace-300) + var(--marge-sûre-bas));
  background:var(--fond-verre);
  backdrop-filter:var(--jt-flou-verre); -webkit-backdrop-filter:var(--jt-flou-verre);
  border-top:1px solid var(--filet);
  display:flex; gap:var(--jt-espace-300); align-items:center;
}
.jt-pouce .jt-bouton{flex:1; min-height:48px}
/* Réserver la place, sinon la barre masque la fin de la page. */
.jt-a-barre-pouce{padding-bottom:calc(76px + var(--marge-sûre-bas))}
@media (min-width:48rem){
  .jt-pouce{display:none}
  .jt-a-barre-pouce{padding-bottom:0}
}


/* ═══════════════════════════════════════════════════════════════════════════
   AJUSTEMENTS DU POUCE — ils s'appliquent à la partie I
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width:47.99rem){
  /* Rien de cliquable sous 44 px, sans exception. */
  .jt-bouton{min-height:max(var(--bouton-h), var(--cible-min))}
  .jt-bouton--petit{min-height:var(--cible-min); padding-inline:var(--jt-espace-400)}
  .jt-bascule button{min-height:var(--cible-min)}
  .jt-rail__lien, .jt-pied a{min-height:var(--cible-min)}

  .jt-barre{
    padding:var(--jt-espace-300) var(--rail-marge);
    padding-top:calc(var(--jt-espace-300) + var(--marge-sûre-haut));
  }
  .jt-contenu{padding-inline:var(--rail-marge)}
  .jt-modale{
    /* Sur téléphone une modale n'est pas une boîte au milieu : elle monte du bas. */
    width:100%; max-width:none; border-radius:var(--jt-rayon-600) var(--jt-rayon-600) 0 0;
    padding:var(--jt-espace-600) var(--jt-espace-500)
            calc(var(--jt-espace-600) + var(--marge-sûre-bas));
  }
  .jt-grille{grid-template-columns:repeat(auto-fill, minmax(140px, 1fr)); gap:var(--jt-espace-400)}
}
