/* ═══════════════════════════════════════════════════════════════
   Observer — vitrine publique
   Type : Chakra Petch (display) · Geist (corps) · Geist Mono (labels)

   Les couleurs, rayons et l'échelle des intitulés ne sont PAS définis ici :
   ils viennent de `tokens.css`, que le panel client lit aussi. Une seule
   liste, donc aucune dérive possible entre le site et l'espace client.
═══════════════════════════════════════════════════════════════ */

@import url("tokens.css");

/* ─── Ce qui reste propre à la vitrine : ses polices ───
   Mêmes familles que le panel, mais servies par le CDN Google plutôt
   qu'auto-hébergées : une page publique n'a pas de session à protéger. */
:root {
  --font-display: 'Chakra Petch', 'Geist', system-ui, sans-serif;
  --font-body:    'Geist', system-ui, sans-serif;
  --font-mono:    'Geist Mono', ui-monospace, monospace;
}

/* ─── Reset & base ─── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) var(--bg);
}

a { color: inherit; text-decoration: none; }
img, svg { max-width: 100%; }
code {
  font-family: var(--font-mono);
  font-size: .85em;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: .1em .35em;
}

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  top: -48px;
  left: 1rem;
  z-index: 200;
  padding: .5rem 1rem;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-control);
  font-size: .85rem;
  transition: top .15s;
}
.skip-link:focus { top: .75rem; }

/* ─── Layout ─── */
.container {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 1.5rem;
}
.container-narrow { max-width: 760px; }

.section { padding: 6rem 0; }
.section-alt {
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.section-head {
  max-width: 640px;
  margin-bottom: 3.5rem;
}
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking-wide);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: .9rem;
}
.section-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.6rem, 3.4vw, 2.3rem);
  line-height: 1.2;
  letter-spacing: .01em;
  margin-bottom: 1rem;
}
.section-desc {
  color: var(--fg-muted);
  font-size: 1rem;
  line-height: 1.7;
}

/* ─── Boutons ─── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .7rem 1.5rem;
  border-radius: var(--r-control);
  font-size: .9rem;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;
  transition: background-color .15s, border-color .15s, color .15s, transform .15s;
}
.btn-primary {
  background: var(--accent);
  color: var(--accent-fg);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { transform: translateY(1px); }
.btn-outline {
  border: 1px solid var(--border-strong);
  color: var(--fg);
  background: transparent;
}
.btn-outline:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.btn-ghost {
  color: var(--fg-muted);
  padding-left: .75rem;
  padding-right: .75rem;
}
.btn-ghost:hover { color: var(--fg); }

/* ─── Chips (niveaux de confiance) ─── */
.chip {
  display: inline-flex;
  align-items: center;
  padding: .12rem .5rem;
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .03em;
  white-space: nowrap;
}
/* Les pastilles écrivent un MOT, à .68rem — 10,9 px. Le seuil qui s'applique
   est donc 4,5:1, pas les 3:1 d'une série de graphe : c'est `--tier-*-ink`
   qu'il faut ici, jamais `--tier-*`. La distinction est expliquée dans
   l'en-tête de tokens.css.
   Les fonds et bordures étaient écrits `rgba(23,162,194,…)` et
   `rgba(201,133,0,…)`, c'est-à-dire les teintes du SOMBRE servies telles
   quelles en clair. */
.chip-det  { color: var(--tier-deterministic-ink); background: var(--tier-det-tint);  border: 1px solid var(--tier-det-line); }
/* `.chip-heur` n'est employé par AUCUNE des cinq pages du site (vérifié le
   2026-08-09, 0 occurrence). Il est jetonisé comme son voisin plutôt que
   supprimé : la suppression touche au balisage, qui n'appartient pas à ce lot. */
.chip-heur { color: var(--tier-heuristic-ink);     background: var(--tier-heur-tint); border: 1px solid var(--tier-heur-line); }

/* ═══════════════════════════ NAV ═══════════════════════════ */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  background: var(--nav-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border);
  transition: border-color .3s;
}
.nav-scrolled { border-bottom-color: var(--border-strong); }

.nav-inner {
  max-width: 1120px;
  margin: 0 auto;
  padding: .8rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 2rem;
}

