/* © 2026 DRAK Infinity (NEQ 2281240509), Montréal (Québec), Canada. Tous droits réservés.
   Feuille V4 (mission 31, lot A, fait 179, ordre du 2 novembre 2026). Écrite à la main (pas générée). Chargée EN PLUS de /site.css,
   /commun/pages-v3.css et /commun/v3.css sur toutes les pages (fait 179g), jamais à leur place : ce fichier ne redéfinit RIEN de ce qui existe
   déjà (barre, pied, cartes, boutons des pages V3), il ajoute seulement les classes dont les pages V4 (nouvelles : À propos, Projets,
   Actualités, Sur mesure, Rendez-vous; et plus tard les pages V3 reprises au lot C) ont besoin pour la pleine largeur, la symétrie et les
   petits textes du cahier des charges de forme (fait 179 a, b, c), plus le bouton Remonter (fait 179h).

   CHOIX D'INTÉGRATION (documenté pour le contrôleur) : nouveau fichier, plutôt qu'une extension de /commun/v3.css ou
   /commun/pages-v3.css. Ces deux feuilles sont GÉNÉRÉES par outils/construire-site.mjs d'après outils/v3-habillage.mjs (v3.css) et
   un fichier source v3-pages.css (pages-v3.css) : les régénérer à chaque construction, main raison pour ne pas y mélanger des classes
   écrites à la main pour une génération de pages différente (V3 contre V4) — un conflit de responsabilité entre deux générateurs sur un
   même fichier, et un risque de voir les classes V4 écrasées ou dédoublées à la prochaine régénération. v4.css reste un fichier À PART,
   écrit à la main comme site/commun/barre.css et rendez-vous.css, et il ne touche à aucune règle existante : les pages V3 déjà en ligne
   (homelab.html, outils.html, etc., lot C de cette mission) gardent leur habillage jusqu'à ce qu'elles soient reprises.

   CLASSES RÉUTILISABLES (préfixe v4- pour ne jamais entrer en collision avec les classes déjà prises par les pages existantes :
   .carte et .note existent déjà dans site/site.css et site/commun/v3.css avec un habillage DIFFÉRENT (effet de verre pour .carte,
   13 px pour .note-discrete, fait 179c ci-dessous) — les réutiliser aurait changé l'apparence de toutes les pages déjà en ligne) :
     .v4-wrap        pleine largeur, retrait de 24 px sur ordinateur et 16 px sur téléphone (fait 179a).
     .v4-prose       colonne de lecture d'environ 70 caractères pour le texte courant (max-width:54ch, fait 179a; voir la
                     correction détaillée plus bas, même fait, mission 31 lot A, second passage).
     .v4-sec         section pleine largeur, espacement vertical régulier; .v4-sec.v4-alt pour la bande de fond alterné.
     .v4-tete        en-tête de page (fil d'Ariane, titre, texte, boutons).
     .v4-deux / .v4-trois / .v4-quatre / .v4-cinq   grilles à 2/3/4/5 colonnes de même hauteur par rangée (fait 179b), 1 colonne sous 900 px.
     .v4-carte       carte réutilisable, hauteur alignée dans sa rangée par la grille parente (align-items:stretch, fait 179b).
     .v4-note        tout texte secondaire : 11 px italique, couleur secondaire (fait 179c; voir la décision ci-dessous).
     .v4-btns        rangée de boutons alignés à la même hauteur (fait 179b); réutilise la classe .bouton déjà établie (site/site.css) pour
                     qu'un bouton V4 ait exactement le même habillage qu'un bouton V3 — aucune nouvelle classe de bouton n'est créée ici.

   DÉCISION fait 179c (11 px contre 13 px déjà en place) : .note-discrete (mission 29, site/commun/v3.css) reste à 13 px — elle est déjà
   posée et visuellement calée sur les pages existantes (Serveur maison, Maison 3D connectée, Facturier en ligne, etc.), que ce lot ne
   touche pas (elles sont au lot C). La réduire à 11 px aurait changé leur apparence sans que ce soit demandé. Le texte de l'ordre pour
   CE lot (179c) demande explicitement 11 px pour les pages V4 : .v4-note est donc une classe distincte, neuve, à 11 px, réservée aux
   pages V4 (nouvelles et, au lot C, reprises). Les deux classes coexistent : .note-discrete pour les pages V3 encore telles quelles,
   .v4-note pour les pages V4.

   Thèmes (fait 179f) : toutes les couleurs ci-dessous passent par les jetons déjà établis de site/commun/v3.css (--fond, --fond2,
   --surface, --texte, --texte2, --trait, --or, --or-texte, posés par :root[data-theme="clair"] et :root[data-theme="nuit"]) : rien n'est
   figé en dur, le thème clair et le thème nuit s'appliquent automatiquement, sans bande sombre dans une page claire ni l'inverse. */

html[data-theme] .v4-wrap{padding-left:max(24px,env(safe-area-inset-left,0px));padding-right:max(24px,env(safe-area-inset-right,0px))}
@media (max-width:640px){
  html[data-theme] .v4-wrap{padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px))}
}
html[data-theme] .v4-prose,html[data-theme] .v4-prose p{max-width:68ch}
/* CORRECTION mission 31, lot A, second passage (fait 179a) : 68ch donnait une colonne d'environ 90 caractères par ligne,
   pas 70. Mesure réelle (canevas, police Manrope 400 réellement chargée par le site, cdn Google Fonts) : à 17px (taille
   héritée de body, site/commun/v3.css), 1ch = largeur du glyphe « 0 » = 10,375px, mais le caractère moyen d'un vrai
   paragraphe du site (573 caractères mesurés, .v4-intro d'À propos) ne fait que 7,633px — l'écart vient de « ch » qui
   mesure UN glyphe fixe, pas la largeur moyenne réelle du texte (lettres étroites comme i, l, t, espaces, apostrophes
   typographiques). 70 caractères réels ÷ 7,633px = 534px de large ÷ 10,375px (1ch) = 51,5ch. Validation par habillage
   réel (division du même paragraphe en lignes avec max-width:54ch appliqué, comptage des caractères par ligne complète
   via Range.getClientRects) : moyenne de 70,1 caractères/ligne sur 8 lignes (74,69,74,69,69,64,68,74), dans la cible
   65-75. 54ch retenu plutôt que 51,5-53ch calculés (voir .v4-tete p et .v4-projet .v4-txt p ci-dessous, tailles 18px et
   16px) : valeur commune qui centre les quatre sélecteurs 68ch de ce fichier le plus près de 70 caractères réels. */
/* CHANGEMENT mission 31-BIS, lot B (fait 191 b, ordre du 4 novembre 2026) : décision assumée de la direction générale, pas
   une erreur corrigée dans l'autre sens — les colonnes repassent à max-width:68ch (plutôt que le 54ch calculé ci-dessus
   pour viser ~70 caractères réels) : « pas de colonne étroite quand la largeur est disponible », avec une cible plus
   généreuse de 65 à 76 caractères réels par ligne sur ordinateur (toujours dans la fourchette de lisibilité reconnue;
   sur téléphone la largeur de l'écran, pas ce « ch », limite déjà les lignes au même intervalle). S'applique à toutes
   les colonnes « 54ch » ci-dessous : .v4-prose, .v4-sec .v4-intro, .v4-tete p, .v4-projet .v4-txt p, .v4-actu .v4-txt p/
   .v4-change/.v4-src, .v4-faq p. */
