@charset "UTF-8";
/* ==========================================================================
   Domaine de Soursac — feuille de style principale
   Quimiac · Mesquer (Loire-Atlantique)

   Sommaire
   00. Polices (@font-face — anciennement fonts.css)
   01. Jetons de design (couleurs, typographie, espacements)
   02. Réinitialisation et bases
   03. Typographie
   04. Mise en page (conteneurs, grilles, sections)
   05. En-tête et navigation
   06. Bannière (hero)
   07. Bandeau « en direct »
   08. Composants (cartes, boutons, figures, listes, encarts)
   09. Galeries et visionneuse
   10. Pied de page
   11. Mouvement et révélations
   12. Accessibilité, impression, préférences système
   ========================================================================== */

/* ==========================================================================
   00. POLICES
   Anciennement assets/css/fonts.css, fusionné ici : une requête de moins par page,
   sur dix-sept pages. ⚠️ Le gain de VITESSE est nul — les unicode-range faisaient
   déjà le tri, seuls ~84 Ko de woff2 étaient téléchargés par page. Ce qui change,
   c'est le poids de déploiement et le nombre de fichiers à tenir à jour.

   Huit des douze @font-face d'origine ont été retirés — la fiche du lot en annonçait
   neuf ; le neuvième aurait été une troisième graisse de Cormorant, or il n'y en a
   que deux d'inutilisées. Le détail de la vérification est dans le rapport :
   — les six variantes « latin-ext ». Vérifié sur l'ensemble du site (17 pages, 5
     pages PHP, JS, JSON, sources) : 35 codepoints non-ASCII distincts, dont un seul,
     U+0153 « œ », appartient à la plage latin-ext — et il appartient aussi à la
     plage latin, qui est déclarée après et l'emporte donc. Aucun caractère du site
     n'a besoin de latin-ext ;
   — Cormorant Garamond 600 et 700, qu'aucune règle de cette feuille n'appelle :
     --font-display n'est employé qu'en graisse 400 (compteurs de .steps, blockquote
     en italique) et 500 (h1 à h4, .weather-hero__temp, .team__name, .stats__val).
   Restent quatre déclarations, toutes réellement servies. Les fichiers .woff2
   correspondants sont hors du périmètre de ce lot : voir le rapport LOT-04.
   ========================================================================== */

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/cormorant-garamond-400-italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/cormorant-garamond-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/cormorant-garamond-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/inter-300-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   01. JETONS DE DESIGN
   ========================================================================== */

:root {
  /* --- Couleurs : mode clair -------------------------------------------- */
  --paper:        #FCFBF8;   /* fond général — blanc chaud, papier */
  --paper-2:      #F4F1EA;   /* fond alterné — sable */
  --paper-3:      #EDE9DF;   /* fond appuyé */
  --surface:      #FFFFFF;   /* cartes */
  --ink:          #16211D;   /* texte principal */
  --ink-2:        #4A5854;   /* texte secondaire — 7.2:1 sur --paper */
  /* Les quatre jetons ci-dessous portent les libellés les plus petits du site et
     échouaient tous les quatre au seuil AA en thème clair (contrat §4).
     ⚠ --line : le contrat annonce #C6C0B2 « 3,02:1 ». La mesure donne 1,81:1 sur
     --surface. La valeur retenue est celle qui atteint réellement le 3,02:1 annoncé
     — seule façon de passer le contrôle 8 de la porte. Écart documenté dans
     _remediation/rapports/LOT-04.md. */
  --ink-3:        #59665F;   /* texte tertiaire — 5,81:1 sur --paper (était #6E7C77 : 4,22:1) */
  --line:         #999489;   /* filets — 3,02:1 sur --surface (était #E2DED4 : 1,34:1) */
  --line-2:       #8A8579;   /* filets appuyés — 3,68:1 sur --surface (était #C9C3B4 : 1,76:1) */

  --pine:         #1F4034;   /* vert pin — couleur principale */
  --pine-2:       #2C5748;   /* vert pin clair — survol */
  --pine-wash:    #EAF0EC;   /* aplat vert très clair */
  --brass:        #7E5F2E;   /* laiton — 5,69:1 sur --paper, 5,22:1 sur --paper-2
                                (était #8A6A38 : 4,43:1 sur sable, sous le seuil AA) */
  --brass-2:      #A9834B;   /* laiton décoratif (filets, puces) */
  --sea:          #2F5D6E;   /* bleu marine atlantique — données en direct */
  --sea-wash:     #EAF1F4;

  --brass-light:  #D8BE8C;   /* laiton clair — libellés sur fond sombre */

  /* Aplats vert profond — volontairement identiques dans les deux thèmes.
     Contrairement à --pine (qui s'éclaircit en thème sombre pour rester
     lisible EN TANT QUE TEXTE), ceux-ci servent de FOND : les inverser
     donnerait un grand pavé vert clair qui jure avec le reste. */
  --pine-solid:   #1F4034;
  --on-pine:      #F6F4EE;

  /* Texte posé SUR --pine (bouton plein). Suit le thème, comme --pine. */
  --on-primary:   #F6F4EE;
  --on-dark:      #F6F4EE;
  --on-dark-2:    #C2CCC7;
  --danger:       #A3341F;
  --danger-wash:  #F6EDE9;   /* repli simple du mélange « --danger 7 % sur --paper »,
                                pour les navigateurs sans mélange de couleurs */

  /* Anneau de focus « double », volontairement identique dans les deux thèmes :
     un anneau sombre doublé d'un liseré clair. Sur n'importe quel fond — papier,
     aplat vert, photographie — l'un des deux contraste toujours. --brass seul ne
     donnait que 2,28:1 sur --pine-solid ; le liseré clair y atteint 10,37:1, et les
     deux anneaux contrastent entre eux à 15,04:1. */
  --focus-ring:   #16211D;
  --focus-halo:   #F6F4EE;

  --overlay:      rgba(14, 24, 20, .55);
  --shadow-sm:    0 1px 2px rgba(22, 33, 29, .05);
  --shadow-md:    0 6px 24px -12px rgba(22, 33, 29, .28);
  --shadow-lg:    0 24px 60px -28px rgba(22, 33, 29, .38);

  /* --- Typographie ------------------------------------------------------- */
  --font-display: 'Cormorant Garamond', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --font-body:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* Trois crans relevés (contrat §5). Le corps du texte était correct ; ce qui
     porte l'information ne l'était pas : navigation, boutons, messages d'erreur et
     libellé « Mot de passe » à 13-14 px ; aides, métadonnées, 103 sur-titres et fil
     d'Ariane à 11-12 px. Le public du site a 60 à 85 ans. Les crans 1 à 5 ne bougent
     pas : le rapport entre crans est conservé, et --prose à 68ch fait encore ~600 px
     à 18 px. */
  --step--2: clamp(.8125rem, .78rem + .16vw, .875rem);     /* 13 → 14 */
  --step--1: clamp(.9375rem, .91rem + .14vw, 1rem);        /* 15 → 16 */
  --step-0:  clamp(1.0625rem, 1.03rem + .16vw, 1.125rem);  /* 17 → 18 */
  --step-1:  clamp(1.125rem, 1.07rem + .26vw, 1.25rem);    /* 18 → 20 */
  --step-2:  clamp(1.375rem, 1.28rem + .48vw, 1.625rem);   /* 22 → 26 */
  --step-3:  clamp(1.75rem, 1.55rem + .95vw, 2.25rem);     /* 28 → 36 */
  --step-4:  clamp(2.125rem, 1.75rem + 1.85vw, 3.125rem);  /* 34 → 50 */
  --step-5:  clamp(2.75rem, 2.05rem + 3.4vw, 4.5rem);      /* 44 → 72 */

  --tracking-eyebrow: .18em;
  --tracking-tight: -.015em;

  /* --- Espacements (base 8) ---------------------------------------------- */
  --sp-1: .25rem;  --sp-2: .5rem;   --sp-3: .75rem;  --sp-4: 1rem;
  --sp-5: 1.5rem;  --sp-6: 2rem;    --sp-7: 3rem;    --sp-8: 4rem;
  --sp-9: 6rem;    --sp-10: 8rem;

  --section-y: clamp(3.5rem, 2.2rem + 6.2vw, 7rem);

  /* --- Formes et mouvement ----------------------------------------------- */
  --radius: 2px;
  --radius-lg: 4px;
  --radius-pill: 999px;
  --ease: cubic-bezier(.22, .61, .36, 1);
  --dur-fast: 160ms;
  --dur: 260ms;
  --dur-slow: 620ms;

  /* --- Gabarits ----------------------------------------------------------- */
  --container: 1200px;
  --container-wide: 1440px;
  --prose: 68ch;

  /* La mesure de l'espace privé — et la SEULE largeur qu'on y déclare.
     704 px à la taille de police par défaut. Six largeurs cohabitaient sur
     gestion.php à 1280 px (704 · 742 · 827 · 640 · 720 · 1120) : deux seulement
     étaient des décisions, les autres étaient plaquées à la main ou prises par
     un tableau qui se dimensionnait tout seul. Un formulaire se LIT avant de se
     remplir — libellé, aide, message d'erreur sont de la prose — il appartient
     donc à la colonne de lecture, pas à la colonne de données.
     Écrite en rem et non en px : la mesure suit alors la taille de police par
     défaut du navigateur, réglage fréquent après soixante ans et que le zoom ne
     couvre pas. C'est le raisonnement déjà tenu quatre lignes plus bas pour
     --header-h.
     Un tableau, lui, ne reçoit AUCUNE largeur : il prend celle du conteneur,
     qui n'est pas un choix mais l'absence de choix. Il n'y a donc pas de
     seconde valeur à retenir, ni de troisième à inventer. */
  --measure: 44rem;
  --gutter: clamp(1.25rem, .8rem + 2.2vw, 2.5rem);
  /* en rem : l'en-tête suit alors la taille de police par défaut du navigateur,
     réglage fréquent après 60 ans et que le zoom, lui, ne couvre pas */
  /* La cible tactile minimale. Ce n'est pas une nouveauté : la ligne 1969 de ce
     fichier écrit déjà 44px avec le commentaire « 40 px était sous la cible
     tactile minimale ». La règle était connue, énoncée, puis contournée là où
     elle comptait — 86 contrôles sous la barre sur gestion.php à 375 px. Le
     jeton lui donne un nom, et un seul endroit où la changer. */
  --touch: 44px;

  /* Hauteur DÉCLARÉE, et non plus mesurée. Elle valait 40px « repli grand
     écran », corrigée par soursac.js une fois exécuté : sur téléphone la vraie
     hauteur était 72, donc le contenu démarrait 32 px trop haut, SOUS la barre,
     puis sautait — au chargement, et de nouveau à document.fonts.ready.
     La barre fait désormais une ligne et une seule au-dessus de 620 px par
     construction (voir P8), et passe EN BAS en dessous, où sa hauteur n'a plus
     à être connue de personne. */
  --header-h: 4.75rem;

  color-scheme: light;
}

/* --- Couleurs : mode sombre (suit le réglage du système) ------------------ */
/* ⚠ LA PALETTE SOMBRE EST DÉCLARÉE DEUX FOIS, avec les mêmes 26 jetons :
   — ici, sous @media (prefers-color-scheme: dark) : le mode sombre AUTOMATIQUE ;
   — une trentaine de lignes plus bas, sous :root[data-theme="dark"] : la bascule
     MANUELLE.
   Les deux blocs sont nécessaires — une requête média ne peut pas être annulée par
   un attribut, et l'attribut doit pouvoir forcer le sombre sur un système réglé en
   clair. TOUTE MODIFICATION D'UN JETON DOIT ÊTRE PORTÉE AUX DEUX ENDROITS : sinon
   les deux thèmes sombres divergent, et l'écart n'est visible que chez celui qui a
   fait l'autre choix. L'ordre compte aussi — le bloc manuel vient après, c'est ce
   qui lui donne le dernier mot. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:     #101613;
    --paper-2:   #151D19;
    --paper-3:   #1B241F;
    --surface:   #18211D;
    --ink:       #EDEAE2;
    --ink-2:     #A3AFA9;   /* 8.2:1 sur --paper */
    --ink-3:     #8B9A94;
    --line:      #5F6D66;   /* 3,03:1 sur --surface (était #29332E : 1,26:1) */
    --line-2:    #6A756F;   /* 3,44:1 sur --surface (était #3A453F : 1,65:1) */

    --pine:      #8FC0AA;
    --pine-2:    #A8D2BF;
    --pine-wash: #17231E;
    --brass:     #C9A46A;
    --brass-2:   #B08F58;
    --sea:       #86B6C8;
    --sea-wash:  #14201F;

    --brass-light: #D8BE8C;
    --on-primary: #0F1714;
    --on-dark:   #F6F4EE;
    --on-dark-2: #C2CCC7;
    --danger:    #E0846E;
    --danger-wash: #1F1E19;

    --overlay:   rgba(6, 11, 9, .6);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-md: 0 6px 24px -12px rgba(0, 0, 0, .6);
    --shadow-lg: 0 24px 60px -28px rgba(0, 0, 0, .75);

    color-scheme: dark;
  }
}

