/* Encre & vermillon.
   Un seul accent : au deuxième, on retombe dans le générique bleu-orange de
   tous les sites de bons plans.

   Le vermillon ne sert qu'à deux choses : ce sur quoi on peut cliquer, et
   l'instant présent (le point « aujourd'hui » de la courbe, la barre au plus
   bas). Tout le reste est de l'encre sur du papier.

   Aucune police distante, aucune image distante, aucun appel réseau : les
   mentions légales affirment que le site n'appelle aucun tiers, et une
   feuille de style qui charge une police depuis un CDN dément cette phrase. */

:root {
  --ink: #17141a;
  --ink-soft: #4a4550;
  --ink-faint: #6f6878;
  --paper: #fffdfb;
  --paper-2: #f6f2ee;
  --line: #e4ded7;
  --accent: #ff4d24;
  /* Encre, et non blanc, sur le vermillon : blanc sur #ff4d24 tombe à 3,26
     — sous le seuil de 4,5. Mesuré, pas estimé. L'encre y monte à 5,51. */
  --accent-ink: #17141a;
  --shadow: 0 1px 0 rgba(23, 20, 26, .06);
  --radius: 6px;
  color-scheme: light;
}

:root[data-theme="dark"] {
  --ink: #f2eee9;
  --ink-soft: #b7aeb8;
  --ink-faint: #8b8291;
  --paper: #15121a;
  --paper-2: #1e1a24;
  --line: #2e2836;
  --accent: #ff6a44;
  --accent-ink: #15121a;
  --shadow: none;
  color-scheme: dark;
}

/* Trois états, et ils doivent tous fonctionner :
   - pas d'attribut → la requête média décide (lecteur sans script compris) ;
   - `data-theme="light"` → clair, même sous un système sombre ;
   - `data-theme="dark"` → sombre, même sous un système clair.
   D'où la garde `:not([data-theme="light"])` sur le bloc média : sans elle,
   un choix explicite « clair » serait écrasé par le réglage du système. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: #f2eee9;
    --ink-soft: #b7aeb8;
    --ink-faint: #8b8291;
    --paper: #15121a;
    --paper-2: #1e1a24;
    --line: #2e2836;
    --accent: #ff6a44;
    --accent-ink: #15121a;
    --shadow: none;
    color-scheme: dark;
  }
}

/* ── Fondations ──────────────────────────────────────────────────────── */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  /* Fond explicite, jamais transparent : sans lui, le mode sombre laisse
     transparaître le blanc du navigateur entre deux repeints. */
  background: var(--paper);
  color: var(--ink);
  font-family: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img, svg { max-width: 100%; }
img { height: auto; }

a { color: var(--ink); }

h1, h2, h3 {
  margin: 1.4rem 0 .5rem;
  line-height: 1.25;
  letter-spacing: -.015em;
}
h1 { font-size: 1.5rem; }
h2 { font-size: 1.1rem; }
h3 { font-size: 1rem; }

p { margin: .5rem 0; }

del, s { text-decoration-thickness: 1px; }

button { font: inherit; }

[hidden] { display: none !important; }

/* Un anneau visible partout, y compris au clavier sur fond sombre. Le
   vermillon sur le papier mesure 3,26 — au-dessus du seuil de 3 exigé pour
   un élément d'interface non textuel. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ── Structure ───────────────────────────────────────────────────────── */

.wrap {
  max-width: 68rem;
  margin-inline: auto;
  padding-inline: 1rem;
}

/* Sorti de l'écran tant qu'il n'a pas le focus : la première tabulation d'un
   lecteur au clavier ne doit pas être la quinzième puce de catégorie. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
}
.skip:focus {
  position: fixed;
  left: .75rem;
  top: .75rem;
  z-index: 100;
  padding: .75rem 1rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--radius);
  text-decoration: none;
  font-weight: 700;
}

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

.quiet { color: var(--ink-faint); }

.lede {
  max-width: 46rem;
  margin: .35rem 0 1rem;
  color: var(--ink-soft);
  font-size: .95rem;
}

/* Un tableau large défile dans sa boîte, jamais en emportant la page : sur
   téléphone, un corps qui glisse latéralement donne un air d'inachevé.

   `position: relative` n'est pas décoratif : sans lui, un descendant en
   `position: absolute` — le `.sr-only` de l'en-tête « Lien » — prend pour
   bloc conteneur le bloc conteneur initial, échappe donc au défilement de
   cette boîte, et sa position statique au fond du tableau portait la zone
   défilable du document à 548 px pour 390 px visibles. C'est pourquoi forcer
   `overflow: hidden` ici ne changeait rien : ce n'est pas le tableau qui
   débordait, c'est ce span. Mesuré dans Chrome ; invisible à la relecture,
   puisque `overflow-x` était déjà là. */
