/* Mise en page du rapport à l'impression.
   Chargée par `index.html` avec `media="print"` : elle ne touche jamais l'écran.

   Un document imprimé n'est pas une page web rétrécie. Le lecteur ne fait pas
   défiler, il tourne des pages ; il ne survole pas un lien, il lit un numéro de
   ligne ; et il imprimera peut-être en noir et blanc. Les quatre parties de cette
   feuille répondent chacune à un de ces faits.

   Ce qui ne peut PAS se faire ici, et qu'il ne faut pas chercher à y ajouter :
   la numérotation des pages et l'en-tête répété. Chromium n'implémente pas les
   boîtes de marge de `@page` (`@top-center`, `counter(page)`), et un élément en
   `position: fixed` n'est tiré que sur la première page. Ils se posent avec les
   gabarits `headerTemplate` / `footerTemplate` du mécanisme de production —
   `src/identite/entete-impression.html`, qui porte le logo. Mesuré, pas supposé :
   `tools/smoke_impression.py` refuse cette feuille si elle prétend le contraire. */

@media print {

  /* ---------------------------------------------------------------- la page */

  /* A4 et non « letter » : le lecteur est un comptable français. Les marges
     latérales sont larges à dessein — 20 mm laissent 170 mm de texte, soit une
     ligne d'environ 85 signes. À 14 mm on gagne une ligne de 95 signes que
     personne ne lit confortablement, et le document ne fait pas une page de
     moins pour autant : la densité se gagne sur les blancs verticaux. */
  @page { size: A4; margin: 20mm 20mm 18mm; }

  /* Le thème sombre suit le réglage du système, pas celui du papier. Sans cette
     remise à plat, qui travaille en thème sombre imprime un rapport blanc sur
     fond sombre — c'est-à-dire, une fois le fond non imprimé, du texte pâle sur
     du blanc. Les valeurs sont recopiées de `base.css` et non recalculées :
     les deux doivent dire la même chose. */
  :root, :root[data-theme="dark"] {
    --ground: #ffffff;
    --surface: #ffffff;
    --surface-2: #f7f8f5;
    --ink: #15181c;
    --ink-soft: #454c57;
    --muted: #5d6573;
    --line: #c8ccc4;
    --line-strong: #a8ada2;
    --accent: #1f4f8f;
    --accent-soft: #e6edf7;
    --bloquant: #a3231b;
    --bloquant-soft: #fae9e7;
    --bloquant-vif: #f6d3ce;
    --avert: #8a5a06;
    --avert-soft: #fbf1df;
    --avert-vif: #f5e2bb;
    --ok: #2a6b4a;
    --ok-soft: #e4f1ea;

    /* La marque, réservée à l'identité. Le marine de Lexora donne 13,19 de
       contraste sur blanc — davantage que l'accent du produit — mais 1,28 sur
       le fond sombre de l'écran : il est sûr exactement là où le document vit,
       et dangereux là où l'application vit. D'où la frontière. Les couleurs de
       sens ci-dessus ne bougent pas : `--ok` vaut 5,47 sur son fond, le vert de
       la marque y vaudrait 2,20. `tests/unit/palette-contraste.test.mjs` refuse
       qu'on les échange. */
    --marque: #003060;
    --marque-accent: #23b695;
  }

  body {
    background: #fff;
    font-size: 10pt;
    line-height: 1.45;
  }

  /* La largeur de l'écran est un maximum, pas une mesure : sur papier elle
     laisserait une colonne vide à droite. */
  .wrap { max-width: none; padding: 0; }

  /* ------------------------------------------------- ce qui ne s'imprime pas */

  /* Le document dédié, quand le mécanisme d'export le monte.
     ---------------------------------------------------------------------------
     `src/export-pdf.js` construit un conteneur `#document-imprime` qui **double**
     le contenu de l'écran, puis marque la racine `data-impression="en-cours"` le
     temps de l'impression. Son commentaire dit l'accord en toutes lettres : « la
     marque posée sur la racine est le seul accord avec la feuille d'impression —
     c'est elle qui décide de ne montrer que ce conteneur. » C'est donc ici, et
     nulle part ailleurs, que la décision se prend.

     Sans cette règle, les deux exemplaires s'impriment. Mesuré sur le résultat de
     fusion, facture de démonstration : **21 pages sans le conteneur, 40 avec** —
     deux en-têtes de fichier, le rapport posé deux fois sur le papier. Chaque lot
     était vert de son côté ; la couture n'appartenait à personne, et c'est le
     genre de défaut que seule la recette aurait montré.

     La marque n'est pas lue à l'écran : hors impression, le conteneur n'existe
     pas, et la règle vit de toute façon dans `@media print`. */
  :root[data-impression] body > *:not(#document-imprime) { display: none !important; }

  /* Tout ce qui n'a de sens que sous une souris. La barre de navigation, le
     dépôt de fichier, les réglages, la fenêtre de connexion, les chevrons qui
     annoncent un repli. `base.css` en écarte déjà une partie ; on complète. */
  .barre, .evitement, .depot, .reglages, .etat, .consentement,
  .fenetre-auth, .puces, .chev, .panneau > *:not(.fichier) {
    display: none !important;
  }

  /* La visionneuse porte la facture entière — cent cinquante lignes et souvent
     bien plus. Imprimée, elle double le document sans rien expliquer : les
     extraits en cause sont déjà cités au fil des anomalies. C'est le seul
     retrait de contenu que cette feuille s'autorise. */
  .visionneuse { display: none !important; }

  /* -------------------------------------------- ce qui doit s'ouvrir, et voir */

  /* Le piège central du rapport imprimé, et il ne se voit pas : le corps d'un
     `<details>` replié a bien `display: block`, mais une hauteur de zéro —
     Chromium le masque par `content-visibility` sur `::details-content`, pas
     par `display`. Sans ces deux règles, le PDF ne contient que les seize
     lignes de titre des anomalies : un sommaire, pas un rapport. Mesuré au
     style calculé, pas déduit du CSS. */
  details::details-content {
    content-visibility: visible !important;
    block-size: auto !important;
  }
  details > summary { list-style: none; }

  /* ----------------------------------------- la couleur n'est pas un message */

  /* En niveaux de gris, les quatre couleurs de sens tombent entre 8 % et 13 %
     de luminance : bloquant 9 %, accent 8 %, conforme 12 %, avertissement 13 %.
     Elles sont indiscernables. Une pastille rouge et une pastille verte y sont
     le même gris, et c'est vrai de toute impression monochrome comme de la
     plupart des daltonismes.

     Chaque gravité reçoit donc un second canal, qui ne doit rien à la couleur :
     un filet à gauche dont l'épaisseur et le style diffèrent. Le libellé en
     toutes lettres était déjà là — il reste, et il ne doit pas disparaître. */
  .rang {
    border-left: 3pt solid var(--bloquant);
    padding-left: 6pt;
    border-bottom: 0.5pt solid var(--line);
  }
  .rang.avert { border-left-style: dotted; border-left-color: var(--accent); }
  .rang:has(.g-avert) { border-left-width: 1.5pt; border-left-color: var(--avert); }

  /* Les fonds teintés des pastilles ne survivent pas à un réglage d'impression
     qui refuse les fonds. On garde la teinte quand elle est imprimée — d'où
     `print-color-adjust` — mais rien ne repose dessus. */
  .grav, .verdict, .etat-badge {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
    border: 0.5pt solid currentColor;
  }

  /* ------------------------------------------- rien ne sort de la page */

  /* À l'écran, un extrait de XML plus large que la colonne se fait défiler :
     `overflow-x: auto`, `white-space: pre`. Sur papier il n'y a pas de barre de
     défilement — la fin de la ligne est simplement **coupée**, sans le moindre
     signe qu'il manquait quelque chose. Or c'est précisément là que se trouve
     la valeur en cause.

     On passe donc au retour à la ligne, avec un retrait pendant : le numéro
     reste seul dans sa gouttière et la suite de la ligne s'aligne dessous, au
     lieu de repartir sous le numéro. Sans ce retrait, une ligne repliée serait
     lisible mais on ne saurait plus où commence la suivante.
     `tools/smoke_impression.py` mesure qu'aucun bloc ne déborde. */
  pre.code { overflow: visible; font-size: 8pt; }
  pre.code .l, pre.code .saut {
    white-space: pre-wrap;
    overflow-wrap: break-word;
    padding-left: calc(3.6em + 24px);
    text-indent: calc(-3.6em - 12px);
  }

  /* Même raison pour les valeurs lues et les expressions évaluées : une adresse
     XPath est longue, et la colonne de droite porte le chiffre qui compte. */
  .expr .cexp, table.valeurs-lues .cexp, details.tech dd, .nomfichier {
    overflow-wrap: break-word;
    word-break: break-word;
  }

  /* L'en-tête de l'application — le nom du produit, sa présentation, le compte
     des règles — parle à qui découvre l'écran. Sur un rapport qui circule, il
     répète une réclame et porte un nom que le document n'a pas à porter :
     Amine a tranché le 22/09/2026 que la sortie est à l'enseigne de Lexora.
     L'identité vient désormais du bandeau répété en haut de chaque page. */
  header.bandeau { display: none !important; }

  /* La ligne en cause dans un extrait de code n'était signalée que par un fond
     rose. Sur une imprimante monochrome, ce fond est un gris à peine plus
     sombre que le cadre. Le chevron en marge, lui, se voit à l'encre noire. */
  pre.code .vise { font-weight: 600; }
  pre.code .vise .n::before { content: "\25B8\00a0"; }
  pre.code mark {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
    text-decoration: underline;
    text-underline-offset: 2px;
  }

  /* Une valeur fausse n'était rouge que par la couleur. */
  .expr .faux, .cause .lu { text-decoration: underline wavy; text-underline-offset: 2px; }

  /* ------------------------------------------ ce qui ne se coupe jamais en deux */

  /* Un rapport peut faire deux pages ou vingt selon la facture : rien ne peut
     être placé à la main, tout doit tenir par des règles. Celles-ci disent ce
     qui n'a pas de sens séparé de ce qui le suit. */

  /* L'en-tête d'un fichier, son bilan chiffré et sa note : jamais coupés, et
     jamais seuls en bas de page. */
  .entete, .bilan, .note-fichier { break-inside: avoid; }
  .entete, .bilan { break-after: avoid; }

  /* Un intitulé de groupe en dernière ligne d'une page annonce un contenu que
     le lecteur ne voit qu'après avoir tourné. */
  .section { break-inside: avoid; break-after: avoid; }

  /* Une anomalie tient d'un seul tenant quand elle le peut. Si elle est plus
     haute qu'une page, le navigateur ignore la consigne et la coupe — c'est le
     comportement voulu, pas un échec : mieux vaut une anomalie à cheval qu'une
     page blanche. */
  .rang { break-inside: avoid; }
  .rang > summary { break-after: avoid; }
  .titre, .cause { break-after: avoid; }

  /* Un extrait de code coupé entre deux pages perd son rapport au numéro de
     ligne : cinq lignes n'ont aucune raison de se séparer. Même chose pour un
     tableau de valeurs lues et pour un bloc de contrôles. */
  pre.code, .expr, .controles, table.valeurs-lues, details.ctl { break-inside: avoid; }

  /* Le renvoi « N signalements portent sur le même montant » est une phrase entière
     dont la moitié ne veut rien dire : coupée par un saut de page, elle annonce un
     lien et laisse la liste des règles sur la page suivante. Le `break-inside` de
     `.rang` le protège tant que l'anomalie tient sur une page ; au-delà, Chromium
     ignore la consigne du parent et le renvoi redevient coupable. */
  .lien-groupe { break-inside: avoid; }
  .expr .tete, .controles > .tete { break-after: avoid; }

  /* Deux lignes seules en haut ou en bas d'une page ne se lisent pas. */
  p, .detail, .conseil, li { orphans: 3; widows: 3; }

  /* ------------------------------------------------------- lire, pas cliquer */

  /* Les repères de ligne étaient des boutons. Sur papier ils restent la seule
     façon de retrouver l'endroit dans la facture : on les garde comme texte, on
     leur retire l'habit de commande. */
  .lien, .lien-min {
    color: var(--ink-soft);
    text-decoration: none;
    font-weight: 500;
  }

  /* Le repli et le déplié ne veulent plus rien dire : tout est ouvert. */
  .rang > summary, details.ctl > summary, .groupe > summary, details.tech summary {
    cursor: auto;
    background: none;
  }
  .rang[open] > summary { background: none; }

  /* --------------------------------------------------------------- densité */

  /* Ce qui coûte des pages, ce sont les blancs empilés, pas le texte. Les
     valeurs viennent de l'échelle de `src/index.html` divisée par un facteur
     constant : les retoucher une par une ferait diverger l'écran et le papier. */
  .fichier { margin-top: 0; border: 0.5pt solid var(--line); border-radius: 0; }
  .fichier + .fichier { margin-top: 8pt; break-before: page; }
  .entete { padding: 6pt 8pt; }
  .bilan div { padding: 5pt 8pt; }
  .section { padding: 5pt 8pt; }
  .rang > summary { padding: 4pt 8pt; }
  .corps { padding: 2pt 8pt 7pt; }
  /* `.lien-groupe` rejoint ses voisines : `base.css` lui pose 80ch et des marges
     en pixels, qui ne suivent pas l'échelle du papier. Mesuré en média `print` :
     648 px de large et 10 px sous le paragraphe, quand `.detail` et `.conseil`
     n'ont plus de laisse et tiennent à 6,67 px. On ne touche pas à la règle de
     `base.css` : elle habille l'écran, et une garde de l'écran d'analyse exige
     qu'elle reste nommée dans son propre bloc d'impression. */
  .detail, .conseil, .lien-groupe { max-width: none; margin-bottom: 5pt; }
  pre.code { margin-bottom: 5pt; padding: 4pt 0; }
  footer, .pied-legal { break-before: avoid; font-size: 8pt; }
}