/* Bandeau « mascotte » (outils/injecter-bandeau-sur-mesure.mjs), posé en dernier enfant d'un .v4-wrap devenu l'en-tête de page (mission 31, lot A,
   fait 179a : /sur-mesure et /en/custom converties à la structure V4) : même disposition (image à droite, pleine largeur sous le texte sur
   téléphone) que l'ancienne .conteneur:has(>.drk-bandeau-fig) de site/site.css, reprise ici sous le préfixe v4- pour ne pas réintroduire de
   conteneur centré à largeur fixe (179a). */
html[data-theme] .v4-wrap:has(>.drk-bandeau-fig){display:grid;grid-template-columns:minmax(0,1fr) minmax(0,320px);column-gap:clamp(24px,5vw,56px);align-items:center}
html[data-theme] .v4-wrap:has(>.drk-bandeau-fig)>*{grid-column:1}
html[data-theme] .v4-wrap:has(>.drk-bandeau-fig)>.drk-bandeau-fig{grid-column:2;grid-row:1/span 6;margin:0}
@media (max-width:720px){
  html[data-theme] .v4-wrap:has(>.drk-bandeau-fig){display:block}
  html[data-theme] .v4-wrap:has(>.drk-bandeau-fig)>.drk-bandeau-fig{grid-column:auto;grid-row:auto}
}
/* CORRECTION mission 31, lot A, contrôle (fait A7) : la mascotte de /sur-mesure et /en/custom (mascotte-sur-mesure.webp) a un fond sombre
   baké dans l'image ; sans version à fond clair dans site/commun/ (vérifié), elle faisait une tache dans la page en thème clair. Cadre
   dédié (outils/injecter-bandeau-sur-mesure.mjs, classe posée sur .drk-bandeau-fig de CE bandeau seulement, jamais sur .drk-bandeau-fig des
   autres bandeaux du site) : fond crème (var(--surface)) rendu visible en marge par un padding (le reset *{box-sizing:border-box} de
   site/site.css garde la largeur totale à l'intérieur de .drk-bandeau-fig, max-width:320px), bordure identique à .v4-projet .v4-vis img,
   ombre identique à celle déjà posée sur #haut (ligne 124 plus bas) : la mascotte devient une photo encadrée plutôt qu'un bloc sombre
   posé directement sur la page. */
html[data-theme] .v4-mascotte-cadre{background:var(--surface);border:1px solid var(--trait);border-radius:28px;padding:14px;box-shadow:0 6px 20px rgba(0,0,0,.18)}

/* Mission 31-BIS, lot A (fait 191 e) : plus de trait entre sections nulle part sur le site (seule la barre du haut garde le sien) ; les
   sections se distinguent par l'alternance des fonds (.v4-alt ci-dessous) et par l'espace. padding-block ramené à clamp(20px,3vw,32px) :
   deux .v4-sec qui se suivent cumulent 40 px (téléphone, 3vw≤12px, clampé au minimum) à 64 px (ordinateur, dès ~1 067 px, clampé au
   maximum) entre elles, conformément au cahier des charges de respiration (fait 191 a, point e). */
html[data-theme] .v4-sec{padding-block:clamp(20px,3vw,32px);display:grid;gap:20px}
html[data-theme] .v4-sec.v4-alt{background:var(--fond2)}
/* Mission 31-BIS, lot B (fait 191 c) : début de phrase qui sert d'étiquette dans un paragraphe long (« Pour qui : »,
   « Comment : », « Zones : », « Langues : », « Ce que ça change pour vous : ») — gras, or sombre, sur sa propre ligne.
   Posée sur un <strong> ou <b> au début du texte, dans les pages qui en ont besoin (197 e, f, g). */
html[data-theme] .v4-label-champ{display:block;font-weight:700;color:var(--or-texte);margin-bottom:4px}
html[data-theme] .v4-sec h2{font-size:clamp(26px,3vw,38px)}
html[data-theme] .v4-sec .v4-intro{color:var(--texte2);font-size:17px;max-width:68ch}
/* CORRECTION mission 31, lot A, second passage (fait 179a) : même police/taille (17px) et même calcul que .v4-prose
   ci-dessus ; 54ch retenu pour la même raison (valeur commune aux quatre sélecteurs 68ch de ce fichier). */
html[data-theme] .v4-eti{font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--or-texte);font-weight:700}

/* Mission 31-BIS, lot A (fait 191 e) : plus de trait sous l'en-tête de page non plus (seule la barre du haut garde le sien). */
html[data-theme] .v4-tete{padding-block:clamp(32px,5vw,52px) clamp(24px,4vw,34px);display:grid;gap:14px}
html[data-theme] .v4-tete .v4-fil{font-size:13px;color:var(--texte2)}
html[data-theme] .v4-tete h1{font-size:clamp(34px,4.6vw,58px)}
/* CORRECTION mission 31-BIS, lot B (faits 195/196 et apparentés) : « .v4-tete p » (générique) écrasait aussi la ligne
   d'audience (<p class="v4-eti">) de chaque en-tête V4 — même bogue, même cause (spécificité d'une classe + élément
   contre une seule classe) que celui trouvé et corrigé par la session principale sur .acc-bandeau-txt p/.v4-eti de
   l'accueil. Trouvé ici sur /homelab : la ligne d'audience s'affichait à 18px, couleur --texte2, sur deux lignes, au
   lieu de 12px or sur une seule ligne (.v4-eti, site/commun/v4.css plus haut). Affecte TOUTES les pages qui portent
   .v4-tete .v4-eti (donc corrigé à la source, pas page par page). */
html[data-theme] .v4-tete p:not(.v4-eti){color:var(--texte2);font-size:18px;max-width:68ch}
/* CORRECTION mission 31-BIS, lot B (trouvé en vérifiant /outils à 390 px) : white-space:nowrap sur TOUTE .v4-tete
   .v4-eti débordait de 319 px sur le Catalogue, dont l'étiquette d'en-tête est une vraie phrase de 97 caractères
   (PORTEE.fr, textes-b3.mjs) — pas une courte ligne d'audience comme sur /homelab ou l'accueil. Retiré : une ligne
   d'audience courte (12px, le cas visé par le correctif ci-dessus) tient déjà sur une seule ligne sans qu'on le
   force, et forcer le nowrap cassait les pages à étiquette plus longue. */
/* AJOUT mission 31, lot A (relecture de mise en page, fait 179a) : le chapeau d'en-tête (.v4-tete p) n'avait aucune
   largeur maximale et s'étirait avec la pleine largeur de la page (jusqu'à 169-234 caractères par ligne mesurés sur
   /projets et /actualites) ; une largeur en ch ramène sa colonne de lecture à la cible du cahier des charges. Propriété
   ajoutée à la règle existante, rien retiré : corrige du même coup /a-propos, /sur-mesure et toute autre page qui
   utilise .v4-tete.
   CORRECTION mission 31, lot A, second passage (fait 179a) : 68ch donnait, à 18px, une colonne réelle d'environ
   88 caractères. Mesure canevas (police Manrope 400, chapeau réel de /a-propos, 322 caractères) : 1ch (glyphe « 0 » à
   18px) = 10,984px, caractère moyen = 8,200px. 70 caractères réels ÷ 8,200px = 574px ÷ 10,984px = 52,25ch calculé.
   Validation par habillage réel à 54ch : moyenne de 70,5 caractères/ligne sur 4 lignes (71,68,74,69), dans la cible
   65-75. 54ch retenu, même valeur commune que .v4-prose (voir le calcul détaillé plus haut). */