/* --- Bascule manuelle ----------------------------------------------------- */
/* ⚠ SECOND EXEMPLAIRE de la palette sombre — voir la note au-dessus du bloc
   @media (prefers-color-scheme: dark). Les 26 jetons doivent rester identiques aux
   siens, valeur par valeur. */
:root[data-theme="dark"] {
  --paper:     #101613;
  --paper-2:   #151D19;
  --paper-3:   #1B241F;
  --surface:   #18211D;
  --ink:       #EDEAE2;
  --ink-2:     #A3AFA9;
  --ink-3:     #8B9A94;
  --line:      #5F6D66;   /* 3,03:1 sur --surface (était #29332E : 1,26:1) */
  --line-2:    #6A756F;   /* 3,44:1 sur --surface (était #3A453F : 1,65:1) */
  --pine:      #8FC0AA;
  --pine-2:    #A8D2BF;
  --pine-wash: #17231E;
  --brass:     #C9A46A;
  --brass-2:   #B08F58;
  --sea:       #86B6C8;
  --sea-wash:  #14201F;
  --brass-light: #D8BE8C;
  --on-primary: #0F1714;
  --on-dark:   #F6F4EE;
  --on-dark-2: #C2CCC7;
  --danger:    #E0846E;
  --danger-wash: #1F1E19;
  --overlay:   rgba(6, 11, 9, .6);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-md: 0 6px 24px -12px rgba(0, 0, 0, .6);
  --shadow-lg: 0 24px 60px -28px rgba(0, 0, 0, .75);
  color-scheme: dark;
}

/* ==========================================================================
   02. RÉINITIALISATION ET BASES
   ========================================================================== */

/* Note « vieux Safari », valable pour toute la feuille : la propriété raccourcie
   « inset » n'existe qu'à partir de Safari 14.1. Le parc visé comprend des iPad
   restés en 14.0. Partout où elle sert, les quatre longhands la précèdent — même
   parade que pour les couleurs mélangées et les hauteurs dynamiques : la valeur
   ancienne d'abord, la moderne
   ensuite, qui l'écrase là où elle est comprise. Sans cela, le voile de la bannière
   ne couvre plus la photo (titre blanc sur ciel clair), la visionneuse ne couvre
   plus l'écran, et les zones cliquables étendues des cartes disparaissent. */

*, *::before, *::after { box-sizing: border-box; }

/* L'attribut hidden doit l'emporter sur nos propres display. La feuille du
   navigateur pose bien [hidden]{display:none}, mais toute déclaration d'auteur
   l'écrase : sans cette règle, soursac.js masque en vain le panneau de
   consentement (.embed__consent est en display:flex) et celui-ci reste affiché
   au-dessus du cadre chargé, sur les onze cadres du site. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  /* overflow-x: hidden transforme tout débordement en contenu perdu, inatteignable
     même au clavier : un mot trop long doit se couper plutôt que sortir du cadre. */
  overflow-wrap: break-word;
}

img, svg, video, iframe { display: block; max-width: 100%; }
img, video { height: auto; }
/* <picture> n'est qu'un conteneur de sélection de source : la boîte inline qu'il
   crée casserait les hauteurs de figure dès que le <img> y est enveloppé (LOT-01). */
picture { display: contents; }

button, input, select, textarea { font: inherit; color: inherit; }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: .22em; }

/* Anneau double : l'outline sombre porte à l'extérieur, le box-shadow clair comble
   l'écart de 3 px et sert de liseré. 3 px et non 2 : à 200 % de grossissement, 2 px
   se repère mal. */
:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--focus-halo);
  border-radius: 1px;
}

::selection { background: var(--pine-solid); color: var(--on-pine); }

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--sp-7) 0;
}

/* ==========================================================================
   03. TYPOGRAPHIE
   ========================================================================== */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
  margin: 0 0 var(--sp-4);
  text-wrap: balance;
}

h1 { font-size: var(--step-5); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); line-height: 1.22; }
h4 {
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: 0;
  line-height: 1.4;
}

p { margin: 0 0 var(--sp-4); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

strong { font-weight: 600; }

small { font-size: var(--step--1); }

.eyebrow {
  display: block;
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--brass);
  margin: 0 0 var(--sp-4);
}
.eyebrow--light { color: var(--brass-light); text-shadow: 0 1px 12px rgba(8, 15, 12, .5); }

.lead {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink-2);
}

.subtle { color: var(--ink-2); }
.tiny { font-size: var(--step--1); color: var(--ink-2); }

.prose { max-width: var(--prose); }
.prose h3 { margin-top: var(--sp-6); }
.prose ul, .prose ol { padding-left: 1.15em; margin: 0 0 var(--sp-4); }
.prose li { margin-bottom: var(--sp-2); }
.prose li::marker { color: var(--brass-2); }
.prose a { color: var(--pine); text-decoration-color: var(--line-2); transition: text-decoration-color var(--dur-fast) var(--ease); }
.prose a:hover { text-decoration-color: currentColor; }

blockquote {
  margin: var(--sp-6) 0;
  padding: 0 0 0 var(--sp-5);
  border-left: 2px solid var(--brass-2);
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.4;
  font-style: italic;
  color: var(--ink);
}
blockquote cite {
  display: block;
  margin-top: var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-style: normal;
  letter-spacing: .04em;
  color: var(--ink-2);
}

/* Filet ornemental court, sous les titres de section */
.rule {
  width: 44px;
  height: 1px;
  background: var(--brass-2);
  border: 0;
  margin: 0 0 var(--sp-5);
}
.rule--center { margin-inline: auto; }

/* ==========================================================================
   04. MISE EN PAGE
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--wide { max-width: var(--container-wide); }
.container--narrow { max-width: 860px; }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: calc(var(--section-y) * .62); }
.section--sand { background: var(--paper-2); }
.section--pine { background: var(--pine-solid); color: var(--on-pine); }
.section--pine h2, .section--pine h3 { color: var(--on-dark); }
.section--pine .subtle, .section--pine .lead { color: var(--on-dark-2); }
.section--line-top { border-top: 1px solid var(--line); }

.section-head { margin-bottom: var(--sp-7); max-width: 62ch; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head p:last-child { margin-bottom: 0; }

.grid { display: grid; gap: var(--sp-6); }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Deux colonnes éditoriales asymétriques */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 1rem + 4vw, 5rem);
  align-items: center;
}
.split--wide-left { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
.split--wide-right { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); }
.split--top { align-items: start; }

@media (max-width: 900px) {
  .grid--3, .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .split, .split--wide-left, .split--wide-right { grid-template-columns: minmax(0, 1fr); }
  .split__media--first { order: -1; }
}
@media (max-width: 620px) {
  .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
}

.stack > * + * { margin-top: var(--sp-4); }
.stack-lg > * + * { margin-top: var(--sp-6); }

.center { text-align: center; }

/* Lien d'évitement */
.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: -100px;
  z-index: 200;
  padding: var(--sp-3) var(--sp-5);
  background: var(--pine-solid);
  color: var(--on-pine);
  font-size: var(--step--1);
  font-weight: 500;
  border-radius: var(--radius);
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--sp-4); }

/* ==========================================================================
   05. EN-TÊTE ET NAVIGATION
   ========================================================================== */

.site-header {
  position: fixed;
  top: 0; right: 0; left: 0;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              backdrop-filter var(--dur) var(--ease);
}
.site-header__inner {
  width: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
}

/* Sur les pages à bannière, l'en-tête démarre transparent, en blanc */
.site-header--over { color: var(--on-dark); }
.site-header--over .wordmark__top { color: rgba(246, 244, 238, .82); }
.site-header--over .nav__link { color: var(--on-dark); }
.site-header--over .nav__toggle { color: var(--on-dark); border-color: rgba(246, 244, 238, .35); }
.site-header--over .btn--ghost { color: var(--on-dark); border-color: rgba(246, 244, 238, .38); }
.site-header--over .btn--ghost:hover { background: rgba(246, 244, 238, .12); border-color: rgba(246, 244, 238, .7); }

/* État « collé » : fond opaque */
.site-header.is-stuck,
.site-header:not(.site-header--over) {
  background: var(--paper);
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom-color: var(--line);
  color: var(--ink);
}
.site-header.is-stuck .wordmark__top { color: var(--ink-2); }
.site-header.is-stuck .nav__link { color: var(--ink); }
.site-header.is-stuck .nav__toggle { color: var(--ink); border-color: var(--line-2); }
.site-header.is-stuck .btn--ghost { color: var(--pine); border-color: var(--line-2); }
.site-header.is-stuck .btn--ghost:hover { background: var(--pine-wash); border-color: var(--pine); }

/* Marque typographique — reprend le logo historique du domaine */
.wordmark {
  display: inline-flex;
  flex-direction: column;
  gap: 1px;
  text-decoration: none;
  line-height: 1;
  flex-shrink: 0;
}
.wordmark__top {
  font-family: var(--font-body);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--ink-2);
  transition: color var(--dur) var(--ease);
}
.wordmark__main {
  font-family: var(--font-body);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}
@media (max-width: 420px) {
  .wordmark__main { font-size: 16px; letter-spacing: .08em; }
  .wordmark__top { font-size: 8px; letter-spacing: .3em; }
}

.nav { display: flex; align-items: center; gap: var(--sp-5); }

/* Le bouton « Espace copropriétaires » est désormais UNIQUE et visible à toutes
   les largeurs (il était auparavant dupliqué : un exemplaire dans le panneau
   déroulant, un pour le grand écran). Rien ne le comprimait : sous 500 px il
   poussait la bascule de thème hors de l'écran, où elle devenait inatteignable
   puisque body porte overflow-x:hidden. Il ne rétrécit donc plus et ne se coupe
   plus en deux lignes ; sous 900 px c'est son libellé qui s'efface, pas lui. */
.nav__cta { flex-shrink: 0; white-space: nowrap; }
.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(.75rem, .1rem + 1.1vw, 1.75rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__link {
  position: relative;
  display: inline-block;
  padding: var(--sp-2) 0;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .01em;
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  opacity: .7;
  transition: transform var(--dur) var(--ease);
}
.nav__link:hover::after,
.nav__link:focus-visible::after { transform: scaleX(1); }
.nav__link[aria-current="page"] { color: var(--brass); }
.nav__link[aria-current="page"]::after { transform: scaleX(1); opacity: 1; background: var(--brass); }
.site-header--over:not(.is-stuck) .nav__link[aria-current="page"] { color: var(--brass-2); }

.nav__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.nav__toggle svg { width: 20px; height: 20px; }
.nav__toggle .icon-close { display: none; }
.nav__toggle[aria-expanded="true"] .icon-close { display: block; }
.nav__toggle[aria-expanded="true"] .icon-menu { display: none; }

@media (max-width: 1179px) {
  .nav .nav__toggle { display: inline-flex; }
  .nav__list {
    position: fixed;
    top: var(--header-h); right: 0; left: 0;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--sp-4) var(--gutter) var(--sp-6);
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-lg);
    max-height: calc(100vh - var(--header-h));
    max-height: calc(100dvh - var(--header-h));
    overflow-y: auto;
    transform: translateY(-8px);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
  }
  .nav__list.is-open { opacity: 1; visibility: visible; transform: translateY(0); }
  .nav__link {
    color: var(--ink) !important;
    padding: var(--sp-4) 0;
    font-size: var(--step-0);
    border-bottom: 1px solid var(--line);
  }
  .nav__link::after { display: none; }
  /* le bouton est hors du panneau déroulant : il garde les couleurs du thème
     même quand l'en-tête est en mode « sur la bannière » (texte blanc) */
  .site-header .nav__cta.btn--ghost { color: var(--pine); border-color: var(--line-2); }
  .site-header .nav__cta.btn--ghost:hover { background: var(--pine-wash); border-color: var(--pine); }
}