.nav-logo {
  display: flex;
  align-items: center;
  gap: .6rem;
}
.nav-logo .eye { width: 34px; height: auto; flex-shrink: 0; }
.nav-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.nav-beta {
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  padding: .1rem .45rem;
  margin-top: .1rem;
}

.nav-links {
  display: flex;
  gap: 1.6rem;
  margin-left: .5rem;
}
.nav-links a {
  font-size: .87rem;
  font-weight: 500;
  color: var(--fg-muted);
  transition: color .15s;
}
.nav-links a:hover { color: var(--fg); }

/* TRANSITIONS COUPÉES LE TEMPS DE LA BASCULE — ce n'est PAS un confort.
   Mesuré : page chargée en sombre, un clic sur « clair », et `.btn-primary`
   restait `#ff6b35` avec son texte sombre, sur fond clair — illisible. Le
   jeton `--accent` valait pourtant bien `#c2410c` au niveau du bouton, et une
   sonde neuve portant la MÊME déclaration rendait la bonne couleur : seuls les
   éléments porteurs d'une `transition` sur `background-color`/`color` restaient
   à l'ancienne valeur, la propriété transitionnée n'étant pas réinvalidée quand
   le changement vient d'une variable modifiée sur un ancêtre.
   Effet secondaire bienvenu : plus de fondu de 150 ms sur toute la page. */
.theme-switching *,
.theme-switching *::before,
.theme-switching *::after {
  transition: none !important;
}

.nav-login {
  margin-left: auto;
  padding: .5rem 1.15rem;
  font-size: .85rem;
}

/* ═══════════════════════════ HERO ═══════════════════════════ */
.hero {
  position: relative;
  padding: 9.5rem 0 5.5rem;
  overflow: hidden;
}

/* Grille hexagonale — même motif que la page de connexion du panel.

   LE MOTIF EST DEVENU UN MASQUE, et la couleur en est sortie (2026-08-09).
   Il portait `stroke='rgba(255,107,53,0.05)'` À L'INTÉRIEUR d'une URI de
   données : une couleur de marque hors de tokens.css, donc celle du sombre
   servie telle quelle en clair — et impossible à corriger sur place, aucune
   `var()` n'étant interpolable dans une chaîne d'URL.

   D'où le découpage : le tracé (géométrie) reste ici, en noir opaque, et ne
   sert plus qu'à découper ; la couleur vient de `--accent-hairline`, qui suit
   l'accent du thème. Deux masques, deux éléments — celui de `.hero-hex`
   (l'estompe radiale) s'applique aussi au `::before` qu'il contient, un masque
   masquant l'élément ET sa descendance. C'est ce qui évite `mask-composite`,
   dont les préfixes WebKit ne s'accordent pas avec la version standard. */
.hero-hex {
  position: absolute;
  inset: 0;
  mask-image: radial-gradient(ellipse 75% 80% at 65% 35%, black 15%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 75% 80% at 65% 35%, black 15%, transparent 78%);
  pointer-events: none;
}
.hero-hex::before {
  content: '';
  position: absolute;
  inset: 0;
  background-color: var(--accent-hairline);
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='97' viewBox='0 0 56 97'%3E%3Cpolygon points='28,2 54,16 54,44 28,58 2,44 2,16' fill='none' stroke='%23000' stroke-width='1'/%3E%3Cpolygon points='28,50 54,64 54,92 28,106 2,92 2,64' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
  mask-size: 56px 97px;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='97' viewBox='0 0 56 97'%3E%3Cpolygon points='28,2 54,16 54,44 28,58 2,44 2,16' fill='none' stroke='%23000' stroke-width='1'/%3E%3Cpolygon points='28,50 54,64 54,92 28,106 2,92 2,64' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
  -webkit-mask-size: 56px 97px;
}

.hero-grid {
  position: relative;
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: 4rem;
  align-items: center;
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .3rem .9rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--fg-muted);
  margin-bottom: 1.6rem;
}
.badge-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
}

.hero-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.1rem, 4.6vw, 3.3rem);
  line-height: 1.15;
  letter-spacing: .005em;
  margin-bottom: 1.4rem;
  text-wrap: balance;
}
.hero-title em {
  font-style: normal;
  color: var(--accent);
}