/* Mission 31-BIS, lot B (fait 191 a) : 32 px entre une rangée de cartes et ce qui suit. CORRECTION (node outils/
   tester-respiration.mjs, après ce lot) : le premier essai (margin-bottom:12px) supposait, à tort, que .v4-sec
   (display:grid, gap:20px) ajoutait son gap entre CES éléments — en réalité .v4-sec n'a quasiment toujours qu'un
   seul enfant direct (.v4-wrap), donc gap:20px ne s'applique nulle part à l'intérieur : le flux normal de .v4-wrap
   (pas une grille) ne doit sa respiration qu'aux marges posées ici, avec le repli habituel des marges adjacentes
   (le plus grand des deux l'emporte, elles ne s'additionnent pas) — 32 px posés directement, pas 12. */
/* CORRECTION mission 31-BIS, lot B (retour du contrôleur) : margin-top AJOUTÉ (était margin-bottom seul) — un
   paragraphe (<p>, margin:0) juste avant une rangée n'avait donc AUCUN écart (0 px, trouvé par le contrôleur sur
   homelab, outils, sur-mesure, facturier, accueil); un <h2>/<h3> avant la rangée avait un peu d'écart par sa propre
   marge, mais pas les 32 px voulus. margin:32px 0 (comme .v4-btns ci-dessous) répare les deux côtés sans ajout : les
   marges adjacentes se replient (le plus grand l'emporte), donc ceci ne change rien là où l'écart était déjà ≥32 px. */
html[data-theme] .v4-deux{display:grid;grid-template-columns:1fr 1fr;gap:clamp(20px,4vw,32px);align-items:stretch;margin:32px 0}
html[data-theme] .v4-trois{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;align-items:stretch;margin:32px 0}
html[data-theme] .v4-quatre{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;align-items:stretch;margin:32px 0}
html[data-theme] .v4-cinq{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;align-items:stretch;margin:32px 0}
/* Variante Rendez-vous (fait 185) : calendrier Cal.com (1.3fr) à gauche, deux cartes (0.7fr) à droite, sur ordinateur seulement
   (maquettes/v4/pages-v4.html, classe .deux.rdv). Sous 900 px, une seule colonne comme .v4-deux (calendrier en premier, pleine largeur). */
html[data-theme] .v4-deux.v4-rdv{grid-template-columns:1.3fr .7fr}
@media (max-width:900px){
  html[data-theme] .v4-deux,html[data-theme] .v4-trois,html[data-theme] .v4-quatre,html[data-theme] .v4-cinq,html[data-theme] .v4-deux.v4-rdv{grid-template-columns:1fr}
}

html[data-theme] .v4-carte{background:var(--surface);border:1px solid var(--trait);border-radius:14px;padding:20px;display:grid;gap:8px;min-width:0;align-content:start;text-decoration:none;color:var(--texte);height:100%;box-sizing:border-box}
html[data-theme] .v4-carte b,html[data-theme] .v4-carte strong,html[data-theme] .v4-carte h3{font-family:var(--serif-titres);font-size:22px;font-weight:600;margin:0}
html[data-theme] .v4-carte p{color:var(--texte2);font-size:15px}
html[data-theme] a.v4-carte:hover b,html[data-theme] a.v4-carte:hover strong{text-decoration:underline;text-decoration-color:var(--or);text-underline-offset:5px}
/* AJOUT mission 31, lot A (fait 179, conversion de /sur-mesure et /en/custom) : certaines cartes gardent un <h3> (titre de carte,
   sémantique conservée) suivi d'une liste à puces (contenu courant conservé mot pour mot, fait 179) ; la liste reprend la couleur
   secondaire du texte de carte et un retrait réduit plutôt que l'indentation par défaut du navigateur. */
html[data-theme] .v4-carte ul{color:var(--texte2);font-size:15px;margin:0;padding-left:20px}

/* CORRECTION mission 31-BIS, lot B (retour du contrôleur, deuxième passage, fait 191 c contre fait 191 d) : max-width
   porté de 70ch à 110ch (mesuré au navigateur, vraies polices : 110ch = ~738 px à 11 px, assez pour ramener les deux
   mentions les plus longues du site, « Photos d'exemple… » et « Prix indicatifs, hors taxes… » de /homelab, de 5-6 à
   3-4 lignes à 1 366 px) — un élargissement ne peut jamais FAIRE GRANDIR le nombre de lignes d'une mention déjà
   conforme ailleurs sur le site, seulement le réduire ou le laisser pareil (la largeur de la carte ou de la colonne
   qui contient la mention reste la vraie limite partout où elle est plus étroite que 110ch). */
html[data-theme] .v4-note,html[data-theme] p.v4-note{font-size:11px;line-height:1.4;font-style:italic;color:var(--texte2);max-width:110ch;margin:6px 0 0}

/* Mission 32, lot A (fait A6, ordre du 6 novembre 2026, gabarit .cle de maquettes/v4/automatisation-v4.html ligne 33 : même rôle, jeton de
   couleur du site plutôt que celui de la maquette) : la phrase d'accroche qui résume une offre (en-tête de page) passe en gras, couleur du
   texte courant, taille héritée du paragraphe qui la porte (jamais une taille à soi) — jamais de l'italique ni du 11 px, qui restent
   réservés aux précisions (.v4-note, ci-dessus). Une seule phrase en gras par bloc (fait A6) : cette classe se pose sur la PHRASE, jamais
   sur tout le paragraphe. */
html[data-theme] .cle,html[data-theme] p.cle{font-weight:700;color:var(--texte)}

/* Mission 31-BIS, lot A (retour de Yannick, 4 novembre 2026) : une rangée de boutons/liens (« Voir les quatre kits », etc.)
   collée au bloc ou au texte qui la précède ou la suit. CORRECTION lot B (node outils/tester-respiration.mjs, après ce
   lot) : margin:14px 0 supposait, à tort, un gap de section qui s'ajouterait (voir la même correction plus haut pour
   .v4-deux/.v4-trois/.v4-quatre/.v4-cinq) — en réalité .v4-wrap n'est pas une grille, les marges adjacentes se
   replient (le plus grand l'emporte, aucune addition) : au moins 24 px posés directement des deux côtés. */
html[data-theme] .v4-btns{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin:24px 0}

/* ── Sommaire à ancres (fait 181, page À propos et pages V4 suivantes qui en ont besoin) : rangée de pastilles sous l'en-tête,
   même habillage que .somm de la maquette (maquettes/v4/pages-v4.html, ligne 30), repris en jetons --surface/--trait/--texte/--or. */
html[data-theme] .v4-somm{display:flex;gap:8px;flex-wrap:wrap;font-size:14px;margin:0;padding:0;list-style:none}
html[data-theme] .v4-somm a{text-decoration:none;border:1px solid var(--trait);border-radius:999px;padding:6px 12px;background:var(--surface);color:var(--texte)}
html[data-theme] .v4-somm a:hover,html[data-theme] .v4-somm a:focus-visible{border-color:var(--or)}

/* ── Bouton « Remonter » (fait 179h) : rond, centré en bas, apparaît après 80 % d'un écran de défilement, remonte en douceur, accessible
   au clavier (un vrai <button>), invisible à l'impression, suit le thème (jetons --surface/--trait/--texte/--or). Posé par
   site/commun/remonter.js sur toutes les pages. */
html[data-theme] #haut{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(18px + env(safe-area-inset-bottom,0px));z-index:40;
  display:flex;align-items:center;justify-content:center;width:46px;height:46px;margin:0;padding:0;
  border:1px solid var(--trait);border-radius:50%;background:var(--surface);color:var(--texte);font-size:20px;line-height:1;cursor:pointer;
  opacity:0;pointer-events:none;transition:opacity .2s,border-color .2s;box-shadow:0 6px 20px rgba(0,0,0,.18)}
