/* Feuille du site de présentation. Volontairement autonome : elle ne partage RIEN avec
   `apps/web/src/styles.css` (84 ko, chargée par l'app). Ce qu'elle reprend, ce sont les
   VALEURS du thème — mêmes couleurs, même famille de police — pour qu'un visiteur qui
   ouvre l'app ensuite reconnaisse le même objet. Copier une dizaine de variables coûte
   moins cher qu'un couplage entre un site statique et le bundle d'une PWA. */

:root {
  --bg: #f6f5f1;
  --panel: #ffffff;
  --ink: #2a2a26;
  --muted: #6b6b63;
  --accent: #4a7c59;
  --accent-2: #c65d3b;
  --border: #e0ddd4;
  color-scheme: light;
}

/* Thème : même mécanique que l'application, et surtout MÊME clé de stockage
   (`spoor.theme`) — le site et l'app partagent une origine, donc un visiteur qui choisit
   le sombre ici le retrouve en se connectant, et réciproquement. L'absence de clé vaut
   « suivre le système », des deux côtés.

   Le site n'expose qu'une BASCULE clair↔sombre (cf. `.theme-toggle` plus bas) : elle ne
   sait donc que poser un choix explicite, jamais revenir au suivi du système. C'est le
   menu de l'application qui garde les trois états et rouvre ce chemin — un en-tête de
   page de présentation n'est pas un panneau de réglages.

   Les valeurs sombres sont écrites deux fois — choix explicite, et défaut système — parce
   qu'une variable CSS ne peut pas hériter conditionnellement d'un autre sélecteur. */
:root[data-theme='dark'] {
  --bg: #16171a;
  --panel: #1f2125;
  --ink: #e6e4de;
  --muted: #9a988f;
  --accent: #6fa87d;
  --accent-2: #d97a55;
  --border: #34363b;
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --bg: #16171a;
    --panel: #1f2125;
    --ink: #e6e4de;
    --muted: #9a988f;
    --accent: #6fa87d;
    --accent-2: #d97a55;
    --border: #34363b;
    color-scheme: dark;
  }
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Une ancre visée ne doit pas se coller sous la barre de navigation collante. */
  scroll-padding-top: 4.5rem;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
}

/* ---- Ossature ---- */

.wrap {
  width: min(46rem, 100% - 2.5rem);
  margin-inline: auto;
}
/* 78rem et non 64 : la vitrine met une capture d'interface à côté d'une liste, et une
   capture d'écran de 1440 px réduite à 480 ne montre plus rien de ce qu'elle illustre.
   La colonne de lecture (`.wrap`), elle, ne bouge pas — un texte large se lit mal. */
.wrap-wide {
  width: min(78rem, 100% - 2.5rem);
  margin-inline: auto;
}

/* ---- Barre de navigation ---- */

.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
.nav-in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1.1rem;
  padding: 0.7rem 0;
}
.nav a {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.95rem;
}
.nav a:hover {
  color: var(--ink);
}

/* En étroit, quatre libellés font passer la barre sur trois lignes et repoussent le
   titre hors de l'écran. Les ancres de la page d'accueil disparaissent — elles ne
   mènent qu'à des sections qu'on va rencontrer en défilant, c'est le seul endroit du
   site où un lien ne perd rien à être retiré. Les liens entre pages, eux, restent. */
@media (max-width: 640px) {
  .nav-in {
    gap: 0.4rem 0.9rem;
  }
  .nav a {
    font-size: 0.88rem;
  }
  .nav .anchor {
    display: none;
  }
}
/* Le lien vers l'application : discret, mais toujours là — c'est la seule sortie de la
   barre, et la seule qui survive au rétrécissement. */
.nav .nav-cta {
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--ink);
}
.nav .nav-cta:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.nav .brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-right: auto;
  color: var(--ink);
  font-weight: 600;
  font-size: 1.1rem;
  letter-spacing: 0.01em;
}
/* La patte suit l'accent du thème : en sombre, le vert clair prend le relais. Coder la
   couleur dans le SVG la laisserait au vert foncé, illisible sur fond sombre. */
/* Le logo est injecté en ligne depuis `assets/logo.svg`, qui ne porte volontairement pas
   de dimensions (il sert aussi de favicon, où seul le viewBox compte). Sans cette taille,
   il s'étalerait sur toute la largeur de la barre. */
.nav .brand svg {
  display: block;
  width: 24px;
  height: 24px;
  flex: none;
  fill: var(--accent);
}

