/* =========================================================================
   UST — styles de page.
   Ils étaient portés par les composants ; réunis ici, un fichier, servi une
   fois et mis en cache. Les noms de classe sont déjà propres à leur page
   (.hero__, .coupe__, .bande__, .compa, .lex__), aucune collision.
   ========================================================================= */

/* ======================================================================
   index
   ====================================================================== */
/* =====================================================================
     1 · LE HÉROS PLEINE PAGE.

     LA GÉOMÉTRIE. .nav est en `sticky`, donc dans le flux : elle mange
     --h-nav (89 px, la barre plus son filet) du premier écran. Le héros
     retranche exactement cette hauteur, si bien que l'en-tête et lui
     remplissent la fenêtre au pixel, sans rien qui dépasse et sans qu'il
     faille défiler pour voir le bas du héros. 100svh et non 100vh : sur un
     mobile à barre d'adresse rétractable, 100vh vaut la fenêtre DÉPLIÉE et
     le héros dépasserait de la hauteur de la barre au premier rendu.

     Aucun data-rev ici : le héros est immobile (§8), il est peint à sa
     place définitive au premier rendu.
     ===================================================================== */
  .hero {
    position: relative; isolation: isolate;
    min-height: calc(100svh - var(--h-nav));
    display: flex; flex-direction: column; justify-content: flex-end;
    padding-block: var(--e-5);
    overflow: hidden;
  }
  .hero__fond { position: absolute; inset: 0; z-index: -1; }
  .hero__fond picture { display: block; height: 100%; }
  /* La photographie remplit le cadre sans jamais être déformée. `cover`
     recadre : c'est le seul endroit du site qui s'y autorise, et c'est un
     fond, pas une planche — la photographie entière reste sur sa fiche,
     à son format, servie par le même fichier.
     Le cadrage est calé à gauche de la source, et ce n'est pas un goût : le
     noyau de la galaxie est au milieu du fichier ; centré, il tombait sous
     la colonne de texte, où le voile l'éteignait. Calé à gauche, il ressort
     à 59 % de la largeur, du côté ouvert du voile. */
  .hero__image { display: block; width: 100%; height: 100%; object-fit: cover; object-position: left center; }
  .hero__voile { position: absolute; inset: 0; }

  /* LE VOILE, ET POURQUOI IL A CES VALEURS-LÀ.
     Le texte du héros est posé sur une photographie du ciel. Sans voile, les
     rapports de tokens.css ne veulent plus rien dire : ils sont calculés sur
     --nuit, et une étoile de champ est un pixel presque blanc. MESURÉ sur le
     rendu, photographie servie sans voile, pixel le plus défavorable de la
     boîte de chaque texte : 1,00:1 sous le titre comme sous le chapô, aux
     deux points de rupture. Le voile n'est donc pas un effet, c'est la
     condition de lisibilité.
     L'OPACITÉ MINIMALE A ÉTÉ CHERCHÉE, PAS DEVINÉE : on compose --nuit sur
     les pixels relevés et on monte l'opacité jusqu'à ce que le pixel le plus
     clair de la zone tienne son seuil. Il faut 0,495 pour le titre (grand
     texte, seuil 3:1), 0,78 pour le chapô, 0,80 pour le surtitre et 0,805
     pour le crédit (seuil 4,5:1). C'est 0,805 qui commande, et --voile-hero
     vaut 0,97 : la marge tient une autre photographie du catalogue, le jour
     où l'objet le plus travaillé change.
     LA GÉOMÉTRIE DU DÉGRADÉ SUIT LA COLONNE DE TEXTE, elle n'est pas
     décorative. La colonne fait --hero-colonne et se termine à 45 % de la
     fenêtre à 1440 : le voile reste plein jusqu'à 48 %, puis s'ouvre. Sous
     62rem le texte prend toute la largeur, le dégradé bascule à la
     verticale, et la photographie garde la bande du haut, au-dessus du
     premier mot. Les pourcentages sont de la géométrie de dégradé, jamais
     des valeurs de palette. */
  /* SOUS 62rem, LE VOILE PLEIN SUIT LE BLOC DE TEXTE, PAS UN POURCENTAGE.
     Première version : un dégradé vertical avec des arrêts en pourcentage de
     la hauteur du héros. Mesuré à 390, le surtitre tombait dans la zone de
     fondu et rendait 2,28:1 — sous le seuil. Un pourcentage ne sait pas où
     commence le texte, et la moindre ligne de chapô en plus le déplace.
     Le voile plein est donc porté par le bloc de texte lui-même, avec un
     fondu de --e-8 au-dessus de sa première ligne : quelle que soit la
     longueur du texte, il arrive toujours à pleine opacité avant le premier
     mot. Le héros garde par-dessous le voile ouvert, qui tient la
     photographie sous le seuil du signal ; les deux se composent, et le fond
     sous le texte est plus sombre encore que --voile-hero seul. */
  .hero__voile { background: var(--voile-hero-ouvert); }
  .hero__corps {
    width: 100%;
    padding-top: var(--e-8);
    background: linear-gradient(to bottom, transparent 0, var(--voile-hero) var(--e-8));
  }
  @media (min-width: 62rem) {
    .hero { justify-content: center; padding-block: var(--e-6); }
    /* Au-delà, le texte a sa colonne : le voile redevient un dégradé
       horizontal, plein sur la colonne, ouvert sur la photographie. */
    .hero__voile {
      background: linear-gradient(to right,
        var(--voile-hero) 0%,
        var(--voile-hero) 48%,
        var(--voile-hero-ouvert) 84%,
        var(--voile-hero-ouvert) 100%);
    }
    .hero__corps { padding-top: 0; background: none; }
  }
  .hero__grille { display: grid; gap: var(--e-6); align-items: center; }
  @media (min-width: 62rem) {
    /* La colonne de texte s'arrête où le voile s'ouvre : le texte ne
       déborde jamais sur la partie claire du dégradé. */
    .hero__grille {
      grid-template-columns: minmax(0, var(--hero-colonne)) minmax(0, 1fr);
      column-gap: var(--e-8);
    }
  }
  /* Sous 62rem la pile se resserre d'un cran : c'est ce qui rend à la
     photographie la bande du haut. Mesuré : sans ce resserrement le héros
     dépassait la fenêtre de 81 px à 390, et sa dernière ligne tombait sous
     le pli. */
  .hero__texte { display: flex; flex-direction: column; gap: var(--e-3); }
  @media (min-width: 62rem) { .hero__texte { gap: var(--e-4); } }
  .hero__titre { max-width: 18ch; }
  /* Voir --d-hero-etroit dans tokens.css : sous ce point de rupture, et pour
     ce titre seul, l'échelle de titrage descend d'un cran pour que le bloc
     insécable « photographie pas. » entre dans la colonne. */
  @media (max-width: 24.5rem) { .hero__titre { font-size: var(--d-hero-etroit); } }
  .hero__chapo { font-size: var(--d-lead); color: var(--champ); max-width: 46ch; line-height: 1.55; }
  .hero__actions { display: flex; flex-wrap: wrap; gap: var(--e-2); margin-top: var(--e-2); }

  /* L'ICÔNE 3D DU HÉROS, à droite du descriptif d'ouverture.
     Elle décore et ne porte rien : le balisage la retire aux lecteurs
     d'écran. Elle n'existe QU'À PARTIR DE 62rem, et c'est une décision, pas
     un oubli : sous ce point de rupture il n'y a pas de « droite du
     descriptif », l'icône tombait sous les boutons, et ses 136 px poussaient
     la dernière ligne du héros hors de la fenêtre. Une icône qui ne
     s'affiche pas parfaitement se retire. */
  .hero__objet { display: none; }
  @media (min-width: 62rem) {
    .hero__objet { display: block; justify-self: center; width: var(--orne-hero); max-width: 100%; }
  }
  /* Le crédit ne déborde jamais de la colonne de texte : au-delà, il entre
     dans la partie ouverte du voile, et --label y tombait à 1,01:1 sur une
     étoile de champ. Mesuré, corrigé, remesuré. */
  .hero__credit { color: var(--label); max-width: var(--hero-colonne); margin-top: var(--e-4); }
  @media (min-width: 62rem) { .hero__credit { margin-top: var(--e-6); } }

  /* LE HÉROS SUR UN ÉCRAN COURT, et pourquoi cette requête est sur la HAUTEUR.
     Le héros doit tenir dans la fenêtre : c'est la demande. `min-height` le
     garantit sur un écran haut, mais elle ne peut rien quand le CONTENU dépasse
     à lui seul la place disponible. Mesuré sur le rendu en ligne : à 1366x768,
     le corps du héros faisait 696 px pour 679 disponibles, et la page débordait
     le pli de 97 px. Même chose à 1280x800 (+63), 1024x768 (+48) et 375x667
     (+153). Ce sont des tailles d'ordinateur portable et de téléphone
     courantes, pas des cas limites.
     Ce qui se resserre, dans cet ordre : l'air autour du bloc, l'air entre ses
     lignes, et le crédit qui le termine. Le titre et le chapô ne bougent pas
     tant que l'air suffit : on retire du vide avant de toucher au texte. */
  @media (max-height: 880px) {
    .hero { padding-block: var(--e-4); }
    .hero__texte { gap: var(--e-2); }
    .hero__actions { margin-top: 0; }
    .hero__credit { margin-top: var(--e-3); }
  }
  @media (min-width: 62rem) and (max-height: 880px) {
    .hero { padding-block: var(--e-4); }
    .hero__texte { gap: var(--e-3); }
    .hero__grille { gap: var(--e-4); }
    .hero__credit { margin-top: var(--e-3); }
  }
  /* Entre 720 et 800 px de haut, un dernier cran d'air : mesuré à 1366x768,
     la résolution d'ordinateur portable la plus répandue, le héros dépassait
     encore de 5 px avec le seul resserrement précédent. */
  @media (min-width: 62rem) and (max-height: 800px) {
    .hero { padding-block: var(--e-3); }
  }

  /* Sous 720 px de haut, l'air ne suffit plus : le titre descend d'un cran, et
     la bande que le corps réservait à la photographie au-dessus de lui se
     réduit. Cette bande est une décision de composition, pas un hasard, mais
     sur un téléphone de 667 px de haut elle coûte 64 px que la fenêtre n'a
     pas : tenir dans le pli passe devant. C'est le dernier levier, et il n'est
     employé que là. */
  @media (max-height: 720px) {
    .hero { padding-block: var(--e-2); }
    .hero__titre { font-size: var(--d-hero-etroit); }
    .hero__credit { margin-top: var(--e-2); }
  }
  @media (max-width: 61.99rem) and (max-height: 720px) {
    .hero__corps { padding-top: var(--e-1); }
  }

  /* =====================================================================
     LE BANDEAU : LE HÉROS, MOINS LA HAUTEUR.
     Doctrine de cohérence, _design/COHERENCE.md, cran 3.

     CE BLOC N'EST PLUS UN STYLE DE PAGE. .hero est devenu le composant
     d'OUVERTURE des 68 pages ; il reste écrit ici, sous l'index, parce que
     sa géométrie et ses modificateurs doivent se lire ensemble et qu'on ne
     déplace pas 150 lignes mesurées à la veille d'une présentation. Le jour
     où pages.css est découpé, il part d'un bloc dans composants.css.

     POURQUOI UN MODIFICATEUR, ET PAS UN SECOND COMPOSANT. Tout ce qui est
     coûteux dans le héros est la MESURE du voile : deux opacités cherchées
     sur le rendu, une géométrie de dégradé calée sur --hero-colonne, un
     basculement horizontal/vertical à 62rem. Réécrire une « ouverture de
     page secondaire » aurait signifié refaire ces mesures, ou pire, les
     recopier sans les refaire. Le bandeau hérite donc du voile TEL QUEL et
     ne change qu'une chose : la hauteur.

     ET LES RAPPORTS DE CONTRASTE SUIVENT SANS ÊTRE REFAITS, parce que la
     partie PLEINE du voile est opaque à 97 % : sous le texte, le fond
     redevient --nuit à la mesure quelle que soit la photographie posée
     dessous. Même sous un pixel blanc pur, le composite tient (calculé sur
     ces hexadécimaux exacts, formule WCAG 2.x) :
       --texte  #EFE7E9  15,39:1     --champ  #A6ACBA  8,23:1
       --label  #B0A1A2  7,54:1      --lampe  #F9435B  5,33:1
     Tous au-dessus du seuil 4,5. Aucun objet graphique porteur ne se pose
     sur un bandeau : --filet-haut y vaut 2,08:1, sous le plancher 3:1 de
     WCAG 1.4.11, comme il vaut déjà 2,19:1 sur --nuit pur. Les filets qui
     s'y trouvent sont décoratifs et portent aria-hidden.
     C'est ce qui autorise le bandeau à servir
     une photographie de jour, quand le héros sert un champ d'étoiles : la
     colonne de texte ne dépend pas du sujet de l'image.
     ===================================================================== */
  .hero--bandeau {
    /* Un PLANCHER, jamais un plafond : un chapô de quatre lignes rend le
       bandeau plus haut, et c'est le chapô qu'on raccourcit, pas le texte
       qu'on rogne. Voir --h-ouv-bandeau dans tokens.css. */
    min-height: var(--h-ouv-bandeau);
    /* Le héros pousse son texte en bas de la fenêtre, parce qu'il en a la
       hauteur et que le bas de fenêtre est là où l'œil finit sa descente.
       Un bandeau n'a pas de descente : son bloc est centré. */
    justify-content: center;
  }
  /* L'ornement d'un bandeau prend la taille d'un ornement de section, pas
     celle de l'icône du héros : 288 px dans une bande de 416 la feraient
     passer devant le titre, qui est ce que la page a à dire. */
  .hero--bandeau .hero__objet { width: var(--orne); }
  /* LE TITRE D'UN BANDEAU DESCEND D'UN CRAN, ET C'EST MESURÉ.
     La colonne de texte du héros fait --hero-colonne, 544 px : c'est la
     géométrie du voile qui la fixe, elle ne s'élargit pas sans refaire les
     deux opacités. Mesuré au rendu à 1440, dans cette colonne :
       « Le ciel ne se photographie pas. Il s'accumule. », 45 signes, à 96 px
       et avec son <br> posé à la main .............. 3 lignes, 294 px
       « Quel télescope intelligent choisir en 2026 », 42 signes, à 96 px et
       sans coupe écrite ........................... 5 lignes, 490 px
     Le second est un titre de page d'index : il répond à une requête, sa
     longueur n'est pas négociable, et il ne peut pas porter de coupe écrite
     à la main puisqu'il contient une donnée (l'année). À 96 px il poussait
     le bandeau à 937 px, soit plus haut que le héros de l'accueil sur un
     portable (679 px à 1366x768) : la hiérarchie s'inversait.
     Le titre passe donc à --d-h2 (30 -> 56 px), où le même texte tient en
     deux lignes. La règle générale est celle-là : L'ÉCHELLE DE TITRAGE SUIT
     LE BUDGET DE HAUTEUR, pas le rang de la page. Le héros a la fenêtre et
     dépense 294 px ; l'ouverture au filet n'a pas de plafond et garde
     l'échelle de titrage ; le bandeau, lui, doit se lire d'emblée comme
     moins que l'accueil, donc son titre descend.
     Et sa mesure de ligne n'est plus 18ch : à 56 px, 18ch coupe la colonne
     bien avant son bord et rend trois lignes là où deux suffisent. La
     colonne du voile borne déjà le titre, et elle le borne juste. */
  .hero--bandeau .hero__titre { max-width: none; }

  /* =====================================================================
     L'ORNEMENT 3D D'UNE SECTION.
     Une icône par section, jamais deux, jamais sur du texte : elle vit
     À CÔTÉ de la tête de section, dans une rangée. Les côtés alternent au
     fil du défilement, par .orne--gauche. Quand la rangée se replie, l'icône
     passe sur sa propre ligne, à sa taille de repli.
     ===================================================================== */
  .orne { display: flex; flex-wrap: wrap; gap: var(--e-5) var(--e-6); align-items: center; }
  .orne > :not(.objet3d) { flex: 1 1 22rem; min-width: 0; }
  .orne--gauche .objet3d { order: -1; }
  .orne .objet3d { width: var(--orne); }

  /* L'OBJET QUI NE S'AFFICHE PAS PARFAITEMENT EST RETIRÉ, PAS RAFISTOLÉ.
     C'est la règle absolue de la grammaire du décor (_design/COHERENCE.md),
     et elle a déjà été appliquée deux fois sur ce projet : six rendus 3D
     écartés au détourage (le champ _ecartes de donnees/objets3d.json dit
     lequel et pourquoi), et l'icône du héros retirée sous 62rem.
     ICI, LE POINT DE RETRAIT EST 48rem, ET IL EST MESURÉ, PAS CHOISI. La
     rangée donne 22rem au contenu (.orne > :not(.objet3d)), 2,5rem de
     gouttière et la largeur de l'icône : sous 48rem elle ne tient plus à
     côté du texte, elle se replie SOUS lui. Une icône seule sur sa ligne
     n'est plus ancrée à un bloc, c'est une vignette orpheline au milieu
     d'une page de lecture. On la retire.
     ELLE NE COÛTE RIEN NON PLUS EN OCTETS : un ornement n'est jamais servi
     en `prioritaire`, donc son <img> est en loading="lazy" ; sans boîte de
     rendu il n'entre jamais dans la fenêtre et n'est jamais téléchargé. */
  @media (max-width: 47.99rem) {
    .orne .objet3d { display: none; }
  }
  /* JAMAIS SUR DU TEXTE, JAMAIS SOUS UN AUTRE CALQUE. L'ornement est un
     FRÈRE du bloc dans le flux : ni position, ni z-index, ni marge négative,
     ni transform. Ces trois déclarations ne corrigent rien aujourd'hui —
     elles referment la porte, parce que la tentation d'aller chercher trois
     pixels de chevauchement se présente à chaque pose. */
  .orne .objet3d { position: static; z-index: auto; transform: none; }
  /* Le comportement commun des cinq icônes : elles remplissent la largeur
     qu'on leur donne et gardent leur carré. La largeur, elle, est propre à
     l'emploi — --orne dans une section, --orne-hero dans le héros. */
  .objet3d { flex: none; max-width: 100%; }
  .objet3d picture, .objet3d img { display: block; width: 100%; height: auto; }
  /* La tête de section porte sa propre mesure de lecture ; dans une rangée
     avec une icône, la contrainte de 62ch la ferait flotter au milieu de sa
     colonne au lieu de la remplir. */
  .tete--ciel { max-width: none; }

  /* =====================================================================
     LES DEUX RANGÉES DE CARTES
     ===================================================================== */
  .tete-ligne { display: flex; flex-wrap: wrap; gap: var(--e-4); align-items: end; justify-content: space-between; }
  .tete-ligne__action { flex: none; }

  .carte__meta { color: var(--label); }
  .carte__grad { margin-top: auto; padding-top: var(--e-4); }
  /* Les vignettes sont posées sur --ciel. Le jeton --gradue, prévu pour les
     TRAITS de seuil, y tombe à 3,74:1 : c'est le plancher d'un objet
     graphique (WCAG 1.4.11), pas celui d'un texte (1.4.3, 4,5:1). Or les
     valeurs de seuil imprimées SONT du texte. Elles passent donc au jeton
     voisin --label, mesuré à 6,49:1 ; les traits, eux, restent en --gradue.
     Aucune valeur nouvelle, deux jetons existants. À remonter dans
     composants.css avec le composant. */
  .carte .grad__seuils { color: var(--label); }

  /* =====================================================================
     LE COMPTEUR — il n'est plus servi par l'accueil, qui a perdu ses quatre
     chiffres d'ouverture sur arbitrage. Il reste le bloc de totaux de
     /a-propos/, seule page qui l'emploie encore ; les deux règles qui ne
     servaient qu'à le poser dans la grille du héros sont parties avec elle.
     ===================================================================== */
  .compteur {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--e-4) var(--e-5);
    padding-top: var(--e-5); border-top: 1px solid var(--filet);
  }
  @media (min-width: 34rem) { .compteur { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  @media (min-width: 62rem) { .compteur { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .compteur dt {
    font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
    line-height: var(--i-mono); text-transform: uppercase; letter-spacing: var(--l-brow);
    color: var(--label);
  }
  /* Règle dure du système (§6) : le nombre est en Martian Mono, l'unité
     reste dans la famille de la prose. */
  .compteur dd { font-size: var(--d-h3); line-height: var(--i-titre); margin-top: var(--e-1); color: var(--texte); }
/* ======================================================================
   telescopes/index
   ====================================================================== */
/* --- Le tableau comparatif ---------------------------------------------- */
  .tableau-cadre { border: 1px solid var(--filet); border-radius: var(--r); }
  .compa { border-collapse: collapse; width: 100%; min-width: 82rem; font-size: var(--d-petit); }
  /* Le tableau d'un duel n'a que quatre colonnes, pas neuf : la largeur
     plancher qui sauve le comparatif complet lui imposerait un défilement
     horizontal pour rien. Aucune autre règle ne change, il reste le même
     tableau, y compris la pile de fiches sous 62 rem. */
  .compa--duel { min-width: 0; }

  /* DEUX COLONNES DE MÊME LARGEUR, et c'est le fond du sujet.
     .deux-cols mesure 320 + 856 px à 1440 : une colonne de titre et une
     colonne de contenu, ce qui est juste sur une fiche produit. Poser dans
     ces largeurs-là les deux appareils d'un duel reviendrait à en désigner un
     avant d'avoir comparé quoi que ce soit. Ici les deux côtés sont égaux,
     ou empilés. Aucune valeur littérale : les gouttières sont des jetons. */
  .duo { display: grid; gap: var(--e-5); grid-template-columns: minmax(0, 1fr); }
  @media (min-width: 56rem) {
    .duo { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--e-6); }
  }

  /* Sous 62 rem, neuf colonnes dans 390 px donnaient 26 % de tableau visible,
     aucune affordance de défilement, et jusqu'à 192 px de vide par ligne parce
     que la hauteur était imposée par les cellules hors champ. Le mobile se fait
     par soustraction, jamais par compression : le tableau devient une pile de
     fiches, chaque cellule reprenant le libellé de sa colonne. Les balises de
     tableau restent en place, seule la présentation change. */
  @media (max-width: 61.99rem) {
    .tableau-cadre { overflow: visible; border: 0; }
    .compa { min-width: 0; display: block; }
    .compa thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
    .compa tbody, .compa tr, .compa th, .compa td { display: block; }
    .compa tr {
      border: 1px solid var(--filet); border-radius: var(--r);
      padding: var(--e-4); margin-bottom: var(--e-3);
    }
    .compa tbody th { border: 0; padding: 0 0 var(--e-3); min-width: 0; white-space: normal; }
    .compa td {
      border: 0; padding: var(--e-1) 0;
      display: flex; justify-content: space-between; gap: var(--e-3);
      border-top: 1px solid var(--filet); text-align: right;
    }
    .compa td::before {
      content: attr(data-libelle);
      font-family: var(--f-corps); font-size: var(--d-petit); color: var(--label);
      text-align: left; flex: none;
    }
  }
  /* Neuf colonnes tenaient dans 62rem : « 35 mm (télé) · 3,4 mm (grand angle) »
     cassait sur cinq lignes. Largeur portée à 82rem, le conteneur défile. */
  .compa tbody th { min-width: 14rem; }
  .compa th, .compa td { text-align: left; padding: var(--e-3) var(--e-4); border-bottom: 1px solid var(--filet); vertical-align: top; }
  .compa thead th {
    font-family: var(--f-data); font-size: var(--d-mono); text-transform: uppercase;
    letter-spacing: var(--l-brow); color: var(--label); font-weight: var(--g-mono-fort);
    position: sticky; top: 0; background: var(--nuit); z-index: 1;
  }
  .compa tbody th { font-weight: var(--g-corps-fort); white-space: nowrap; }
  .compa tbody th a { text-decoration: none; }
  @media (hover: hover) {
    .compa tbody th a:hover { text-decoration: underline; text-decoration-color: var(--lampe); }
  }
  .compa td { font-family: var(--f-data); font-size: var(--d-mono); color: var(--champ); font-variant-numeric: tabular-nums; }
  .compa tbody tr:last-child th, .compa tbody tr:last-child td { border-bottom: 0; }
  .compa__puce { margin-left: var(--e-1); }
  /* LE FILET DU SURTITRE A REMONTÉ DANS composants.css. Il était écrit ICI
     TROIS FOIS, à l'identique, plus une quatrième sous le nom .integ-marque
     dans composants.css : quatre copies d'un trait de 3 rem sur 1 px. C'est
     la faute de .bande, au carré. Une seule règle le dessine désormais, et
     .filet-brow en est le nom retenu. Voir _design/COHERENCE.md. */

  /* Le hero : 62ch est la mesure d'une prose, pas celle d'un titrage à 96 px.
     Le h1 prend sa propre mesure, comptée dans sa fonte. */
  .tete--hero { max-width: none; }
  .tete--hero h1 { max-width: 20ch; }
  .tete--hero p { max-width: 46ch; }

  /* LE TITRE ET SON CHAPÔ CÔTE À CÔTE, sur la page qui doit ouvrir sur des
     appareils. Empilés, ils prenaient 640 px de haut et laissaient la moitié
     droite vide ; alignés par le bas, la tête ne fait plus que la hauteur du
     titre. Les deux gardent leur mesure de lecture, qui ne bouge pas. Sous
     60 rem la règle ne s'applique pas : la pile reste la pile. */
  @media (min-width: 60rem) {
    .tete--duo {
      display: grid; align-items: end;
      grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
      column-gap: var(--e-8); row-gap: var(--e-3);
    }
    .tete--duo .tete__brow { grid-column: 1 / -1; }
    .tete--duo h1 { max-width: 15ch; }
  }

  /* LA BARRE DE SECTION DU CATALOGUE. Un titre à gauche, la note de prix à
     droite, un filet au-dessus : de quoi nommer la section sans remettre un
     chapeau complet entre le chapô de la page et la première photographie. */
  .cata-tete {
    display: grid; gap: var(--e-2);
    padding-top: var(--e-3); border-top: 1px solid var(--filet);
    align-items: baseline;
  }
  @media (min-width: 60rem) {
    .cata-tete { grid-template-columns: minmax(0, 1fr) minmax(0, 30rem); gap: var(--e-6); }
  }
  .cata-tete h2 { color: var(--texte); }
  .cata-tete h2 strong { font-weight: inherit; color: var(--lampe-haute); }
  .cata-tete__note { color: var(--label); max-width: none; }
  .note-sourde { color: var(--label); max-width: none; }

  /* --- La bande : les lignes du panel, un axe partagé ----------------------
     Aucune valeur de graduation n'est redéfinie ici : les pistes, l'index,
     les seuils et l'état vide viennent des classes .grad de composants.css.
     Cette feuille ne pose que la mise en page des lignes.                   */
  /* L'AIR ÉTAIT COMPTÉ DEUX FOIS, ET IL EST RENDU.
     `.bande` existait DEUX fois dans ce fichier : ici, pour les lignes de
     /telescopes/, et dans le bloc index pour la bande constructeur de
     l'accueil. L'accueil a perdu sa bande ; sa déclaration, recopiée ici pour
     conserver le rendu au pixel, posait --section-large (192 px à 1440) de
     padding-block, --marge de padding-inline, un fond --ciel et un filet
     --filet, DANS une .section qui porte déjà --section (150 px), le --marge
     de son .enveloppe et le même --ciel en fond. Le donneur d'ordre a
     tranché : « applique les 405 px ». Ce qui est retiré, et pourquoi.
     · LE PADDING-BLOCK. Le rythme de page appartient à la .section : elle
       dépense --section au-dessus et au-dessous, plus --e-6, le gap de sa
       .pile-6, entre le chapô et la bande. La bande ne garde que son air de
       composant, et seulement là où un enfant ne le porte pas : chaque .rang
       pose --e-5 en haut et en bas, sauf le dernier, .rang--axe, dont le
       padding-bottom est nul par construction. --e-5 en bas sur la bande rend
       à la légende de l'axe les 32 px que toutes les autres ont déjà, et rien
       de plus. Rien en haut : la première ligne apporte son --e-5 sous le
       filet, exactement comme .maison sous le sien.
     · LE PADDING-INLINE. Le même double compte sur l'autre axe. .enveloppe
       pose déjà --marge, et la bande enfonçait ses lignes de 40 px de plus
       que le chapô qui les annonce : 140 px contre 100 px à 1440, 44,9 contre
       22,4 à 390. Le filet, lui, courait sur toute la largeur du .contenu :
       il était plus large que ce qu'il bornait.
     · LE FOND. Identique au --ciel que la section porte déjà : la déclaration
       ne peignait rien. Aucun pixel ne change en la retirant.
     · LE FILET. Un filet de 1 px tracé entre deux aplats de même couleur ne
       borne rien : à retirer le double emploi, il fallait trancher entre
       détacher la bande, et supprimer le filet. Détacher voudrait dire lui
       poser --nuit dans une section --ciel : 1,22:1 mesuré, très loin sous le
       seuil de 2:1 que pose base.css, donc un plan qui aurait ENCORE besoin
       d'un filet pour se lire, et une inversion du gabarit, où le panneau est
       la section. Refusé. La bande ne se détache pas : elle EST la liste. Un
       seul filet lui reste, celui du haut, qui ouvre la liste au ras de sa
       première ligne au lieu de flotter 193 px au-dessus d'elle. Il vaut
       --filet sur --ciel, 1,24:1, exactement le poids des filets qui séparent
       les lignes entre elles : la borne de la liste ne pèse pas plus que ses
       séparateurs, et c'est .rang:first-child qui l'accueille en renonçant au
       sien. En bas, le filet du .dementi ferme déjà la liste, 72 px sous la
       légende de l'axe ; en garder un second à 40 px de lui aurait refait, en
       petit, la faute que ce bloc corrige.
     GAIN MESURÉ, à contenu identique, en relevant les deux états dans la même
     page pour que le lot voisin qui écrit telescopes.php ne fausse rien :
     373 px de hauteur de page à 1440, 158 px à 390. Le lot précédent
     annonçait 405 px et 191 px : c'est le compte d'un dépouillement à zéro,
     padding et filets compris, qui aurait laissé la légende de l'axe collée
     au pli de la section. Les 32 px rendus à cette légende, plus le filet du
     haut, sont exactement la différence : 405 moins 32 font 373, et 191 moins
     33 font 158. L'air avant la première ligne tombe de 857 à 665 px à 1440
     et de 680 à 586 px à 390 ; entre le chapô et cette ligne, de 265 à 73 px
     à 1440 et de 168 à 73 px à 390, soit --e-6 plus le filet plus --e-5, la
     même ouverture aux deux largeurs. */
  .bande {
    display: flex; flex-direction: column;
    padding-bottom: var(--e-5);
    border-top: 1px solid var(--filet);
  }
  .rang {
    display: grid; gap: var(--e-3);
    padding-block: var(--e-5);
    border-top: 1px solid var(--filet);
    align-items: center;
  }
  .rang:first-child { border-top: 0; }
  .rang--axe { padding-block: var(--e-3) 0; border-top-color: var(--filet-haut); }
  @media (min-width: 60rem) {
    .rang { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); column-gap: var(--e-6); }
    /* L'axe se cale sous les pistes, jamais sous la colonne d'identité. */
    .rang--axe > .grad { grid-column: -2 / -1; }
  }
  .rang__id { display: flex; flex-direction: column; gap: var(--e-1); min-width: 0; }
  .rang__nom { letter-spacing: -.015em; }
  .rang__lien { text-decoration: none; }
  @media (hover: hover) { .rang__lien:hover { color: var(--lampe-haute); } }
  .rang__specs {
    display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3);
    color: var(--champ); max-width: none;
  }
  .rang__specs span + span::before { content: "· "; color: var(--filet-haut); }
  .rang__equiv { color: var(--label); max-width: none; }
  .rang__compte {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2);
    margin-top: var(--e-1); color: var(--label); max-width: none;
  }
  .rang__axe-mot { color: var(--label); max-width: none; }
  @media (min-width: 60rem) { .rang__axe-mot { text-align: right; } }

  .dementi {
    display: grid; gap: var(--e-3);
    border-top: 1px solid var(--filet); padding-top: var(--e-5);
  }
  @media (min-width: 60rem) { .dementi { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e-6); } }
  .dementi p { color: var(--label); max-width: 46ch; }

  /* --- Les cinq maisons, en lignes ---------------------------------------- */
  .maisons { list-style: none; padding: 0; display: flex; flex-direction: column; }
  .maison {
    display: grid; gap: var(--e-2) var(--e-6);
    padding-block: var(--e-5); border-top: 1px solid var(--filet);
  }
  .maison:last-child { border-bottom: 1px solid var(--filet); }
  @media (min-width: 60rem) { .maison { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); } }
  .maison__id { display: flex; flex-direction: column; gap: var(--e-1); }
  .maison__nom { letter-spacing: -.015em; }
  .maison__lien { text-decoration: none; }
  @media (hover: hover) { .maison__lien:hover { color: var(--lampe-haute); } }
  .maison__dire { display: flex; flex-direction: column; gap: var(--e-3); }
  .maison__dire p { color: var(--champ); }
  .maison__gamme { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 4px; color: var(--label); }

  /* --- « Lequel pour vous » : quatre routes, même grammaire que .maison ---
     Une colonne d'identité (le numéro, la situation, le critère) et une
     colonne de dire (l'appareil, ses chiffres, son contrepoids). Aucun cadre,
     aucune carte : ce sont des lignes, comme la liste des maisons plus bas,
     pour qu'on lise quatre réponses et non quatre offres. */
  .routes { list-style: none; padding: 0; display: flex; flex-direction: column; }
  .route {
    display: grid; gap: var(--e-3) var(--e-6);
    padding-block: var(--e-5); border-top: 1px solid var(--filet);
  }
  .route:last-child { border-bottom: 1px solid var(--filet); }
  @media (min-width: 60rem) { .route { grid-template-columns: minmax(0, 24rem) minmax(0, 1fr); } }
  .route__id { display: flex; flex-direction: column; gap: var(--e-2); }
  /* Le numéro porte la lampe, comme les 01/02/03 de .pratique : c'est le
     même objet, il garde la même couleur. */
  .route__n { color: var(--lampe); }
  .route__situation { letter-spacing: -.015em; max-width: 24ch; }
  .route__critere { color: var(--label); }
  .route__dire { display: flex; flex-direction: column; gap: var(--e-2); max-width: 60ch; }
  .route__nom { font-size: var(--d-h4); }
  .route__faits { color: var(--texte); }
  .route__sup { color: var(--champ); }
  .route__contre { color: var(--label); }