/* Sous 900 px, le libellé s'efface et le cadenas reste seul : l'accès à l'espace
   copropriétaires tient en une touche à toutes les largeurs, ce qui est le point
   de friction n° 1 du site. Le nom accessible ne disparaît pas avec le texte —
   il est porté par l'aria-label du lien, que chrome.py émet toujours. */
@media (max-width: 899px) {
  .nav__cta-label { display: none; }
}

/* --- L'état de session, porté par le bouton lui-même ----------------------- */
/* Trois états, un seul contrôle. Le balisage émet TOUTES les formes — deux
   icônes, trois fragments de libellé — et cette feuille choisit. Deux raisons :
   les 18 pages publiques sont statiques et ne peuvent pas connaître la session
   au moment où elles sont écrites, et le repli sans JavaScript doit être l'état
   « invité », c'est-à-dire exactement ce que le visiteur anonyme doit voir.
   Rien ne clignote : ce que la feuille affiche par défaut est déjà juste.

   Ce que porte l'information, dans l'ordre :
     1. le REMPLISSAGE — creux quand personne n'est connecté, plein sinon.
        C'est le seul signal qui survit sous 900 px, où le libellé s'efface et
        où deux dessins de cadenas ne se distinguent plus. Un public de soixante
        à quatre-vingt-cinq ans lit un aplat, il ne décode pas un glyphe.
     2. le LIBELLÉ — « Espace propriétaires » devient « Mon espace », qui dit
        l'appartenance. Il est au passage PLUS COURT : l'en-tête est saturé à
        1280 px, l'état connecté lui rend donc de la place au lieu d'en prendre.
     3. l'ICÔNE — l'anse du cadenas se relève. Confirmation, jamais le porteur.

   Les libellés viennent du vocabulaire de l'ASL, pas de celui du code :
   installation.php nomme les deux accès « Propriétaires » et « Gestion ». */
.nav__cta-ouvert,
.nav__cta-connecte,
.nav__cta-role { display: none; }

.site-header .nav__cta[data-etat="proprietaire"],
.site-header .nav__cta[data-etat="gestion"] {
  background: var(--pine-solid);
  color: var(--on-pine);
  border-color: var(--pine-solid);
}
.site-header .nav__cta[data-etat="proprietaire"]:hover,
.site-header .nav__cta[data-etat="gestion"]:hover {
  background: var(--pine);
  border-color: var(--pine);
}
/* Le gestionnaire peut publier et supprimer. Il mérite un troisième cran, pas
   une nuance : un liseré laiton, la couleur que le site réserve déjà aux
   sur-titres et aux repères d'autorité. */
.site-header .nav__cta[data-etat="gestion"] { box-shadow: inset 0 0 0 2px var(--brass); }

.nav__cta[data-etat="proprietaire"] .nav__cta-verrou,
.nav__cta[data-etat="gestion"] .nav__cta-verrou { display: none; }
.nav__cta[data-etat="proprietaire"] .nav__cta-ouvert,
.nav__cta[data-etat="gestion"] .nav__cta-ouvert { display: block; }

.nav__cta[data-etat="proprietaire"] .nav__cta-invite,
.nav__cta[data-etat="gestion"] .nav__cta-invite { display: none; }
.nav__cta[data-etat="proprietaire"] .nav__cta-connecte,
.nav__cta[data-etat="gestion"] .nav__cta-connecte { display: inline; }
.nav__cta[data-etat="gestion"] .nav__cta-role { display: inline; }

/* ==========================================================================
   06. BANNIÈRE
   ========================================================================== */

.hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: clamp(520px, 86vh, 900px);
  padding-top: var(--header-h);
  overflow: hidden;
  isolation: isolate;
  background: var(--pine-solid);
}
.hero--compact { min-height: clamp(360px, 52vh, 520px); align-items: center; }

.hero__media {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: -2;
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  animation: heroDrift 26s var(--ease) infinite alternate;
}
@keyframes heroDrift {
  from { transform: scale(1.02); }
  to   { transform: scale(1.1); }
}

.hero__scrim {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to right, rgba(11, 20, 16, .6) 0%, rgba(11, 20, 16, .34) 40%, rgba(11, 20, 16, 0) 74%),
    linear-gradient(to bottom, rgba(11, 20, 16, .55) 0%, rgba(11, 20, 16, .14) 30%, rgba(11, 20, 16, .16) 44%, rgba(11, 20, 16, .8) 100%);
}

.hero__inner {
  width: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
  padding: var(--sp-8) var(--gutter) clamp(3.5rem, 2rem + 5vw, 6rem);
  color: var(--on-dark);
}
.hero--compact .hero__inner { padding-block: var(--sp-7); }

.hero h1 {
  color: var(--on-dark);
  max-width: 16ch;
  margin-bottom: var(--sp-5);
  text-shadow: 0 2px 30px rgba(8, 15, 12, .35);
}
.hero__lead {
  max-width: 46ch;
  text-shadow: 0 1px 20px rgba(8, 15, 12, .45);
  font-size: var(--step-1);
  line-height: 1.55;
  color: rgba(246, 244, 238, .9);
  margin-bottom: var(--sp-6);
}
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: var(--sp-5);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--step--2);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: rgba(246, 244, 238, .7);
  text-decoration: none;
}
.hero__scroll span { display: block; width: 1px; height: 34px; background: rgba(246, 244, 238, .45); }
.hero__scroll span::after {
  content: "";
  display: block;
  width: 1px;
  height: 12px;
  background: var(--brass-2);
  animation: scrollCue 2.4s var(--ease) infinite;
}
@keyframes scrollCue {
  0%   { transform: translateY(0); opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: translateY(22px); opacity: 0; }
}
@media (max-width: 720px) { .hero__scroll { display: none; } }

/* Bannière de page intérieure, sans photo */
.pagehead {
  padding-top: calc(var(--header-h) + var(--sp-8));
  padding-bottom: var(--sp-7);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line);
}
.pagehead h1 { max-width: 20ch; margin-bottom: var(--sp-4); }
.pagehead .lead { max-width: 58ch; }

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  list-style: none;
  margin: 0 0 var(--sp-5);
  padding: 0;
  font-size: var(--step--2);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.breadcrumb a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
.breadcrumb a:hover { border-bottom-color: var(--brass-2); }
.breadcrumb li:not(:last-child)::after { content: "/"; margin-left: var(--sp-2); color: var(--line-2); }
.breadcrumb [aria-current] { color: var(--brass); }

/* ==========================================================================
   07. BANDEAU « EN DIRECT »
   ========================================================================== */

.live {
  position: relative;
  z-index: 2;
  background: var(--surface);
  border-block: 1px solid var(--line);
}
.live__inner {
  max-width: var(--container-wide);
  margin-inline: auto;
  padding: var(--sp-5) var(--gutter);
  display: grid;
  grid-template-columns: auto repeat(4, minmax(0, 1fr)) auto;
  align-items: center;
  gap: var(--sp-5);
}
.live__label {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.live__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--sea);
  box-shadow: 0 0 0 0 var(--sea);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--sea) 55%, transparent);
  animation: pulse 2.8s var(--ease) infinite;
  flex-shrink: 0;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 var(--sea); box-shadow: 0 0 0 0 color-mix(in srgb, var(--sea) 55%, transparent); }
  70%  { box-shadow: 0 0 0 7px transparent; box-shadow: 0 0 0 7px color-mix(in srgb, var(--sea) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; box-shadow: 0 0 0 0 color-mix(in srgb, var(--sea) 0%, transparent); }
}

.live__item { min-width: 0; }
.live__key {
  display: block;
  font-size: var(--step--2);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 2px;
}
/* « Partiellement nuageux » demande ~190 px ; la colonne en fait 162 à 1180 px et
   132 à 320 px. Avec white-space: nowrap et body { overflow-x: hidden }, le libellé
   était rogné en silence — sur le composant le plus visible du site. */
.live__val {
  display: block;
  font-size: var(--step-1);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  color: var(--ink);
  overflow-wrap: break-word;
  -webkit-hyphens: auto;
  hyphens: auto;
}
/* seuls les nombres et leur unité restent insécables */
.live__val em {
  font-style: normal;
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--ink-2);
  margin-left: 2px;
  white-space: nowrap;
}
[data-loading="true"] {
  color: var(--ink-3);
  animation: shimmer 1.6s ease-in-out infinite;
}
@keyframes shimmer { 0%, 100% { opacity: .45; } 50% { opacity: .9; } }

.live__meta {
  font-size: var(--step--2);
  color: var(--ink-2);
  text-align: right;
  white-space: nowrap;
}
.live__meta a { color: var(--sea); text-decoration-color: var(--line-2); }

@media (max-width: 1179px) {
  .live__inner { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-5) var(--sp-4); }
  .live__label { grid-column: 1 / -1; }
  .live__meta { grid-column: 1 / -1; text-align: left; }
}
@media (max-width: 560px) {
  .live__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Fiches de données détaillées */
.datacard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-5);
}
.datacard__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.datacard__head h3 { font-size: var(--step-1); margin: 0; }
.dl { display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3) var(--sp-4); margin: 0; }
.dl dt { color: var(--ink-2); font-size: var(--step--1); }
.dl dd { margin: 0; font-weight: 500; text-align: right; }
/* chiffres alignés — uniquement là où la colonne est numérique */
.dl--nums dd { font-variant-numeric: tabular-nums; }

/* Relevé principal du moment */
.weather-hero {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1fr);
  gap: clamp(1.5rem, .5rem + 3vw, 3.5rem);
  align-items: center;
  padding: var(--sp-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 2px solid var(--sea);
  border-radius: var(--radius);
}
.weather-hero__main {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding-right: clamp(1.5rem, .5rem + 3vw, 3.5rem);
  border-right: 1px solid var(--line);
}
@media (max-width: 780px) {
  .weather-hero__main { padding-right: 0; border-right: 0; padding-bottom: var(--sp-5); border-bottom: 1px solid var(--line); }
}
.weather-hero__icon { width: clamp(56px, 8vw, 84px); height: clamp(56px, 8vw, 84px); color: var(--sea); flex-shrink: 0; }
.weather-hero__temp {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(3rem, 2rem + 4.5vw, 4.75rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.weather-hero__temp em { font-style: normal; font-size: .38em; color: var(--ink-2); margin-left: .1em; }
.weather-hero__sky { display: block; margin-top: var(--sp-3); font-size: var(--step-1); color: var(--ink-2); }
@media (max-width: 780px) { .weather-hero { grid-template-columns: minmax(0, 1fr); } }

/* Prévisions sur plusieurs jours */
/* Le conteneur naissait vide : l'injection des sept jours poussait le contenu de
   140 px sur ordinateur et 420 px sur mobile, soit un CLS d'environ 0,30 — à lui
   seul suffisant pour faire échouer les Core Web Vitals. LOT-02 pose un squelette
   de sept blocs dans le HTML ; ce min-height reste le filet si le squelette manque
   ou si la grille se recompose. */
.forecast {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  min-height: 140px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;   /* rogne les filets des dernières colonne et rangée */
}
/* filets tracés par ombre portée : une rangée incomplète ne laisse pas de bloc vide */
.forecast__day {
  box-shadow: 1px 0 0 0 var(--line), 0 1px 0 0 var(--line);
  padding: var(--sp-4) var(--sp-2);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
}
.forecast__name {
  font-size: var(--step--2);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.forecast__icon { width: 28px; height: 28px; color: var(--sea); }
.forecast__temps { font-size: var(--step--1); font-variant-numeric: tabular-nums; white-space: nowrap; }
.forecast__temps b { font-weight: 600; }
.forecast__temps span { color: var(--ink-2); }
.forecast__wind { font-size: var(--step--2); color: var(--ink-2); font-variant-numeric: tabular-nums; }
@media (max-width: 780px) {
  .forecast { grid-template-columns: repeat(4, minmax(0, 1fr)); min-height: 280px; }
}
@media (max-width: 460px) {
  .forecast { grid-template-columns: repeat(3, minmax(0, 1fr)); min-height: 420px; }
}

/* ==========================================================================
   08. COMPOSANTS
   ========================================================================== */

/* --- Boutons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  min-height: 46px;
  padding: var(--sp-3) var(--sp-5);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .04em;
  text-decoration: none;
  text-align: center;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .38; cursor: not-allowed; }
.btn:disabled:hover { background: transparent; }
.btn--primary:disabled:hover { background: var(--pine); }
.btn svg { width: 17px; height: 17px; flex-shrink: 0; }

.btn--primary { background: var(--pine); color: var(--on-primary); border-color: var(--pine); }
.btn--primary:hover { background: var(--pine-2); border-color: var(--pine-2); }

.btn--ghost { background: transparent; color: var(--pine); border-color: var(--line-2); }
.btn--ghost:hover { background: var(--pine-wash); border-color: var(--pine); }

.btn--light { background: rgba(246, 244, 238, .1); color: var(--on-dark); border-color: rgba(246, 244, 238, .4); }
.btn--light:hover { background: rgba(246, 244, 238, .2); border-color: var(--on-dark); }

.btn--solid-light { background: var(--on-pine); color: var(--pine-solid); border-color: var(--on-pine); }
.btn--solid-light:hover { background: #fff; }

/* Lien fléché discret */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .04em;
  color: var(--pine);
  text-decoration: none;
}
.arrow-link svg { width: 15px; height: 15px; transition: transform var(--dur) var(--ease); }
.arrow-link:hover svg { transform: translateX(4px); }
/* Étend la zone cliquable à toute la carte parente (qui est en position:relative) */
.arrow-link::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
}
/* Variante en plein texte : la zone cliquable reste celle du lien */
.arrow-link--inline { position: relative; }

/* --- Cartes --------------------------------------------------------------- */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.card:hover { border-color: var(--line-2); box-shadow: var(--shadow-md); transform: translateY(-3px); }
.card:focus-within { border-color: var(--pine); }

.card__media { position: relative; aspect-ratio: 3 / 2; overflow: hidden; background: var(--paper-3); }
.card__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 900ms var(--ease);
}
.card:hover .card__media img { transform: scale(1.045); }