.hero-sub {
  color: var(--fg-muted);
  font-size: 1.05rem;
  line-height: 1.75;
  max-width: 34rem;
  margin-bottom: 2rem;
}
.hero-sub strong { color: var(--fg); font-weight: 600; }

.hero-actions {
  display: flex;
  gap: .9rem;
  flex-wrap: wrap;
  margin-bottom: 1.4rem;
}

/* Le téléchargement direct, sous les boutons : volontairement discret.
   Modrinth est la voie mise en avant parce qu'elle porte la dépendance
   PacketEvents et les notes de version ; celle-ci reste ouverte parce que le
   plugin ne doit pas être distribué par un seul canal. */
.hero-alt {
  font-size: .82rem;
  color: var(--fg-subtle);
  margin: -.8rem 0 1.2rem;
}

.hero-alt a {
  color: var(--fg-subtle);
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: .2em;
}

.hero-alt a:hover {
  color: var(--fg);
  text-decoration-color: currentColor;
}

.hero-facts {
  font-family: var(--font-mono);
  font-size: .74rem;
  letter-spacing: .04em;
  color: var(--fg-subtle);
}

/* ─── Aperçu du panel (flux de détections) ─── */
.hero-feed { min-width: 0; }

.feed-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
}

.feed-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .75rem 1.1rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.feed-title {
  font-family: var(--font-mono);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--fg-muted);
}
.feed-live {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--font-mono);
  font-size: .7rem;
  color: var(--cleared);
}
.live-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--cleared);
  box-shadow: 0 0 6px var(--cleared-glow);
}

.feed-rows { list-style: none; }
.feed-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .35rem .75rem;
  padding: .8rem 1.1rem;
  border-bottom: 1px solid var(--border);
  font-size: .84rem;
}
.feed-row:last-child { border-bottom: none; }

.feed-time {
  font-family: var(--font-mono);
  font-size: .72rem;
  color: var(--fg-subtle);
}
.feed-check { font-weight: 600; }
.feed-player {
  font-family: var(--font-mono);
  font-size: .76rem;
  color: var(--fg-muted);
}
.feed-vl {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: .72rem;
  color: var(--fg-subtle);
}
.feed-row .chip { margin-left: auto; }
.feed-row .chip + .feed-vl { margin-left: .25rem; }

.feed-row-cleared { background: var(--cleared-wash); }
.feed-row-cleared .feed-check { color: var(--fg-muted); text-decoration: line-through; text-decoration-color: var(--fg-subtle); }
.feed-cleared {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: .72rem;
  color: var(--cleared);
}

.feed-foot {
  padding: .7rem 1.1rem;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
  font-size: .76rem;
  color: var(--fg-muted);
}

.feed-caption {
  margin-top: .7rem;
  text-align: right;
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .04em;
  color: var(--fg-subtle);
}

/* ═══════════════════════════ PRINCIPE ═══════════════════════════ */
.pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin-bottom: 3.5rem;
}
.pillar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 1.5rem 1.4rem;
}
.pillar h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.02rem;
  margin-bottom: .6rem;
}
.pillar p {
  font-size: .88rem;
  line-height: 1.65;
  color: var(--fg-muted);
}

.tiers {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 1.6rem 1.6rem 1.3rem;
}
.tiers-title {
  font-family: var(--font-mono);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: 1.1rem;
}
.tiers-list { list-style: none; }
.tier {
  display: grid;
  grid-template-columns: 10px 8.5rem 1fr;
  gap: .9rem;
  align-items: baseline;
  padding: .65rem 0;
  border-top: 1px solid var(--border);
}
.tier-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  align-self: center;
}
.tier-det  .tier-dot { background: var(--tier-det); }
.tier-pred .tier-dot { background: var(--tier-pred); }
.tier-heur .tier-dot { background: var(--tier-heur); }
.tier-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: .92rem;
}
/* LE POINT GARDE LA SÉRIE, LE NOM PREND L'ENCRE — c'est le même partage qu'au
   niveau des jetons. `.tier-dot` est un objet graphique (seuil 3:1, juste
   au-dessus) ; `.tier-name` est du texte à .92rem, soit 14,7 px, et le seuil
   « grand texte » de WCAG commence à 18,66 px en gras : il n'y a pas droit.
   Mesuré avant : `--tier-deterministic` en clair lisait 3,73 sur `--surface`,
   le fond du bloc `.tiers` ; avec l'encre, 5,17. */
