/* nazzareno.fr — éditorial sobre, papier chaud, encre, un accent vert bouteille */

@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/spectral-400.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/spectral-400-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/spectral-500.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/spectral-600.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibmplexmono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibmplexmono-500.woff2") format("woff2");
}

:root {
  --paper: #f6f3ec;
  --ink: #201d18;
  --ink-soft: #6b6457;
  --accent: #2c5c42;
  --rule: #ddd6c8;
  --serif: "Spectral", Georgia, "Times New Roman", serif;
  --mono: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #191713;
    --ink: #e9e4d8;
    --ink-soft: #9a9284;
    --accent: #7fb695;
    --rule: #35312a;
  }
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--serif);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

.wrap {
  max-width: 43rem;
  margin: 0 auto;
  padding: 0 1.5rem;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
a:hover {
  color: var(--ink);
}

::selection {
  background: var(--accent);
  color: var(--paper);
}

/* ---- header ---- */

header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
@supports (backdrop-filter: blur(10px)) {
  header {
    background: color-mix(in srgb, var(--paper) 86%, transparent);
    backdrop-filter: blur(10px) saturate(1.15);
  }
}
header .wrap {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
}
header a {
  color: var(--ink);
  text-decoration: none;
}
header a:hover {
  color: var(--accent);
}
header .brand {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
header .nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex: none;
}
/* le groupe de gauche cede la place en premier : c'est le nom qui se tronque,
   jamais le lien projets ni le bouton contact. */
header .nav-start {
  flex: 0 1 auto;
  min-width: 0;
}
header .section {
  color: var(--ink-soft);
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}
header .section:hover,
header .section:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
header .lang {
  color: var(--ink-soft);
  white-space: nowrap;
}
header .lang a {
  color: var(--accent);
  /* souligné : la langue active ne doit pas se distinguer que par la couleur */
  text-decoration: underline;
  text-underline-offset: 3px;
}
header .contact {
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
  color: var(--accent);
  line-height: 1;
  white-space: nowrap;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
header .contact:hover,
header .contact:focus-visible {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}
/* sous 30rem, quatre elements ne tiennent plus : le nom de famille tombe
   plutot que de finir en points de suspension. */
@media (max-width: 30rem) {
  header .brand-rest {
    display: none;
  }
}
@media (max-width: 26rem) {
  header .wrap {
    gap: 0.6rem;
    font-size: 0.75rem;
    /* le ruban seul deborde de la gouttiere du corps de page : quatre elements
       sur 320 px ne passent pas autrement. */
    padding-left: 1rem;
    padding-right: 1rem;
  }
  header .nav {
    gap: 0.6rem;
  }
  header .contact {
    padding: 0.3rem 0.6rem;
  }
}

/* les ancres ne doivent pas passer sous le ruban */
[id] {
  scroll-margin-top: 4.5rem;
}

/* ---- hero ---- */

.hero {
  padding: 5.5rem 0 4.5rem;
}
.hero .kicker {
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 1.25rem;
}
.hero h1 {
  font-size: clamp(2.2rem, 6vw, 3.1rem);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin-bottom: 1.5rem;
}
.hero .lede {
  font-size: 1.1875rem;
  color: var(--ink-soft);
  max-width: 36rem;
}
.hero .links {
  margin-top: 2.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  font-family: var(--mono);
  font-size: 0.875rem;
}

/* entrée en douceur, désactivée si l'utilisateur préfère */
@media (prefers-reduced-motion: no-preference) {
  .hero > * {
    animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }
  .hero > *:nth-child(2) { animation-delay: 0.08s; }
  .hero > *:nth-child(3) { animation-delay: 0.16s; }
  .hero > *:nth-child(4) { animation-delay: 0.24s; }
  @keyframes rise {
    from { opacity: 0; transform: translateY(0.6rem); }
    to { opacity: 1; transform: none; }
  }
}

/* ---- sections ---- */

section {
  border-top: 1px solid var(--rule);
  padding: 3.25rem 0;
}
.label {
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.4rem;
}
.label b {
  font-weight: 500;
  color: var(--accent);
  margin-right: 0.75rem;
}
section h2 {
  font-size: 1.5rem;
  font-weight: 600;
  margin-bottom: 1rem;
  letter-spacing: -0.01em;
}
section p + p {
  margin-top: 1rem;
}
.muted {
  color: var(--ink-soft);
}
.meta {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
  margin-top: 1.25rem;
}

/* listes d'offres et de projets */

.items {
  list-style: none;
  margin-top: 0.5rem;
}
.items li {
  border-top: 1px solid var(--rule);
  padding: 1.4rem 0;
  display: grid;
  grid-template-columns: 11rem 1fr;
  gap: 1.25rem;
}
.items li:first-child {
  border-top: none;
  padding-top: 0.5rem;
}
.items h3 {
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.45;
}
.items h3 a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 1px;
}
.items h3 a:hover,
.items h3 a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.items .more {
  font-family: var(--mono);
  font-size: 0.78125rem;
  letter-spacing: 0.02em;
  margin-top: 0.5rem;
}
.items p {
  color: var(--ink-soft);
  font-size: 1rem;
}
@media (max-width: 40rem) {
  .items li {
    grid-template-columns: 1fr;
    gap: 0.35rem;
  }
}

/* étapes d'un engagement : ici la numérotation porte un ordre réel */

.subhead {
  font-size: 1.0625rem;
  font-weight: 600;
  margin-top: 2.25rem;
}
.steps {
  list-style: none;
  counter-reset: step;
  margin-top: 1.25rem;
  display: grid;
  gap: 1.25rem;
}
.steps li {
  display: grid;
  grid-template-columns: 2.25rem 1fr;
}
.steps li::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--accent);
  line-height: 1.75;
}
.steps strong {
  font-weight: 600;
}
.steps p {
  color: var(--ink-soft);
  font-size: 1rem;
  margin-top: 0.15rem;
}