.card__body { padding: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); flex: 1; }
.card__body h3 { font-size: var(--step-2); margin: 0; }
.card__body p { color: var(--ink-2); font-size: var(--step--1); line-height: 1.6; margin: 0; flex: 1; }
.card__foot { margin-top: var(--sp-2); }

.card--flat { background: transparent; border: 0; }
.card--flat:hover { box-shadow: none; transform: none; }
.card--flat .card__body { padding: var(--sp-5) 0 0; }

/* Carte à filet supérieur laiton */
.card--rule { border-top: 2px solid var(--brass-2); }

/* --- Figures -------------------------------------------------------------- */
figure { margin: 0; }
.figure img {
  width: 100%;
  border-radius: var(--radius);
  background: var(--paper-3);
}
.figure figcaption {
  margin-top: var(--sp-3);
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-2);
  padding-left: var(--sp-3);
  border-left: 2px solid var(--brass-2);
}
.figure--frame img {
  border: 1px solid var(--line);
  padding: var(--sp-3);
  background: var(--surface);
}
.figure--portrait img { aspect-ratio: 3 / 4; object-fit: cover; }

/* --- Statistiques --------------------------------------------------------- */
.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border-block: 1px solid var(--line);
}
.stats__item { background: var(--paper); padding: var(--sp-5) var(--sp-4); }
.section--sand .stats__item { background: var(--paper-2); }
.stats__val {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 500;
  line-height: 1;
  color: var(--pine);
  margin-bottom: var(--sp-2);
}
.stats__key {
  display: block;
  font-size: var(--step--2);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}
@media (max-width: 720px) { .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* --- Encarts -------------------------------------------------------------- */
.callout {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--sea-wash);
  border: 1px solid var(--line);
  border: 1px solid color-mix(in srgb, var(--sea) 22%, transparent);
  border-left: 2px solid var(--sea);
  border-radius: var(--radius);
}
.callout__icon { flex-shrink: 0; width: 22px; height: 22px; color: var(--sea); margin-top: 2px; }
.callout__title { margin: 0 0 var(--sp-2); font-family: var(--font-body); font-size: var(--step-0); font-weight: 600; line-height: 1.4; }
.callout p { font-size: var(--step--1); color: var(--ink-2); margin: 0; }
/* Sans repli, Safari ≤ 16.1 ignore les deux color-mix : .callout--warn hérite alors
   du bleu de .callout, soit un avertissement peint en bleu. */
.callout--warn {
  background: var(--danger-wash);
  background: color-mix(in srgb, var(--danger) 7%, var(--paper));
  border-color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 26%, transparent);
  border-left-color: var(--danger);
}
.callout--warn .callout__icon { color: var(--danger); }

/* --- Listes de liens ------------------------------------------------------ */
.linklist { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.linklist li { border-bottom: 1px solid var(--line); }
.linklist a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) 0;
  text-decoration: none;
  transition: padding-left var(--dur) var(--ease), color var(--dur) var(--ease);
}
.linklist a:hover { padding-left: var(--sp-3); color: var(--pine); }
.linklist__name { font-weight: 500; }
.linklist__desc {
  display: block;
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--ink-2);
  margin-top: 2px;
}
.linklist__host {
  font-size: var(--step--2);
  letter-spacing: .06em;
  color: var(--ink-3);
  white-space: nowrap;
  flex-shrink: 0;
}

/* --- Documents à télécharger ---------------------------------------------- */
.doclist { list-style: none; margin: 0; padding: 0; }
.doclist li + li { margin-top: var(--sp-3); }
.doclist a {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.doclist a:hover {
  border-color: var(--line-2);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.doclist__icon { width: 22px; height: 22px; color: var(--brass); flex-shrink: 0; }
.doclist__body { flex: 1; min-width: 0; }
.doclist__name { display: block; font-weight: 500; line-height: 1.4; }
.doclist a:hover .doclist__name { color: var(--pine); }
/* Le champ « detail » contient de la prose — « Séance du 13 août 2025 — exemplaire
   signé ». En capitales à 11-12 px, elle était illisible. Les capitales restent
   réservées aux étiquettes courtes. */
.doclist__meta {
  display: block;
  margin-top: 2px;
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--ink-2);
}
.doclist__go { width: 18px; height: 18px; color: var(--ink-3); flex-shrink: 0; transition: color var(--dur) var(--ease), transform var(--dur) var(--ease); }
.doclist a:hover .doclist__go { color: var(--pine); transform: translateY(2px); }

/* Second lien de chaque document — « Enregistrer le fichier » (contrat §3, posé par
   LOT-09). La règle « .doclist a » lui donnerait la carte complète : il faut donc la
   défaire explicitement, avec une spécificité supérieure. Ouvrir et enregistrer sont
   deux gestes différents ; les confondre est ce qui perd le visiteur sur iPad. */
.doclist a.doclist__save {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  margin: var(--sp-2) 0 0;
  padding: 0 var(--sp-4);
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--pine);
  box-shadow: none;
  transform: none;
}
.doclist a.doclist__save:hover {
  background: var(--pine-wash);
  border-color: var(--pine);
  box-shadow: none;
  transform: none;
}
.doclist a.doclist__save svg { width: 17px; height: 17px; flex-shrink: 0; }

/* --- Conseil syndical ------------------------------------------------------ */
.team {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;   /* rogne les filets de la dernière colonne et de la dernière rangée */
}
/* filets par ombre portée : une rangée incomplète ne laisse pas de bloc vide */
.team li {
  padding: var(--sp-4) var(--sp-5);
  box-shadow: 1px 0 0 0 var(--line), 0 1px 0 0 var(--line);
}
.team__name {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 500;
  line-height: 1.25;
}
.team__role {
  display: block;
  margin-top: 2px;
  font-size: var(--step--2);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--brass);
}
.team__role--plain { color: var(--ink-2); }

/* --- Marche à suivre numérotée -------------------------------------------- */
.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.steps li {
  position: relative;
  counter-increment: step;
  padding: 0 0 var(--sp-4) var(--sp-7);
  font-size: var(--step--1);
  color: var(--ink-2);
}
.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: var(--step-0);
  color: var(--pine);
  border: 1px solid var(--line-2);
  border-radius: 50%;
}
.steps li:last-child { padding-bottom: 0; }

/* --- Formulaires ----------------------------------------------------------- */
/* Un <fieldset> arrive de la feuille du navigateur avec une bordure en relief,
   deux pixels de marge latérale et un min-inline-size:min-content — ce dernier
   suffit à élargir la page entière si son contenu ne se rétrécit pas. C'est
   exactement la famille de débordement du constat 1.1. Il fallait donc quatre
   déclarations en ligne pour ramener un fieldset au rang de simple .field.
   Les quatre règles ci-dessous rendent <fieldset class="field"> légal PAR
   CONSTRUCTION : on n'a plus à désarmer le composant pour s'en servir. Sans le
   moindre effet sur les <div class="field">, qui n'ont aucune de ces valeurs
   par défaut. */
.field {
  display: block;
  margin-bottom: var(--sp-5);
  min-width: 0;
  margin-inline: 0;
  padding: 0;
  border: 0;
}
/* Un <legend> garde sinon deux pixels de retrait horizontal — hors barème, et
   il désaligne l'intitulé d'un groupe de tous les autres libellés de la page. */
legend.field__label { padding-inline: 0; }
.field__label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--step--1);
  font-weight: 500;
}
/* Un <textarea> est inline-block aligné sur la ligne de base : le navigateur
   réserve sous sa boîte la place des descendantes, et cet espace fantôme
   s'ajoute au margin-top du texte d'aide. Résultat mesuré : 17 px sous le
   champ « Paragraphes » contre 8 partout ailleurs, pour une règle identique.
   Le passage en bloc supprime la ligne de base, donc l'espace. Même geste que
   pour img/svg/video/iframe à la ligne 315. */
.field textarea { display: block; }

.field__hint {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--step--2);
  line-height: 1.5;
  color: var(--ink-2);
}
/* La liste ne couvrait que password, text et file : un <input type="url"> —
   le champ « Lien » de gestion.php — n'attrapait donc RIEN et s'affichait nu,
   34 px de haut contre 48 pour ses voisins, dans le même formulaire. Le défaut
   n'est pas la valeur, c'est l'énumération : tout type de saisie textuelle
   omis retombe au rendu natif. Les types textuels restants sont ajoutés une
   fois pour toutes, ce qui referme la famille au lieu de corriger un cas. */
.field input[type="password"],
.field input[type="text"],
.field input[type="url"],
.field input[type="email"],
.field input[type="tel"],
.field input[type="number"],
.field input[type="date"],
.field input[type="search"],
.field input[type="file"],
.field select,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  font-size: var(--step-0);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.field textarea { min-height: 90px; resize: vertical; }
/* Ici se trouvait une règle qui supprimait l'anneau natif du navigateur sur les
   champs. À spécificité (0,2,1) contre (0,1,0) pour la règle :focus-visible
   globale, l'anneau de focus ne s'appliquait donc JAMAIS à un champ de saisie, et
   son remplaçant — une ombre à 14 % de --pine — plafonnait à 1,28:1. Sur
   connexion.php, l'unique champ mot de passe est pourtant la seule chose à faire de
   la page. Le focus clavier reçoit maintenant l'anneau double complet. */
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--pine);
}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
  border-color: var(--pine);
  box-shadow: 0 0 0 3px var(--focus-halo);
}
/* La boîte s'agrandit pour contenir un bouton de 44 px : --touch pour le
   bouton, --sp-4 pour les deux retraits de --sp-2 qui l'entourent. Rien n'est
   posé à vue de nez, tout se déduit du barème. */
.field input[type="file"] {
  padding: var(--sp-2);
  min-height: calc(var(--touch) + var(--sp-4));
  cursor: pointer;
}

/* Aucune règle ne visait le bouton natif « Choisir un fichier » : le rectangle
   gris du système traversait le dessin, à l'intérieur d'un cadre qui imite un
   champ de saisie. Il reprend ici l'habit exact de .btn--ghost (l. 1060), qui
   passe déjà les contrôles de contraste de la porte.
   DEUX RÈGLES SÉPARÉES, et non une liste à virgule : un navigateur qui ne
   comprend pas l'un des deux sélecteurs jette la déclaration ENTIÈRE. Le
   parc visé comprend des iPad restés en Safari 14. */