.tier-det  .tier-name { color: var(--tier-deterministic-ink); }
.tier-pred .tier-name { color: var(--tier-prediction-ink); }
.tier-heur .tier-name { color: var(--tier-heuristic-ink); }
.tier-desc {
  font-size: .86rem;
  color: var(--fg-muted);
  line-height: 1.6;
}
/* ═══════════════════════════ PROTECTIONS ═══════════════════════════ */
.prot-group { margin-bottom: 2.2rem; }
.prot-group:last-of-type { margin-bottom: 1.6rem; }

.prot-cat {
  font-family: var(--font-mono);
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-subtle);
  margin-bottom: .8rem;
}

.prot-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: .8rem;
}

.prot-card {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: .95rem 1.1rem;
  transition: border-color .15s;
}
.prot-card:hover { border-color: var(--border-strong); }
.prot-name {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-weight: 600;
  font-size: .9rem;
  margin-bottom: .25rem;
}
.prot-desc {
  display: block;
  font-size: .8rem;
  line-height: 1.5;
  color: var(--fg-muted);
}

/* ═══════════════════════════ INSTALLATION ═══════════════════════════ */
.steps {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  counter-reset: none;
  margin-bottom: 2rem;
}
.step {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 1.5rem 1.4rem;
}
.step-num {
  display: block;
  font-family: var(--font-mono);
  font-size: .78rem;
  color: var(--accent);
  margin-bottom: .8rem;
}
.step h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.02rem;
  margin-bottom: .5rem;
}
.step p {
  font-size: .87rem;
  line-height: 1.65;
  color: var(--fg-muted);
}

/* Bloc licence Premium : le texte à gauche, l'extrait de config à droite. */
.license {
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  gap: 2rem;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 1.8rem 1.7rem;
  margin-bottom: 2rem;
}
.license-kicker {
  font-family: var(--font-mono);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: .55rem;
}
.license-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  margin-bottom: .7rem;
}
.license-copy p:not(.license-kicker) {
  font-size: .89rem;
  line-height: 1.7;
  color: var(--fg-muted);
}
.license-copy p + p { margin-top: .7rem; }
.license-code {
  font-family: var(--font-mono);
  font-size: .78rem;
  line-height: 1.75;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-control);
  padding: 1rem 1.2rem;
  overflow-x: auto;
}
.license-code code {
  background: none;
  border: none;
  padding: 0;
  font-size: 1em;
}

.install-reqs {
  font-size: .85rem;
  color: var(--fg-muted);
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
}
.req {
  font-family: var(--font-mono);
  font-size: .74rem;
  padding: .2rem .6rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  color: var(--fg);
}

/* ═══════════════════════════ TARIFS ═══════════════════════════ */
/* Quatre paliers depuis le 2026-08-01 (deux auparavant) : le rembourrage et la
   taille du prix se resserrent, sinon les cartes débordent en dessous de
   1200px avant même la première rupture. */
.pricing {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  margin-bottom: 1.8rem;
}
.price-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 1.5rem 1.3rem;
}
/* LA VEDETTE SE POSE SUR `--surface-raised`, PAS SUR `--surface-3`.
   `--surface-3` monte d'un cran en sombre mais CREUSE en clair : la carte à
   29 € se retrouvait grise au milieu de trois cartes blanches, et se lisait
   « indisponible » plutôt que « recommandée » (constaté en rendu, 2026-08-09).
   Le jeton nomme désormais l'intention, et chaque thème choisit le cran qui la
   sert. Effet de bord voulu : l'intitulé « SERVEUR », qui est en `--accent`,
   passe de 4,31 à 5,18 en clair — il n'est plus posé sur le creux. */