/* ---- contact ---- */

.contact .big {
  display: inline-block;
  font-size: clamp(1.35rem, 4.5vw, 1.9rem);
  font-weight: 500;
  margin: 0.75rem 0 1.5rem;
}

/* ---- footer ---- */

footer {
  border-top: 1px solid var(--rule);
}
footer .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.5rem;
  justify-content: space-between;
  padding-top: 1.4rem;
  padding-bottom: 2.2rem;
  font-family: var(--mono);
  font-size: 0.78125rem;
  color: var(--ink-soft);
}
footer nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}
footer a {
  color: var(--ink-soft);
}
footer a:hover {
  color: var(--accent);
}

/* ---- articles projet ---- */

/* La marge laterale vient de .wrap, mais ce selecteur est plus specifique et
   la remettait a zero: sur mobile, le texte de toutes les pages projet
   touchait le bord de l'ecran. */
.article main {
  padding: 3.25rem 1.5rem 4rem;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.article .kicker {
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}
.article h1 {
  font-size: clamp(1.8rem, 5vw, 2.5rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.article .standfirst {
  font-size: 1.1875rem;
  color: var(--ink-soft);
  max-width: 38rem;
}
.article .facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  margin-top: 0.4rem;
}
.article .facts span {
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.2rem 0.7rem;
  color: var(--ink-soft);
}
.article h2 {
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-top: 1.6rem;
  border-top: 1px solid var(--rule);
  padding-top: 1.75rem;
}
.article p,
.article ul,
.article ol {
  max-width: 38rem;
}
/* la remise a zero globale mange le retrait des listes : sans cette marge,
   les numeros d'un <ol> tombent hors du bloc et disparaissent sur mobile. */
.article ul,
.article ol {
  margin-left: 1.15rem;
}
.article li + li {
  margin-top: 0.4rem;
}
.article .aside {
  border-left: 2px solid var(--accent);
  padding: 0.15rem 0 0.15rem 1.1rem;
  color: var(--ink-soft);
}

/* planche image sur fond sombre, pour les rendus graphiques */
figure.plate {
  margin: 0.75rem 0;
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
  background: #0c1018;
}
figure.plate img {
  display: block;
  width: 100%;
  height: auto;
}
figure.plate figcaption {
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.5;
  color: #8a93a4;
  padding: 0.75rem 0.9rem;
  border-top: 1px solid #1c2432;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1rem;
  justify-content: space-between;
}
figure.plate figcaption a {
  color: #9fc7ae;
}

.backlink {
  font-family: var(--mono);
  font-size: 0.8125rem;
  border-top: 1px solid var(--rule);
  padding-top: 1.75rem;
  margin-top: 1rem;
}

/* ---- gabarit billet ----

   Porte par body class="article post". Le gabarit .article reste en place pour
   les autres pages projet ; ce bloc le suit dans la feuille, a specificite
   egale, et se contente donc de lui retirer son decorum de magazine : pastilles
   bordees, filet au-dessus de chaque titre, titre de une, chapo en gros corps.
   Ce qui reste est un billet date, ecrit au fil, signe a la fin. */

.post main.wrap {
  /* un peu plus large que les 43rem du reste du site : sur un blog la colonne
     porte aussi un tableau et un canevas, pas seulement de la prose. */
  max-width: 46rem;
}
.post main {
  padding-top: 2.75rem;
  gap: 1.05rem;
}

/* en-tete de billet : la date precede le titre, comme sur un blog date */
.post .post-date {
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  margin-bottom: 0.1rem;
}
.post h1 {
  font-size: clamp(1.55rem, 4vw, 2.05rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0;
  margin-bottom: 0.3rem;
}
.post .standfirst {
  /* meme corps que le texte : un billet n'a pas de chapo de magazine, juste un
     premier paragraphe un peu en retrait. */
  font-size: 1.0625rem;
  max-width: 40rem;
}

/* ligne de mots-cles, a la place des pastilles */
.post .facts {
  display: block;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.7;
  color: var(--ink-soft);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: 0.45rem 0;
  margin: 0.35rem 0 0.2rem;
}
.post .facts b {
  font-weight: 500;
  color: var(--ink);
  margin-right: 0.35rem;
}
.post .facts span {
  border: 0;
  border-radius: 0;
  padding: 0;
}
.post .facts span + span::before {
  content: "· ";
  color: var(--rule);
}

.post h2 {
  font-size: 1.1875rem;
  font-weight: 600;
  letter-spacing: 0;
  border-top: 0;
  padding-top: 0;
  margin-top: 1.45rem;
}

.post p,
.post ul {
  max-width: 40rem;
}

.post .aside {
  border-left: 2px solid var(--rule);
  font-size: 1rem;
}

/* signature de fin, puis le retour a l'index : un seul filet pour les deux,
   sans quoi .backlink en remettrait un seconde ligne plus bas. */
.post .byline {
  font-family: var(--mono);
  font-size: 0.78125rem;
  color: var(--ink-soft);
  border-top: 1px solid var(--rule);
  padding-top: 1.2rem;
  margin-top: 1.5rem;
}
.post .backlink {
  border-top: 0;
  padding-top: 0;
  margin-top: 0;
}

/* Route de service ?disque=<id> : le canevas seul, carre, plein cadre. Comme
   ?poster=1 et ?debug=1, elle n'est atteignable que par la barre d'adresse et
   ne coute rien a la page ordinaire -- c'est main.js qui pose la classe. Elle
   sert a produire assets/img/eclipse-disque-*.webp avec le shader de l'encart
   lui-meme, plutot qu'a redessiner a cote trois images qui divergeraient au
   premier changement de shader. */
body.vue-disque > header,
body.vue-disque > footer,
body.vue-disque main > *:not(#eclipse-sim),
body.vue-disque #eclipse-sim > *:not(.sim-canvas) {
  display: none;
}
/* main.wrap et non main : .post main.wrap est plus specifique et rendrait
   sinon ses 46rem, laissant le canevas au milieu d'une page vide. */
body.vue-disque main.wrap {
  max-width: none;
  padding: 0;
  gap: 0;
}
/* .sim porte margin: 1.5rem 0, qui decalerait le canevas vers le bas et ferait
   sortir sa derniere bande du cadre de capture. */
body.vue-disque #eclipse-sim {
  margin: 0;
}
body.vue-disque .sim-canvas {
  aspect-ratio: 1 / 1;
  margin: 0;
  border: 0;
  border-radius: 0;
}

/* ---- les trois disques au maximum ----

   Trois carres a la MEME echelle: c'est la comparaison qui porte l'information
   (le croissant parisien, les deux totalites), donc aucun des trois n'est
   recadre pour mieux remplir son cases. Ils debordent la colonne de prose de
   40rem pour occuper les 46rem du billet, et s'empilent sous 46rem. */
.disques {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin: 1.4rem 0 0.75rem;
  max-width: none;
}
@media (max-width: 46rem) {
  .disques {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
}
.disque {
  margin: 0;
}
.disque img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: #000;
}
.disque figcaption {
  margin-top: 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--ink-soft);
}
.disque figcaption b {
  display: block;
  font-family: var(--serif);
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
}
.disque figcaption span {
  display: block;
}
.disques-note {
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

/* ---- figures calculees ----

   SVG en ligne, produit par tools/eclipse/figures.py. En ligne et non en
   <img> : c'est la seule forme qui herite des variables CSS de la page, donc
   qui existe vraiment dans les DEUX schemas de couleur.

   Les couleurs de serie ne sont pas --accent. Mesure au validateur de palette,
   le vert bouteille du site tombe sous le plancher de chroma et se lit comme du
   gris des qu'il porte une courbe. Le couple ci-dessous passe les cinq
   controles (bande de clarte, chroma, separation daltonienne, plancher en
   vision normale, contraste >= 3:1) sur le papier ET sur l'encre. */

.fig {
  margin: 1.4rem 0;
  max-width: 40rem;
}
.fig-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  --serie-a: #0f8f63;
  --serie-b: #2a78d6;
}
@media (prefers-color-scheme: dark) {
  .fig-svg { --serie-a: #199e70; --serie-b: #5b8fe0; }
}

.fig-grille {
  stroke: var(--rule);
  stroke-width: 1;
}
.fig-axe {
  stroke: var(--ink-soft);
  stroke-width: 1;
}
.fig-trait {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fig-a { stroke: var(--serie-a); }
/* Les deux series des figures contagion. Couple valide au meme validateur que
   le vert de l'eclipse: cinq controles passes sur le papier ET sur l'encre. */
.fig-serie-a { stroke: var(--serie-a); }
.fig-serie-b { stroke: var(--serie-b); }
/* Intervalle bootstrap a 95 % : un trait vertical en retrait derriere le point. */
.fig-ic {
  stroke-width: 2;
  opacity: 0.5;
}
/* Sur un point, la couleur de serie passe au disque; l'anneau garde la couleur
   du fond (convention de l'eclipse), donc on re-nomme le stroke ici. */
.fig-point.fig-serie-a { fill: var(--serie-a); stroke: var(--paper); }
.fig-point.fig-serie-b { fill: var(--serie-b); stroke: var(--paper); }
/* Trace de contexte : gris de retrait, pas une seconde couleur de serie. Le
   mettre a egalite chromatique avec la courbe reelle ferait croire a deux
   mesures concurrentes, alors que l'une est le sujet et l'autre le repere. */
.fig-repere,
.fig-repere-trait {
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 1.5;
}
.fig-repere { stroke-width: 1.5; }
/* Bande d'episode (figure des deux crises) : un voile d'encre, pas une couleur
   de serie. Chaque crise ne dure que quelques semaines sur 36 ans, la bande
   est etroite : l'opacite est deux crans au-dessus du voile habituel pour
   rester visible sur les deux fonds. */
.fig-episode {
  fill: var(--ink);
  opacity: 0.15;
}
/* Meme figure : ~1800 sommets par courbe, le trait de 2 px des autres figures
   empate des courbes aussi bruitees; un trait fin les garde separables. */
.fig-trait-fin { stroke-width: 1.2; }

.fig-tick,
.fig-note,
.fig-serie {
  font-family: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
  font-size: 12px;
}
/* Le texte porte les jetons d'encre, jamais la couleur de la serie : c'est la
   marque a cote de lui qui porte l'identite. */
.fig-tick { fill: var(--ink-soft); }
.fig-tick-y { text-anchor: end; }
.fig-tick-x { text-anchor: middle; }
.fig-note { fill: var(--ink); }
.fig-serie { fill: var(--ink); font-size: 12.5px; }
.fig-titre { fill: var(--ink-soft); font-family: var(--mono); font-size: 12px; }

/* Anneau dans la couleur du fond plutot qu'un contour : c'est ce qui detache
   le point quand il tombe pile sur une courbe. */
.fig-point {
  fill: var(--ink);
  stroke: var(--paper);
  stroke-width: 2;
}

.fig figcaption {
  margin-top: 0.55rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-soft);
}
.fig figcaption em { font-style: italic; }

/* Vue tabulaire de chaque figure. Repliee par defaut, et <details> n'exige pas
   une ligne de JavaScript : la page tient sa promesse jusque dans les figures. */
.fig-data {
  margin-top: 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
}
.fig-data summary {
  cursor: pointer;
  color: var(--accent);
  letter-spacing: 0.02em;
}
.fig-data summary:hover { color: var(--ink); }
.fig-data :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
/* six colonnes ne tiennent pas dans un ecran de poche : meme motif que la
   table des contacts de l'eclipse, le tableau defile dans son conteneur,
   jamais le corps de la page. */
.fig-data .scroller {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.fig-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0.5rem;
}
.fig-table th,
.fig-table td {
  border-bottom: 1px solid var(--rule);
  padding: 0.3rem 0.5rem;
  text-align: right;
  white-space: nowrap;
}
.fig-table thead th {
  color: var(--ink-soft);
  font-weight: 500;
}
.fig-table th:first-child,
.fig-table td:first-child { text-align: left; }

/* ---- explorateur contagion ----

   L'encart interactif de la page contagion. Cache par defaut : c'est ui.js qui
   le revele apres avoir charge le JSON, et sans JavaScript le <noscript> porte
   les memes chiffres. Les deux barres reprennent exactement les couleurs de
   serie des figures, deja validees sur les deux fonds. */

.explorateur {
  margin: 1.4rem 0;
  max-width: 40rem;
  border: 1px solid var(--rule);
  padding: 1.1rem 1.25rem;
  background: var(--paper);
  --serie-a: #0f8f63;
  --serie-b: #2a78d6;
}
@media (prefers-color-scheme: dark) {
  .explorateur { --serie-a: #199e70; --serie-b: #5b8fe0; }
}
.explo-titre {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
  margin-bottom: 0.75rem;
}
.explo-label label {
  font-family: var(--mono);
  font-size: 0.8125rem;
}
.explorateur input[type="range"] {
  display: block;
  width: 100%;
  margin: 0.5rem 0 1rem;
  accent-color: var(--accent);
}
.explorateur :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.explo-ligne {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 0.6rem;
  align-items: center;
}
.explo-ligne + .explo-ligne { margin-top: 0.45rem; }
.explo-ligne span {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
}
.explo-piste {
  position: relative;
  height: 0.75rem;
  background: var(--rule);
  background: color-mix(in srgb, var(--rule) 55%, var(--paper));
}
.explo-barre {
  height: 100%;
  width: 0;
  transition: width 0.15s ease;
}
.explo-a { background: var(--serie-a); }
.explo-b { background: var(--serie-b); }
/* La ligne pleine periode, posee par ui.js a rho*100 % sur les deux pistes :
   la reference que la brute quitte et que la corrigee garde. */
.explo-repere {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--ink);
  opacity: 0.5;
}
#explo-valeurs {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
}
.explo-sans-js {
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-soft);
}
@media (prefers-reduced-motion: reduce) {
  .explo-barre { transition: none; }
}
@media (max-width: 26rem) {
  .explo-ligne { grid-template-columns: 4.5rem 1fr; }
}