html[data-theme] #haut.vu{opacity:1;pointer-events:auto}
html[data-theme] #haut:hover,html[data-theme] #haut:focus-visible{border-color:var(--or)}
@media (prefers-reduced-motion:reduce){html[data-theme] #haut{transition:none}}
@media print{#haut{display:none!important}}

/* ── Projets (fait 182, page /projets et /en/projets) : liste de projets livrés, image à gauche ou à droite en
   alternance par simple ordre dans le DOM, règle CSS classique, sur les enfants directs de .v4-liste-projets, pas de
   logique côté serveur ni côté script. Bouton d'aperçu animé (site/commun/apercus.js) : insère/retire un <iframe>
   dans .v4-vis au clic, jamais de fenêtre surgissante (fait 179i). */
html[data-theme] .v4-projet{display:grid;grid-template-columns:1.15fr 1fr;gap:28px;align-items:start;padding-block:36px;border-bottom:1px solid var(--trait)}
html[data-theme] .v4-liste-projets>.v4-projet:first-child{padding-top:8px}
html[data-theme] .v4-projet:nth-child(even){direction:rtl}
html[data-theme] .v4-projet:nth-child(even)>*{direction:ltr}
html[data-theme] .v4-projet .v4-vis{display:grid;gap:10px;min-width:0}
/* Mission 31-BIS, lot B (fait 197 d) : images des projets (et des chantiers en cours, .v4-img-43 plus bas) réduites de
   moitié — 480 px de large au plus sur ordinateur ; width:100% garde le comportement normal (pleine largeur de sa
   colonne) tant que cette colonne fait moins de 480 px, donc aucun effet sur téléphone. */
html[data-theme] .v4-projet .v4-vis img{width:100%;max-width:480px;height:auto;border-radius:14px;border:1px solid var(--trait);background:var(--surface);display:block}
html[data-theme] .v4-projet .v4-vis iframe{width:100%;height:420px;border:1px solid var(--trait);border-radius:14px;background:var(--surface);display:block}
html[data-theme] .v4-projet .v4-txt{display:grid;gap:10px;align-self:center;min-width:0}
html[data-theme] .v4-projet h3{font-family:var(--serif-titres);font-size:clamp(24px,2.6vw,30px);font-weight:600;margin:0}
html[data-theme] .v4-projet .v4-txt p{color:var(--texte2);font-size:16px;max-width:68ch}
/* AJOUT mission 31, lot A (fait 179a) : le descriptif de chaque projet (.v4-txt p) n'est pas dans une .v4-carte, donc pas
   couvert par la limite de largeur des cartes ; sur grand écran sa colonne dépassait 85 caractères. Une largeur en ch,
   même cible que .v4-prose.
   CORRECTION mission 31, lot A, second passage (fait 179a) : 68ch donnait, à 16px, une colonne réelle d'environ
   93 caractères. Mesure canevas (police Manrope 400, descriptifs réels de /projets, 534 caractères) : 1ch (glyphe « 0 »
   à 16px) = 9,766px, caractère moyen = 7,272px. 70 caractères réels ÷ 7,272px = 509px ÷ 9,766px = 52,12ch calculé.
   Validation par habillage réel à 54ch : moyenne de 71,0 caractères/ligne sur 7 lignes (71,70,71,72,71,71,71), dans la
   cible 65-75. 54ch retenu, même valeur commune que .v4-prose (voir le calcul détaillé plus haut). */
html[data-theme] .v4-apercu-btn{font:inherit;font-weight:700;font-size:14px;border:1.5px solid var(--trait);background:var(--surface);color:var(--texte);border-radius:999px;padding:8px 14px;cursor:pointer;justify-self:start}
html[data-theme] .v4-apercu-btn:hover,html[data-theme] .v4-apercu-btn:focus-visible{border-color:var(--or)}
html[data-theme] .v4-carte img{width:100%;height:auto;border-radius:10px;display:block}
/* CORRECTION mission 31, lot A, contrôle (fait 179b) : les deux cartes « Serveur maison » (image 1050×1150, portrait) et
   « Maison 3D » (image 1024×688, paysage) de la section « Deux chantiers » (.v4-deux, /projets et /en/projets) avaient
   des images de formats différents, donc des hauteurs de carte différentes et un vide sous la carte la plus courte.
   Classe posée sur l'image des deux cartes (outils/projets-donnees.mjs, carteEnCours) : même cadre 4:3 pour les deux,
   recadrage par object-fit, donc même hauteur d'image et textes qui commencent à la même hauteur dans les deux cartes. */
/* CORRECTION mission 31-BIS, lot B (fait 191 f) : object-fit:cover rognait les chantiers en cours (fait 197 d) et les
   images d'Actualités dans leur cadre 4:3 — contain, fond --surface qui comble les bandes vides au lieu de rogner.
   Fait 197 d : 480 px de large au plus sur ordinateur pour les chantiers en cours (même raison que .v4-projet .v4-vis
   img ci-dessus). */
html[data-theme] .v4-img-43{width:100%;max-width:480px;height:auto;aspect-ratio:4/3;object-fit:contain;border-radius:10px;display:block}
/* Exception : sur Actualités (.v4-actu), l'image suit les 40 % de colonne (fait 197 e) plutôt que le plafond de 480 px
   ci-dessus (qui vise les chantiers en cours de /projets, pas cette page) — à 1 366 px, 40 % dépasse légèrement 480 px. */
html[data-theme] .v4-actu .v4-vis img.v4-img-43{max-width:none}
@media (max-width:900px){
  html[data-theme] .v4-projet{grid-template-columns:1fr}
  html[data-theme] .v4-projet:nth-child(even){direction:ltr}
  html[data-theme] .v4-projet .v4-vis iframe{height:360px}
}

/* ── Actualités (mission 31, lot A, fait 183 version finale) : chaque bloc en DEUX COLONNES à partir de 900 px
   (date et texte à gauche, image à droite), EN ALTERNANCE gauche/droite d'un bloc à l'autre, même mécanisme que
   .v4-projet:nth-child(even) ci-dessus (direction:rtl sur le bloc, direction:ltr remis sur ses enfants directs) :
   réutilise .v4-vis/.v4-txt/.v4-img-43 déjà posés pour Projets plutôt que de redéfinir une grille image/texte.
   Sous 900 px, une seule colonne (texte d'abord, puis image : plus lisible qu'une grande image avant le texte sur
   téléphone, fait 183, point 4). Remplace l'ancienne grille à une seule colonne 120px/1fr (fait 179a, pleine moitié
   droite vide) signalée non conforme par le contrôle du 2 novembre 2026 (CONTROLE-JOURNAL-31-LOT-A.md, point A4). */
/* Mission 31-BIS, lot B (fait 197 e) : texte à 60 % (3fr), image à 40 % (2fr) — DOM dans l'ordre texte puis image —
   32 px d'écart entre les deux colonnes (gap porté de 28 à 32). */
html[data-theme] .v4-actu{display:grid;grid-template-columns:3fr 2fr;gap:32px;align-items:start;padding-block:36px;border-bottom:1px solid var(--trait)}
html[data-theme] .v4-wrap>.v4-actu:first-child{padding-top:8px}
html[data-theme] .v4-actu:nth-child(even){direction:rtl}
html[data-theme] .v4-actu:nth-child(even)>*{direction:ltr}
html[data-theme] .v4-actu .v4-txt{display:grid;gap:10px;min-width:0}
html[data-theme] .v4-actu .v4-vis{display:grid;min-width:0}
html[data-theme] .v4-actu time{font-weight:700;color:var(--or-texte);white-space:nowrap}
html[data-theme] .v4-actu h3{font-size:24px;margin:0}
html[data-theme] .v4-actu .v4-txt p{color:var(--texte2);max-width:68ch}
/* AJOUT mission 31, lot A (relecture de mise en page, fait 179a) : largeur de colonne en ch ramenée à la même cible
   que .v4-prose (70 caractères réels par ligne ≈ 54ch à la taille héritée du corps de texte ; calcul détaillé plus
   haut dans ce fichier, .v4-prose). S'applique à tous les paragraphes de .v4-txt (résumé, « Ce que ça change pour
   vous », sources). */
/* « Ce que ça change pour vous » (fait 183, point 5; étiquette dorée fait 197 e) : texte courant à 15 px, étiquette en
   gras or (.v4-label-champ, posée sur le <b> par outils/actualites-donnees.mjs), PAS en 11 px italique (c'est
   l'information la plus utile du bloc) ; 16 px au-dessus (au lieu des 4 px uniformes d'avant cette mission) ; les
   sources restent en petit texte italique à 11 px dessous (.v4-src, classe distincte, ci-dessous). */
html[data-theme] .v4-actu .v4-change{font-size:15px;line-height:1.5;color:var(--texte2);max-width:68ch;margin:16px 0 0}
html[data-theme] .v4-actu .v4-src{font-size:11px;line-height:1.4;font-style:italic;color:var(--texte2);max-width:68ch;margin:4px 0 0}
html[data-theme] .v4-actu .v4-src a{color:inherit}
@media (max-width:900px){
  html[data-theme] .v4-actu{grid-template-columns:1fr}
  html[data-theme] .v4-actu:nth-child(even){direction:ltr}
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════════════════════
   LOT C (mission 31, vague 0, agent 0b) : contrat de balisage partagé des six pages de services et de produits
   (fait 186, maquette maquettes/v4/pages-v4-b.html). AJOUTS SEULEMENT à partir d'ici : rien au-dessus de cette
   ligne n'est modifié (classes déjà en place, posées par le lot A et réutilisées telles quelles par le lot C,
   SANS être redéfinies ici) : .v4-wrap, .v4-sec/.v4-alt, .v4-tete, .v4-fil, .v4-eti, .v4-deux/.v4-trois/
   .v4-quatre/.v4-cinq, .v4-carte, .v4-note, .v4-btns, .v4-intro, .v4-prose, .v4-img-43, et la paire
   .v4-projet/.v4-vis/.v4-txt (alternance image/texte, section Projets du lot A) — à réutiliser telle quelle pour
   l'alternance des quatre kits de /homelab/kits plutôt que d'inventer une classe « v4-kit » concurrente.

   Squelette imposé à toutes les sections (cohérent avec le modèle déjà posé au lot A, page Rendez-vous) :
     <section class="v4-sec [v4-alt]" id="…"><div class="v4-wrap"> … </div></section>
   Les cartes gardent toujours un <h3> (jamais un <h4> directement sous un <h2> de section, fait 188). Bouton
   principal : a.bouton; bouton secondaire : a.bouton.contour (SEULE variante). AVERTISSEMENT pour les agents de
   la vague 1 : `.bouton.secondaire` existe encore dans site/site.css et dans le marquage actuel de site/homelab.html
   (et ses pages sœurs) — c'est un ALIAS visuel strictement identique à `.bouton.contour` (même règle CSS,
   site/commun/v3.css ligne 49), pas une variante concurrente dans le résultat visuel, mais DEUX noms de classe
   pour un même style. Ne pas réutiliser `.bouton.secondaire` dans le nouveau marquage V4 : ne garder que
   `.bouton.contour`. Ne pas retirer `.bouton.secondaire` de site.css/v3.css ici (hors des fichiers dont cet
   agent est propriétaire) — signalé au rapport de cet agent pour qui reprendra les pages existantes. */

/* v4-tete-vis : variante de .v4-tete à deux colonnes (texte 1,1fr, visuel 0,9fr), posée en ajoutant cette classe
   À CÔTÉ de .v4-tete sur le même élément (<div class="v4-tete v4-tete-vis wrap">), une seule colonne sous 900 px;
   le visuel réutilise .v4-vis (déjà établi par .v4-projet), jamais une nouvelle classe de colonne image. */
/* CORRECTION mission 31-BIS, lot B (faits 195, 196) : align-items:center centrait verticalement la colonne de texte
   dans la hauteur de la rangée de grille, fixée par la colonne de la photo (bien plus haute) — le fil d'Ariane se
   retrouvait donc à ~150px sous la barre, pas 24px. align-items:start aligne les deux colonnes sur leur haut ;
   padding-top ramené à 24px fixe (au lieu du clamp 32-52px partagé avec les en-têtes sans photo) pour que le fil
   d'Ariane suive la barre de près, comme demandé pour /homelab et /maison-3d-connectee. */
html[data-theme] .v4-tete.v4-tete-vis{grid-template-columns:1.1fr .9fr;align-items:start;gap:clamp(24px,5vw,56px);padding-top:24px}
html[data-theme] .v4-tete-vis .v4-vis{display:grid;gap:10px;min-width:0}
html[data-theme] .v4-tete-vis .v4-vis img{width:100%;height:auto;border-radius:14px;border:1px solid var(--trait);background:var(--surface);display:block}
@media (max-width:900px){html[data-theme] .v4-tete.v4-tete-vis{grid-template-columns:1fr}}

/* v4-photo : <figure> réutilisable pour une photo d'exemple avec sa légende (<figcaption class="v4-note">), hors
   de toute grille d'alternance image/texte (galeries « La bonne taille », « En images », etc.). */
html[data-theme] .v4-photo{display:grid;gap:6px;margin:0;min-width:0}
/* CHANGEMENT mission 31-BIS, lot B (fait 191 f) : object-fit:cover rognait les photos d'exemple pour les caler toutes au
   même cadre 4:3 (homelab, Maison 3D, captures du Facturier en ligne, formats très différents les uns des autres —
   450×807 à 800×600 pour les trois photos du home lab). Le cadre garde sa hauteur régulière (4:3, pour que la grille
   reste alignée), mais plus aucune photo n'est rognée : object-fit:contain, fond --surface qui comble les bandes vides,
   comme le demande le cahier des charges (« object-fit: contain sur fond crème si le format ne peut pas suivre »).
   .v4-photo-contenu (ci-dessous) devient un alias sans effet, gardé pour ne rien casser côté appelants existants. */
html[data-theme] .v4-photo img{width:100%;height:auto;aspect-ratio:4/3;object-fit:contain;border-radius:12px;display:block;border:1px solid var(--trait);background:var(--surface)}
html[data-theme] .v4-photo-contenu img{object-fit:contain}

/* v4-num : pastille ronde numérotée pour une étape (« Comment ça se passe », etc.), chiffre écrit dans le
   marquage (pas un compteur CSS); jetons --btn/--btn-texte (déjà ceux de .bouton, site/commun/v3.css) plutôt que
   --or directement, pour garder un contraste texte/fond garanti dans les deux thèmes. */
html[data-theme] .v4-num{display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:50%;background:var(--btn);color:var(--btn-texte);font-weight:800;font-size:15px;margin-bottom:2px}

/* v4-prix : bloc de prix à deux lignes en gras (Travail / Matériel), suivi d'une ligne .v4-note (conditions). */
html[data-theme] .v4-prix{display:grid;gap:4px;font-size:15px;margin:4px 0}
html[data-theme] .v4-prix b{font-weight:700;color:var(--texte)}

/* v4-tabl : conteneur de tableau qui défile HORIZONTALEMENT DANS SON PROPRE CADRE si besoin (overflow-x:auto sur
   ce conteneur, pas sur la page) : document.documentElement.scrollWidth reste égal à clientWidth même si la
   table a plus de colonnes que la largeur disponible sur téléphone. */
html[data-theme] .v4-tabl{width:100%;overflow-x:auto;border:1px solid var(--trait);border-radius:14px}
html[data-theme] .v4-tabl table{width:100%;border-collapse:collapse;min-width:640px}
html[data-theme] .v4-tabl th,html[data-theme] .v4-tabl td{padding:10px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--trait);font-size:14.5px;color:var(--texte2)}
html[data-theme] .v4-tabl th{color:var(--texte);font-weight:700;background:var(--fond2)}
html[data-theme] .v4-tabl tr:last-child td{border-bottom:0}
/* Piège CSS Grid : .v4-wrap est un item direct d'une grille (.v4-sec{display:grid}) ; sans cette règle, sa taille
   minimale automatique reste basée sur le contenu le plus large d'un descendant (ici min-width:640px du tableau
   ci-dessus), même si .v4-tabl a son propre overflow-x:auto — la page entière déborde alors horizontalement à
   l'écran mobile. Trouvé et corrigé une première fois dans v4-facturier.css (agent 1d), reproduit sur /homelab
   (agent 2b, vague 2) : posé ici une seule fois, pour toute page qui charge v4.css et porte un .v4-tabl.
   Mission 31, lot C, vague 2 (agent 2b, confirmé par la session, 3 novembre 2026). */