.price-card-premium {
  border-color: var(--accent);
  background: var(--surface-raised);
}
.price-name {
  font-family: var(--font-mono);
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: .9rem;
}
.price-card-premium .price-name { color: var(--accent); }
.price-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.65rem;
  line-height: 1.1;
  margin-bottom: .6rem;
}
.price-coverage {
  font-size: .92rem;
  font-weight: 500;
  margin-bottom: 1.2rem;
}
.price-fine {
  font-size: .76rem;
  font-weight: 400;
  color: var(--fg-subtle);
}
.price-list {
  list-style: none;
  margin-bottom: 1.6rem;
  flex: 1;
}
.price-list li {
  position: relative;
  padding: .3rem 0 .3rem 1.3rem;
  font-size: .87rem;
  color: var(--fg-muted);
}
.price-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: .78em;
  width: .55em;
  height: .28em;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}
.price-btn { width: 100%; }

.pricing-note {
  font-size: .85rem;
  color: var(--fg-muted);
  max-width: 620px;
}

/* ═══════════════════════════ CTA FINAL ═══════════════════════════ */
.cta-final {
  position: relative;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-card);
  padding: 3.5rem 2rem;
  overflow: hidden;
}
.cta-final::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 60% 90% at 50% 0%, var(--accent-veil) 0%, transparent 65%);
  pointer-events: none;
}
.cta-eye { display: flex; justify-content: center; margin-bottom: 1.4rem; }
.cta-eye .eye { width: 64px; height: auto; }
.cta-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  margin-bottom: .8rem;
}
.cta-desc {
  color: var(--fg-muted);
  font-size: .97rem;
  max-width: 460px;
  margin: 0 auto 1.8rem;
}
.cta-actions {
  position: relative;
  display: flex;
  justify-content: center;
  gap: .9rem;
  flex-wrap: wrap;
}

/* ═══════════════════════════ FOOTER ═══════════════════════════ */
.footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  padding: 3rem 0 2rem;
}
.footer-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  padding-bottom: 2rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.5rem;
}
.footer-brand {
  display: flex;
  align-items: center;
  gap: .9rem;
}
.footer-brand .eye { width: 40px; height: auto; flex-shrink: 0; }
.footer-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.footer-tagline {
  font-size: .78rem;
  color: var(--fg-subtle);
  margin-top: .15rem;
}
.footer-links {
  display: flex;
  gap: 1.4rem;
  flex-wrap: wrap;
}
.footer-links a {
  font-size: .85rem;
  color: var(--fg-muted);
  transition: color .15s;
}
.footer-links a:hover { color: var(--fg); }

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.footer-status {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-family: var(--font-mono);
  font-size: .74rem;
  color: var(--fg-muted);
}
.version-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
}
.footer-credit {
  font-family: var(--font-mono);
  font-size: .74rem;
  color: var(--fg-subtle);
}

/* ═══════════════════════════ ANIMATIONS ═══════════════════════════
   Tout est gaté : .js (posée par main.js) + prefers-reduced-motion.
   Sans JS ou en reduced-motion, le contenu est simplement visible. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity .6s ease, transform .6s ease;
  }
  .js .reveal.visible {
    opacity: 1;
    transform: translateY(0);
  }

  /* LE CADRE NE DOIT JAMAIS ÊTRE VIDE, ET UNE SEULE LIGNE EST MISE EN SCÈNE.

     L'ancienne cadence — `calc(.45s + var(--i) * .35s)` plus 0,5 s d'animation
     — laissait la carte occupée mais CREUSE : en-tête et pied dessinés, rien
     entre les deux pendant près d'une demi-seconde, et la dernière ligne posée
     à 2,0 s. Pire : la ligne « écarté — explicable par la latence » (`--i: 2`),
     qui est la thèse entière de la page montrée plutôt qu'affirmée, n'arrivait
     qu'à 1,15 s — après les deux banales.

     Ce que fait le `max(0s, …)` : les deux premières lignes (i = 0 et 1)
     donnent un délai négatif, ramené à zéro — elles sont là d'emblée, et le
     cadre est plein dès la première image. La mise en scène ne commence qu'à
     partir de i = 2, c'est-à-dire exactement sur la ligne écartée, qui apparaît
     à 0,175 s. Le pas restant est la moitié de l'ancien (.35s → .175s) : la
     séquence entière est finie à 0,85 s au lieu de 2,5 s.

     Le gattage ne change pas : sans `.js` ou en `prefers-reduced-motion`, cette
     règle n'existe pas et les quatre lignes sont simplement visibles. */
  .js .feed-row {
    opacity: 0;
    animation: feed-in .5s ease forwards;
    animation-delay: calc(max(0s, (var(--i, 0) - 1) * .175s));
  }
  @keyframes feed-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .live-dot { animation: live-blink 2s ease-in-out infinite; }
  @keyframes live-blink {
    0%, 100% { opacity: 1; }
    50%      { opacity: .35; }
  }

  /* Le symbole répond au SURVOL, et seulement à lui.
     Il suivait le curseur en continu, sur les trois yeux de la page ; ce que
     ça coûtait est expliqué dans main.js. Ce qui reste tient en une règle, et
     ne s'applique qu'au logo de la barre — le seul œil qu'on puisse survoler
     volontairement, parce qu'il est dans un lien.

     Le dessin arrive par une balise <img> : rien de son intérieur n'est
     joignable d'ici. On anime donc l'image entière — c'est aussi ce qui rend
     ce geste identique côté panel, où la marque est le même fichier. */
  .nav-logo .eye { transition: transform .18s ease-out, filter .18s ease-out; }
  .nav-logo:hover .eye,
  .nav-logo:focus-visible .eye {
    transform: scale(1.08);
    filter: brightness(1.12);
  }
}