/* ---- pages légales ---- */

.legal main {
  padding: 3.5rem 0 4rem;
}
.legal h1 {
  font-size: 1.9rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 0.5rem;
}
.legal .updated {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
  margin-bottom: 2.5rem;
}
.legal h2 {
  font-size: 1.2rem;
  font-weight: 600;
  margin: 2.25rem 0 0.75rem;
}
.legal p + p {
  margin-top: 0.75rem;
}
.legal ul {
  margin: 0.75rem 0 0.75rem 1.25rem;
}
.legal li + li {
  margin-top: 0.35rem;
}

/* ---- simulateur d'eclipse ---- */

.sim {
  margin: 1.5rem 0;
  /* reference de positionnement de la superposition ?debug=1, et d'elle seule */
  position: relative;
}

/* huit colonnes ne tiennent pas dans les 43rem de .wrap: la table defile
   toute seule, jamais le corps de la page. tabindex la rend atteignable au
   clavier, sans quoi le defilement serait reserve a la souris. */
.sim .scroller {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.sim .scroller:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
table.contacts {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: 0.8125rem;
  margin: 1rem 0;
}
table.contacts caption {
  font-family: var(--serif);
  font-size: 0.875rem;
  color: var(--ink-soft);
  text-align: left;
  padding-bottom: 0.6rem;
  /* la legende appartient a la table, donc elle se replierait sur la largeur
     de la table (900 px) et non sur celle de la fenetre: sur un telephone, la
     moitie partirait hors champ. On la borne a la largeur visible et on
     l'epingle a gauche pour qu'elle reste lisible quand la table defile. */
  position: sticky;
  left: 0;
  max-width: min(100vw, 43rem);
}
table.contacts th,
table.contacts td {
  border-bottom: 1px solid var(--rule);
  padding: 0.5rem 0.6rem;
  text-align: right;
  vertical-align: top;
  white-space: nowrap;
}
table.contacts th:first-child,
table.contacts td:first-child {
  text-align: left;
  font-family: var(--serif);
}
table.contacts thead th {
  color: var(--ink-soft);
  font-weight: 500;
  vertical-align: bottom;
}
table.contacts td.absent {
  color: var(--ink-soft);
  text-align: center;
  font-style: italic;
}

/* glose d'en-tete et fuseau: lus a voix haute avec la cellule, discrets a l'oeil */
table.contacts .gloss {
  display: block;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 400;
  font-style: normal;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  white-space: normal;
  max-width: 9rem;
}
table.contacts thead .gloss {
  margin-top: 0.15rem;
}
table.contacts .utc {
  display: block;
  font-size: 0.6875rem;
  color: var(--ink-soft);
}
.sim .sim-note {
  font-size: 1rem;
  color: var(--ink-soft);
}

/* Cache par defaut : tant que main.js n'a pas confirme un contexte WebGL2
   utilisable, le canevas ne doit strictement rien afficher, ni gener la
   lecture. C'est data-webgl="ok" qui le revele, jamais l'inverse — la page
   doit rester identique, table comprise, si JS ou WebGL2 est absent. */
.sim-canvas {
  display: none;
  width: 100%;
  aspect-ratio: 12 / 5;
  /* Le glisse horizontal sur le canevas tourne le regard ; le glisse vertical
     doit rester au defilement de la page, sinon on emprisonne le doigt. */
  touch-action: pan-y;
  margin-bottom: 1rem;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: #0d0d0f;
}
.sim[data-webgl="ok"] .sim-canvas {
  display: block;
}

/* Le poster est l'exacte image miroir du canevas : visible par defaut, cache
   des que data-webgl="ok" revele le simulateur. Sans JavaScript, sans WebGL2
   ou sans l'extension de filtrage flottant, c'est lui qui montre ce que la
   page a a dire ; le tableau des contacts, lui, reste la dans tous les cas.
   loading="lazy" dans le balisage evite le telechargement quand le simulateur
   demarre avant que l'image n'approche du champ. */
.sim-poster {
  display: block;
  margin: 0 0 1rem;
}
.sim-poster img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: #0d0d0f;
}
.sim-poster figcaption {
  margin-top: 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-soft);
}
.sim[data-webgl="ok"] .sim-poster {
  display: none;
}