/* ======================================================================
   telescopes/[slug]
   ====================================================================== */
.fil { display: flex; flex-wrap: wrap; gap: var(--e-1); color: var(--label); margin-bottom: var(--e-6); }

  .fil a { text-decoration: none; display: inline-flex; align-items: center; min-height: 1.5rem; }
  @media (hover: hover) { .fil a:hover { color: var(--texte); } }

  /* --- LA TÊTE DE FICHE : l'appareil, son prix, l'appel ------------------
     La photographie prend la moitié gauche et le texte la droite. Sous
     60 rem la grille s'empile, l'image d'abord : sur un téléphone, la
     première chose à l'écran doit être l'appareil qu'on vend. */
  .prodhero { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  @media (min-width: 60rem) { .prodhero { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
  /* La boîte du packshot est ronde de coin et cadrée comme une carte : sans
     ce filet, un détouré sur fond dégradé n'a plus de bord du tout. */
  .prodhero__media .pack {
    border: 1px solid var(--filet); border-radius: var(--r);
    border-bottom: 1px solid var(--filet);
  }
  .prodhero__legende { color: var(--label); margin-top: var(--e-2); max-width: none; }
  /* La colonne de texte est collante sur grand écran : le prix et l'appel
     restent à l'écran pendant qu'on regarde l'image. */
  @media (min-width: 60rem) {
    .prodhero__texte { position: sticky; top: 6rem; }
  }

  /* --- LE PRIX PUBLIC, ET SA DATE ---------------------------------------
     Martian Mono porte la donnée, et un prix en est une. Il monte à
     l'échelle d'un titre parce qu'une page qui vend n'a pas à faire chercher
     son prix. La nuance et la réserve le suivent immédiatement : elles ne
     partent jamais dans une note de bas de page. */
  .prix {
    display: flex; flex-direction: column; gap: var(--e-1);
    border-left: 2px solid var(--lampe-nuit); padding-left: var(--e-4);
  }
  .prix__valeur {
    font-size: var(--d-h2); font-weight: var(--g-mono-fort); color: var(--texte);
    line-height: 1.1; letter-spacing: -.02em;
  }
  .prix__nuance { color: var(--champ); max-width: none; }
  .prix__releve { color: var(--label); max-width: 60ch; }

  /* --- LA PLANCHE DES AUTRES VUES ---------------------------------------
     Le cadre garde le format du fichier : la boîte carrée du packshot est
     réservée à la tête de fiche et aux cartes, où neuf appareils doivent
     s'aligner. Ici il n'y en a qu'un, il n'a rien à aligner. */
  .vitrine { display: flex; flex-direction: column; gap: var(--e-3); }
  .vitrine .cadre { display: block; }
  .vitrine picture { display: block; }
  .vitrine img { display: block; width: 100%; height: auto; }
  .vitrine__legende { color: var(--label); max-width: none; }

  .produit { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  /* La fiche technique et la clé de ses mots forment un bloc : la liste garde
     son rythme, la glose se pose dessous. Sans ce conteneur, la glose
     deviendrait une troisième colonne de la grille. */
  .fiche-bloc { display: flex; flex-direction: column; gap: var(--e-3); }
  @media (min-width: 60rem) { .produit { grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); } }
  /* La fiche technique est plus courte que la colonne de gauche : sans cela une
     bande morte s'ouvrait sous elle jusqu'au bas de la section. */
  .produit > .pile { align-self: stretch; }
  .produit__actions { display: flex; gap: var(--e-2); flex-wrap: wrap; margin-top: var(--e-1); }
  /* La ligne qui suit les actions : ce qui se passe quand on demande un prix.
     Elle est en Martian Mono sourde, comme les autres notes de la fiche, et
     surtout PAS en accent : elle informe, elle ne pousse pas. */
  .produit__vente { color: var(--label); max-width: 62ch; margin-top: var(--e-2); }
  /* Le prix du bloc « Se procurer ». Martian Mono porte la donnée, et un prix
     en est une ; sa réserve juridique le suit immédiatement, jamais ailleurs. */
  .produit__prix { font-size: var(--d-h3); color: var(--texte); }
  .fiche {
    position: sticky; top: 6rem;
    border: 1px solid var(--filet); border-radius: var(--r); padding: var(--e-3) var(--e-4);
    background: var(--ciel);
  }

  /* --- Le relevé et la graduation (C5, G5, G6) -----------------------------
     Aucun jeton de graduation n'est redéfini ici : la piste, l'index, les
     seuils et l'état vide viennent du composant et de composants.css. */
  .releve {
    display: flex; flex-direction: column; gap: var(--e-3);
    border: 1px solid var(--filet); border-radius: var(--r);
    padding: var(--e-4); background: var(--ciel);
  }
  .releve__etat {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2);
    color: var(--label); max-width: none;
  }

  /* --- L'équivalence de pose et son démenti (G2) -------------------------- */
  .equiv { display: flex; flex-direction: column; gap: var(--e-2); border-left: 2px solid var(--lampe-nuit); padding-left: var(--e-4); }
  /* La chasse de Martian Mono est très large : sans équilibrage, la durée
     rejette « ici. » seul sur la ligne suivante. */
  .equiv__phrase { font-size: var(--d-lead); line-height: 1.5; color: var(--texte); text-wrap: balance; }
  .equiv__dementi { color: var(--label); max-width: 62ch; }

  .deux-cols { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  @media (min-width: 60rem) { .deux-cols { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); } }

  .prose { color: var(--champ); }
  .note-sourde { color: var(--label); max-width: 62ch; }

  .liste-tirets { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--e-3); }
  .liste-tirets li { padding-left: 1.25rem; position: relative; color: var(--champ); }
  .liste-tirets li::before {
    content: ""; position: absolute; left: 0; top: 0.7em; width: 12px; height: 1px; background: var(--lampe);
  }
  .liste-tirets--sourde li::before { background: var(--filet-haut); }
  .pile-3 { gap: var(--e-3); }

  .pratique { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 0; }
  .pratique li { display: grid; grid-template-columns: 3rem minmax(0, 1fr); gap: var(--e-3); padding: var(--e-4) 0; border-top: 1px solid var(--filet); }
  .pratique li:last-child { border-bottom: 1px solid var(--filet); }
  .pratique__n { color: var(--lampe); padding-top: 0.2em; }
  .pratique p { color: var(--champ); max-width: 60ch; }

  .carte__meta { color: var(--champ); margin-top: auto; padding-top: var(--e-2); }
  .carte__format { color: var(--label); }

/* ======================================================================
   constructeurs/[slug]
   ====================================================================== */
.fil { display: flex; flex-wrap: wrap; gap: var(--e-1); color: var(--label); margin-bottom: var(--e-6); }

  .fil a { text-decoration: none; display: inline-flex; align-items: center; min-height: 1.5rem; }
  @media (hover: hover) { .fil a:hover { color: var(--texte); } }

  .entete { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  @media (min-width: 60rem) { .entete { grid-template-columns: minmax(0, 1fr) minmax(0, 30rem); } }
  .prose { color: var(--champ); }

  /* Le relevé : aucun jeton de graduation n'est redéfini ici, la piste et
     l'état vide viennent du composant et de composants.css. */
  .releve {
    display: flex; flex-direction: column; gap: var(--e-3);
    border: 1px solid var(--filet); border-radius: var(--r);
    padding: var(--e-4); background: var(--ciel);
  }
  .releve__etat {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2);
    color: var(--label); max-width: none;
  }

  .carte__resume { color: var(--champ); font-size: var(--d-petit); }
  .carte__equiv { color: var(--label); padding-top: var(--e-2); }
  .carte__meta { color: var(--label); margin-top: auto; padding-top: var(--e-2); }
  .carte__puce { margin-top: var(--e-3); }
  .mini { margin-top: auto; padding-top: var(--e-4); }

  .dementi { color: var(--label); max-width: 78ch; }

  .liste-tirets { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--e-3); max-width: 62ch; }
  .liste-tirets li { padding-left: 1.25rem; position: relative; color: var(--champ); }
  .liste-tirets li::before { content: ""; position: absolute; left: 0; top: 0.7em; width: 12px; height: 1px; background: var(--lampe); }