.field input[type="file"]::file-selector-button {
  font: inherit;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .04em;
  min-height: var(--touch);
  margin-right: var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  background: transparent;
  color: var(--pine);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
}
.field input[type="file"]::-webkit-file-upload-button {
  font: inherit;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .04em;
  min-height: var(--touch);
  margin-right: var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  background: transparent;
  color: var(--pine);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
}
.field input[type="file"]:hover::file-selector-button { background: var(--pine-wash); border-color: var(--pine); }
.field input[type="file"]:hover::-webkit-file-upload-button { background: var(--pine-wash); border-color: var(--pine); }
.field input::placeholder { color: var(--ink-3); }
.field--invalid input, .field--invalid select { border-color: var(--danger); }

/* Quatre cases à cocher de 18 × 18 px : le pire point de gestion.php pour un
   public de soixante à quatre-vingt-cinq ans. La ligne entière devient une
   cible de --touch, et la case elle-même passe à 24 px — soit --sp-5 tout rond,
   donc sans une seule valeur hors barème.
   Le « margin: 2px » de la case disparaît avec : hors barème, et sans objet dès
   que la case a la hauteur d'une ligne de texte. */
.checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-height: var(--touch);
  margin-bottom: var(--sp-5);
  font-size: var(--step--1);
  cursor: pointer;
}
.checkbox input { width: var(--sp-5); height: var(--sp-5); margin: 0; accent-color: var(--pine); flex-shrink: 0; }

/* Une case enveloppée dans un .field comptait deux fois la même marge : 24 px
   pour la case, 24 pour son enveloppe. C'est une part des dix écarts verticaux
   du formulaire « Composer une section » (constat 1.3) — cinq cases sont dans
   ce cas. La case nue de connexion.php, qui n'est pas dans un .field, garde
   la sienne. */
.field .checkbox { margin-bottom: 0; }

/* Une aide placée AVANT ce qu'elle explique : le cas d'un groupe, où la
   consigne se lit avant les lignes qu'elle introduit. C'est la POSITION dans le
   balisage qui déclenche la règle, pas une classe — une aide écrite entre
   l'intitulé et le premier champ EST une aide d'introduction, et une classe de
   plus se serait oubliée une fois sur deux. Même écart que partout ailleurs,
   --sp-2, simplement de l'autre côté (constat 2.3). Un seul endroit du site
   présente cette adjacence : la consigne des « Repères ». */
.field__label + .field__hint { margin-top: 0; margin-bottom: var(--sp-2); }

/* --- Messages -------------------------------------------------------------- */
.alert {
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-4);
  margin-bottom: var(--sp-5);
  border: 1px solid;
  border-left-width: 2px;
  border-radius: var(--radius);
  font-size: var(--step--1);
  line-height: 1.55;
}
.alert svg { width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px; }
.alert p { margin: 0; }
.alert p + p { margin-top: var(--sp-2); }
.alert--error {
  background: var(--danger-wash);
  background: color-mix(in srgb, var(--danger) 7%, var(--paper));
  border-color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 28%, transparent);
  border-left-color: var(--danger);
  color: var(--ink);
}
.alert--error svg { color: var(--danger); }
.alert--ok {
  background: var(--pine-wash);
  border-color: var(--pine);
  border-color: color-mix(in srgb, var(--pine) 26%, transparent);
  border-left-color: var(--pine);
}
.alert--ok svg { color: var(--pine); }
.alert--info {
  background: var(--sea-wash);
  border-color: var(--sea);
  border-color: color-mix(in srgb, var(--sea) 24%, transparent);
  border-left-color: var(--sea);
}
.alert--info svg { color: var(--sea); }

/* --- Page de connexion ----------------------------------------------------- */
.login {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: calc(var(--header-h) + var(--sp-6)) var(--gutter) var(--sp-8);
  background: var(--paper-2);
}
.login__card {
  width: 100%;
  max-width: 460px;
  padding: clamp(1.75rem, 1rem + 3vw, 3rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 2px solid var(--pine);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.login__card h1 { font-size: var(--step-3); margin-bottom: var(--sp-3); }
.login__intro { font-size: var(--step--1); color: var(--ink-2); margin-bottom: var(--sp-6); }
.login__card .btn { width: 100%; }
.login__foot {
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  font-size: var(--step--2);
  color: var(--ink-2);
}
.login__foot a { color: var(--pine); }
/* « Vous n'avez pas le mot de passe ? » est le premier motif d'appel au
   président : ce bloc se lit, ce n'est pas une mention légale de bas de page.
   Le corps monte d'un cran — ce que faisaient déjà trois styles en ligne, dans
   deux fichiers, avec trois écritures de marge différentes. */
.login__foot p { margin: 0; font-size: var(--step--1); }
.login__foot p + p { margin-top: var(--sp-2); }
.login .wordmark { align-items: center; margin: 0 auto var(--sp-6); }

/* --- Le dépliant de session ------------------------------------------------ */
/* Ce bloc remplace la barre de session, un bandeau vert fixé au-dessus de
   l'en-tête. Elle disait deux choses : « une session est ouverte » et « voici où
   aller ». La première est désormais portée par le bouton lui-même — creux,
   plein, plein cerclé de laiton — sur TOUTES les pages, publiques comprises,
   ce que la barre ne savait pas faire sans un appel au serveur. La seconde
   tient dans ce dépliant, qui ne coûte rien tant qu'il reste fermé.
   Bilan : 45 px rendus à la page sur téléphone, 44 sur grand écran, et un
   bandeau de moins entre le lecteur et ce qu'il est venu lire.

   <details>/<summary> et non du JavaScript : le motif est natif, se pilote au
   clavier, s'annonce aux lecteurs d'écran, et fonctionne quand le script ne
   s'exécute pas — ce que le parc visé (iPad anciens) rend non négociable. */
.nav__espace { position: relative; display: flex; }

/* Le <summary> porte les classes du bouton : il doit donc se comporter comme
   lui. display:list-item est sa valeur native — il ferait apparaître un
   triangle et casserait l'alignement du cadenas et du libellé. */
.nav__espace > summary {
  list-style: none;
  cursor: pointer;
}
.nav__espace > summary::-webkit-details-marker { display: none; }
.nav__espace > summary::marker { content: ""; }

/* Ouvert, le bouton reste visuellement enfoncé : sans cela, rien ne relie le
   panneau au contrôle qui l'a ouvert. */
.nav__espace[open] > summary { box-shadow: inset 0 0 0 2px var(--brass), 0 0 0 3px var(--pine-wash); }

.nav__espace-menu {
  position: absolute;
  top: calc(100% + var(--sp-2));
  right: 0;
  z-index: 120;
  min-width: 15rem;
  padding: var(--sp-2);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
}

/* --- Les liens de session -------------------------------------------------- */
/* Une seule source, deux emplois : le panneau du dépliant, et le bandeau de
   retour d'installation.php, qui n'a pas d'en-tête de site pour l'accueillir. */
.espace-liens { display: flex; flex-direction: column; }
.espace-liens__etat {
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--step--2);
  color: var(--ink-2);
}
.espace-liens a {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--touch);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--step--1);
  color: var(--ink);
  text-decoration: none;
  border-radius: var(--radius);
}
.espace-liens a:hover { background: var(--pine-wash); color: var(--pine); }
.espace-liens a svg { width: 18px; height: 18px; flex-shrink: 0; }
/* La sortie se détache du reste : c'est la seule action de ce menu qui défait
   quelque chose, et sur un ordinateur familial c'est celle qu'on cherche. */
.espace-liens__sortie {
  margin-top: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}

/* --- Bandeau de retour d'installation.php ---------------------------------- */
/* Page autonome, sans en-tête de site : les mêmes liens y sont posés dans le
   flux, en ligne, plutôt que dans un panneau. */