/* ═══════════════════════════ RESPONSIVE ═══════════════════════════ */

/* Quatre cartes ne tiennent pas sous 1100px : deux rangees de deux, puis une
   seule colonne. La rupture a 640px existante ne suffisait plus. */
@media (max-width: 1100px) {
  .pricing { grid-template-columns: repeat(2, 1fr); max-width: 820px; }
}

@media (max-width: 960px) {
  .hero-grid {
    grid-template-columns: 1fr;
    gap: 3rem;
  }
  .hero { padding-top: 8rem; }
  .hero-feed { max-width: 560px; }

  .pillars { grid-template-columns: 1fr; }
  .tier { grid-template-columns: 10px 7.5rem 1fr; }
  .steps { grid-template-columns: 1fr; }
  .license { grid-template-columns: 1fr; gap: 1.4rem; }

  /* V3 — LA BARRE NE PEUT PAS SE VIDER DE SON SEUL LIEN QUI RAPPORTE.
     `.nav-links { display: none }` ne laissait que le logo et « Connexion » :
     « Tarifs » devenait inatteignable autrement qu'en faisant défiler 11 800 px
     (mesuré à 360 px). Faute d'un tiroir — le balisage n'appartient pas à ce
     lot — le lien reste, seul, et prend l'allure de l'action qu'il est.
     Les trois autres ancres sortent : elles mènent à du contenu qu'on
     rencontre de toute façon en descendant. */
  .nav-links { margin-left: auto; gap: 0; }
  .nav-links a:not([href="#tarifs"]) { display: none; }
  .nav-links a[href="#tarifs"] {
    display: inline-flex;
    align-items: center;
    padding: .5rem 1.05rem;
    border-radius: var(--r-control);
    background: var(--accent);
    color: var(--accent-fg);
    font-weight: 600;
    white-space: nowrap;
  }
  .nav-links a[href="#tarifs"]:hover {
    background: var(--accent-hover);
    color: var(--accent-fg);
  }
  /* `.nav-login` gardait `margin-left: auto` ; avec deux marges automatiques,
     l'espace libre se partage et les deux actions se retrouvent au milieu. */
  .nav-login { margin-left: 0; }
  .nav-inner { gap: .8rem; }
}

@media (max-width: 640px) {
  .section { padding: 4rem 0; }
  .section-head { margin-bottom: 2.5rem; }

  .hero { padding: 7rem 0 4rem; }
  .hero-actions .btn-primary { width: 100%; }

  .tier {
    grid-template-columns: 10px 1fr;
    row-gap: .2rem;
  }
  .tier-desc { grid-column: 2; }

  .pricing { grid-template-columns: 1fr; }

  .feed-row { row-gap: .3rem; }
  .feed-vl, .feed-row .chip, .feed-cleared { margin-left: 0; }
  .feed-time { flex-basis: 100%; }

  .footer-top { flex-direction: column; }

  /* V9 — DIX-NEUF CARTES EN FILE INDIENNE.
     `minmax(240px, 1fr)` retombe à UNE colonne dès qu'il reste moins de
     ~504 px : la section « Ce qu'Observer arrête » faisait à elle seule près
     de 2 000 px sur téléphone. Le plancher descend donc — mais SEULEMENT ici :
     appliqué à toutes les largeurs, il donnerait sept colonnes sur un écran de
     1440 px, c'est-à-dire des cartes de 140 px pour des descriptions d'une
     ligne et demie. Le grand écran garde ses quatre colonnes.
     140 px, et non 150 : à 360 px de large il reste 312 px de contenu, deux
     colonnes coûtent 2×149,6 px — 150 aurait raté la seconde colonne d'un
     demi-pixel, ce qui est exactement le défaut qu'on corrige. */
  .prot-grid {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: .6rem;
  }
  .prot-card { padding: .8rem .85rem; }
}