/* ======================================================================
   catalogue/index
   ====================================================================== */
.filtres { display: flex; flex-wrap: wrap; gap: var(--e-2); }
  .filtre {
    display: inline-flex; align-items: baseline; gap: var(--e-1);
    padding: var(--e-1) var(--e-3); border-radius: var(--r-pill);
    border: 1px solid var(--filet); color: var(--champ);
    font-size: var(--d-petit); font-weight: var(--g-corps-moyen);
    transition: border-color var(--t-ui) ease, color var(--t-ui) ease, background-color var(--t-ui) ease;
  }
  .filtre .mono { color: var(--label); }
  /* Tout survol est gardé : rien ne se déclenche au doigt. */
  @media (hover: hover) { .filtre:hover { border-color: var(--filet-haut); color: var(--texte); } }
  /* Un filtre pressé est ce que la main touche : c'est le seul emploi de la
     lampe ici (loi d'accent n° 2). --nuit sur --lampe = 5,61:1. */
  .filtre.est-actif { background: var(--lampe); border-color: var(--lampe); color: var(--nuit); }
  .filtre.est-actif .mono { color: var(--nuit); }

  /* La ligne de compte du catalogue est un PARAGRAPHE, pas le bloc de chiffres
     de l'accueil et des outils, qui porte le même nom de classe et se dessine
     en grille bordée. Scopées par page, les deux ne se croisaient jamais ;
     réunies dans un seul fichier servi partout, la grille venait ceinturer
     cette ligne d'un filet et la tasser sur une demi-colonne. Le sélecteur est
     donc porté par l'attribut que cette page seule écrit. */
  p.compteur[data-compteur] {
    display: block; border-top: 0; padding-top: 0;
    color: var(--champ);
  }
  /* G7, conséquence assumée : les cadres n'ont plus la même hauteur, donc les
     colonnes ne s'alignent pas au pixel. Chaque carte prend sa hauteur propre —
     étirées, celles d'un panoramique creusaient un vide de 200 px sous leur
     titre pour rattraper le carré voisin. Mesuré, puis corrigé ici. */
  .grille[data-liste] { align-items: start; }
  .carte__mesure { padding-top: var(--e-3); }
  .carte[hidden] { display: none; }

  /* --gradue est calibré sur --nuit, où il donne 4,58:1. Posé sur une carte
     --ciel il tombe à 3,74:1 : c'est le plancher d'un objet graphique
     (WCAG 1.4.11), pas celui d'un texte (1.4.3, 4,5:1). Or les valeurs de
     seuil imprimées SONT du texte. Sur carte elles passent donc au jeton
     voisin --label, mesuré à 6,49:1 ; les traits, eux, restent en --gradue.
     Aucune valeur nouvelle, deux jetons existants. À remonter dans
     composants.css avec le composant. */
  .carte .grad__seuils { color: var(--label); }