/* Sélecteur de thème — une icône, sans libellé, sans cadre. Masqué tant que le script n'a
   pas tourné : un bouton qui ne fait rien vaut moins que pas de bouton.

   L'icône montre l'ÉTAT COURANT (Auto, Sombre ou Clair), pas la destination de l'appui :
   sur un cycle à trois, montrer la destination obligerait à connaître l'ordre.

   Le choix se fait ENTIÈREMENT ici, et sur `data-theme` SEUL — pas de `prefers-color-scheme`
   dans ces règles, contrairement aux variables de couleur plus haut. C'est voulu : ce qu'on
   affiche est le RÉGLAGE, et « Auto » est justement l'absence de réglage
   (`:root:not([data-theme])`), quelle que soit la couleur qui en résulte. */
.theme-toggle {
  display: none;
  align-items: center;
  padding: 0.35rem;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.theme-toggle.ready {
  display: inline-flex;
}
.theme-toggle:hover {
  color: var(--ink);
}
.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.theme-toggle svg {
  display: none;
}
:root:not([data-theme]) .theme-toggle .i-auto,
:root[data-theme='dark'] .theme-toggle .i-moon,
:root[data-theme='light'] .theme-toggle .i-sun {
  display: block;
}

/* Le libellé du bouton n'existe que pour les lecteurs d'écran : le porteur des chaînes
   traduites (cf. build.mjs) ne doit rien peser à l'écran. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- Vitrine (liste à gauche, capture à droite) ---- */

/* Témoin de disposition, lu par le script (cf. layout.html) pour choisir le format de
   capture montré par défaut. Un `matchMedia('(max-width: 820px)')` écrit dans le script
   aurait recopié le seuil : deux valeurs à tenir d'accord, et une bascule qui se
   désynchronise de la mise en page à la première retouche. Ici la requête média reste la
   SEULE définition du seuil, et le script se contente de lui demander où on en est. */
:root {
  --vitrine-etroite: 0;
}

/* Deux colonnes, la capture prenant la plus large. `minmax(0, …)` et non `1fr` seul : une
   piste de grille refuse par défaut de descendre sous la taille de son contenu, et une
   capture de 1440 px de large ferait alors déborder la page entière horizontalement. */
.showcase {
  display: grid;
  grid-template-columns: minmax(0, 19rem) minmax(0, 1fr);
  gap: 1.8rem;
  align-items: start;
  margin: 1.4rem 0 2rem;
}
.showcase-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.showcase-list button {
  font: inherit;
  text-align: left;
  display: block;
  padding: 0.75rem 0.9rem;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: inherit;
}
.showcase-list button strong {
  display: block;
  margin-bottom: 0.15rem;
}
.showcase-list button span {
  color: var(--muted);
  font-size: 0.92rem;
}

/* Tout ce qui suppose un clic n'apparaît QUE si le script a tourné : sans lui, la liste
   n'est plus une commande mais une simple énumération, suivie de toutes les captures. */
.showcase.ready .showcase-list button {
  cursor: pointer;
}
.showcase.ready .showcase-list button:hover {
  border-color: var(--border);
}
.showcase.ready .showcase-list button[aria-selected='true'] {
  background: var(--panel);
  border-color: var(--border);
}
.showcase.ready .showcase-list button[aria-selected='true'] strong {
  color: var(--accent);
}
.showcase-list button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Cadre à proportions FIXES : les captures n'ont pas toutes la même forme (le téléphone
   est en portrait, les autres en paysage). Sans ça, changer d'onglet ferait sauter la
   hauteur de la page sous le curseur. `contain` plutôt que `cover` : une capture d'écran
   recadrée perd justement ce qu'on voulait montrer. */