/* SOUS 420 px, LA BARRE DÉBORDAIT DÉJÀ — avant ce lot, et sans « Tarifs ».
   Mesuré à 360 px sur la version livrée : le bord droit de « Connexion »
   tombait à 382 px pour un client de 360. Rien ne le montrait, parce que
   `body { overflow-x: hidden }` coupe ce qui dépasse et que `scrollWidth`
   reste donc égal à `clientWidth` : la sonde de débordement de la revue du
   2026-08-09 ne pouvait pas le voir. Il faut mesurer les BOÎTES, pas la page.
   Ajouter une seconde action imposait donc de resserrer pour de bon. */
@media (max-width: 420px) {
  .nav-inner { padding: .7rem 1rem; gap: .55rem; }
  .nav-logo { gap: .45rem; }
  .nav-logo .eye { width: 28px; }
  .nav-name { font-size: .95rem; }
  /* La mention « Beta » sort de la BARRE, pas de la page : elle reste dans le
     balisage (le verrou de marque la lit) et se retrouve dans le pied. À cette
     largeur, elle coûte 42 px à un endroit où il en manque. */
  .nav-beta { display: none; }
  .nav-links a[href="#tarifs"],
  .nav-login {
    padding: .45rem .7rem;
    font-size: .8rem;
  }
}

/* `.price-soon` A ÉTÉ RETIRÉE ICI LE 2026-08-09, avec ses deux commentaires.

   Elle habillait « Bientôt sur Modrinth » sur la carte Gratuit, la seule des
   quatre à n'avoir pas d'action, et son commentaire expliquait longuement
   qu'elle imitait la boîte d'un `.btn` pour que les quatre cartes s'alignent
   par le pied. La carte porte désormais un vrai `.btn.price-btn` (« Télécharger
   le .jar ») : l'alignement est obtenu par le bouton lui-même, et le
   commentaire décrivait un mécanisme qui n'existait plus.

   Sur ce dépôt, une note périmée coûte plus cher qu'une note absente — on lui
   fait confiance. La règle et son explication partent donc ensemble ; le motif
   du remplacement est écrit dans `index.html`, à l'endroit exact où la mention
   se trouvait. Vérifié avant retrait : zéro occurrence de `price-soon` dans les
   cinq pages du site et dans `main.js` (les deux restantes dans `index.html`
   sont dans ce commentaire-là). */

/* ═══════════════════════════ QUESTIONS (FAQ) ═══════════════════════════
   `<details>`/`<summary>` natifs — aucun script, donc le dépli fonctionne
   sans JS. Le balisage emprunte `.pillar` pour son cadre ; ce bloc ne fait
   qu'ajouter ce qu'un `<details>` réclame en plus d'une carte.

   LE REMBOURRAGE DESCEND DE LA CARTE VERS SES DEUX MOITIÉS. `.pillar` pose
   1,5rem/1,4rem sur le conteneur : la zone cliquable se réduisait alors au
   texte du `<summary>`, la marge autour restant inerte. En le déplaçant sur
   `.faq-q` et `.faq-a`, c'est la LARGEUR ENTIÈRE de la carte qui ouvre — et
   c'est aussi ce qui donne à la réponse l'air d'appartenir à sa question. */
.faq {
  display: grid;
  gap: .8rem;
}
.faq-item { padding: 0; }