.table-scroll {
  position: relative;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* ── En-tête ─────────────────────────────────────────────────────────── */

.masthead {
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}

.masthead-inner {
  display: flex;
  align-items: center;
  gap: .85rem;
  min-height: 56px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  min-height: 44px;
  color: var(--ink);
  text-decoration: none;
}

/* La marque : une trace d'encre penchée, en vermillon. Dessinée en CSS, donc
   nette à toute densité et sans un octet de plus à télécharger. */
.brand-mark {
  flex: 0 0 auto;
  width: .55rem;
  height: 1.15rem;
  background: var(--accent);
  border-radius: 1px;
  transform: skewX(-14deg);
}

.brand-name {
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: -.025em;
}

.tagline {
  margin: 0;
  color: var(--ink-faint);
  font-size: .85rem;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  margin-left: auto;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
}
.theme-toggle:hover {
  color: var(--ink);
  border-color: var(--ink-faint);
}

/* Le bouton montre la destination, pas l'état courant : en clair on propose
   la lune, en sombre le soleil. */
.theme-icon { display: none; }
.theme-icon-moon { display: block; }
[data-theme="dark"] .theme-icon-moon { display: none; }
[data-theme="dark"] .theme-icon-sun { display: block; }

/* ── Navigation par catégories ───────────────────────────────────────── */

/* Collée en haut : changer de catégorie reste à portée après quinze offres
   défilées, sans remonter. */
.filters {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow);
}