/* ======================================================================
   catalogue/[slug]
   ====================================================================== */
.fil { display: flex; flex-wrap: wrap; gap: var(--e-1); color: var(--champ); margin-bottom: var(--e-6); }
  .fil a { text-decoration: none; display: inline-flex; align-items: center; min-height: 1.5rem; }

  /* Le cadre d'ouverture n'est jamais recadré (G7) : c'est sa HAUTEUR qui est
     bornée et sa largeur qui suit le ratio du capteur. Sans cette borne, une
     source 1:1 — les 15 fiches Unistellar et Vaonis — servait un hero de
     1 240 px de haut, presque tout noir, et devenait le pire LCP du site.
     L'image reste entière : on change l'échelle, jamais le cadrage. */
  .planche {
    --planche-h: clamp(22rem, 10rem + 32vw, 40rem);
    max-width: calc(var(--planche-h) * var(--ratio, 3 / 2));
    margin: 0 0 clamp(2rem, 1rem + 4vw, 3.5rem);
  }
  .planche__legende {
    display: flex; flex-wrap: wrap; justify-content: space-between;
    gap: var(--e-2); margin-top: var(--e-3); color: var(--champ); max-width: none;
  }

  .objet { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  @media (min-width: 60rem) { .objet { grid-template-columns: minmax(0, 1fr) minmax(0, 24rem); } }
  /* La désignation est une donnée : Martian Mono, jamais la lampe — celle-ci
     n'éclaire que ce que la main touche (loi d'accent n° 2). */
  .designation {
    font-family: var(--f-data); font-size: var(--d-h4); font-weight: var(--g-mono);
    letter-spacing: var(--l-mono); color: var(--champ); font-variant-numeric: tabular-nums;
  }
  .fiche { border: 1px solid var(--filet); border-radius: var(--r); padding: var(--e-3) var(--e-4); }
  .profondeur {
    margin-top: var(--e-3); padding-top: var(--e-4);
    border-top: 1px solid var(--filet); max-width: var(--etroit);
  }

  .prise { margin: 0; }
  /* G7 : le cadre garde le format de son capteur, servi en --ratio depuis la
     version affichée. Le object-fit n'est plus qu'un repli. */
  .prise__media {
    border-radius: var(--r-s); overflow: hidden; background: var(--nuit);
    aspect-ratio: var(--ratio, 3 / 2);
  }
  .prise__media img, .prise__media picture { width: 100%; height: 100%; }
  .prise__media img { object-fit: cover; }
  .prise__legende { margin-top: var(--e-3); }
  .prise__ligne { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--e-1) var(--e-3); max-width: none; }
  .prise__date, .prise__source { color: var(--champ); }
  .prise__source { max-width: none; }

  /* Même règle qu'au catalogue : à formats mêlés, une carte prend sa hauteur
     propre plutôt que de creuser un vide sous son titre (G7). */
  .voisins { align-items: start; }
  .carte__mesure { padding-top: var(--e-3); }

  /* --gradue est calibré sur --nuit, où il donne 4,58:1. Posé sur une carte
     --ciel il tombe à 3,74:1 : c'est le plancher d'un objet graphique
     (WCAG 1.4.11), pas celui d'un texte (1.4.3, 4,5:1). Or les valeurs de
     seuil imprimées SONT du texte. Sur carte elles passent donc au jeton
     voisin --label, mesuré à 6,49:1 ; les traits, eux, restent en --gradue.
     Aucune valeur nouvelle, deux jetons existants. À remonter dans
     composants.css avec le composant. */
  .carte .grad__seuils { color: var(--label); }