/* Superposition ?debug=1. Aucune page publique ne la porte : elle n'est creee
   qu'a la demande, par main.js. */
.sim-debug {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 2;
  max-width: calc(100% - 1rem);
  overflow-x: auto;
  padding: 0.4rem 0.6rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  line-height: 1.5;
  white-space: pre;
  color: #d7f5c8;
  background: rgba(0, 0, 0, 0.72);
  border-radius: 3px;
  pointer-events: none;
}
.sim-canvas:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
/* Sous 48rem les deux panneaux (lieu gauche, lieu droit) s'empilent
   verticalement plutot que cote a cote : main.js lit la forme du cadre et
   decoupe en consequence, il n'y a donc qu'un seul point de rupture, ici. */
@media (max-width: 48rem) {
  .sim-canvas {
    aspect-ratio: 4 / 5;
  }
}

/* Commandes du simulateur. Comme le canevas, elles n'existent que si main.js a
   confirme un contexte WebGL2 : sans JavaScript la page reste exactement celle
   des taches 10 et 11, tableau compris. */
.sim-controls {
  display: none;
}
.sim[data-webgl="ok"] .sim-controls {
  display: block;
  margin-bottom: 1.25rem;
  /* laisse l'agent utilisateur peindre les commandes natives dans le bon
     schema : c'est surtout la piste du curseur de frise qui en depend. */
  color-scheme: light dark;
}