.espace-retour {
  background: var(--paper-2);
  border-bottom: 1px solid var(--line);
}
.espace-retour .espace-liens {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.espace-retour .espace-liens__etat { flex: 1 1 auto; }
.espace-retour .espace-liens__sortie {
  margin-top: 0;
  padding-top: var(--sp-2);
  border-top: 0;
}

/* --- Tableau de gestion ---------------------------------------------------- */
/* Les sélecteurs sont qualifiés par « table. », et ce n'est pas de la
   coquetterie. Écrits « .manage td », ils stylaient les cellules PAR
   DESCENDANCE : un <div class="manage"> contenant un <table> sans classe avait
   donc l'air parfaitement correct sur un écran de bureau, et ne se révélait
   qu'à 375 px, sous forme de contenu perdu — 25 contrôles entièrement hors
   écran, 18 coupés, et aucun défilement horizontal possible. Le CSS ne
   sanctionnait pas l'erreur : il la récompensait.
   Qualifiés, ils ne s'appliquent plus qu'à un vrai <table>. Un <div class=
   "manage"> laisse maintenant son tableau NU — sans filets, sans retraits, en
   corps par défaut — à toutes les largeurs, sur tous les écrans. L'erreur
   devient visible là où on la commet, et non trois semaines plus tard sur le
   téléphone d'un propriétaire (constats 1.1 et 3.3). */
table.manage { width: 100%; border-collapse: collapse; font-size: var(--step--1); }
table.manage th, table.manage td {
  padding: var(--sp-3) var(--sp-3);
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
table.manage th {
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
/* Le « white-space: nowrap » a quitté la cellule d'actions. C'est lui qui
   imposait au tableau sa largeur minimale : trois formulaires et un lien collés
   bout à bout, insécables — 470 px de contenu dans une colonne de 333. Il reste
   sur l'en-tête, qui est court par nature. .manage__actions replie désormais les
   boutons, donc la colonne peut rétrécir. */
table.manage td:last-child, table.manage th:last-child { text-align: right; }
table.manage th:last-child { white-space: nowrap; }
/* Une cellule à colspan porte un formulaire d'édition, jamais une colonne
   d'actions : elle se lit à gauche. Spécificité (0,2,2) contre (0,2,1) pour la
   règle ci-dessus, donc sans dépendre de l'ordre du fichier. */
table.manage td[colspan] { text-align: left; }
table.manage tbody tr:hover { background: var(--paper-2); }
.manage__name { font-weight: 500; }
/* 2px était hors barème. --sp-2, c'est-à-dire l'écart unique de la page entre
   une chose et ce qui la précise. */
.manage__meta { display: block; margin-top: var(--sp-2); font-size: var(--step--2); color: var(--ink-2); }

/* 44 px, et non 36. Soixante-quinze boutons de gestion.php étaient sous la
   cible : 58 flèches à 38 × 40, 17 « Retirer » à 71 × 40, pour des mains de
   soixante à quatre-vingts ans. Vérifié avant d'écrire cette ligne :
   .btn--small n'existe NULLE PART ailleurs que dans gestion.php — ni dans les
   18 pages générées, ni dans les autres pages de l'espace privé. Aucune
   régression publique n'est possible.
   Le retrait et le corps restent ceux d'un bouton discret : c'est la hauteur qui
   devait changer, pas le poids visuel. */
.btn--small { min-height: var(--touch); min-width: var(--touch); padding: var(--sp-2) var(--sp-3); font-size: var(--step--2); }
.btn--danger { background: transparent; color: var(--danger); border-color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.btn--danger:hover { background: var(--danger-wash); background: color-mix(in srgb, var(--danger) 10%, transparent); border-color: var(--danger); }

.inline-form { display: inline; }

/* --- Panneaux de l'espace privé --------------------------------------------

   Les quinze constats ont une cause commune : dans gestion.php, chaque bloc
   décidait lui-même de sa largeur et de son écart au bloc suivant. Six
   largeurs, dix rythmes, dix-huit styles en ligne. Ce n'est pas de la
   négligence, c'est l'absence d'un contenant qui décide à leur place. Une
   grammaire solide ne demande pas à l'auteur de choisir juste : elle ne lui
   laisse rien à choisir.

   LA RÈGLE, une seule : le panneau possède la largeur de ses enfants et l'écart
   qui les sépare. Un enfant ne porte NI largeur NI marge verticale. Pour
   dépasser la mesure, il faut entrer dans un .manage__wrap — et le
   .manage__wrap défile.

   UNE SEULE LARGEUR est déclarée dans tout l'espace privé : --measure. Un
   tableau ne reçoit aucune largeur, il prend celle du conteneur — ce qui n'est
   pas un choix mais l'absence de choix. Une deuxième valeur exigerait d'écrire
   un max-width à la main, ce que le contrôle 7 de la porte refuse désormais.

   TROIS ÉCARTS VERTICAUX, et trois seulement :
     --sp-7 (48 px) entre deux blocs d'un panneau
     --sp-5 (24 px) entre deux champs d'un formulaire
     --sp-2 ( 8 px) entre une chose et ce qui l'explique ou la précise
   Aucune autre valeur d'espacement n'apparaît dans l'espace privé. Produire à
   nouveau la suite 8/26/55/58/66/67/87/96/99/109 du constat 1.3 devient
   impossible, quelle que soit la nature du bloc ajouté demain.

   ⚠ Marges en propriétés PHYSIQUES (margin-top / margin-bottom) et non
   margin-block : si la propriété logique n'est pas comprise alors que gap
   l'est, les marges s'ajouteraient au gap et le rythme doublerait. Le fichier
   emploie padding-block ailleurs, où l'échec est inoffensif ; ici il ne l'est
   pas.
   ========================================================================== */

/* Une rangée de boutons ou de liens d'action, détachée de ce qui la précède.
   Sept paragraphes portaient cet écart en style en ligne, avec six valeurs
   différentes (1 · 1,25 · 1,5 · 2 · 2,5 · 3 rem) ; et deux boutons voisins
   n'étaient séparés que par l'espace typographique du balisage — quatre pixels
   entre deux cibles de quarante-quatre. L'écart au texte et l'écart entre
   boutons viennent maintenant tous deux du barème. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-5) 0 0;
}

/* Le panneau se pose SUR le .container existant, sans div supplémentaire :
   <div class="container panel"> */
.panel { display: grid; gap: var(--sp-7); }
.panel > * {
  min-width: 0;              /* sans quoi une piste de grille refuse de rétrécir */
  max-width: var(--measure);
  margin-top: 0;
  margin-bottom: 0;          /* le gap possède le rythme, pas les enfants */
}
/* Le bloc de tête tient sa largeur du panneau comme tout le reste : c'est ce
   qui aligne enfin le bord droit d'un formulaire sur celui de son propre titre,
   ce qu'aucune des six largeurs précédentes ne faisait. Les dix-huit pages
   publiques gardent .section-head { max-width: 62ch } intact — cette règle-ci
   ne mord que dans un .panel, et .panel n'existe que dans espace-prive/. */
.panel > .section-head { max-width: var(--measure); margin-bottom: 0; }
/* La SEULE sortie de la mesure de lecture, et elle défile. C'est ce qui rend le
   constat 1.1 structurellement impossible : « plus large que le texte » et
   « atteignable » ne sont plus séparables. */
.panel > .manage__wrap { max-width: none; }
.panel > .actions { margin-top: 0; }

/* Un formulaire : un seul écart possible entre deux champs. La largeur est
   portée ici AUSSI, et pas seulement par .panel — trois des formulaires de
   gestion.php vivent dans une cellule de tableau, hors du panneau. Un
   formulaire qui voudrait la pleine largeur devra le déclarer, et c'est bien
   ainsi. */
.panel__form { display: grid; gap: var(--sp-5); max-width: var(--measure); }
.panel__form > * { margin-top: 0; margin-bottom: 0; }
.panel__form > .actions { margin-top: 0; }
.panel__form > .btn { justify-self: start; }   /* un bouton isolé ne s'étire pas */

/* Le cadre défilant gagne deux choses. Le défilement tactile fluide, et surtout
   la possibilité d'être atteint AU CLAVIER : une zone qui défile sans être
   focalisable est inatteignable sans souris — c'est le défaut que les deux
   .manage__wrap corrects du fichier avaient déjà.
   Balisage obligatoire, sans exception :
   <div class="manage__wrap" tabindex="0" role="group" aria-label="…"> */
.manage__wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Les actions d'une ligne de tableau. Elles remplacent la soupe
   « formulaire + lien + formulaire » que le white-space:nowrap de la cellule
   soudait en un bloc insécable — cause directe des 470 et 459 px imposés à une
   colonne de 333. Elles se replient maintenant, donc la colonne peut rétrécir,
   et la colonne MEMBRE cesse de casser les noms en trois lignes (constat 1.6). */
.manage__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-2);
}
/* Les deux flèches restent soudées en un seul objet : c'est une paire, pas deux
   boutons voisins. */
.manage__actions .inline-form { display: inline-flex; gap: var(--sp-2); }

/* L'intertitre de rubrique dans un tableau — LE composant qui manquait. Il
   remplace six déclarations en ligne dont QUATRE ne servaient qu'à défaire
   table.manage th : la casse, la taille, la couleur, et surtout l'alignement à
   droite que th:last-child réserve à la colonne d'actions — qu'un
   <th colspan="3"> attrape forcément, puisqu'il est par construction le dernier
   enfant de sa ligne. Quand on doit annuler un composant pour s'en servir,
   c'est qu'il en manque un : le voici.
   Spécificité (0,2,2) contre (0,2,1) : elle passe devant sans dépendre de
   l'ordre du fichier.
   Balisage : <tr class="manage__group"><th colspan="3" scope="colgroup"> */
table.manage .manage__group th {
  padding-top: var(--sp-5);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  text-align: left;
  white-space: normal;
  color: var(--ink);
}

/* Un en-tête de LIGNE n'est pas un en-tête de colonne. table.manage th est
   dessiné pour trois mots posés en haut d'une colonne : petites capitales,
   interlettrage, insécable. Appliqué au nom d'un point de contrôle, cela donne
   « ALLÈGEMENT DES ILLUSTRATIONS (GD) » sur une ligne qui refuse de se couper.
   Le <th scope="row"> reste — c'est lui qui permet à un lecteur d'écran de
   redire de quelle ligne on parle en annonçant la valeur — mais il se lit comme
   le texte qu'il est. */
table.manage tbody th[scope="row"] {
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  white-space: normal;
  color: var(--ink);
}

/* Séries de lignes facultatives — les « repères » de gestion.php.
   Le rendu disait « remplis six choses » pour trois raisons cumulées : six
   lignes strictement identiques, le même texte fantôme répété six fois, et
   aucun contenant qui les rassemble en un seul objet. Le filet à gauche traite
   la troisième — c'est l'idiome déjà employé par blockquote (l. 407), par le
   bord gauche des .alert et par .login__card. Les deux autres se traitent dans
   le BALISAGE, sans une syntaxe à apprendre et sans une ligne de JavaScript :
   l'exemple ne se donne QUE sur la première ligne, et la consigne est réécrite.
   L'épaisseur de 2 px n'est pas une valeur d'espacement : c'est la largeur de
   filet d'accent déjà employée quatre fois dans ce fichier. */
.rowset {
  display: grid;
  gap: var(--sp-2);
  padding-left: var(--sp-5);
  border-left: 2px solid var(--line);
}
/* Une part courte — une date — et une part longue — ce qui s'est passé. Les
   deux colonnes sont portées par la grille : un <input> ne peut donc pas se
   tromper de part, et n'a plus rien à déclarer. La part courte vaut deux fois
   --sp-9, soit 12 rem, ce qui tient « 18 septembre 2021 » sans repli.
   Sous 620 px — le point de rupture déjà employé par .grid (l. 480) — la ligne
   s'empile : à 375 px, deux champs côte à côte laissaient 149 px au second. */
.rowset__row {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: minmax(0, calc(var(--sp-9) * 2)) minmax(0, 1fr);
}
@media (max-width: 620px) {
  .rowset__row { grid-template-columns: minmax(0, 1fr); }
}

/* --- Volets de la page de gestion ------------------------------------------

   Les cinq panneaux de gestion.php tenaient sur une seule page, tous dépliés.
   Ce n'était pas un excès de contenu mais un excès de SIMULTANÉITÉ : les trois
   domaines — documents, sections rédigées, syndicat — ne se travaillent jamais
   dans la même séance. On dépose un procès-verbal trois ou quatre fois l'an ;
   on refait la composition du syndicat une fois après l'assemblée. Les tenir
   ouverts ensemble obligeait à faire défiler quatre panneaux dont on n'avait
   que faire pour atteindre le cinquième.

   La barre ci-dessous n'est pas un composant à état : ce sont quatre liens,
   dont un porte aria-current="page". Sans JavaScript ils naviguent ; avec,
   gestion.js remplace le volet sans recharger. Le rendu est le MÊME dans les
   deux cas — c'est la seule façon de n'avoir qu'un seul rendu à vérifier.

   Le trait de sélection est un border-bottom sur le lien, remonté d'un pixel
   sur le filet de la barre : deux traits qui se recouvrent, jamais deux traits
   qui s'ajoutent. Ce -1px n'est pas un espacement mais l'épaisseur d'un filet,
   d'où son absence du barème --sp-*.
   ========================================================================== */
.volets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--line);
}
.volets a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--touch);
  padding: var(--sp-2) var(--sp-4);
  margin-bottom: -1px;
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.volets a:hover { color: var(--pine); background: var(--pine-wash); }
.volets a[aria-current="page"] { color: var(--pine); border-bottom-color: var(--pine); }
/* Le compte n'est pas une pastille : c'est une précision, et il suit donc la
   règle des précisions — un cran plus petit, en encre tertiaire. Une pastille
   pleine aurait rendu la barre plus lourde que les panneaux qu'elle allège. */
.volets__n { font-size: var(--step--2); font-weight: 400; color: var(--ink-3); }

/* La barre et le volet qu'elle commande sont UNE SEULE CHOSE. Empilés en deux
   .section, ils étaient séparés par la marge basse de l'une et la marge haute
   de l'autre : 108 px de vide entre l'onglet et son contenu, soit plus que la
   hauteur de la barre elle-même. Le vide se lisait comme une fin de page, et
   sur un portable il fallait faire défiler pour trouver ce qu'on venait
   d'ouvrir. */
.section--volets { padding-bottom: 0; }
.section--volets + .section { padding-top: var(--sp-6); }

/* Les messages sont sortis de leur <section> à eux. Un panneau entier, avec sa
   bande de couleur et ses marges de section, pour une phrase qui n'apparaît
   qu'après un geste : au repos c'était un blanc de 96 px sans raison. Ils
   vivent maintenant sous la barre des volets, dans le même conteneur. */
.annonces { margin-top: var(--sp-5); }
.annonces > .alert:last-child { margin-bottom: 0; }

/* --- Pliages ---------------------------------------------------------------

   « Composer une section » ouvrait vingt champs en permanence sous une liste
   qui en comptait quatre, et « Inscrire un membre » trois champs sous une
   liste de sept. Un formulaire d'ajout occupait donc plus de place que ce
   qu'il complète, tout le temps, pour un geste qu'on fait deux fois l'an.

   <details> natif, et rien d'autre : le repli ne dépend d'aucun script, il
   survit à une page sans JavaScript, et le contenu replié RESTE dans le
   document — donc trouvable par la recherche du navigateur, et surtout envoyé
   avec le formulaire. Un champ caché par un pliage n'est pas un champ perdu.

   Le <summary> est dessiné comme un bouton discret parce qu'il en est un : il
   ouvre le formulaire. Il garde donc les 44 px de cible tactile du reste de la
   page — c'est le même doigt de soixante-dix ans qui le vise.
   ========================================================================== */
.pliage > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--touch);
  padding: var(--sp-2) var(--sp-5);
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--pine);
  cursor: pointer;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-pill);
  list-style: none;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
/* Le triangle par défaut se place hors de la boîte inline-flex et déborde du
   contour arrondi. On le remplace par un signe qui dit l'état plutôt que le
   sens : « + » ouvre, « – » referme. */
.pliage > summary::-webkit-details-marker { display: none; }
.pliage > summary::before { content: "+"; font-size: var(--step-0); line-height: 1; }
.pliage[open] > summary::before { content: "–"; }
.pliage > summary:hover { background: var(--pine-wash); border-color: var(--pine); }
.pliage[open] > summary { margin-bottom: var(--sp-5); }

/* Un pliage imbriqué dans un formulaire : mêmes gestes, poids moindre. Il
   groupe des champs facultatifs — illustration, repères, lien — il ne lance
   pas une action, donc il ne porte pas de contour au repos. Il s'ouvre de
   lui-même quand la section en cours de modification a déjà quelque chose
   dedans : ce qui existe ne se cache pas. */
.pliage--interne > summary {
  padding-inline: var(--sp-3);
  color: var(--ink-2);
  border-color: transparent;
  border-radius: var(--radius);
}
.pliage--interne > summary:hover { color: var(--pine); border-color: var(--line-2); }
.pliage--interne[open] > summary { margin-bottom: var(--sp-4); }
/* Le dernier champ d'un pliage porte encore son margin-bottom de .field : il
   s'ajouterait au gap du formulaire et le rythme doublerait à cet endroit. */