/* ======================================================================
   outils/index
   ====================================================================== */
/* Aucune couleur, durée ni graisse littérale : tout vient de tokens.css.
     Tous les survols sont gardés, aucun ne translate ni ne redimensionne. */
  .tuile {
    display: flex; flex-direction: column; gap: var(--e-2); text-decoration: none;
    padding: var(--e-5); border: 1px solid var(--filet); border-radius: var(--r);
    background: var(--ciel);
    transition: border-color var(--t-ui) ease, box-shadow var(--t-ui) ease;
  }
  @media (hover: hover) {
    .tuile:hover { border-color: var(--filet-haut); box-shadow: var(--ombre-carte); }
  }
  .tuile__texte { color: var(--champ); font-size: var(--d-petit); }
  .tuile .btn--3 { margin-top: auto; padding-top: var(--e-4); }

  .carte__texte { color: var(--champ); font-size: var(--d-petit); }
  /* La carte entière est cliquable par ::after (composants.css) ; ce pied
     ne fait que le dire à l'œil. */
  .carte__pied {
    display: flex; align-items: center; gap: var(--e-1);
    margin-top: auto; padding-top: var(--e-3); color: var(--label);
  }
  /* La cible réelle est la carte entière (::after, composants.css) ; le lien
     lui-même est porté à 24 px pour que la mesure le dise aussi (WCAG 2.2,
     2.5.8) — sans changer une ligne au rendu. */
  .carte__titre a {
    color: var(--texte); text-decoration: none;
    display: inline-flex; align-items: center; min-height: 1.5rem;
  }
  @media (hover: hover) { .carte:has(a:hover) .carte__pied { color: var(--lampe); } }

  .deux { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  @media (min-width: 52rem) { .deux { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

  .liste-tirets { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--e-3); }
  .liste-tirets li { padding-left: 1.25rem; position: relative; color: var(--champ); }
  /* Le tiret d'énumération ne se touche pas : il ne porte pas la lampe. */
  .liste-tirets li::before {
    content: ""; position: absolute; left: 0; top: .7em;
    width: 12px; height: 1px; background: var(--filet-haut);
  }
  .liste-tirets--sourde li::before { background: var(--filet); }
  .liste-tirets__note { color: var(--label); }

  .note { color: var(--label); font-size: var(--d-petit); }
  .note--filet { max-width: 68ch; padding-top: var(--e-4); border-top: 1px solid var(--filet); }

/* ======================================================================
   outils/lexique
   ====================================================================== */
/* Aucune couleur, durée ni graisse littérale : tout vient de tokens.css. */
  .alpha {
    display: flex; flex-wrap: wrap; gap: var(--e-1);
    /* Elle se cale SOUS l'en-tête collant : elle lit sa hauteur au jeton,
       elle ne la recopie plus. */
    position: sticky; top: var(--h-entete); z-index: 10;
    background: var(--nuit); padding-block: var(--e-3);
    border-bottom: 1px solid var(--filet);
  }
  .alpha__l {
    display: grid; place-items: center; width: 2rem; height: 2rem;
    border-radius: var(--r-s); text-decoration: none;
    color: var(--champ); border: 1px solid var(--filet);
    transition: color var(--t-micro) ease, border-color var(--t-micro) ease;
  }
  /* La lampe n'éclaire que ce que la main touche (loi n° 2). */
  @media (hover: hover) {
    .alpha__l:hover { color: var(--lampe); border-color: var(--lampe-bord); }
  }

  /* La lettre de rubrique n'est pas un élément qu'on touche : elle reste en
     encre. Elle porte un filet, jamais un aplat clair (loi n° 3). */
  .lettre {
    /* L'ancre se dégage de l'en-tête ET de la barre alphabétique : la garde
       est la même qu'avant (72 px), mais assise sur la hauteur de barre au
       lieu de la recopier. */
    color: var(--texte); scroll-margin-top: calc(var(--h-entete) + var(--e-8) + var(--e-1));
    padding-bottom: var(--e-2); border-bottom: 1px solid var(--filet);
  }

  .lex { display: flex; flex-direction: column; }
  .lex__item {
    display: grid; gap: var(--e-2);
    padding: var(--e-4) 0; border-top: 1px solid var(--filet);
    /* L'entrée visée par un lien du site ne doit pas se coller sous
       l'en-tête collant ni sous la barre alphabétique. */
    scroll-margin-top: calc(var(--h-entete) + var(--e-8) + var(--e-4));
  }
  @media (min-width: 52rem) {
    .lex__item { grid-template-columns: minmax(0, 16rem) minmax(0, 1fr); gap: var(--e-5); }
  }
  .lex__item dd { margin: 0; }
  .lex__def { color: var(--champ); }

  .lex__voir {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e-1) var(--e-3);
    margin-top: var(--e-3);
  }
  .lex__voir .brow { flex: none; }

  /* La provenance d'une entrée réécrite d'après une autre source. Même
     traitement que .sch__sources : c'est la même chose, sous une entrée au
     lieu d'un dessin. */
  .lex__sources { color: var(--gradue); }
  .lex__sources .lien { font-size: inherit; }

  /* --- Le schéma des neuf classes de Bortle ----------------------------
     Il vit ici et non dans schemas.css parce qu'il ne se pose que sur cette
     page, à l'entrée « Bortle (échelle de) ». Tout ce qui pouvait être repris
     l'a été : .sch, .sch__boite, .sch__rangee, .sch__rangee-nom,
     .sch__etiquettes, .sch__axe-x, .sch__legende et .sch__sources viennent
     de schemas.css sans une déclaration de plus.
     Palette : celle de la graduation, jeton pour jeton. --texte pour la
     donnée mesurée, --gradue pour les repères et les bornes ouvertes,
     --filet pour la piste. Aucun --lampe : ce dessin n'a pas d'index, il se
     lit en entier, et la lampe n'éclaire que ce que la main touche. */
  .sch--bortle .sch__boite { gap: var(--e-1); }
  .sch__echelle { display: block; width: 100%; height: 100%; }
  /* La piste, c'est-à-dire ce que la source ne couvre pas : les sauts d'un
     dixième de magnitude entre deux classes, et le creux de la classe sans
     valeur SQM. */
  .sch__bortle-piste  { fill: var(--filet); }
  .sch__bortle-classe { fill: var(--texte); }
  /* Une borne ouverte n'est pas une mesure : elle porte le jeton des repères,
     pas celui de la donnée. Sa largeur est dessinée, la légende le dit. */
  .sch__bortle-ouvert { fill: var(--gradue); }
  /* Cinq repères d'axe bien espacés n'ont pas besoin du dédoublement de ligne
     que les raies serrées du duo-bande imposent. */
  .sch--bortle .sch__etiquettes--bas { min-height: var(--grad-ligne); }
  .sch--bortle .sch__etiquettes--bas span:nth-child(2n) { top: 0; }

  /* Les neuf classes en toutes lettres. Le dessin donne la structure, cette
     liste donne les noms et ce qu'on voit : c'est elle qu'on lit. */
  .sch__classes {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: var(--e-4);
  }
  .sch__cl {
    display: grid; grid-template-columns: var(--e-4) minmax(0, 1fr);
    gap: var(--e-1) var(--e-3);
  }
  .sch__cl-rang { color: var(--gradue); font-weight: var(--g-mono-fort); }
  .sch__cl-corps { display: flex; flex-direction: column; gap: var(--e-1); min-width: 0; }
  /* Le nom et la mesure tiennent sur une ligne quand la colonne est large, et
     passent l'un sous l'autre quand elle ne l'est plus. Neuf classes qui
     dépensent chacune trois lignes font un pavé ; deux lignes se parcourent. */
  .sch__cl-tete {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e-1) var(--e-3);
  }
  .sch__cl-nom { color: var(--texte); font-size: var(--d-petit); }
  .sch__cl-mesure { color: var(--label); max-width: none; }
  .sch__cl-trait { color: var(--champ); font-size: var(--d-petit); }

/* ======================================================================
   a-propos
   ====================================================================== */