.sim-controls .sim-lieux,
.sim-controls .sim-lectures {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem 1.25rem;
}
.sim-controls .champ {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.sim-controls label {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
}
.sim-controls select,
.sim-controls button {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink);
  background: var(--paper);
  /* var(--rule) sert de filet de separation partout ailleurs sur le site, et il
     ne vaut que 1,3 sur le papier : sur un bord de commande c'est en dessous
     du 3 exige par WCAG 1.4.11, alors que ce trait est la SEULE chose qui dise
     ou commence le bouton. --ink-soft donne 5,3 en clair et 5,8 en sombre. */
  border: 1px solid var(--ink-soft);
  border-radius: 3px;
  padding: 0.3rem 0.45rem;
  max-width: 100%;
}
.sim-controls button {
  cursor: pointer;
  min-width: 6rem;
}
.sim-controls select:hover,
.sim-controls button:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.sim-controls :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sim-controls .sim-frise {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0.75rem 0 0;
}
.sim-controls input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  accent-color: var(--accent);
}

.sim-controls .sim-lectures {
  margin: 0.75rem 0 0;
}
.sim-controls output {
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

@media (max-width: 48rem) {
  .sim-controls .sim-lieux,
  .sim-controls .sim-lectures {
    grid-template-columns: 1fr;
  }
}

/* Visible des lecteurs d'ecran, invisible a l'oeil : reserve aux etiquettes
   dont le role est evident visuellement mais pas dans l'arbre d'accessibilite. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