.showcase-frame {
  aspect-ratio: 16 / 10;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
/* `picture` est enveloppé autour de l'image pour proposer le WebP avant le PNG. C'est un
   conteneur INLINE par défaut : sans cette règle, il poserait l'image sur une ligne de
   texte et laisserait le blanc de la ligne de base sous le cadre. Il doit donc reprendre
   la boîte que l'image occupait seule auparavant. */
.showcase-frame picture {
  display: block;
  width: 100%;
  height: 100%;
}
.showcase-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* Bascule Ordinateur / Téléphone. Le format choisi vaut pour TOUS les onglets — c'est un
   état, pas une navigation : on ne le rechoisit pas à chaque fonction regardée. */
.showcase-devices {
  display: none;
  justify-content: flex-end;
  gap: 0;
  margin-bottom: 0.7rem;
}
.showcase.ready .showcase-devices {
  display: flex;
}
.showcase-devices button {
  display: flex;
  align-items: center;
  padding: 0.32rem 0.6rem;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.showcase-devices button:first-child {
  border-radius: 999px 0 0 999px;
}
.showcase-devices button:last-child {
  border-radius: 0 999px 999px 0;
  border-left: 0;
}
.showcase-devices button[aria-pressed='true'] {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* Une seule des deux captures est montrée, selon le format choisi. Les deux sont dans le
   DOM : basculer ne doit pas déclencher un téléchargement et un cadre vide au milieu. */
.showcase .shot-mobile {
  display: none;
}
.showcase[data-vue='mobile'] .shot-desktop {
  display: none;
}
.showcase[data-vue='mobile'] .shot-mobile {
  display: block;
}
/* Le cadre suit le format : un téléphone dans un cadre 16/10 pleine largeur flotterait au
   milieu du vide. Il devient donc étroit et debout, comme l'appareil qu'il montre. */
.showcase[data-vue='mobile'] .showcase-frame {
  aspect-ratio: 1082 / 2202;
  max-width: 22rem;
  margin-inline: auto;
}
/* Cadre d'une entrée dont la capture n'existe pas encore (cf. build.mjs). */
.showcase-todo {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  margin: 0;
  color: var(--muted);
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* Doublon du texte de la liste, montré seulement en étroit (cf. la requête média). */
.showcase-cap {
  display: none;
  margin: 0.7rem 0 0;
  color: var(--muted);
  font-size: 0.95rem;
}
/* Sans script, les panneaux s'empilent : chaque capture reprend sa hauteur naturelle. */
.showcase:not(.ready) .showcase-frame {
  aspect-ratio: auto;
  margin-bottom: 0.8rem;
}
.showcase:not(.ready) .showcase-frame img,
.showcase:not(.ready) .showcase-frame picture {
  height: auto;
}

/* En étroit, la liste devient une rangée de puces SOUS la capture. Deux raisons de la
   descendre : l'image est ce qu'on est venu voir, et un bandeau qui déborde s'offre à
   faire défiler bien mieux quand il suit le contenu qu'il commande.
   Elle reste une RANGÉE et non une liste verticale — six items empilés repousseraient la
   capture hors de l'écran, et on taperait un onglet sans voir ce qu'il change. Les puces
   ne portent que le titre ; la phrase réapparaît sous la capture (`.showcase-cap`).

   ⚠️ L'ordre visuel diffère de l'ordre du DOM (la liste y reste première, pour le mode
   deux colonnes). Le parcours au clavier suit le DOM : on atteint donc les onglets avant
   l'image qu'ils commandent. Acceptable ici — le groupe reste cohérent et annoncé comme
   un `tablist` — mais c'est la raison de ne PAS étendre ce procédé au reste de la page. */
@media (max-width: 820px) {
  :root {
    --vitrine-etroite: 1;
  }
  .showcase {
    grid-template-columns: 1fr;
    gap: 0.9rem;
  }
  .showcase.ready .showcase-list {
    order: 2;
  }
  /* La capture téléphone fait deux fois plus haut que large : à pleine largeur elle
     occuperait tout l'écran, et les puces passées dessous seraient invisibles sans
     défiler. On la plafonne donc en hauteur, la largeur suivant le rapport. */
  .showcase[data-vue='mobile'] .showcase-frame {
    height: min(58vh, 682px);
    width: auto;
    max-width: 100%;
  }
  .showcase.ready .showcase-list {
    flex-direction: row;
    overflow-x: auto;
    gap: 0.45rem;
    padding-bottom: 0.35rem;
    scrollbar-width: none;
  }
  .showcase.ready .showcase-list::-webkit-scrollbar {
    display: none;
  }
  .showcase.ready .showcase-list button {
    flex: none;
    padding: 0.4rem 0.85rem;
    border-color: var(--border);
    border-radius: 999px;
  }
  .showcase.ready .showcase-list button strong {
    margin-bottom: 0;
    font-size: 0.9rem;
  }
  .showcase.ready .showcase-list button span {
    display: none;
  }
  .showcase.ready .showcase-cap {
    display: block;
  }
}

/* Sélecteur de langue — n'apparaît qu'à partir de deux langues (cf. build.mjs).
   Même contrôle que dans l'app (`.app-menu-select select`) : un `<select>` natif, sans
   cadre propre, qui prend la couleur et la taille des autres liens de la barre pour ne
   pas se signaler comme un bouton à part. Fermé, il n'occupe que la largeur de son
   option choisie — il ne s'allonge pas avec le nombre de langues. */
.lang-switch {
  border: none;
  background: none;
  color: var(--muted);
  font-size: 0.95rem;
  font-family: inherit;
}
.lang-switch:hover {
  color: var(--ink);
}

/* ---- Titres et texte ---- */

h1,
h2,
h3 {
  line-height: 1.25;
  text-wrap: balance;
}
h1 {
  font-size: clamp(2rem, 5vw, 2.9rem);
  margin: 0 0 0.6rem;
  letter-spacing: -0.02em;
}
h2 {
  font-size: 1.5rem;
  margin: 2.8rem 0 0.9rem;
  letter-spacing: -0.01em;
}
h3 {
  font-size: 1.1rem;
  margin: 1.8rem 0 0.4rem;
}
p {
  margin: 0 0 1rem;
}
a {
  color: var(--accent);
}
ul {
  padding-left: 1.2rem;
}
li {
  margin-bottom: 0.4rem;
}
strong {
  font-weight: 600;
}
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  padding: 0.1em 0.35em;
  border-radius: 4px;
}
hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 3rem 0;
}

/* ---- En-tête d'accueil ---- */

.hero {
  padding: 4rem 0 1rem;
}
.hero .lede {
  font-size: 1.25rem;
  color: var(--muted);
  max-width: 34rem;
}
.tagline {
  display: inline-block;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--accent);
  font-weight: 600;
  margin-bottom: 0.8rem;
}

/* ---- Boutons ---- */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  margin: 1.8rem 0;
}
.btn {
  display: inline-block;
  padding: 0.65rem 1.2rem;
  border-radius: 8px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  font-weight: 500;
  font-size: 1rem;
}
.btn:hover {
  filter: brightness(1.08);
}
.btn-ghost {
  background: transparent;
  color: var(--accent);
}
.btn-ghost:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  filter: none;
}