.filters-inner {
  display: flex;
  align-items: center;
  gap: .35rem;
  padding-block: .3rem;
  overflow-x: auto;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

/* Au pouce, une rangée qui glisse est le bon geste et trois lignes de puces
   mangeraient l'écran. À la souris, il n'y a pas de geste : la dernière
   catégorie restait coupée en plein mot, sans rien pour dire qu'elle
   continue. Au-dessus de 640 px, la barre passe donc à la ligne — elle ne
   tronque plus rien. */
@media (min-width: 641px) {
  .filters-inner {
    flex-wrap: wrap;
    overflow-x: visible;
  }
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  flex: 0 0 auto;
  /* 44 px : en dessous, la cible devient difficile à viser au pouce. La
     densité s'arrête là où l'ergonomie commence. */
  min-height: 44px;
  padding: 0 .75rem;
  background: var(--paper);
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: .85rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.chip:hover {
  color: var(--ink);
  border-color: var(--ink-faint);
}

/* L'état retenu se lit sans ambiguïté : c'est ce qui distingue un filtre
   d'une simple rangée de liens. */
.chip-on,
.chip[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.chip-count {
  font-size: .78rem;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.chip-on .chip-count { color: inherit; opacity: .7; }

.chip-quiet {
  margin-left: auto;
  border-style: dashed;
  font-weight: 400;
}

/* ── Mentions obligatoires ───────────────────────────────────────────── */

/* Obligatoire, donc présente et lisible — mais tenue sur une ligne : tout ce
   qui précède la première offre est du terrain perdu. */
.disclosure {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-block: .5rem;
  color: var(--ink-soft);
  font-size: .8rem;
  line-height: 1.4;
}

.ad-label {
  flex: 0 0 auto;
  padding: .1rem .4rem;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: 3px;
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.disclosure-footer {
  color: var(--ink-faint);
  font-size: .8rem;
}

/* ── Le fil d'offres ─────────────────────────────────────────────────── */

.feed {
  margin-top: .25rem;
  border-top: 1px solid var(--line);
}

.feed-title {
  margin: .9rem 0 .5rem;
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -.025em;
}

.feed-empty {
  padding: 2rem .25rem;
  color: var(--ink-faint);
}

.offer {
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.offer:hover { background: var(--paper-2); }

/* Le filtre sur place s'appuie dessus : `hidden` doit l'emporter sur la
   grille, quel que soit l'ordre des règles. */
.offer[hidden] { display: none; }

/* Trois colonnes : ce qu'on regarde, ce qu'on lit, ce qu'on décide. Empilé
   en une seule rangée, le bloc faisait 160 px et trois offres tenaient par
   écran ; en colonnes il en tient six. */
.offer-row {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 .9rem;
  padding: .6rem .25rem;
}

.offer-thumb {
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  overflow: hidden;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.offer-thumb img {
  max-width: 100%;
  max-height: 72px;
  object-fit: contain;
}

/* Sans photo du marchand, une marque discrète plutôt qu'une illustration
   pleine tuile : une grande image de substitution donne un air de maquette.
   Petite et monochrome, elle se lit comme « pas de photo ». */
.offer-thumb .mark {
  width: 38%;
  max-height: 38%;
  opacity: .8;
}

.offer-body { min-width: 0; }

/* Deux lignes au plus : un titre marchand à rallonge ferait exploser la
   hauteur de ligne et ruinerait la densité demandée. */
.offer-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
  font-size: .95rem;
  font-weight: 600;
  line-height: 1.3;
}
.offer-title a {
  color: var(--ink);
  text-decoration: none;
}
.offer-title a:hover {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 2px;
}

.offer-meta {
  display: flex;
  align-items: baseline;
  gap: .3rem;
  margin: .15rem 0 0;
  color: var(--ink-faint);
  font-size: .8rem;
  white-space: nowrap;
  overflow: hidden;
}

.offer-merchant {
  color: var(--ink-soft);
  font-weight: 600;
}

.offer-sep { opacity: .55; }

.offer-ended {
  color: var(--ink-faint);
  font-weight: 700;
}

/* Colonne de droite. Tout ce qui se chiffre s'empile à gauche, le bouton
   occupe la hauteur entière à droite : l'œil descend une colonne de prix
   alignés au lieu de les chercher ligne après ligne. */
.offer-figures {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  justify-items: end;
  column-gap: .85rem;
  row-gap: 0;
  text-align: right;
}
.offer-figures > * {
  grid-column: 1;
  margin: 0;
}
/* Le nombre de lignes varie (courbe et prix à la matière sont facultatifs) :
   le bouton enjambe assez de lignes pour rester centré dans tous les cas.
   Les lignes en trop sont vides et de hauteur nulle. */
.offer-figures > .offer-cta {
  grid-column: 2;
  grid-row: 1 / span 6;
  align-self: center;
}

.offer-price {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: .35rem;
  font-variant-numeric: tabular-nums;
}
.offer-price strong {
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: -.025em;
}
.offer-price del {
  color: var(--ink-faint);
  font-size: .82rem;
}

/* L'écart au prix habituel : encadré, jamais coloré. Le vermillon y serait
   à 3,26 sur le papier, sous le seuil de lisibilité. */
.offer-off {
  padding: .05rem .3rem;
  border: 1px solid var(--ink-faint);
  border-radius: 3px;
  font-size: .78rem;
  font-weight: 800;
  white-space: nowrap;
}

/* Le prix à la matière est l'argument du site : c'est lui qui rend deux
   conditionnements comparables. */
.offer-unit {
  color: var(--ink-soft);
  font-size: .8rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.offer-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  max-width: 12rem;
  padding: 0 .85rem;
  overflow: hidden;
  background: var(--accent);
  color: var(--accent-ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  font-size: .82rem;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
}
.offer-cta:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.offer-cta-small {
  padding: 0 .7rem;
  font-size: .78rem;
}

.offer-cta-wide {
  display: flex;
  max-width: none;
  width: 100%;
  min-height: 52px;
  margin-top: .9rem;
  font-size: .95rem;
}

/* Une offre terminée reste en ligne — la retirer donnerait l'impression
   qu'elle n'a jamais existé — mais se lit au premier coup d'œil comme
   caduque. Jamais par l'opacité : elle atténue tout le sous-arbre
   indistinctement et fait tomber le texte à 2,2:1, sous le seuil, dans les
   deux thèmes. Un fond, une barre sur le prix et la mention « terminée »
   suffisent, et les couleurs de texte restent aux valeurs de jetons — mesuré
   par `tests/test_site_contrast.py` (paires `--paper-2`), pas estimé ici. */
.offer-over { background: var(--paper-2); }
.offer-over .offer-price { text-decoration: line-through; }

/* ── Badge de qualité ────────────────────────────────────────────────── */

/* L'échelle des quatre paliers est dans le REMPLISSAGE et le POIDS, pas dans
   la teinte.

   La version précédente distinguait les paliers par quatre couleurs de texte
   (`--tier-1` à `--tier-4`). Contraintes à 4,5:1 sur le papier, ces quatre
   couleurs devenaient quatre gris quasi identiques : le palier ne se lisait
   qu'en lisant le libellé. Un badge de qualité qu'il faut lire ne sert à
   rien dans un fil qu'on parcourt du regard. Les quatre jetons sont donc
   supprimés — la teinte est le seul canal que la contrainte de contraste
   écrase, la hiérarchie vit donc ailleurs :

     palier 4  aplat vermillon        ██████████
     palier 3  contour vermillon      (○────────)
     palier 2  contour neutre         (·────────)
     palier 1  rien du tout            ─────────

   Et la couleur ne va JAMAIS sur un fond lu par `currentColor` : ce badge a
   déjà été rendu blanc sur blanc de cette manière. Les seuls `color` posés
   ici accompagnent un `background` écrit en toutes lettres. */
.offer-badge {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  max-width: 100%;
  margin: 0 0 .15rem;
  color: var(--ink);
  /* Contour transparent, pas absent : les paliers cernés n'ont plus qu'à
     changer sa couleur, et le palier 1 garde la même hauteur de ligne que
     s'il en portait un. */
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: .72rem;
  line-height: 1.6;
}

/* Réservée aux paliers cernés : sur l'aplat du palier 4 elle serait un point
   sombre au milieu du vermillon, et le palier 1 n'a plus de pilule où la
   poser. */
.offer-badge-dot {
  display: none;
  flex: 0 0 auto;
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
}

.offer-badge-text {
  font-weight: 700;
  white-space: nowrap;
}

/* Un signal qui ne dit pas sur quoi il repose n'est que du marketing : le
   critère accompagne le libellé partout où la place le permet.

   Sur les paliers faibles il est rapetissé et estompé : à taille égale du
   titre du produit, cette phrase lui disputait le regard alors qu'elle
   justifie une affirmation tiède. Elle reste à pleine taille au palier 4,
   où l'affirmation est la plus forte et mérite le plus d'être étayée. */
.offer-badge-why {
  min-width: 0;
  color: var(--ink-faint);
  font-size: .9em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Palier 1, « Prix en baisse » — pas de pilule du tout. C'est le plus faible
   des quatre ; lui donner la même boîte qu'aux autres exagère ce qu'il dit.
   Le libellé reste en gras et en `--ink-soft` : dans le tableau du catalogue
   il voisine « Prix habituel » en `--ink-faint` maigre, qui ne signale
   aucune baisse — les deux ne doivent pas se confondre. */
.tier-1 .offer-badge-text { color: var(--ink-soft); }

/* Les deux paliers cernés portent leur propre fond, et c'est mesuré : sans
   lui la pilule prend le fond de la ligne, donc `--paper-2` au survol et sur
   une offre terminée. Le vermillon du palier 3 y tombe à 2,97 — sous les 3
   exigés d'un élément d'interface non textuel, et c'est justement le contour
   qui porte le palier. Sur `--paper` il remonte à 3,26 partout, et le critère
   en `--ink-faint` passe de 4,80 à 5,27. */
.tier-2,
.tier-3 {
  padding: .05rem .55rem .05rem .45rem;
  background: var(--paper);
}

/* Palier 2, « Bon prix » — pilule cernée de neutre. */
.tier-2 { border-color: var(--ink-faint); }
.tier-2 .offer-badge-dot {
  display: block;
  background: var(--ink-faint);
}

/* Palier 3, « Très bon prix » — même pilule, prise dans l'accent et d'un
   trait deux fois plus épais. L'épaisseur n'est pas décorative : sans elle,
   les paliers 2 et 3 ne diffèrent que par la teinte d'un filet d'un pixel,
   donc pas du tout pour un lecteur qui distingue mal les couleurs. Le
   rembourrage est repris d'un pixel pour que les deux pilules gardent la
   même hauteur.

   Le contour et la pastille ne sont pas du texte : le vermillon y mesure
   3,26 sur le papier en clair et 6,53 en sombre, au-dessus du seuil de 3
   exigé d'un élément d'interface non textuel. */
.tier-3 {
  padding: 0 .5rem 0 .4rem;
  border-width: 2px;
  border-color: var(--accent);
}
.tier-3 .offer-badge-dot {
  display: block;
  width: .5rem;
  height: .5rem;
  background: var(--accent);
}

/* Palier 4, « Excellent prix » — aplat vermillon. C'est l'affirmation la plus
   forte du site, et le plus rare des quatre paliers : la seule à prendre
   l'accent en aplat, sans quoi l'aplat cesserait de vouloir dire quelque
   chose. L'encre sur le vermillon mesure 5,51 en clair et 6,53 en sombre —
   mesuré par `tests/test_site_contrast.py` (paire `accent-ink` / `accent`),
   pas estimé ici. */
.tier-4 {
  padding: .1rem .65rem;
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
  font-size: .76rem;
}
.tier-4 .offer-badge-why {
  color: var(--accent-ink);
  font-size: 1em;
}

/* ── Courbe des 90 jours ─────────────────────────────────────────────── */

/* Le tracé prend `currentColor` : la couleur se pose donc ici, sur le SVG.
   Le point du jour est le seul élément vermillon de la ligne — c'est
   l'instant présent, pas une décoration. */
.spark {
  display: block;
  width: auto;
  height: 1.4rem;
  max-width: 6rem;
  color: var(--ink-faint);
  overflow: visible;
}
.spark-now { fill: var(--accent); }

/* ── Pagination ──────────────────────────────────────────────────────── */

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: .75rem;
  margin: 1.5rem 0;
}
.pager a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 .9rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: .88rem;
  font-weight: 600;
  text-decoration: none;
}
.pager a:hover { border-color: var(--accent); }

.pager-position {
  color: var(--ink-faint);
  font-size: .85rem;
  font-variant-numeric: tabular-nums;
}

/* ── Catalogue ───────────────────────────────────────────────────────── */

.catalogue-block { margin: 1.75rem 0 0; }

.catalogue-title {
  margin: 0 0 .4rem;
  padding-bottom: .3rem;
  border-bottom: 2px solid var(--ink);
  font-size: .8rem;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
}
/* Cible de 44 px : c'est le seul chemin vers les catégories sans offre du
   jour, elle doit se viser au pouce comme une puce de navigation. */
.catalogue-title a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: inherit;
  text-decoration: none;
}
.catalogue-title a:hover {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

.catalogue {
  width: 100%;
  border-collapse: collapse;
  font-size: .88rem;
}
.catalogue th,
.catalogue td {
  padding: .5rem .6rem;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: middle;
}
.catalogue thead th {
  color: var(--ink-faint);
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.catalogue tbody th {
  font-weight: 600;
  min-width: 14rem;
}
.catalogue tbody tr:hover { background: var(--paper-2); }
.catalogue a { color: var(--ink); }

.catalogue-merchant {
  display: block;
  color: var(--ink-faint);
  font-size: .78rem;
  font-weight: 400;
}

/* Les chiffres s'alignent sur la droite et gardent la même chasse : une
   colonne de prix se compare d'un coup d'œil, ou ne se compare pas. */
.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ── Fiche produit ───────────────────────────────────────────────────── */

.crumbs {
  margin: .9rem 0 .3rem;
  color: var(--ink-faint);
  font-size: .82rem;
}
.crumbs a { color: var(--ink-soft); }

/* Fil d'Ariane à une seule étape des mentions légales — `.crumbs` sert les
   pages qui remontent jusqu'à une catégorie. */
.crumb { margin: .9rem 0 .3rem; color: var(--ink-faint); font-size: .82rem; }
.crumb a { color: var(--ink-soft); }

.sheet {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
  margin: .5rem 0 1.5rem;
}

.sheet-media {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  padding: 1rem;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.sheet-media img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
/* Sans photo du marchand, une marque discrète plutôt qu'une illustration
   pleine tuile : cf. `.offer-thumb .mark`, même intention. */
.sheet-media .mark { width: 38%; max-height: 38%; opacity: .8; }

.sheet-main { min-width: 0; }

.sheet-title {
  margin: .35rem 0 .5rem;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -.025em;
}

.sheet-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .6rem;
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.sheet-price strong {
  font-size: clamp(2rem, 7vw, 2.75rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.035em;
}

.sheet-unit {
  color: var(--ink-soft);
  font-size: .95rem;
  font-weight: 600;
}

/* Le jugement du site, isolé par un filet vermillon : c'est la phrase que
   le lecteur est venu chercher. */
.sheet-verdict {
  margin: .9rem 0 0;
  padding: .5rem .8rem;
  border-left: 3px solid var(--accent);
  background: var(--paper-2);
  font-weight: 700;
}

.sheet-freshness {
  margin: .6rem 0 0;
  color: var(--ink-faint);
  font-size: .85rem;
}

.sheet-history { margin: 2rem 0 0; }

/* Histogramme en CSS pur : aucune dépendance, aucun script, imprimable.
   L'échelle ne part pas de zéro — voir `history_bars`. */
.bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 170px;
  margin: .75rem 0 .5rem;
  padding: .5rem;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* Filet, pas la parade : la vraie borne est sur `.bar-tip` lui-même
     (largeur maximale, ancrage à droite des dernières barres) — sans quoi
     `clip` disparaît sous Safari < 16, qui le traite comme `visible`, et le
     débordement horizontal revient sur l'écran étroit qu'il visait à
     corriger. `clip` et non `hidden` au cas où : la bulle doit continuer à
     s'afficher AU-DESSUS du graphique, et `hidden` rognerait aussi cet axe. */
  overflow-x: clip;
}

.bar {
  position: relative;
  flex: 1 1 0;
  min-width: 2px;
  background: var(--ink-faint);
  border-radius: 1px;
}
.bar-low { background: var(--accent); }

.bar-tip {
  position: absolute;
  bottom: 100%;
  left: 50%;
  z-index: 3;
  transform: translate(-50%, -4px);
  max-width: 9rem;
  padding: .2rem .45rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: 3px;
  font-size: .72rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease;
}
.bar:hover .bar-tip { opacity: 1; }

/* Centrée sur sa barre, la bulle des dernières barres dépassait à droite —
   jusqu'à 41 px, assez pour faire glisser toute la page sur écran étroit
   (cf. `.bars` ci-dessus). Ancrée sur le bord droit de la barre plutôt que
   centrée, elle s'étend vers la gauche à la place et ne dépasse plus jamais
   du conteneur, avec ou sans `overflow-x: clip`. */
.bar:nth-last-child(-n+6) .bar-tip {
  left: auto;
  right: 0;
  transform: translateY(-4px);
}

.chart-legend {
  margin: .5rem 0 0;
  color: var(--ink-faint);
  font-size: .8rem;
}

.landmarks {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 2rem;
  margin: 0;
}
.landmarks dt {
  color: var(--ink-faint);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.landmarks dd {
  margin: .1rem 0 0;
  font-size: 1.05rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── Pied de page ────────────────────────────────────────────────────── */

.footer {
  margin-top: 3rem;
  padding: 1.5rem 0 2.5rem;
  background: var(--paper-2);
  border-top: 1px solid var(--line);
  color: var(--ink-soft);
  font-size: .85rem;
}
.footer p { margin: .35rem 0; }
.footer a { color: var(--ink-soft); }

/* ── Écrans étroits ──────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .tagline { display: none; }

  /* La colonne des chiffres passe sous le texte : à trois colonnes sur un
     téléphone, le titre se réduirait à deux mots par ligne. */
  .offer-row {
    grid-template-columns: 56px minmax(0, 1fr);
    gap: .35rem .7rem;
    padding: .65rem .25rem;
  }
  .offer-thumb { width: 56px; height: 56px; }
  .offer-thumb img { max-height: 56px; }

  /* La grille des chiffres est conservée, simplement passée pleine largeur :
     en la laissant retomber sur un `flex-wrap`, le bouton partait à la ligne
     et la rangée gagnait 35 px — quatre offres par écran au lieu de cinq. */
  .offer-figures {
    grid-column: 1 / -1;
    justify-items: start;
    text-align: left;
  }

  /* La courbe disparaît sous 640 px : à 4 rem de large elle ne se lit plus,
     et elle coûte une ligne entière. L'historique complet reste sur la
     fiche produit, où il a la place de dire quelque chose. */
  .spark { display: none; }

  .sheet { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .sheet-media { max-width: 12rem; }

  .landmarks { gap: .5rem 1.25rem; }
}

/* ── Impression et mouvement réduit ──────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
    box-shadow: none !important;
  }
}

@media print {
  .filters, .theme-toggle, .offer-cta, .pager, .skip { display: none !important; }
  * { box-shadow: none !important; transition: none !important; }
  body { background: #fff; color: #000; }
  .offer { break-inside: avoid; }
}

.legal-body p, .legal-body li { max-width: 46rem; }
.legal-body h2 { margin-top: 1.8rem; }