html[data-theme] .v4-wrap:has(.v4-tabl){min-width:0}
/* Mission 31-BIS, lot A (retour de Yannick, 4 novembre 2026) : le défilement horizontal de .v4-tabl (ci-dessus) n'était pas
   praticable sur téléphone — rien n'indiquait qu'il fallait faire défiler, et les colonnes « Taux » et « Matériel » restaient
   invisibles. Sous 860 px, le tableau devient une carte par ligne (même mécanisme que .hl-table, déjà en place sur les pages
   V3 : pages-v3.css) : chaque <tr> est une carte, chaque <td> affiche son étiquette (attribut data-label, déjà posé par
   outils/homelab-pages.mjs, tableauForfaits()) au-dessus de sa valeur. Plus de défilement horizontal à deviner. */
@media (max-width:860px){
  html[data-theme] .v4-tabl{overflow:visible;border:0;border-radius:0}
  html[data-theme] .v4-tabl table{min-width:0}
  html[data-theme] .v4-tabl,html[data-theme] .v4-tabl table,html[data-theme] .v4-tabl tbody,html[data-theme] .v4-tabl tr,html[data-theme] .v4-tabl th,html[data-theme] .v4-tabl td{display:block;width:100%}
  html[data-theme] .v4-tabl thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  html[data-theme] .v4-tabl tbody tr{margin:0 0 16px;border:1px solid var(--trait);border-radius:14px;overflow:hidden}
  html[data-theme] .v4-tabl tbody tr:last-child{margin-bottom:0}
  html[data-theme] .v4-tabl tbody th{border-bottom:1px solid var(--trait);background:var(--fond2);white-space:normal}
  html[data-theme] .v4-tabl td{display:grid;grid-template-columns:minmax(96px,34%) minmax(0,1fr);gap:10px;border-bottom:1px solid var(--trait);white-space:normal!important}
  /* CORRECTION mission 32, lot A (contrôleur, 1er passage sur le fait A8) : une étiquette d'un seul mot plus longue que
     96 px (ex. « TÉLÉCHARGEMENT », 12 px majuscules, espacement .06em) n'a aucun espace où se couper — sans
     overflow-wrap, elle débordait visuellement sur la valeur voisine au lieu de passer à la ligne dans sa propre
     colonne (trouvé sur /facturier-en-ligne, 390 px : « TÉLÉCHARGEMENTSur l'ordinateur… », collé). */
  html[data-theme] .v4-tabl td::before{content:attr(data-label);font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--or-texte);overflow-wrap:anywhere;min-width:0}
  html[data-theme] .v4-tabl td:empty,html[data-theme] .v4-tabl td:last-child:has(>span[aria-hidden]){display:none}
}