/* Aucune couleur, aucune durée, aucune graisse littérale : tout descend
     de tokens.css. Aucune règle de survol non gardée. */
  .deux { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  @media (min-width: 52rem) { .deux { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .deux p { color: var(--champ); }

  .liste-tirets { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--e-3); }
  .liste-tirets li { padding-left: 1.25rem; position: relative; color: var(--champ); }
  /* Le filet de puce n'est pas la lampe : elle n'éclaire que ce que la main
     touche (loi n° 2). Un tiret d'énumération ne se touche pas. */
  .liste-tirets li::before {
    content: ""; position: absolute; left: 0; top: .7em;
    width: 12px; height: 1px; background: var(--filet-haut);
  }

  /* Le compteur : la même grammaire qu'à l'accueil — le nombre en Martian
     Mono, l'unité dans la famille de la prose (§6). */
  .compteur {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--e-4) var(--e-5);
    padding-top: var(--e-5); border-top: 1px solid var(--filet);
  }
  @media (min-width: 40rem) { .compteur { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  .compteur dt {
    font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
    line-height: var(--i-mono); text-transform: uppercase; letter-spacing: var(--l-brow);
    color: var(--label);
  }
  .compteur dd { font-size: var(--d-h3); line-height: var(--i-titre); margin-top: var(--e-1); color: var(--texte); }

  /* Les trois règles */
  .regles { list-style: none; padding: 0; margin: 0; }
  .regle__texte { color: var(--champ); font-size: var(--d-petit); max-width: none; }
  .regle__texte b { color: var(--texte); font-weight: var(--g-mono-fort); }
  .regles .brow { display: block; }

  .bilan {
    max-width: 68ch; color: var(--champ);
    padding-top: var(--e-4); border-top: 1px solid var(--filet);
  }
  .bilan b { color: var(--texte); font-weight: var(--g-mono-fort); }

  /* La réserve : un panneau, pas une note de bas de page. */
  .reserve {
    display: grid; gap: var(--e-6);
    background: var(--ciel); border: 1px solid var(--filet); border-radius: var(--r);
    padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  }
  @media (min-width: 58rem) {
    .reserve { grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); gap: var(--e-8); align-items: start; }
  }
  .reserve__texte p { color: var(--champ); max-width: 56ch; }
  /* §6 : une note est de la prose, pas de la donnée. */
  .reserve__note { color: var(--label); font-size: var(--d-petit); }
  .reserve__mesures { min-width: 0; }
  .reserve b { color: var(--texte); font-weight: var(--g-mono-fort); }

/* ======================================================================
   contact
   ====================================================================== */
/* Aucune couleur, durée ni graisse littérale : tout vient de tokens.css. */
  /* `contact-deux` et non `deux` : les styles étaient scopés par page sous
     Astro, ils ne le sont plus dans une feuille unique. Or `deux` sert déjà à
     outils et a-propos, avec deux colonnes égales à partir de 52 rem. Sous le
     même nom, la dernière rubrique écrite gagnait : le contact héritait de
     deux colonnes égales entre 52 et 60 rem, et les deux autres pages
     recevaient en retour la grille 34/20 rem du formulaire. Le nom porte donc
     la portée que la feuille ne porte plus. */
  .contact-deux { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: start; }
  /* Les deux colonnes sont dimensionnées sur leur contenu, pas sur le
     conteneur : un formulaire de 34 rem laissé dans une colonne de 1 fr
     ouvrait un couloir vide de 400 px entre lui et son aparté. */
  @media (min-width: 60rem) {
    .contact-deux { grid-template-columns: minmax(0, 34rem) minmax(0, 20rem); justify-content: start; }
  }

  .form { display: flex; flex-direction: column; gap: var(--e-5); }
  .form__champs {
    display: flex; flex-direction: column; gap: var(--e-5);
    border: 0; padding: 0; margin: 0; min-width: 0;
  }

  /* LE POT DE MIEL. Hors flux et hors écran, jamais display:none ni
     visibility:hidden, qu'un robot de spam détecte trivialement. Le masquage
     aux lecteurs d'écran est fait par aria-hidden dans le gabarit, pas ici :
     une règle de feuille de style ne masque rien à un lecteur d'écran. */
  .pot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

  /* `min-width: 0` n'est pas décoratif : un <select> se dimensionne sur sa
     plus longue option, et « Je ne sais pas encore, conseillez-moi » faisait
     403 px dans un écran de 390. Sans cette ligne, la page débordait
     horizontalement sur téléphone. Mesuré, pas supposé. */
  .champ { display: flex; flex-direction: column; gap: var(--e-2); border: 0; padding: 0; margin: 0; min-width: 0; }
  .champ label, .champ legend { font-size: var(--d-petit); font-weight: var(--g-corps-moyen); padding: 0; }
  /* Le mot « obligatoire » plutôt qu'un astérisque : un astérisque suppose une
     légende à chercher ailleurs, et les lecteurs d'écran l'annoncent
     « étoile ». Trois champs sur onze le portent, il reste lisible. */
  .champ__requis { color: var(--label); }
  /* `password` a rejoint la liste le jour où le forum est arrivé : c'est le
     premier mot de passe du site, et sans lui le champ serait le seul du
     formulaire à porter la boîte du navigateur au lieu du filet de la
     maison. Rien d'autre ne bouge dans cette règle. */
  .champ input[type="text"], .champ input[type="email"], .champ input[type="tel"],
  .champ input[type="password"], .champ select, .champ textarea {
    background: var(--saisie-fond); border: 1px solid var(--saisie-bord);
    border-radius: var(--r-s);
    padding: var(--e-2) var(--e-3); color: var(--texte); font-size: var(--d-corps);
    transition: border-color var(--t-ui) ease, background-color var(--t-ui) ease;
  }
  @media (hover: hover) {
    .champ input:hover, .champ select:hover, .champ textarea:hover {
      border-color: var(--lampe-haute);
    }
  }
  /* Le select reste dans le registre du formulaire : un filet bas, pas une
     boîte. Un contrôle encadré serait le seul du site à l'être.
     `appearance` n'est PAS remis à none : mesuré au navigateur, un select
     sans apparence perd le chevron que le système dessine, et devient
     impossible à distinguer d'un champ de saisie déjà rempli. Le fond
     transparent et le filet bas suffisent à l'aligner sur les autres champs
     tout en lui laissant sa seule affordance. */
  .champ select { width: 100%; min-width: 0; }
  /* Sans cela, la liste ouverte reprend le fond du système et perd la nuit. */
  .champ option { background: var(--ciel); color: var(--texte); }
  .champ input:focus, .champ select:focus, .champ textarea:focus {
    outline: 0; border-color: var(--saisie-bord-actif);
  }
  .champ input:focus-visible, .champ select:focus-visible, .champ textarea:focus-visible {
    outline: var(--focus-l) solid var(--focus-couleur); outline-offset: var(--focus-o);
  }

  /* L'ERREUR SE VOIT ET S'ENTEND. La couleur ne la porte pas seule : le champ
     reçoit aria-invalid, et le message est du texte lié par aria-describedby,
     annoncé par role="alert". La lampe n'éclaire que ce que la main touche :
     un champ à reprendre en est. */
  .champ:has([aria-invalid="true"]) input,
  .champ:has([aria-invalid="true"]) select { border-color: var(--lampe); }
  .champ__erreur { color: var(--lampe); font-size: var(--d-petit); }
  /* Une aide n'est pas une erreur : elle reste dans le neutre sourd. */
  .champ__aide { color: var(--label); }

  /* L'INTERTITRE DU FACULTATIF. Il ne décore pas : il dit au visiteur pressé
     qu'il peut envoyer à cet endroit précis. */
  .form__plus { display: flex; flex-direction: column; gap: var(--e-1); padding-top: var(--e-3);
                border-top: 1px solid var(--filet); }
  .form__plus-titre { font-size: var(--d-h4); font-weight: var(--g-corps-moyen); color: var(--texte); }
  .form__plus-note { color: var(--label); }

  .radios { display: flex; flex-wrap: wrap; gap: var(--e-2); }
  .radio { position: relative; display: inline-flex; }
  .radio input { position: absolute; inset: 0; opacity: 0; }
  .radio span {
    display: block; padding: var(--e-1) var(--e-3);
    border: 1px solid var(--saisie-bord); border-radius: var(--r-pill);
    font-size: var(--d-petit); color: var(--champ);
    transition: border-color var(--t-ui) ease, color var(--t-ui) ease;
  }
  /* La lampe n'éclaire que ce que la main touche : une option cochée l'est. */
  .radio:has(input:checked) span { border-color: var(--lampe-bord); color: var(--lampe); }
  .radio:has(input:focus-visible) span {
    outline: var(--focus-l) solid var(--focus-couleur); outline-offset: var(--focus-o);
  }

  /* §6 : Martian Mono porte la donnée, jamais un paragraphe. Cette note en
     est un : elle reste dans la famille de la prose, au corps petit. */
  .form__note {
    color: var(--label); font-size: var(--d-petit); max-width: none;
    padding-top: var(--e-4); border-top: 1px solid var(--filet);
  }

  .aparte p { color: var(--champ); }
  @media (min-width: 60rem) {
    .aparte {
      padding-left: var(--e-5); border-left: 1px solid var(--filet);
    }
  }

/* ======================================================================
   mentions-legales
   ====================================================================== */
.prose h2 { margin-bottom: var(--e-2); }
  .prose p { color: var(--champ); }
  .prose p + p { margin-top: var(--e-2); }
  /* §6 : une note reste de la prose ; Martian Mono porte la donnée. */
  .note { color: var(--label); font-size: var(--d-petit); }

/* ======================================================================
   confidentialite
   ====================================================================== */
.prose h2 { margin-bottom: var(--e-2); }
  .prose p { color: var(--champ); }
  .prose p + p { margin-top: var(--e-2); }
  .note { color: var(--label); font-size: var(--d-petit); }

/* ======================================================================
   404
   ====================================================================== */
/* Aucune valeur littérale : tout vient de tokens.css. */
  /* Le puits est centré verticalement, pas rétréci : en grille, des marges
     automatiques rendent l'enfant fit-content — d'où le width: 100 %, sans
     quoi la page se réduisait à 620 px au milieu de l'écran. */
  .perdu { display: grid; align-content: center; min-height: 58vh; }
  .perdu > .contenu { width: 100%; }
  .perdu__actions { display: flex; flex-wrap: wrap; gap: var(--e-2); }
  .perdu__pistes { margin-top: var(--e-5); max-width: var(--etroit); }

  .pistes { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--filet); }
  .piste { border-bottom: 1px solid var(--filet); }
  .piste__lien {
    display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline;
    gap: var(--e-2) var(--e-4); padding-block: var(--e-3);
    text-decoration: none; color: var(--texte);
    transition: color var(--t-micro) ease;
  }
  .piste__pose { color: var(--label); }
  @media (hover: hover) {
    .piste__lien:hover { color: var(--lampe); }
    .piste__lien:hover .piste__pose { color: var(--lampe); }
  }

/* ======================================================================
   forum
   ======================================================================
   Doctrine : _design/COHERENCE.md et _design/FORUM.md, §8.
   Aucune valeur littérale : tout vient de tokens.css, y compris l'unique
   jeton que ce lot ajoute, --forum-fiche, dont la mesure est écrite là-bas.

   AUCUN data-rev DANS LE FORUM, et c'est une règle, pas un oubli. La
   révélation au défilement est faite pour une page qu'on parcourt ; une page
   de forum se lit du haut vers le bas, et une carte qui apparaît au moment où
   l'œil l'atteint est une carte qui n'est pas là quand on la regarde.
   Vingt-cinq messages, ce seraient vingt-cinq animations.
   Le barème 100/200/300/400/560 ms ne sert donc ici qu'aux survols de lien et
   à l'état du bouton, sous @media (hover: hover), et prefers-reduced-motion
   coupe tout (base.css).
   ====================================================================== */

  /* --- Le retour d'un formulaire, en une ligne ------------------------
     La lampe n'éclaire que ce que la main touche : un champ à reprendre en
     est, et un envoi refusé aussi. La réussite, elle, reste dans le neutre
     clair : elle n'appelle aucun geste. */
  .forum-retour {
    max-width: none; padding: var(--e-3) var(--e-4);
    border-radius: var(--r-s); border: 1px solid var(--filet);
    background: var(--ciel); font-size: var(--d-petit);
  }
  .forum-retour--echec { border-color: var(--lampe-bord); color: var(--lampe); }
  .forum-retour--ok { color: var(--texte); }
  .forum-retour__reprise { color: var(--label); }

  /* --- La panne, et l'accès refusé --------------------------------------
     Deux pages qui n'ont qu'un bloc. Le vocabulaire est celui de .reserve :
     un panneau, pas une note de bas de page. */
  /* LE RETRAIT D'UN PANNEAU, ÉCRIT UNE FOIS POUR LES QUATRE DU FORUM.
     La valeur est celle de .appel et de .reserve, qui sont les deux panneaux
     déjà en place : c'est la même décision, pas une nouvelle. Elle est ici en
     une seule règle plutôt qu'en quatre copies, pour la raison qui a fermé
     les quinze `style="background: var(--ciel)"` du §3 de la doctrine. Le
     jour où elle devient un jeton, six règles bougent ensemble. */
  .forum-panne, .forum-garde, .forum-droits, .msg {
    padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
  }
  .forum-panne {
    display: flex; flex-direction: column; gap: var(--e-3);
    background: var(--ciel); border: 1px solid var(--filet);
    border-radius: var(--r);
  }
  .forum-panne p { color: var(--champ); }
  .forum-panne__reste { color: var(--label); font-size: var(--d-petit); }

  /* --- L'accueil du forum : les trois espaces ---------------------------
     La grille est celle du site (.autofit-l, 22rem), et les trois cartes
     s'empilent d'elles-mêmes sous 48rem. Aucune hauteur n'est forcée : une
     carte dont le chapô est plus long est plus haute, et la rangée s'aligne
     par le haut, jamais par un rognage. */
  /* La grille est celle du site : .grille .autofit-l, posées dans le
     gabarit. Cette règle ne fait que défaire la puce de la liste. */
  .forum-espaces { list-style: none; padding: 0; margin: 0; }
  .forum-espace .carte__corps { gap: var(--e-3); }
  .forum-espace__objet { color: var(--texte); font-weight: var(--g-corps-moyen); max-width: none; }
  .forum-espace__chapo { color: var(--champ); font-size: var(--d-petit); max-width: none; }
  /* La garde d'un espace qui en porte une : elle est signalée dès la carte,
     pas seulement à l'intérieur. Un lecteur qui entre dans Achats / Ventes
     l'a déjà lue une fois. */
  .forum-espace__garde {
    color: var(--label); max-width: none;
    padding-top: var(--e-2); border-top: 1px solid var(--filet);
  }
  .forum-espace__compte {
    display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3);
    color: var(--label); max-width: none; margin-top: auto; padding-top: var(--e-3);
  }

  .forum-maison { display: grid; gap: var(--e-6); align-items: start; }
  @media (min-width: 58rem) {
    .forum-maison { grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); gap: var(--e-8); }
  }
  .forum-maison p { color: var(--champ); }

  /* --- L'EXCLUSION DE RESPONSABILITÉ ------------------------------------
     Elle est ENCADRÉE ET TOUJOURS DÉPLIÉE, jamais dans un accordéon ni
     derrière un lien : c'est la première ligne de la décision d'architecture
     sur ce point (§6.2). Le filet prend le palier sombre de la lampe, celui
     du panneau d'appel : la teinte dit qu'il faut lire, sans que le texte
     lui-même passe en accent, ce que la loi n° 2 interdit. */
  .forum-garde {
    display: flex; flex-direction: column; gap: var(--e-3);
    background: var(--ciel); border: 1px solid var(--lampe-nuit);
    border-radius: var(--r);
  }
  .forum-garde p { color: var(--champ); }
  .forum-garde strong { color: var(--texte); }
  .forum-garde__lien { padding-top: var(--e-1); }
  /* LA FORME COURTE, CELLE DES PAGES DE SUJET. On arrive sur une annonce par
     un lien partagé, jamais par l'index : une exclusion qui ne vivrait que
     sur l'index ne protégerait personne. Une phrase, un lien, même filet. */
  .forum-garde--courte {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e-1) var(--e-3);
    padding: var(--e-3) var(--e-4); font-size: var(--d-petit); max-width: none;
  }

  /* --- La barre d'un espace : ce qu'il contient, et ce qu'on y fait ----- */
  .forum-barre {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--e-3) var(--e-4);
    padding-bottom: var(--e-4); border-bottom: 1px solid var(--filet);
  }
  .forum-barre__compte { color: var(--label); max-width: none; }
  .forum-barre__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-3) var(--e-4); }

  /* --- L'ÉTAT VIDE EST UN ÉTAT DE PREMIER RANG --------------------------
     Même grammaire que la graduation à zéro et que la planche sans image :
     on écrit ce qui manque, à la place que le contenu aurait prise. Jamais
     une liste escamotée, qui se lit comme une panne. */
  .forum-vide {
    display: flex; flex-direction: column; gap: var(--e-2);
    padding: var(--e-6) var(--e-5);
    border: 1px solid var(--filet-haut); border-radius: var(--r-media);
    background: var(--nuit);
  }
  .forum-vide .mono { color: var(--label); max-width: none; }
  .forum-vide p { color: var(--champ); }
  .forum-vide__ligne { color: var(--label); max-width: none; }

  /* --- La liste des sujets ---------------------------------------------
     Le vocabulaire de .pistes (la 404) : une liste de liens séparés par un
     filet, la cible haute de 24 px au moins (WCAG 2.2, 2.5.8). */
  .sujets { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--filet); }
  .sujet { border-bottom: 1px solid var(--filet); padding-block: var(--e-3); }
  /* La cible tactile de 24 px (WCAG 2.2, 2.5.8) est posée une seule fois,
     dans base.css, sur la liste des liens de texte isolés du site. Ces
     règles-ci ne posent que ce qui leur est propre. */
  .sujet__lien { text-decoration: none; color: var(--texte); transition: color var(--t-micro) ease; }
  .sujet__titre { font-size: var(--d-h4); font-weight: var(--g-corps-moyen); line-height: var(--i-titre); }
  @media (hover: hover) { .sujet__lien:hover { color: var(--lampe); } }
  .sujet__meta {
    display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3);
    color: var(--label); max-width: none; margin-top: var(--e-1);
  }
  /* Un sujet épinglé le DIT. Une ligne remontée sans marque se lit comme une
     ligne remontée toute seule, et le tri du forum devient incompréhensible. */
  .sujet__marque {
    color: var(--texte); text-transform: uppercase; letter-spacing: var(--l-brow);
  }
  .sujet__marque--clos { color: var(--label); }
  .sujet--epingle { background: var(--ciel); padding-inline: var(--e-3); }

  /* --- La pagination ---------------------------------------------------
     Des liens, rien d'autre : sans JavaScript une page de forum reste
     entièrement franchissable, et c'est exactement le genre de page qu'on
     ouvre dans un onglet. */
  .forum-pages {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--e-3); padding-top: var(--e-4); border-top: 1px solid var(--filet);
  }
  .forum-pages__rang { color: var(--label); max-width: none; margin-inline: auto; }
  .forum-pages__pas {
    font-size: var(--d-petit); color: var(--lampe); text-decoration: none;
    transition: color var(--t-micro) ease;
  }
  @media (hover: hover) { .forum-pages__pas:hover { color: var(--lampe-haute); } }

  /* =====================================================================
     LE GABARIT DE MESSAGE DU CARNET
     « titre ; colonne gauche = message (utilisateur, date, corps) ;
       colonne droite = fiche membre » (§2.7 de l'instruction-base).

     LA GRILLE PLACE, LE DOM ORDONNE, ET LES DEUX NE DISENT PAS LA MÊME
     CHOSE. Dans le document, la fiche vient APRÈS le corps : un lecteur
     d'écran lit l'auteur, le message, puis la fiche. À l'écran, les zones
     nommées la remettent à droite, comme le carnet la dessine. C'est le seul
     endroit du site où l'ordre visuel et l'ordre de lecture divergent, et
     c'est ce qui évite de traverser vingt-cinq fiches pour lire vingt-cinq
     messages.

     LA CARTE EST --ciel SUR --nuit, FILET --filet : le vocabulaire des
     cartes du site, sans rien inventer. Ce n'est pas .carte : une carte du
     site est une cible de lien entière, dont la limite est fonctionnelle et
     relève du plancher 3:1 ; un message est un bloc de texte, dont la limite
     est décorative.
     ===================================================================== */
  .msgs { display: flex; flex-direction: column; gap: var(--e-5); }

  .msg {
    display: grid; gap: var(--e-3);
    background: var(--ciel); border: 1px solid var(--filet);
    border-radius: var(--r);
    /* L'ANCRE PERMANENTE NE DOIT PAS TOMBER SOUS LA BARRE. .nav est en
       sticky : sans ce retrait, un lien vers #m-412 pose le message
       exactement dessous, en-tête compris. Le jeton est celui de la barre,
       pas un nombre recopié. */
    scroll-margin-top: calc(var(--h-nav) + var(--e-3));
  }
  .msg__tete {
    display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
    gap: var(--e-1) var(--e-3);
    padding-bottom: var(--e-3); border-bottom: 1px solid var(--filet);
  }
  .msg__qui { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e-2); max-width: none; }
  .msg__pseudo {
    font-weight: var(--g-corps-fort); color: var(--texte); text-decoration: none;
    transition: color var(--t-micro) ease;
  }
  @media (hover: hover) { a.msg__pseudo:hover { color: var(--lampe); } }
  .msg__pseudo--retire { color: var(--label); font-weight: var(--g-corps); }
  /* Le rang réemploie le vocabulaire du surtitre : Martian Mono, capitales,
     --l-brow, --label. C'est .brow, qui existe déjà (base.css). */
  .msg__rang { color: var(--label); }
  .msg__quand { display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-2); color: var(--label); max-width: none; }
  /* L'ANCRE PERMANENTE DU MESSAGE EST UNE CIBLE, PAS UNE MENTION.
     Mesurée sur le banc, aux quatre largeurs : 154 x 18 px, sous le plancher
     de 24 px de WCAG 2.5.8. C'est le seul lien du forum qui y tombait. Même
     remède que la barre de fil et le pied du site, qui ont connu le même
     défaut : une boîte en ligne devient une boîte à hauteur minimale, sans
     déplacer un seul pixel de texte puisque la ligne fait déjà cette
     hauteur-là dans son flux. */
  .msg__quand a {
    color: var(--label); text-decoration: none;
    display: inline-flex; align-items: center; min-height: 1.5rem;
    transition: color var(--t-micro) ease;
  }
  @media (hover: hover) { .msg__quand a:hover { color: var(--lampe); } }
  /* LA MENTION DE CORRECTION RESTE EN --label, ET C'EST UNE MESURE.
     Elle avait été posée en --gradue pour se faire plus sourde que la date :
     relevé au rendu, --gradue sur --ciel vaut 3,74:1 à 13 px, sous le seuil
     4,5 de WCAG AA. --gradue est le jeton des graduations et des seuils, un
     objet graphique porteur au plancher 3:1, pas une encre de texte.
     --label vaut 6,49:1 sur --ciel. La nuance se dit par les mots, pas par
     une couleur qui ne tient pas. */
  .msg__corrige { color: var(--label); }

  .msg__corps p { color: var(--champ); }
  .msg__corps p + p { margin-top: var(--e-3); }
  /* Un message retiré laisse sa place et le dit. L'escamoter casserait la
     conversation des autres et se lirait comme un défaut. */
  .msg__retrait { color: var(--label); font-style: italic; }

  .msg__actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2) var(--e-4);
    margin-top: var(--e-4); font-size: var(--d-petit);
  }
  .msg__action, .msg__signal > summary {
    color: var(--label); text-decoration: none; cursor: pointer;
    transition: color var(--t-micro) ease;
  }
  @media (hover: hover) {
    .msg__action:hover, .msg__signal > summary:hover { color: var(--lampe); }
  }
  /* <details> et non l'accordéon du site : celui-là est piloté par un script
     et sert des blocs de prose. Ici il faut un formulaire qui reste envoyable
     quand rien ne s'exécute. C'est le seul motif, et il n'en faut pas deux. */
  .msg__signal { font-size: var(--d-petit); }
  .msg__signal > summary { list-style: none; }
  .msg__signal > summary::-webkit-details-marker { display: none; }
  .msg__signal[open] > summary { color: var(--lampe); }
  .msg__signal-form {
    display: flex; flex-direction: column; gap: var(--e-3);
    margin-top: var(--e-3); padding: var(--e-4);
    background: var(--nuit); border: 1px solid var(--filet); border-radius: var(--r-s);
    max-width: var(--lecture);
  }
  .msg__signal-form .btn { align-self: flex-start; }

  .msg__correction { max-width: var(--lecture); }
  .msg__correction-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2) var(--e-4); }

  /* --- LA FICHE MEMBRE, COLONNE DE DROITE ------------------------------- */
  .msg__fiche { display: flex; flex-direction: column; gap: var(--e-4); }
  .msg__fiche--absente { gap: var(--e-1); }
  .msg__fiche-note { color: var(--label); max-width: none; }
  .msg__fiche-note-2 { color: var(--label); font-size: var(--d-petit); }

  /* L'identité est un bloc : le monogramme et le pseudo sur une ligne, le
     rang sur la sienne, serrés l'un contre l'autre. Aucune marge négative
     n'est employée pour les rapprocher : c'est un conteneur avec sa propre
     gouttière, pas un rattrapage. */
  .fiche__identite { display: flex; flex-direction: column; gap: calc(var(--e-1) / 2); }
  .fiche__tete { display: flex; align-items: center; gap: var(--e-3); min-width: 0; }
  .fiche__pseudo {
    font-weight: var(--g-corps-fort); color: var(--texte); text-decoration: none;
    overflow-wrap: anywhere; transition: color var(--t-micro) ease;
  }
  @media (hover: hover) { .fiche__pseudo:hover { color: var(--lampe); } }
  .fiche__rang { color: var(--label); }

  /* LE MONOGRAMME TIENT LA PLACE DE LA PHOTO DU CARNET, et il ne prend
     aucune couleur : la loi n° 2 réserve la lampe à ce que la main touche, et
     une pastille colorée par membre ouvrirait une seconde palette. */
  .monog { display: block; width: var(--e-7); flex: none; }
  .monog svg { display: block; width: 100%; height: auto; }
  /* La taille des lettres est portée par l'attribut du SVG, dans les unités
     de son viewBox, à côté du cx, du cy et du r du cercle : c'est de la
     géométrie de dessin, elle appartient au tracé. Cette règle-ci ne pose que
     ce que le système décide, la famille, la graisse et la couleur. */
  .monog__lettres {
    font-family: var(--f-data); font-weight: var(--g-mono-fort);
    letter-spacing: var(--l-mono); fill: var(--label);
  }
  .monog--grand { width: var(--e-8); }

  .fiche__donnees { display: flex; flex-direction: column; gap: var(--e-3); }
  .fiche__donnees > div { display: flex; flex-direction: column; gap: calc(var(--e-1) / 4); }
  .fiche__donnees dt {
    font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
    line-height: var(--i-mono); text-transform: uppercase; letter-spacing: var(--l-brow);
    color: var(--label);
  }
  .fiche__donnees dd { color: var(--texte); font-size: var(--d-petit); }
  .fiche__donnees dd.mono { font-size: var(--d-mono); }
  .fiche__pays { color: var(--label); }

  .fiche__materiel { display: flex; flex-direction: column; gap: var(--e-2); }
  /* CHAQUE APPAREIL DÉCLARÉ EST UN LIEN VERS SA FICHE. C'est ce qui fait que
     le forum irrigue le catalogue au lieu de vivre à côté, et c'est de là
     que sort le nombre de membres par appareil sur les neuf fiches. */
  .fiche__appareils { list-style: none; padding: 0; display: flex; flex-direction: column; gap: calc(var(--e-1) / 2); }
  .fiche__appareils a {
    font-size: var(--d-petit); color: var(--champ); text-decoration: none;
    transition: color var(--t-micro) ease;
  }
  @media (hover: hover) { .fiche__appareils a:hover { color: var(--lampe); } }
  /* Le champ libre s'affiche en texte et n'est JAMAIS lié : « dwarf3 »,
     « Dwarf 3 » et « DWARF III » ne se rencontrent jamais. */
  .fiche__autre { color: var(--label); font-size: var(--d-petit); max-width: none; }
  .fiche__voir { font-size: var(--d-petit); }

  /* LES DEUX COLONNES DU CARNET N'EXISTENT QU'AU-DESSUS DU POINT DE REPLI.
     Le point est MESURÉ, pas choisi : au-dessous, la colonne de gauche cesse
     de tenir une ligne de lecture décente une fois --forum-fiche prélevée.
     Le relevé est dans le rapport du lot.
     Au-dessous, la fiche passe SOUS le corps et se réduit à ce qui identifie
     l'auteur ; le lien « voir la fiche » est là, à sa place, et mène à la
     page qui porte la fiche entière. */
  .fiche__voir { display: block; }
  .msg__fiche .fiche__plus { display: none; }
  @media (min-width: 62rem) {
    .msg {
      grid-template-columns: minmax(0, 1fr) var(--forum-fiche);
      grid-template-areas: "tete fiche" "corps fiche";
      column-gap: var(--e-6);
    }
    .msg__tete { grid-area: tete; }
    .msg__corps { grid-area: corps; }
    .msg__fiche {
      grid-area: fiche;
      padding-left: var(--e-5); border-left: 1px solid var(--filet);
    }
    .msg__fiche .fiche__plus { display: flex; }
    .msg__fiche .fiche__voir { display: none; }
  }

  /* La fiche sur SA page : entière à toutes les largeurs, et sans le lien
     qui renverrait à la page où l'on est déjà. */
  .msg__fiche--pleine .fiche__plus { display: flex; }

  .membre { display: grid; gap: var(--e-6); align-items: start; }
  @media (min-width: 58rem) {
    .membre { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); gap: var(--e-8); }
  }
  .membre .msg__fiche {
    background: var(--ciel); border: 1px solid var(--filet);
    border-radius: var(--r); padding: var(--e-5);
  }

  /* --- Répondre, et ouvrir un sujet ------------------------------------- */
  .forum-colonne { max-width: var(--etroit); }
  .forum-reponse { max-width: var(--etroit); padding-top: var(--e-5); border-top: 1px solid var(--filet); }
  .forum-reponse__pied { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-3) var(--e-5); }
  .forum-reponse__note { color: var(--label); max-width: none; }
  .forum-reponse__note a { color: var(--label); }
  @media (hover: hover) { .forum-reponse__note a:hover { color: var(--texte); } }
  .sujet__resume {
    display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3);
    color: var(--label); max-width: none;
  }

  /* --- Les cases à cocher -----------------------------------------------
     LE MOT « OBLIGATOIRE » ET LA CASE NON PRÉ-COCHÉE. Une déclaration de
     propriété ou de majorité numérique pré-cochée ne déclare rien : c'est le
     site qui l'aurait cochée. Le gabarit ne pose donc jamais `checked`, et la
     case porte `required`. */
  .champ--coche { gap: var(--e-1); }
  .coche { display: flex; align-items: baseline; gap: var(--e-2); font-size: var(--d-petit); }
  .coche input { accent-color: var(--lampe); flex: none; }
  .coche span { color: var(--champ); }
  .coches { list-style: none; padding: 0; margin: 0; }

  /* --- Les quatre états de l'inscription --------------------------------
     Ce n'est pas une frise décorative : c'est le seul endroit où quelqu'un
     apprend qu'il devra attendre une approbation humaine. */
  /* QUATRE ÉTATS, LUS DE HAUT EN BAS. Une frise en quatre colonnes se
     replierait en trois plus un dès qu'une fenêtre rétrécit, et une étape
     orpheline sur sa ligne ne se lit plus comme une étape. La liste garde
     donc son axe naturel, celui d'une liste ordonnée. */
  .forum-chaine { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--filet); }
  .forum-chaine li {
    display: flex; align-items: baseline; gap: var(--e-3);
    padding-block: var(--e-2); border-bottom: 1px solid var(--filet);
  }
  .forum-chaine__rang { color: var(--lampe); }
  .forum-chaine__quoi { color: var(--champ); font-size: var(--d-petit); }

  /* --- Mon compte -------------------------------------------------------- */
  .forum-compte-tete {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-3) var(--e-4);
    padding-bottom: var(--e-5); border-bottom: 1px solid var(--filet);
  }
  .forum-compte-tete form { margin-left: auto; }
  .forum-compte-tete__meta { display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3); color: var(--label); max-width: none; }

  .forum-droits {
    display: flex; flex-direction: column; gap: var(--e-4);
    background: var(--ciel); border: 1px solid var(--filet);
    border-radius: var(--r);
  }
  .forum-droits p { color: var(--champ); }
  .forum-droits__delai, .forum-droits__note { color: var(--label); }
  .forum-droits__fermer > summary {
    color: var(--lampe); cursor: pointer; font-size: var(--d-petit); list-style: none;
  }
  .forum-droits__fermer > summary::-webkit-details-marker { display: none; }
  .forum-droits__fermer .form {
    margin-top: var(--e-4); padding: var(--e-4);
    background: var(--nuit); border: 1px solid var(--filet); border-radius: var(--r-s);
    max-width: var(--lecture);
  }
  .forum-droits__fermer .btn { align-self: flex-start; }

  /* --- La modération ------------------------------------------------------
     Une file, pas un tableau de bord : ce qui attend, ce qu'on en fait, et le
     journal qui prouve ce qui a été fait. */
  .mod__liste { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--filet); }
  .mod__ligne {
    display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
    gap: var(--e-3) var(--e-4);
    padding-block: var(--e-4); border-bottom: 1px solid var(--filet);
  }
  .mod__quoi { font-weight: var(--g-corps-moyen); color: var(--texte); max-width: none; }
  .mod__meta { display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3); color: var(--label); max-width: none; }
  .mod__texte { color: var(--champ); font-size: var(--d-petit); }
  .mod__gestes { display: flex; flex-wrap: wrap; gap: var(--e-2); }
  .mod__journal { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--e-1); }
  .mod__journal li { display: flex; flex-wrap: wrap; gap: var(--e-1) var(--e-3); color: var(--champ); }
  .mod__journal-quand { color: var(--label); }
  .mod__journal-qui { color: var(--texte); }
  .mod__journal-cible { color: var(--label); }

  .membre__travaux { min-width: 0; }

  /* =====================================================================
     LE TITRE D'UN SUJET DESCEND D'UN CRAN, ET C'EST MESURÉ, PAS CHOISI.

     La doctrine dit que l'ouverture au filet garde l'échelle de titrage, et
     c'est vrai des 62 pages où le titre est écrit par la maison : le plus
     long des neuf noms d'appareils fait 20 signes et tient sur une ligne.
     UN TITRE DE SUJET N'EST PAS ÉCRIT PAR LA MAISON. Il est tapé par un
     membre et vaut jusqu'à 120 signes, la borne du modèle de données.

     Relevé au rendu, sur un titre de 120 signes, à --d-display :
       1440x900 .... 6 lignes, 587 px de titre, OUVERTURE 785 px
       1024x768 .... 6 lignes, 511 px de titre, OUVERTURE 696 px
       768x1024 .... 6 lignes, 414 px de titre, OUVERTURE 592 px
       390x844 ..... 7 lignes, 315 px de titre, OUVERTURE 485 px
     Le héros de l'accueil vaut 811 px à 1440x900 et 679 px à 1024x768. À
     1024x768, l'ouverture d'un sujet de forum passait donc AU-DESSUS du
     héros de l'accueil : la hiérarchie s'inversait, exactement le cas que le
     §4 de la doctrine a refusé pour le bandeau de /telescopes/.

     La règle appliquée est celle de la doctrine, mot pour mot : l'échelle de
     titrage SUIT LE BUDGET DE HAUTEUR, pas le rang de la page. Le titre passe
     à --d-h2, et sa mesure de ligne tombe avec lui : à 56 px, la coupe à 20ch
     rend six lignes là où trois suffisent, et la colonne de contenu borne
     déjà le titre, elle le borne juste. Même geste que
     `.hero--bandeau .hero__titre`.

     APRÈS, sur le même titre de 120 signes, et sur celui de 62 signes du banc :
       120 signes  1440x900 .. 3 lignes, 171 px, OUVERTURE 369 px
                   1024x768 .. 3 lignes, 142 px, OUVERTURE 327 px
                   768x1024 .. 3 lignes, 124 px, OUVERTURE 301 px
                   390x844 ... 5 lignes, 161 px, OUVERTURE 331 px
        62 signes  1440x900 .. 2 lignes, 114 px, OUVERTURE 312 px
     La hiérarchie est rétablie et elle se lit dans les nombres : accueil 811,
     bandeau du forum 527, ouverture d'un sujet 369 au pire des cas.
     ===================================================================== */
  .ouv--titre-libre .tete--hero h1 { font-size: var(--d-h2); max-width: none; }

  /* La ligne du forum sous le relevé de l'auteur, sur les neuf fiches
     produit. Même registre que .releve__etat : une donnée, en Martian Mono,
     dans le neutre sourd. Elle ne prend pas l'accent : elle informe, elle ne
     pousse pas, exactement comme .produit__vente au-dessus d'elle. */
  .releve__forum { color: var(--label); max-width: none; padding-top: var(--e-2); }
  .releve__forum .nombre { color: var(--texte); }
  .releve__forum a { color: var(--label); }
  @media (hover: hover) { .releve__forum a:hover { color: var(--texte); } }

  /* --- LE RENOUVELLEMENT D'UNE ANNONCE ---------------------------------
     Quatre-vingt-dix jours, puis clôture automatique, renouvelable d'un clic
     par son auteur. Le bloc n'existe que dans l'espace des annonces et que
     pour l'auteur du sujet : c'est un geste de propriétaire, pas une
     commande de page. Il réemploie le retrait et le filet des panneaux du
     forum, sans rien ajouter. */
  .forum-annonce {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--e-3) var(--e-4);
    padding: var(--e-4); border: 1px solid var(--filet); border-radius: var(--r);
    background: var(--ciel);
  }
  .forum-annonce__etat { color: var(--label); max-width: none; }
  .forum-annonce__gestes { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2) var(--e-4); }