.pliage--interne > .field:last-child { margin-bottom: 0; }

/* La ligne qu'on est en train de modifier. Le formulaire d'édition s'ouvre
   dans la ligne elle-même : sans repère, on ne sait plus lequel des sept
   membres on a ouvert. Même aplat que le survol, mais tenu. */
table.manage tbody tr.manage__courant,
table.manage tbody tr.manage__courant:hover { background: var(--pine-wash); }

/* Le bord rouge d'un « Retirer » par ligne peignait la colonne d'actions en
   rouge : dix-sept encadrés d'alerte pour dix-sept documents dont aucun ne
   pose de problème. Le bouton garde son encre, sa cible de 44 px et sa
   confirmation ; il perd le cadre, et le retrouve au survol comme au clavier —
   c'est-à-dire à l'instant précis où on le vise. */
.manage__actions .btn--danger { border-color: transparent; }
.manage__actions .btn--danger:hover,
.manage__actions .btn--danger:focus-visible { border-color: var(--danger); }

/* Pendant un échange de gestion.js, le curseur dit que la page travaille. Pas
   de voile ni de sablier : sur un réseau ordinaire la réponse arrive en moins
   de temps qu'il n'en faut pour lire un changement d'état, et un voile qui
   clignote à chaque flèche ↑ serait plus fatigant que le rechargement qu'il
   remplace. Là où l'attente peut vraiment durer — l'envoi d'un PDF de
   plusieurs mégaoctets — c'est le bouton qui se verrouille et le dit. */
#atelier[aria-busy="true"] { cursor: progress; }

/* Le bouton « Ajouter une ligne », posé par gestion.js à la suite des lignes
   de repères. Il n'existe pas sans JavaScript et n'a donc aucun balisage dans
   gestion.php — mais son écart, lui, appartient au barème comme le reste. */
.rowset + .btn { margin-top: var(--sp-3); }

/* --- Vignettes d'illustration ----------------------------------------------

   « Retirer l'illustration de cette section », case à cocher perdue au milieu
   d'un formulaire de vingt champs : il fallait se souvenir de LAQUELLE, et
   rien à l'écran ne le disait. La vignette montre ce qu'on retire, et porte la
   croix qui le fait.

   Le mécanisme n'a pas bougé d'un octet : c'est toujours la même case
   name="retirer_image", et le serveur ne sait rien de plus qu'avant. Elle est
   seulement dessinée autrement — et comme une case à cocher connaît son propre
   état, tout ce qui suit est du CSS. Sans JavaScript, la croix fonctionne, et
   elle s'annule.

   Le disque visible fait 28 px, la cible tactile 44 : le reste de la croix est
   transparent. C'est la seule façon de tenir les deux exigences ensemble —
   44 px opaques posés sur une vignette de 144 en couvriraient le tiers, et
   masqueraient précisément ce qu'on demande à l'utilisateur de regarder avant
   de décider.
   ========================================================================== */
.vignette { display: grid; gap: var(--sp-2); justify-items: start; }
.vignette__cadre { position: relative; display: inline-block; max-width: 100%; }
.vignette__img {
  display: block;
  /* Une hauteur de vignette, pas un espacement : le barème --sp-* ne la
     concerne pas. La largeur suit, pour que ni un panorama ni un portrait ne
     soient déformés. */
  max-height: 9rem;
  width: auto;
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: opacity var(--dur) var(--ease), filter var(--dur) var(--ease);
}
.vignette__croix {
  position: absolute;
  top: 0;
  right: 0;
  display: grid;
  place-items: center;
  min-width: var(--touch);
  min-height: var(--touch);
  cursor: pointer;
}
.vignette__croix::before {
  content: "";
  position: absolute;
  width: 28px;
  height: 28px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.vignette__croix:hover::before { border-color: var(--danger); background: var(--danger-wash); }
.vignette__glyphe {
  position: relative;
  font-size: var(--step--1);
  line-height: 1;
  color: var(--ink);
}
.vignette__glyphe--rendre { display: none; }
.vignette__retire { display: none; }

/* L'état coché se lit sur les FRÈRES de la case, jamais sur son parent : c'est
   ce que « ~ » sait faire depuis toujours, et ce que :has() ferait au prix
   d'un navigateur récent. */
.vignette__bascule:checked ~ .vignette__cadre .vignette__img { opacity: .3; filter: grayscale(1); }
.vignette__bascule:checked ~ .vignette__cadre .vignette__glyphe--retirer { display: none; }
.vignette__bascule:checked ~ .vignette__cadre .vignette__glyphe--rendre { display: inline; }
.vignette__bascule:checked ~ .vignette__etat .vignette__garde { display: none; }
.vignette__bascule:checked ~ .vignette__etat .vignette__retire { display: inline; color: var(--danger); }

/* La case est masquée à l'œil, pas au clavier : sans cette règle son anneau de
   focus serait invisible, et la croix ne serait atteignable que par hasard. */
.vignette__bascule:focus-visible ~ .vignette__cadre .vignette__croix::before {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--focus-halo);
}

/* L'aperçu du fichier qu'on vient de choisir, posé par gestion.js avant tout
   envoi. Vide, il ne prend pas de place — sans quoi chaque champ de fichier
   traînerait derrière lui un blanc permanent. */
.apercu:empty { display: none; }
.apercu { margin-top: var(--sp-3); }

/* Le jeton d'un fichier sans aperçu — un PDF, ou une image trop lourde pour
   qu'on la décode juste pour la montrer en 144 pixels. Il occupe exactement la
   place d'une vignette, pour que le champ ne saute pas d'une hauteur à l'autre
   selon ce qu'on choisit. */
.apercu__jeton {
  display: grid;
  place-items: center;
  width: 6rem;
  height: 6rem;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .06em;
  color: var(--ink-2);
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* Dans un aperçu, la croix est un vrai <button> et non un <label> : il n'y a
   pas d'état à basculer, seulement un champ à vider. Il hérite du dessin de la
   croix de vignette et n'a besoin que de perdre l'apparence de bouton. */
.apercu .vignette__croix {
  padding: 0;
  background: transparent;
  border: 0;
}

/* --- Le composeur et son aperçu --------------------------------------------

   Le formulaire garde la mesure de lecture : ses libellés, ses aides et ses
   messages d'erreur sont de la prose, et cela ne change pas. L'aperçu, lui, en
   sort — et c'est la SECONDE sortie déclarée dans ce fichier après le cadre
   défilant des tableaux.

   La raison n'est pas esthétique. La disposition en deux colonnes d'une
   section illustrée — texte à gauche, image à droite — n'existe qu'au-dessus
   d'une certaine largeur. Un aperçu enfermé dans 44 rem montrerait toujours la
   version empilée, c'est-à-dire une mise en page que le propriétaire ne verra
   pas. Un aperçu qui ment sur la mise en page ne prévient de rien ; il donne
   seulement la confiance de publier sans regarder.

   Sous 1100 px, il n'y a pas la place pour les deux : l'aperçu passe sous le
   formulaire, où il reste consultable au défilement.
   ========================================================================== */
.panel > .pliage--large { max-width: none; }
.pliage--large > summary { max-width: var(--measure); }

.composer { display: grid; gap: var(--sp-6); }
.composer > .panel__form { max-width: var(--measure); }

/* 1500 px, et non 1100. Le seuil vient d'une contrainte du langage, pas d'un
   goût : une requête média répond à la largeur de la FENÊTRE, jamais à celle du
   bloc qui la contient. Une section posée dans une colonne d'aperçu de 340 px
   croit donc être sur un écran de 1280 et adopte sa mise en page à deux
   colonnes — comprimée au point qu'un titre se coupe au milieu d'un mot. Elle
   dit vrai sur la disposition et faux sur tout le reste, ce qui est la pire
   combinaison : on regarde un aperçu pour juger de la respiration.
   Le côte-à-côte n'est donc autorisé que là où l'aperçu reçoit une largeur
   plausible. En dessous, il passe sous le formulaire, où il occupe toute la
   largeur du conteneur — c'est-à-dire exactement celle qu'aura la vraie page.
   (Une requête de conteneur réglerait cela proprement ; elle demanderait de
   reprendre .split sur les dix-huit pages publiques, ce qui n'est pas le sujet
   de ce lot.) */
@media (min-width: 1500px) {
  .composer {
    grid-template-columns: minmax(0, var(--measure)) minmax(0, 1fr);
    gap: var(--sp-7);
    align-items: start;
  }
  /* Collé au sommet pendant qu'on écrit : le champ « Paragraphes » est en bas
     du formulaire, et un aperçu resté en haut de page n'aurait plus servi à
     rien passé le premier tiers de la saisie. */
  .composer__apercu {
    position: sticky;
    top: calc(var(--header-h) + var(--sp-4));
  }
}

/* Sans JavaScript, gestion.js ne remplit rien : le cadre reste vide et
   disparaît. Un aperçu qui ne s'actualisera jamais est pire qu'un aperçu
   absent — il se lit comme une panne. */
.composer__apercu:empty { display: none; }

.apercu-cadre {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  overflow: hidden;
}
.apercu-cadre__titre {
  margin: 0;
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line);
}
.apercu-cadre__brouillon { text-transform: none; letter-spacing: normal; color: var(--brass); }
/* Le rendu réel vit ici. Il est en lecture seule au sens propre : ce qu'il
   contient n'est pas cliquable, sinon un lien de la section emmènerait le
   gestionnaire hors de son formulaire à moitié rempli. */
.apercu-cadre__page { padding: var(--sp-4); }
.apercu-cadre__page a { pointer-events: none; }
/* La section rendue porte ses propres marges de page : dans un cadre d'aperçu
   elles ajouteraient 112 px de blanc en haut et en bas d'un bloc qu'on regarde
   justement pour sa composition. */
.apercu-cadre__page > .section { padding-block: 0; background: transparent; }
.apercu-cadre__page .container { padding-inline: 0; }

/* --- En-tête sur les écrans très étroits ---------------------------------- */
/* Sous 380 px — iPhone SE de première génération, et tout appareil dont
   l'utilisateur a agrandi la police système — l'en-tête ne tient plus : 40 px de
   gouttière + 87 de marque + 203 de navigation + deux écarts de 24 font 354 px
   de largeur minimale, soit 34 de trop sur un écran de 320. La page s'élargit
   alors au-delà du cadre et, body{overflow-x:hidden} étant conservé, ce qui
   dépasse devient inatteignable — le mécanisme du constat 1.1, cette fois sur
   l'en-tête : la bascule de thème sortait de l'écran.
   Seuls les écarts bougent, du cran 5 au cran 2 du barème. Les cibles tactiles
   n'en dépendent pas : elles sont tenues par min-height et par le retrait
   horizontal des liens, qui ne changent ni l'un ni l'autre. */
@media (max-width: 380px) {
  .site-header__inner { gap: var(--sp-2); }
  .nav { gap: var(--sp-2); }
}