/* v4-faq : <details class="v4-faq"><summary>…</summary><p>…</p></details> pour une question fréquente, marqueur
   natif remplacé par un signe +/- en jeton --or-texte. */
html[data-theme] .v4-faq{border-bottom:1px solid var(--trait);padding-block:14px}
html[data-theme] .v4-faq summary{cursor:pointer;font-weight:700;font-size:16px;color:var(--texte);list-style:none}
html[data-theme] .v4-faq summary::-webkit-details-marker{display:none}
html[data-theme] .v4-faq summary::before{content:'+';display:inline-block;width:1em;margin-right:8px;color:var(--or-texte);font-weight:700}
html[data-theme] .v4-faq[open] summary::before{content:'–'}
html[data-theme] .v4-faq p{color:var(--texte2);font-size:15px;max-width:68ch;margin:10px 0 0}

/* v4-bande : modificateur de .v4-sec pour la bande finale (centrée, pleine largeur, fond alterné, un bouton au
   moins) : combiner avec .v4-sec, jamais seul (<section class="v4-sec v4-bande"><div class="v4-wrap">…</div></section>).
   Pas de trait de bas de section : c'est presque toujours le dernier bloc d'une page, juste avant le pied. */
html[data-theme] .v4-sec.v4-bande{background:var(--fond2);text-align:center;border-bottom:0}
html[data-theme] .v4-bande .v4-wrap{display:grid;gap:16px;justify-items:center}
html[data-theme] .v4-bande .v4-wrap>*{max-width:60ch}
html[data-theme] .v4-bande .v4-btns{max-width:none;justify-content:center}

/* v4-vedette : modificateur de .v4-carte pour la carte vedette du Facturier sur le Catalogue (plus grande que les
   cartes .v4-produit, deux colonnes texte/image dès 900 px), même principe que .v4-deux.v4-rdv ci-dessus. */
html[data-theme] .v4-carte.v4-vedette{display:grid;grid-template-columns:1.1fr .9fr;align-items:center;gap:clamp(24px,4vw,40px);padding:clamp(24px,4vw,40px)}
/* Mission 31-BIS, lot B : min-width:0 sur les deux pistes de la grille — sans lui, l'image (1200 px d'origine) pousse sa
   piste au-delà de la largeur disponible à 390 px (même piège que .v4-wrap:has(.v4-tabl) plus haut dans ce fichier). */
html[data-theme] .v4-carte.v4-vedette>*{min-width:0}
html[data-theme] .v4-vedette img{width:100%;height:auto;border-radius:14px;display:block}
@media (max-width:900px){html[data-theme] .v4-carte.v4-vedette{grid-template-columns:1fr}}

/* v4-produit : modificateur de .v4-carte pour une fiche d'outil du Catalogue, posée sur un <article> (jamais un
   <a> englobant, fait 179e) : l'image est SON PROPRE lien, le <h3> contient SON PROPRE lien, un bouton plus bas
   est ENCORE un autre lien — trois liens distincts, jamais imbriqués. */