/* ======================================================================
   evenementiel
   ====================================================================== */
/* Aucune valeur littérale : tout vient de tokens.css. Aucun survol, aucune
   durée : la page n'ajoute rien au barème.
   Le formulaire reprend .contact-deux, .form, .champ, .radios et .aparte de la
   rubrique contact, tels quels : même handler, même mécanique, même dessin.
   Les cartes reprennent .carte et .carte__media de composants.css, comme le
   catalogue. Ce qui suit est le seul registre propre à la page. */
  /* Les trois volets d'un public : ce que je propose, ce que vous y voyez,
     comment on organise. La grille est celle du site (.grille .autofit-l) ;
     seule la couleur de la prose est posée ici, comme .deux le fait pour
     À propos, et le surtitre garde le neutre sourd des labels. */
  .volets p { color: var(--champ); }
  .volets .brow { color: var(--label); }
  /* La carte d'un objet montré : une phrase de prose au corps petit, puis la
     ligne de relevé (appareil, date) en Martian Mono, dans le neutre sourd,
     calée en bas de la carte comme .carte__meta le fait sur les fiches. */
  .montre__texte { color: var(--champ); font-size: var(--d-petit); max-width: none; }
  .montre__releve { color: var(--label); margin-top: auto; padding-top: var(--e-2); max-width: none; }