/* AFFORDANCE : rien ne disait que ça s'ouvrait.
   Trois choses à la fois — le curseur, un chevron, et le retrait du marqueur
   natif. Ce dernier ne se retire PAS d'une seule façon : Chromium et Firefox
   obéissent à `list-style: none` (et à tout `display` autre que `list-item`),
   les Safari d'avant la 18 exigent `::-webkit-details-marker`. Les deux sont
   écrits, sinon un triangle noir se superpose au chevron sur l'un des deux. */
.faq-q {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.05rem 1.3rem;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.45;
  transition: color .15s;
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q:hover { color: var(--accent); }
/* Le chevron est un carré dont on ne garde que deux côtés, comme les coches de
   `.price-list` : même geste, même provenance de couleur, aucun glyphe à
   charger. Il pointe vers le bas fermé, vers le haut ouvert. */
.faq-q::after {
  content: '';
  flex-shrink: 0;
  width: .45rem;
  height: .45rem;
  margin-top: .3rem;
  border-right: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(45deg);
  transition: transform .18s ease;
}
.faq-item[open] .faq-q::after {
  transform: rotate(-135deg);
  margin-top: .45rem;
}
/* Ouverte, la question se rapproche de sa réponse : le rembourrage bas du
   `<summary>` ferait sinon un trou de 1rem au milieu de la carte. */
.faq-item[open] .faq-q { padding-bottom: .5rem; }

.faq-a { padding: 0 1.3rem 1.15rem; }
.faq-a p {
  font-size: .89rem;
  line-height: 1.7;
  color: var(--fg-muted);
}
.faq-a p + p { margin-top: .7rem; }
.faq-a strong { color: var(--fg); font-weight: 600; }
.faq-a a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: .18em;
}

/* « Une question qui n'est pas ici ? » — collée à la dernière carte sans cette
   marge, alors qu'elle n'appartient plus à la liste. */
.faq-more {
  margin-top: 1.5rem;
  font-size: .89rem;
  color: var(--fg-muted);
}
.faq-more a {
  color: var(--accent);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: .18em;
}

/* ═══════════════════════════ PAGES LÉGALES ═══════════════════════════
   Mentions légales, confidentialité, conditions, sous-traitance.

   Une seule colonne, large de 68 caractères environ : ces textes se LISENT,
   parfois longuement, et souvent par quelqu'un qui cherche un point précis. La
   grille de la vitrine, faite pour convaincre, y serait un obstacle. */
.legal {
  max-width: 44rem;
  margin: 0 auto;
  padding: 7rem 1.25rem 5rem;
}
.legal h1 {
  font-family: var(--font-display);
  font-size: 1.9rem;
  line-height: 1.2;
  margin-bottom: .5rem;
}
.legal .legal-date {
  font-size: .85rem;
  color: var(--fg-subtle);
  margin-bottom: 2.5rem;
}
.legal h2 {
  font-family: var(--font-display);
  font-size: 1.15rem;
  margin: 2.5rem 0 .75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}
.legal h3 {
  font-size: .95rem;
  font-weight: 600;
  margin: 1.5rem 0 .5rem;
}
.legal p,
.legal li {
  font-size: .93rem;
  line-height: 1.75;
  color: var(--fg-muted);
}
.legal p { margin-bottom: .9rem; }
.legal ul, .legal ol { margin: 0 0 1rem 1.25rem; }
.legal li { margin-bottom: .4rem; }
.legal strong { color: var(--fg); font-weight: 600; }
.legal a { color: var(--accent); }
.legal code {
  font-family: var(--font-mono);
  font-size: .85em;
  background: var(--surface-3);
  padding: .1em .35em;
  border-radius: 4px;
}
.legal table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1.2rem;
  font-size: .87rem;
}
.legal th, .legal td {
  text-align: left;
  vertical-align: top;
  padding: .55rem .7rem;
  border-bottom: 1px solid var(--border);
  color: var(--fg-muted);
}
.legal th { color: var(--fg); font-weight: 600; }

/* À COMPLÉTER — délibérément voyant.
   Une mention légale incomplète est pire qu'absente : elle a l'air d'être en
   règle. Ce bloc doit sauter aux yeux tant qu'il reste une valeur à remplir. */
.legal .legal-back {
  display: inline-block;
  margin-bottom: 2rem;
  font-size: .85rem;
  color: var(--fg-muted);
}