html[data-theme] .v4-carte.v4-produit{display:grid;gap:10px}
/* CORRECTION mission 31-BIS, lot B (fait 191 f, retour de qa-site après ce lot) : les visuels de produit (800×450,
   16:9) sont légèrement rognés par object-fit:cover dans ce cadre 16:10 — contain, fond --surface déjà posé plus
   loin (même traitement que .v4-photo, .acc-offre-cadre, .acc-preuve-img-sm plus haut dans ce fichier). */
html[data-theme] .v4-produit>a:has(img),html[data-theme] .v4-produit img{width:100%;height:auto;aspect-ratio:16/10;object-fit:contain;background:var(--surface);border-radius:10px;display:block}
html[data-theme] .v4-produit h3{font-family:var(--serif-titres);font-size:20px;font-weight:600;margin:0}
html[data-theme] .v4-produit h3 a{color:inherit;text-decoration:none}
/* Mission 31-BIS, lot B (fait 197 a) : :first-child ne marche plus pour cibler le lien de l'image — le texte passe
   maintenant AVANT elle dans la carte (191 g). :has(img) cible le bon lien quelle que soit sa position. */
html[data-theme] .v4-produit>a:has(img):hover,html[data-theme] .v4-produit h3 a:hover{text-decoration:underline;text-decoration-color:var(--or);text-underline-offset:5px}
/* CORRECTION mission 31-BIS, lot B (retour du contrôleur, deuxième passage) : :not(.v4-note) ajouté — même spécificité
   (0,1,1) que « .v4-note,p.v4-note » de plus haut dans ce fichier, mais posée plus tard dans la cascade, cette règle
   gagnait et ramenait à 0 la marge des mentions 11 px coupées en plusieurs <p> (fait 191 c, outils-catalogue-donnees.mjs,
   fonction noteHtml) : plusieurs mentions empilées d'une même fiche de produit ne laissaient alors que 0-2 px entre
   elles (mesuré), un seul pavé visuel malgré la coupure en paragraphes séparés. Même mécanisme déjà trouvé plusieurs
   fois dans ce lot (.v4-tete p, .acc-bandeau-txt p, .acc-offre p). */
html[data-theme] .v4-produit p:not(.v4-note){color:var(--texte2);font-size:15px;margin:0}

/* v4-schema : <details class="v4-schema"><summary class="bouton contour">Voir le schéma réseau</summary>
   <figure>…</figure></details> — seul le marqueur natif est retiré ici; le chargement différé de l'image tient à
   loading="lazy" sur l'<img> ET au fait qu'elle est hors du flux visible tant que <details> est fermé (vérifié :
   aucune requête réseau pour cette image avant l'ouverture, dans un navigateur réel). */
html[data-theme] .v4-schema{margin-top:10px}
html[data-theme] .v4-schema summary{list-style:none;display:inline-block}
html[data-theme] .v4-schema summary::-webkit-details-marker{display:none}
html[data-theme] .v4-schema[open] summary{margin-bottom:14px}
html[data-theme] .v4-schema figure{margin:0;display:grid;gap:8px}
html[data-theme] .v4-schema img{width:100%;height:auto;border-radius:14px;border:1px solid var(--trait);background:var(--surface);display:block}

/* CORRECTION mission 31-BIS, lot C (fait 193) : la vidéo n'est plus jouée en pleine page — une vignette cliquable
   (image d'affiche, icône de lecture, durée en coin) ouvre une fenêtre légère par-dessus la page (gabarit repris du
   mécanisme déjà en service sur l'accueil pour les cartes Preuves, outils/v2-donnees.mjs, VIDEO_MODAL_HTML/SCRIPT,
   renommé en v4-fenetre-* ici pour rester dans le système V4 sans tirer toute l'ancienne mécanique V2). Posé par
   outils/video-bloc.mjs ; script : site/commun/video-fenetre.js, chargé SEULEMENT sur les pages qui ont une vidéo. */
/* Mission 32, lot A (fait A7, ordre du 6 novembre 2026, gabarit .vig de maquettes/v4/automatisation-v4.html) : la vignette n'est
   plus un grand bloc pleine largeur — une vignette de 220 px (16:9, affiche, bouton rond doré de lecture, ombre) à gauche, le
   texte à droite; la fenêtre légère, elle, garde sa grande taille (.v4-fenetre-video, inchangée). Jetons du site à la place de
   ceux de la maquette : var(--marine) → var(--fond2) (fond de la vignette avant chargement de l'image, même rôle que l'ancien
   .v4-video-decl); var(--or) existe déjà sous ce nom; triangle de lecture : var(--panneau) (déjà le ton sombre du bouton doré
   ailleurs sur le site) plutôt que le gris foncé codé en dur de la maquette. Sur téléphone (≤900px, même seuil que .v4-tete-vis
   ci-dessus) : la vignette passe sous le texte, pleine largeur, 320 px au plus (fait A7). */