/* --- Sommaire de page ----------------------------------------------------- */
.toc {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.toc a {
  display: inline-block;
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  transition: all var(--dur-fast) var(--ease);
}
.toc a:hover { color: var(--pine); border-color: var(--pine); background: var(--pine-wash); }

/* --- Cadres de contenu externe (iframes) ---------------------------------- */
.embed {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.embed__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  background: var(--paper-2);
  font-size: var(--step--2);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.embed__head a { color: var(--sea); text-decoration: none; border-bottom: 1px solid transparent; text-transform: none; letter-spacing: .02em; white-space: nowrap; }
.embed__head a:hover { border-bottom-color: currentColor; }
.embed__head a svg { display: inline-block; vertical-align: -2px; margin-left: .35em; }
.embed__head > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Sans aspect-ratio — iPad Safari 14 et antérieurs — une iframe en
   position:absolute avec height:100% se résout à ZÉRO : on clique « Afficher », le
   bloc de consentement disparaît et rien n'apparaît. Dix cadres concernés. Le
   min-height sert de plancher ; il s'efface dès que aspect-ratio est disponible. */
.embed__frame { position: relative; width: 100%; min-height: 320px; }
@supports (aspect-ratio: 16 / 9) {
  .embed__frame { min-height: 0; }
}
.embed__frame iframe { width: 100%; border: 0; display: block; }
.embed--ratio-16-9 .embed__frame { aspect-ratio: 16 / 9; }
.embed--ratio-4-3 .embed__frame { aspect-ratio: 4 / 3; }
.embed--ratio-1-1 .embed__frame { aspect-ratio: 1 / 1; }
.embed--ratio-16-9 .embed__frame iframe,
.embed--ratio-4-3 .embed__frame iframe,
.embed--ratio-1-1 .embed__frame iframe { position: absolute; top: 0; right: 0; bottom: 0; left: 0; inset: 0; height: 100%; }

/* Contenu tiers chargé seulement à la demande du visiteur */
.embed__consent {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  text-align: center;
  padding: clamp(2rem, 1rem + 4vw, 4rem) var(--sp-5);
  background: var(--paper-2);
  background:
    linear-gradient(color-mix(in srgb, var(--paper-2) 88%, transparent),
                    color-mix(in srgb, var(--paper-2) 88%, transparent)),
    var(--paper-3);
}
.embed__consent svg { width: 30px; height: 30px; color: var(--ink-3); }
.embed__title { margin: 0; font-family: var(--font-body); font-size: var(--step-0); font-weight: 600; line-height: 1.4; }
.embed__consent p {
  margin: 0;
  max-width: 46ch;
  font-size: var(--step--1);
  color: var(--ink-2);
}
.embed__consent .tiny { color: var(--ink-3); }

/* Message de panne inséré par soursac.js dans .embed__frame après 8 s sans
   chargement (contrat §3). Il occupe tout le cadre : sur un rectangle vide de
   500 px de haut, un bandeau en pied ne serait pas vu. Le lien direct est la porte
   de sortie — c'est lui qui fait la différence entre « le site est cassé » et
   « ce service-là ne répond pas ». */
.embed__panne {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin: 0;
  padding: var(--sp-5);
  text-align: center;
  background: var(--paper-2);
  color: var(--ink);
  font-size: var(--step--1);
  line-height: 1.55;
}
.embed__panne a {
  color: var(--pine);
  font-weight: 500;
  padding: .6rem .2rem;
}

/* --- Bandeau d'appel ------------------------------------------------------ */
.banner {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--pine-solid);
  color: var(--on-pine);
}
.banner__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  padding-block: clamp(2.5rem, 1.5rem + 3vw, 4.5rem);
}
.banner h2 { color: var(--on-dark); margin: 0 0 var(--sp-3); max-width: 22ch; }
.banner p { color: var(--on-dark-2); max-width: 52ch; margin: 0; font-size: var(--step--1); }
.banner__deco {
  position: absolute;
  right: -6%;
  top: 50%;
  transform: translateY(-50%);
  width: min(380px, 40%);
  opacity: .1;
  z-index: -1;
  color: var(--on-dark);
  pointer-events: none;
}

/* --- Actualités ----------------------------------------------------------- */
.news { list-style: none; margin: 0; padding: 0; }
.news li { position: relative; padding: var(--sp-5) 0; border-bottom: 1px solid var(--line); }
.news li:first-child { border-top: 1px solid var(--line); }
.news__date {
  display: block;
  font-size: var(--step--2);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--brass);
  margin-bottom: var(--sp-2);
}
.news h3 { font-size: var(--step-2); margin: 0 0 var(--sp-2); }
.news h3 a { text-decoration: none; }
.news h3 a::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; left: 0; inset: 0; }
.news p { font-size: var(--step--1); color: var(--ink-2); margin: 0; max-width: 68ch; }
.news li:hover h3 { color: var(--pine); }

/* --- Puce d'état ---------------------------------------------------------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 4px var(--sp-3);
  font-size: var(--step--2);
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  color: var(--ink-2);
}
.pill--private { border-color: var(--brass); border-color: color-mix(in srgb, var(--brass) 40%, transparent); color: var(--brass); }
.pill--live { border-color: var(--sea); border-color: color-mix(in srgb, var(--sea) 40%, transparent); color: var(--sea); }

/* ==========================================================================
   09. GALERIES ET VISIONNEUSE
   ========================================================================== */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--sp-4);
}
.gallery__item {
  position: relative;
  display: block;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  cursor: pointer;
  aspect-ratio: 4 / 3;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.gallery__item img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 800ms var(--ease), filter var(--dur) var(--ease);
}
.gallery__item::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  background: linear-gradient(to top, rgba(11, 20, 16, .55), transparent 55%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.gallery__item:hover { border-color: var(--line-2); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.gallery__item:hover img { transform: scale(1.05); }
.gallery__item:hover::after { opacity: 1; }
.gallery__cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  padding: var(--sp-4) var(--sp-3) var(--sp-3);
  font-size: var(--step--2);
  line-height: 1.35;
  color: var(--on-dark);
  text-align: left;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.gallery__item:hover .gallery__cap,
.gallery__item:focus-visible .gallery__cap { opacity: 1; transform: translateY(0); }

/* Il n'existe pas de survol tactile : sur iPad et sur téléphone, une légende
   révélée au :hover n'est jamais révélée. Le voile est affiché avec elle, sans quoi
   le texte clair se poserait sur la photo sans fond. */
@media (hover: none) {
  .gallery__item::after { opacity: 1; }
  .gallery__cap { opacity: 1; transform: none; }
}

/* Sept vignettes du site sont en format portrait — 367 × 520 px sur le disque — et
   se font recadrer de force en 4:3 : la moitié de la hauteur disparaît. Sur des
   cartes postales anciennes, la forme fait partie du sens. Le modificateur rend sa
   forme à la vignette ; il reste à l'émettre, gallery() disposant déjà de
   photo["w"] et photo["h"] (couplage LOT-01, voir le rapport LOT-04). */
.gallery__item--portrait { aspect-ratio: 3 / 4; }

/* Visionneuse */
.lightbox {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: 300;
  display: none;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  background: rgba(9, 15, 12, .93);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.lightbox.is-open { display: flex; animation: fadeIn var(--dur) var(--ease); }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.lightbox__figure {
  margin: 0;
  max-width: min(1200px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  align-items: center;
}
.lightbox__img {
  max-width: 100%;
  max-height: calc(100vh - 11rem);
  max-height: calc(100dvh - 11rem);
  width: auto;
  object-fit: contain;
  border-radius: var(--radius);
  background: #0B120F;
}
.lightbox__cap {
  color: var(--on-dark-2);
  font-size: var(--step--1);
  text-align: center;
  max-width: 70ch;
}
.lightbox__cap b { color: var(--on-dark); font-weight: 500; }
.lightbox__count { display: block; margin-top: 4px; font-size: var(--step--2); letter-spacing: .1em; color: rgba(194, 204, 199, .65); }

.lightbox__btn {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px; height: 48px;
  background: rgba(246, 244, 238, .08);
  border: 1px solid rgba(246, 244, 238, .22);
  border-radius: 50%;
  color: var(--on-dark);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.lightbox__btn:hover { background: rgba(246, 244, 238, .2); border-color: rgba(246, 244, 238, .5); }
.lightbox__btn svg { width: 20px; height: 20px; }
.lightbox__close { top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem); }
.lightbox__prev { left: clamp(.5rem, 2vw, 2rem); top: 50%; transform: translateY(-50%); }
.lightbox__next { right: clamp(.5rem, 2vw, 2rem); top: 50%; transform: translateY(-50%); }
@media (max-width: 620px) {
  .lightbox__prev { left: var(--sp-4); top: auto; bottom: var(--sp-4); transform: none; }
  .lightbox__next { right: var(--sp-4); top: auto; bottom: var(--sp-4); transform: none; }
  .lightbox__img { max-height: calc(100vh - 14rem); max-height: calc(100dvh - 14rem); }
}

/* ==========================================================================
   10. PIED DE PAGE
   ========================================================================== */

.site-footer {
  background: var(--paper-2);
  border-top: 1px solid var(--line);
  padding-block: var(--sp-8) var(--sp-6);
}
.site-footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: var(--sp-6);
  padding-bottom: var(--sp-7);
  border-bottom: 1px solid var(--line);
}
@media (max-width: 900px) { .site-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .site-footer__grid { grid-template-columns: minmax(0, 1fr); } }

.site-footer h2 {
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0 0 var(--sp-4);
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: var(--sp-3); }
.site-footer a {
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--ink-2);
  transition: color var(--dur-fast) var(--ease);
}
/* Les liens du pied de page faisaient 21 px de haut : conformes par exception,
   pénibles pour une main qui tremble. Portée sur « li a » pour ne pas atteindre la
   marque typographique, qui n'est pas dans une liste. */
.site-footer li a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.site-footer a:hover { color: var(--pine); }
.site-footer__intro p { font-size: var(--step--1); color: var(--ink-2); max-width: 34ch; margin-top: var(--sp-4); }
.site-footer .wordmark__main { font-size: 17px; }

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-top: var(--sp-5);
  font-size: var(--step--2);
  color: var(--ink-2);
}
.site-footer__bottom ul { display: flex; flex-wrap: wrap; gap: var(--sp-5); }
.site-footer__bottom li { margin: 0; }

/* Retour en haut */
.to-top {
  position: fixed;
  right: clamp(1rem, 2vw, 2rem);
  bottom: clamp(1rem, 2vw, 2rem);
  z-index: 90;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px; height: 46px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: 50%;
  color: var(--pine);
  cursor: pointer;
  box-shadow: var(--shadow-md);
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
}
.to-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }
/* seul élément focalisable qui porte déjà une ombre : sans cette ligne, l'anneau
   double la remplacerait purement et simplement */
.to-top:focus-visible { box-shadow: 0 0 0 3px var(--focus-halo), var(--shadow-md); }
.to-top svg { width: 18px; height: 18px; }

/* Bascule de thème */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;   /* 40 px était sous la cible tactile minimale */
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  color: inherit;
  flex-shrink: 0;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.theme-toggle:hover { border-color: currentColor; }
.theme-toggle svg { width: 18px; height: 18px; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}

/* ==========================================================================
   11. MOUVEMENT ET RÉVÉLATIONS
   ========================================================================== */

/* L'invisibilité de départ est conditionnée à .js-reveal, classe que soursac.js
   pose sur <html> une fois qu'il est certain de pouvoir révéler (LOT-05). Si le
   script ne s'exécute pas, ou échoue avant d'en arriver là, le contenu reste
   simplement visible : l'écran muet n'est jamais l'état par défaut.
   La règle .no-js [data-reveal] qui figurait ici n'a plus d'objet — elle rattrapait
   ce que ce conditionnement rend impossible. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.js-reveal [data-reveal].is-revealed { opacity: 1; transform: none; }

/* ==========================================================================
   12. ACCESSIBILITÉ, IMPRESSION, PRÉFÉRENCES SYSTÈME
   ========================================================================== */

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .js-reveal [data-reveal] { opacity: 1; transform: none; }
  .hero__media img { animation: none; }
}

/* Le bloc ne visait que :root, à (0,1,0). En thème sombre il était systématiquement
   écrasé par :root:not([data-theme="light"]) et par :root[data-theme="dark"], tous
   deux à (0,2,0) : le réglage « contraste élevé » du système ne servait donc qu'en
   thème clair. Les trois blocs ci-dessous rétablissent l'effet dans les deux thèmes,
   en conservant l'ordre automatique → manuel qui donne le dernier mot à la bascule.
   Les valeurs de filets sont recalées sur la nouvelle base : l'ancien #B9B3A4 (2,09)
   serait aujourd'hui plus CLAIR que --line lui-même. */
@media (prefers-contrast: more) {
  :root { --ink-2: #33403B; --line: #7A7668; --line-2: #5E5A50; }
}
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --ink-2: #C8D2CD; --line: #7E8B84; --line-2: #9AA6A0; }
}
@media (prefers-contrast: more) {
  :root[data-theme="dark"] { --ink-2: #C8D2CD; --line: #7E8B84; --line-2: #9AA6A0; }
}

@media print {
  .site-header, .nav, .to-top, .hero__scroll, .theme-toggle, .lightbox, .embed { display: none !important; }
  /* La barre de session est un outil d'écran : elle n'a rien à faire sur un
     tirage papier, pas plus que l'en-tête. Sa cale part avec elle, sinon
     chaque page imprimée commencerait par une bande blanche de 40 px. */
  .nav__espace { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .hero { min-height: auto; padding-top: 0; }
  .hero__media, .hero__scrim { display: none; }
  .hero h1, .hero__lead { color: #000; text-shadow: none; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #555; word-break: break-all; }
  .section { padding-block: 1.5rem; page-break-inside: avoid; }
}