/* ---- Blocs ---- */

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.2rem 1.4rem;
}
.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  margin: 1.2rem 0;
}
.grid h3 {
  margin-top: 0;
}
.grid p:last-child {
  margin-bottom: 0;
}

/* Encadré d'avertissement — l'état de bêta, les limites qu'on ne veut pas voir manquées. */
.note {
  border-left: 3px solid var(--accent-2);
  background: color-mix(in srgb, var(--accent-2) 8%, transparent);
  border-radius: 0 8px 8px 0;
  padding: 0.9rem 1.1rem;
  margin: 1.5rem 0;
}
.note p:last-child {
  margin-bottom: 0;
}

/* ---- Captures d'écran ---- */

figure {
  margin: 1.5rem 0;
}
figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}
figcaption {
  margin-top: 0.5rem;
  font-size: 0.9rem;
  color: var(--muted);
}

/* Capture de téléphone : bornée en hauteur et centrée, sinon une image de 1200 px de haut
   occupe deux écrans à elle seule et coupe le propos en deux. */
.shots {
  text-align: center;
}
.shot-phone {
  width: auto;
  max-width: 100%;
  max-height: 32rem;
  margin-inline: auto;
}
.shots figcaption {
  text-align: left;
}

/* ---- Questions / réponses ---- */

details {
  border-bottom: 1px solid var(--border);
  padding: 0.9rem 0;
}
details summary {
  cursor: pointer;
  font-weight: 600;
  list-style-position: outside;
}
details summary::marker {
  color: var(--accent);
}
details[open] summary {
  margin-bottom: 0.7rem;
}
details p:last-child {
  margin-bottom: 0;
}

/* ---- Tableaux (pages légales) ---- */

.table-scroll {
  overflow-x: auto;
  margin: 1.2rem 0;
}
table {
  border-collapse: collapse;
  width: 100%;
  min-width: 32rem;
  font-size: 0.95rem;
}
th,
td {
  text-align: left;
  vertical-align: top;
  padding: 0.55rem 0.7rem;
  border-bottom: 1px solid var(--border);
}
th {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---- Pied de page ---- */

footer {
  margin-top: 4rem;
  border-top: 1px solid var(--border);
  padding: 2rem 0 3rem;
  font-size: 0.92rem;
  color: var(--muted);
}
footer nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.2rem;
  margin-bottom: 1rem;
}
footer a {
  color: var(--muted);
}
footer a:hover {
  color: var(--ink);
}

/* Bandeau de date en tête des pages légales : une politique sans date ne vaut rien. */
.updated {
  color: var(--muted);
  font-size: 0.9rem;
  margin-bottom: 2rem;
}
/* Ligne d'appoint SOUS un couple de boutons : l'offre de contact ne doit pas concurrencer
   l'action principale (créer un compte), juste rester atteignable. */
.fine {
  color: var(--muted);
  font-size: 0.9rem;
  margin-top: 1rem;
}