html[data-theme] .v4-video{margin:0;display:grid;grid-template-columns:220px 1fr;gap:18px;align-items:center}
html[data-theme] .v4-video-decl{position:relative;display:block;width:220px;flex:none;aspect-ratio:16/9;border-radius:10px;overflow:hidden;border:0;padding:0;background:var(--fond2);cursor:pointer;box-shadow:0 10px 24px rgba(0,0,0,.18)}
html[data-theme] .v4-video-decl img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;opacity:.85}
html[data-theme] .v4-video-jouer{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:44px;height:44px;border-radius:50%;background:var(--or);display:flex;align-items:center;justify-content:center;box-shadow:0 4px 14px rgba(0,0,0,.3);transition:transform .2s}
html[data-theme] .v4-video-jouer svg{width:20px;height:20px;margin-left:2px;color:var(--panneau)}
html[data-theme] .v4-video-decl:hover .v4-video-jouer,html[data-theme] .v4-video-decl:focus-visible .v4-video-jouer{transform:translate(-50%,-50%) scale(1.08)}
html[data-theme] .v4-video-duree{position:absolute;right:8px;bottom:8px;padding:2px 6px;border-radius:5px;background:rgba(0,0,0,.7);color:#fff;font-size:11px;font-weight:600;letter-spacing:.02em}
html[data-theme] .v4-video-txt{margin:0;color:var(--texte2);font-size:15px;line-height:1.5}
@media (max-width:900px){
  html[data-theme] .v4-video{grid-template-columns:1fr}
  html[data-theme] .v4-video-decl{width:100%;max-width:320px}
}

/* v4-fenetre : fenêtre légère générique (fond assombri, clic dehors ferme, croix, Échap) — gabarit partagé par la
   vidéo (fait 193, .v4-fenetre-video) et les aperçus de projets (fait 194, .v4-fenetre-apercu, site/commun/v4.css
   plus bas où .v4-apercu-btn est défini). Chaque variante fixe seulement la taille et le fond de .v4-fenetre-boite.
   z-index:100005 — CORRECTION mission 31-BIS, lot C : 200 (premier essai) restait SOUS le bouton de menu mobile
   (#drakMenuBtn, site/commun/barre.css, z-index:1200) et son panneau (#drakMenu, z-index:1190) : à 390 px, la croix
   de fermeture de la fenêtre se faisait recouvrir par le bouton hamburger, invisible et impossible à cliquer (trouvé
   au navigateur réel, elementFromPoint renvoyait #drakMenuBtn à l'endroit même de la croix). Porté au-dessus de tout
   le site, dans le même ordre de grandeur que l'autre vrai plein écran déjà en place (.drkf-voile/.jmodal,
   site/commun/formulaires.css, 100000/100001), avec une valeur propre pour ne jamais coïncider exactement. */
html[data-theme] .v4-fenetre{position:fixed;inset:0;z-index:100005;display:flex;align-items:center;justify-content:center;padding:20px}
html[data-theme] .v4-fenetre[hidden]{display:none}
html[data-theme] .v4-fenetre-fond{position:absolute;inset:0;background:rgba(6,10,22,.82);opacity:0;animation:v4FenetreFondu .2s ease forwards}
html[data-theme] .v4-fenetre-boite{position:relative;z-index:1;opacity:0;animation:v4FenetreFondu .2s ease forwards}
html[data-theme] .v4-fenetre-fermer{position:absolute;top:10px;right:10px;z-index:2;width:38px;height:38px;display:flex;align-items:center;justify-content:center;border:0;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;cursor:pointer}
html[data-theme] .v4-fenetre-fermer svg{width:20px;height:20px}
html[data-theme] .v4-fenetre-fermer:hover,html[data-theme] .v4-fenetre-fermer:focus-visible{background:rgba(0,0,0,.8)}
@keyframes v4FenetreFondu{from{opacity:0}to{opacity:1}}
@media(prefers-reduced-motion:reduce){html[data-theme] .v4-fenetre-fond,html[data-theme] .v4-fenetre-boite{animation:none;opacity:1}}

html[data-theme] .v4-fenetre-video .v4-fenetre-boite{width:100%;max-width:960px;background:#000;border-radius:14px;overflow:hidden}
html[data-theme] .v4-fenetre-video video{display:block;width:100%;aspect-ratio:16/9;background:#000}
@media (max-width:640px){
  html[data-theme] .v4-fenetre-video{padding:0}
  html[data-theme] .v4-fenetre-video .v4-fenetre-boite{max-width:none;border-radius:0;height:100%;display:flex;align-items:center}
}

/* CORRECTION mission 31-BIS, lot C (fait 194) : variante de .v4-fenetre pour les aperçus de projets (/projets) — même
   gabarit que la vidéo (fait 193 ci-dessus), mais 90 % de la fenêtre plutôt qu'une boîte à largeur fixe, puisque le
   contenu est un site complet dans un cadre sandboxé, pas une vidéo 16:9. Posé par outils/projets-donnees.mjs,
   comportement par site/commun/apercus.js (réécrit pour ce lot). */
html[data-theme] .v4-fenetre-apercu .v4-fenetre-boite{width:90vw;height:90vh;max-width:1200px;background:var(--fond);border-radius:14px;overflow:hidden}
html[data-theme] .v4-fenetre-apercu .v4-fenetre-cadre{width:100%;height:100%;overflow:auto}
html[data-theme] .v4-fenetre-apercu iframe{display:block;width:100%;height:100%;border:0}
@media (max-width:640px){
  html[data-theme] .v4-fenetre-apercu{padding:0}
  html[data-theme] .v4-fenetre-apercu .v4-fenetre-boite{width:100vw;height:100vh;max-width:none;border-radius:0}
}

/* v4-demo : démonstration cliquable de la Maison 3D connectée (image, puis une colonne de boutons .v4-cmd); sous
   900 px, les boutons passent SOUS l'image (jamais à côté) et deviennent une rangée qui défile horizontalement. */
html[data-theme] .v4-demo{display:grid;grid-template-columns:1.4fr .6fr;gap:24px;align-items:start}
html[data-theme] .v4-demo img{width:100%;height:auto;border-radius:14px;border:1px solid var(--trait);display:block}
html[data-theme] .v4-demo .v4-cmd{display:grid;gap:8px;align-content:start}
html[data-theme] .v4-demo .v4-cmd button{font:inherit;text-align:left;border:1px solid var(--trait);background:var(--surface);color:var(--texte);border-radius:10px;padding:10px 14px;cursor:pointer}
html[data-theme] .v4-demo .v4-cmd button:hover,html[data-theme] .v4-demo .v4-cmd button:focus-visible{border-color:var(--or)}
@media (max-width:900px){
  html[data-theme] .v4-demo{grid-template-columns:1fr}
  html[data-theme] .v4-demo .v4-cmd{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px}
  html[data-theme] .v4-demo .v4-cmd button{flex:0 0 auto}
}

/* v4-qr : grille de blocs question/réponse de l'Entrevue, un <article> par question (titre de question en <h3>,
   gras par l'habillage de carte habituel, réponse en <p> dessous). */
html[data-theme] .v4-qr{display:grid;gap:14px}
html[data-theme] .v4-qr article{background:var(--surface);border:1px solid var(--trait);border-radius:14px;padding:18px;display:grid;gap:6px}
html[data-theme] .v4-qr h3{font-family:var(--serif-titres);font-size:20px;font-weight:600;margin:0}
html[data-theme] .v4-qr p{color:var(--texte2);font-size:15px;max-width:70ch;margin:0}

/* v4-form-deux : section Devis de /homelab (et toute page sœur). Mission 31-BIS, lot A (fait 198) : REFONTE DE LA MISE EN PAGE,
   AUCUN CHAMP CHANGÉ. Avant cette mission : deux colonnes au niveau de la section (texte/rendez-vous à gauche, formulaire à droite),
   ce qui laissait une grande colonne gauche vide sous le texte, court, et serrait le formulaire dans une demi-largeur. Maintenant :
   le texte et le bouton de rendez-vous occupent toute la largeur de la section (une seule colonne), le formulaire suit dessous, lui
   aussi sur toute la largeur. « Grille de deux colonnes sur ordinateur, une sur téléphone » (fait 198) : déjà portée par les paires
   de champs que le formulaire regroupe lui-même dans des <div class="deux"> (prénom et nom/courriel, vous êtes/pays, municipalité/
   format, voir formulaires-donnees.mjs et homelab-pages.mjs) — plutôt que forcer une SECONDE grille à deux colonnes au niveau du
   <form> entier, qui aurait emboîté ces paires déjà à deux colonnes dans une moitié de la largeur et les aurait resserrées à
   l'excès. Le <form> reste donc une seule colonne pleine largeur ; chaque <div class="deux"> garde sa propre grille à deux
   colonnes (site.css, .deux), maintenant à pleine largeur du formulaire. */
html[data-theme] .v4-form-deux{display:grid;gap:28px}
html[data-theme] .v4-form-deux form{display:grid;gap:14px;max-width:760px}
/* :not(.hl-case) : les cases à cocher (Usages, consentement) gardent leur propre disposition en ligne (case puis texte, voir
   .hl-case plus bas dans pages-v3.css) — sans cette exclusion, cette règle plus spécifique les empilait au lieu de les aligner. */
html[data-theme] .v4-form-deux form label:not(.hl-case){display:grid;gap:4px;font-size:14px;font-weight:600;color:var(--texte)}
html[data-theme] .v4-form-deux form input,html[data-theme] .v4-form-deux form select,html[data-theme] .v4-form-deux form textarea{font:inherit;padding:10px 12px;border:1px solid var(--trait);border-radius:10px;background:var(--surface);color:var(--texte)}
html[data-theme] .v4-form-deux form .large{grid-column:1/-1}
/* Bouton d'envoi : 320 px sur ordinateur, pleine largeur sur téléphone, 32 px sous le dernier champ (fait 198). */
html[data-theme] .v4-form-deux form button[type="submit"]{justify-self:start;width:320px;max-width:100%;margin-top:32px}
@media (max-width:900px){
  html[data-theme] .v4-form-deux form button[type="submit"]{width:100%;justify-self:stretch}
}
