/*
 * Composants réutilisables — construits exclusivement à partir de
 * tokens.css. Aucune valeur brute ici : toujours var(--...).
 */

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Empêche tout scroll/swipe horizontal (demande explicite d'Elena,
     2026-10-08, à propos du menu mobile plein écran) : certains carrousels
     en défilement continu du site laissent un scrollWidth fantôme
     dépassant la largeur de l'écran sur mobile (contenu déjà clippé
     visuellement par leurs propres conteneurs overflow:hidden, donc rien
     n'est réellement visible en scrollant — mais le geste restait
     possible). Un overflow-x:hidden global, sans incidence visuelle,
     supprime le geste à la racine plutôt que de corriger chaque
     carrousel. */
  overflow-x: hidden;
}

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

/* Focus clavier — aucun style dédié n'existait auparavant : au clavier, les
   boutons/liens n'affichaient que l'anneau bleu par défaut du navigateur,
   qui jure avec la charte et ne respecte pas forcément le contraste minimum
   requis partout sur le site. `:focus-visible` (pas `:focus`) pour ne
   montrer l'anneau qu'à la navigation clavier, jamais au clic souris. Noir
   par défaut (contraste maximal sur fond clair) ; jaune dans .section--dark
   où le noir se fondrait dans le fond sombre — un seul système à deux
   variantes, pas un anneau différent par section. */
:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.section--dark :focus-visible {
  outline-color: var(--color-accent);
}

body {
  margin: 0;
  /* overflow-x:hidden posé sur html seul ne suffit pas partout : selon le
     navigateur, l'élément "racine scrollable" peut rester <body> si son
     propre overflow-x n'est pas aussi contraint (vérifié : scrollTo(500,0)
     déplaçait encore la page malgré html{overflow-x:hidden}) — les deux
     ensemble ferment le geste de façon fiable. */
  overflow-x: hidden;
  background: var(--color-background);
  color: var(--color-text);
  font-family: var(--font-family-body);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  /* Garde-fou responsive : sans ça, un mot long dans un titre (ex.
     "accompagnement") ne casse jamais et fait déborder la page
     horizontalement sur mobile — repéré lors de l'audit responsive du
     2026-09-18, corrige un bug déjà présent avant cette session. */
  overflow-wrap: break-word;
}

/* Titres */

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
}

h1 {
  font-size: var(--font-size-h1);
  font-weight: 700;
}

h2 {
  font-size: var(--font-size-h2);
  font-weight: 700;
}

h3 {
  font-size: var(--font-size-h3);
  font-weight: 600;
}

h4 {
  font-size: var(--font-size-h4);
  font-weight: 700;
}

/* Styles visuels h1-h4 découplés du tag Hn — pour piloter le rendu visuel
   indépendamment du niveau de titre SEO (ou de l'absence de titre), sans
   changer ce rendu (validé par Sébastien, home 2026-09-14). */
.heading-style-h1 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: var(--font-size-h1);
  font-weight: 700;
}

.heading-style-h2 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: var(--font-size-h2);
  font-weight: 700;
}

.heading-style-h3 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: var(--font-size-h3);
  font-weight: 600;
}

.heading-style-h4 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: var(--font-size-h4);
  font-weight: 700;
}

p {
  margin: 0 0 var(--space-sm);
  color: var(--color-text);
}

.text-secondary {
  color: var(--color-text-secondary);
}

.text-tertiary {
  color: var(--color-text-tertiary);
}

/* .rating-badge — pastille "★★★★★ 4,9/5 par nos clients" (hero), unifie le
   texte brut de la home et l'image Google de /page-conformite/, validé
   Sébastien */
.rating-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: var(--color-background);
  padding: var(--space-xs) var(--space-md);
  color: var(--color-text-secondary);
}

.rating-badge__stars {
  color: var(--color-accent);
  letter-spacing: 1px;
}

/* Rendu cliquable (voir docs/missing-components.md) : hover gris clair léger,
   réutilise --color-background-alt déjà validé. */
.rating-badge {
  transition: background-color 0.2s ease;
}

.rating-badge:hover {
  background: var(--color-background-alt);
}

/* Liens — par défaut identiques au texte environnant (choix de design
   existant, validé). --color-link est STRICTEMENT réservé au contenu
   d'article : n'appliquer .prose qu'au wrapper du corps d'article
   (étape 1b), jamais sur une page marketing (home/service/offres). */

a {
  color: inherit;
  text-decoration: none;
}

/* Menu mobile plein écran (structure dans layout.css) — couleurs. */
.site-header__mobile-icon span {
  background: var(--color-heading);
}

.site-header__mobile-toggle nav {
  background: var(--color-background);
}

.site-header__mobile-toggle li a:not(.btn) {
  color: var(--color-heading);
  border-bottom: 1px solid var(--color-border);
}

.site-header__mobile-toggle li a:not(.btn):hover {
  background: var(--color-background-alt);
}

/* Le panneau doit rester accessible/fonctionnel même sans JS pour poser la
   classe .is-open (dégradation propre) : la transition n'est donc câblée
   que si du JS tourne réellement — sinon opacity:0 resterait bloqué à 0
   pour toujours, panneau invisible bien qu'ouvert. */
@media (scripting: enabled) {
  .site-header__mobile-toggle nav {
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity 0.22s ease, transform 0.22s ease;
  }

  .site-header__mobile-toggle nav.is-open {
    opacity: 1;
    transform: translateY(0);
  }
}

.prose a {
  color: var(--color-link);
  text-decoration: underline;
}

/* Boutons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  border: none;
  font-family: var(--font-family-body);
  font-weight: 500;
  font-size: var(--font-size-body);
  text-decoration: none;
  cursor: pointer;
  /* Un bouton ne doit jamais passer son texte à la ligne (ex: header pile
     à la bascule nav mobile -> desktop, où l'espace est tendu) — s'il
     manque de place, c'est au conteneur de céder, pas au label de casser
     en deux lignes dans une pilule prévue pour une seule. */
  white-space: nowrap;
}

/* display:inline-flex ci-dessus écraserait sinon le display:none de
   l'attribut [hidden] (règle UA de priorité la plus basse, qu'une règle
   auteur, même moins spécifique, l'emporte dessus) — utilisé par ex. pour
   masquer/révéler le bouton "Voir plus d'articles" de /media-presse/. */
.btn[hidden] {
  display: none;
}

/* Sous 480px, certains CTA de contenu ont un libellé bien plus long que les
   pilules du header (ex: "Voir toutes les plateformes compatibles") — le
   nowrap ci-dessus les ferait déborder hors de l'écran au lieu de passer
   à la ligne, repéré lors de l'audit responsive du 2026-10-06. Le header
   n'est pas concerné (déjà basculé en menu mobile plein écran bien avant
   cette largeur). */
@media (max-width: 480px) {
  .btn {
    white-space: normal;
  }
}

/* Boutons avec un peu de matière : léger dégradé (à peine perceptible, pas
   un effet "glossy") + liseré intérieur clair en haut (le bord qui "attrape"
   la lumière) donnent du volume au repos ; l'état survol ne soulève plus le
   bouton mais le fait "s'enfoncer" (translateY positif + ombre qui passe à
   l'intérieur) pour un retour plus tactile, presque une pression physique. */
.btn-primary {
  background: linear-gradient(180deg, #2b2b2b 0%, var(--color-primary) 100%);
  color: var(--color-primary-text);
  border-radius: var(--radius-sm);
  padding: var(--padding-btn-compact);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 1px 2px rgba(0, 0, 0, 0.18);
  transition: background 0.2s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.btn-primary:hover {
  background: linear-gradient(180deg, #333333 0%, var(--color-primary-hover) 100%);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.35);
  transform: translateY(1px);
}

.btn-primary:hover span {
  transform: translateX(3px);
}

.btn-primary--large {
  border-radius: var(--radius-md);
  padding: var(--padding-btn-large);
}

.btn-secondary {
  background: var(--color-secondary-bg);
  color: var(--color-secondary-text);
  border-radius: var(--radius-sm);
  /* Horizontal élargi (ronde 5) pour laisser respirer le texte des pilules
     de filtre (hero) — vertical inchangé. Survol volontairement minimal :
     juste le changement de couleur, pas d'ombre ni de mouvement — le seul
     bouton qui "bouge" au survol doit rester le CTA principal. */
  padding: var(--padding-btn-compact) var(--space-md);
  transition: background-color 0.25s ease;
}

.btn-secondary:hover {
  background: var(--color-secondary-bg-hover);
}

.btn-ghost {
  background: transparent;
  color: var(--color-heading);
  padding: var(--padding-btn-compact) 0;
}

.btn-ghost:hover {
  text-decoration: underline;
}

/* .btn-info — CTA fond bleu clair (ex: "En savoir plus sur le Dossier
   Fiscal"). Ne réutilise que des tokens déjà validés, aucune nouvelle
   couleur : --color-background-info + --color-heading. */
.btn-info {
  background: var(--color-background-info);
  color: var(--color-heading);
  border-radius: var(--radius-sm);
  padding: var(--padding-btn-compact) var(--space-md);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.btn-info:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-card);
}

.btn-info:hover span {
  transform: translateX(3px);
}

/* Cards
   Quatre familles de cards mesurées séparément (voir docs/da-exploration.md,
   section "Cards — quatre blocs de DA") + .card générique en repli pour le
   contenu qui ne correspond à aucune des quatre. */

/* .card — générique/repli, encadrés de contenu d'article (key takeaways,
   exemple chiffré, étapes, tableau comparatif) */
.card {
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-card);
}

/* .card-compare — comparatif risque/bénéfice (FIFO vs DeclarAid,
   /page-conformite/), validé Sébastien */
.card-compare {
  border-radius: var(--radius-lg);
  padding: var(--space-card);
  text-align: center;
}

.card-compare--bad {
  background: var(--color-error-bg);
}

.card-compare--good {
  background: var(--color-success-bg);
}

.card-compare--bad .icon-outline {
  color: var(--color-error-text);
}

.card-compare--good .icon-outline {
  color: var(--color-success-text);
}

/* .card-pricing — /nos-offres/. Affinée après premier rendu, validée par
   Sébastien : titre agrandi, période/description resserrées et hiérarchisées,
   séparateur avant la checklist, hauteur de card et bas de bouton alignés
   entre les 3 offres (voir .card-pricing__description et .card-pricing__cta
   ci-dessous). */
.card-pricing {
  display: flex;
  flex-direction: column;
  background: var(--color-background-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pricing);
  box-shadow: none;
  padding: var(--space-lg);
  text-align: left;
  position: relative;
}

/* .card-pricing--featured — offre mise en avant (Portefeuille) : fond blanc pur
   (vs. --color-background-alt des 2 autres cards) + ombre, pour ressortir
   du lot. Demandé explicitement par Sébastien. */
.card-pricing--featured {
  background: var(--color-background);
  box-shadow: var(--shadow-card-marketing);
}

.card-pricing__price {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-h2);
  font-weight: 500;
  color: var(--color-heading);
  margin: var(--space-sm) 0 0;
}

.card-pricing__title {
  font-size: 24px;
}

.card-pricing__period {
  font-size: 13px;
  letter-spacing: -0.2px;
  color: var(--color-text-secondary);
}

.card-pricing__description {
  font-size: 12px;
  line-height: 1.25;
  color: var(--color-text-tertiary);
  /* Réserve une hauteur égale entre les 3 cards (la description la plus
     longue, Portefeuille, fait ~5 lignes) pour que le séparateur et la
     checklist démarrent à la même hauteur sur les 3 offres. */
  min-height: 6.25em;
}

.card-pricing__separator {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: var(--space-sm) 0;
}

.card-pricing__cta {
  margin-top: auto;
}

/* Checklist des fonctionnalités par offre : réutilise .split-section__checklist
   (layout) avec une coche verte (incluse) / une croix atténuée (non incluse),
   scoping sous .card-pricing pour ne pas affecter les autres usages de
   .split-section__checklist (page-conformite, home) qui n'ont pas cette
   distinction inclus/exclu. */
.card-pricing .split-section__checklist li svg {
  color: var(--color-success-text);
}

.card-pricing .split-section__checklist li.text-tertiary {
  opacity: 0.55;
}

.card-pricing .split-section__checklist li.text-tertiary svg {
  color: var(--color-text-tertiary);
}

/* .badge — pilule (ex: badge en haut des cards pricing). Non cliquable par
   défaut (span) ; utiliser <a class="badge"> pour la variante cliquable
   utilisée sur les cards des articles de blog. */
.badge {
  display: inline-block;
  background: var(--color-primary);
  color: var(--color-primary-text);
  border-radius: var(--radius-pill);
  padding: var(--padding-badge);
  font-size: var(--font-size-xs);
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: default;
}

/* .card-pricing .badge — les libellés combinés (ex: "Multi-investissement +
   Dossier Fiscal") sont plus longs que les tags de blog qui utilisent aussi
   .badge (mot unique) : on autorise le retour à la ligne et on centre le
   texte pour rester dans la largeur de la card, sans toucher aux autres
   usages de .badge (nowrap conservé partout ailleurs). */
.card-pricing .badge {
  display: block;
  width: fit-content;
  max-width: 100%;
  white-space: normal;
  line-height: 1.3;
  text-align: center;
}

a.badge {
  cursor: pointer;
}

/* .badge-outline — pilule à fond clair + bordure/texte colorés (hero:
   "Bourse : calculs PMP" / "Crypto : 150-VH du CGI"). Couleurs validées
   ronde 3, distinctes de --color-background-info. */
.badge-outline {
  display: inline-block;
  border-radius: var(--radius-pill);
  padding: var(--padding-badge);
  font-size: var(--font-size-xs);
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  border: 1px solid transparent;
}

.badge-outline--success {
  background: var(--color-success-bg);
  color: var(--color-success-text);
  border-color: var(--color-success-text);
}

.badge-outline--info {
  background: var(--color-info-bg);
  color: var(--color-info-text);
  border-color: var(--color-info-text);
}

/* .badge-step — cercle numéroté (1/2/3), distinct de .badge (pilule) */
.badge-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-badge-step);
  height: var(--size-badge-step);
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-accent-text);
  font-weight: 700;
  font-size: var(--font-size-body);
}

/* .icon-badge — cercle plein + icône ligne blanche centrée (SVG en enfant).
   Deux variantes de fond : noire par défaut, jaune (--icon-badge--accent).
   Ombre portée teintée (même noir que le fond, pas un gris neutre) pour un
   léger effet de flottaison plutôt qu'un aplat posé à plat sur la card. */
.icon-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-icon-badge);
  height: var(--size-icon-badge);
  margin-bottom: var(--space-md);
  border-radius: 50%;
  background: var(--color-primary);
  color: var(--color-primary-text);
  box-shadow: rgba(23, 23, 23, 0.22) 0px 8px 16px -6px;
  transition: background-color 0.2s ease;
}

.icon-badge svg {
  width: 28px;
  height: 28px;
}

.icon-badge--accent {
  background: var(--color-accent);
}

/* .icon-badge--persona-* — variante pastel (reprend les 4 teintes
   historiques des cards "Pour qui ?"), icône sombre plutôt que blanche
   (contraste faible sur fond clair) et sans l'ombre teintée de
   .icon-badge/.icon-badge--accent (pensée pour un aplat sombre/jaune, trop
   lourde sur un pastel) : une touche de couleur qui fait ressortir chaque
   item dans une mise en page sans card/bordure. */
.icon-badge--persona-yellow {
  background: var(--color-persona-yellow-bg);
  color: var(--color-heading);
  box-shadow: none;
}

.icon-badge--persona-blue {
  background: var(--color-persona-blue-bg);
  color: var(--color-heading);
  box-shadow: none;
}

.icon-badge--persona-pink {
  background: var(--color-persona-pink-bg);
  color: var(--color-heading);
  box-shadow: none;
}

.icon-badge--persona-green {
  background: var(--color-success-bg);
  color: var(--color-heading);
  box-shadow: none;
}

/* .card-step — étapes numérotées en carte (ex: "Dossier Fiscal : le bouclier
   anti-contrôle"), même mécanique hover que .feature-columns--tech
   (translateY + --shadow-faq, déjà validés), scopée pour ne pas affecter
   les 65+ autres usages de .card sur le site. Validé par Elena le
   2026-09-17. */
.card-step {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card-step:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-faq);
}

/* .card-marketing — cards marketing classiques (home/service/offres, hors
   FAQ et pricing — ex: encadré liste de courtiers) */
.card-marketing {
  background: var(--color-background);
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card-marketing);
  padding: var(--padding-card-marketing);
}

/* Encarts "tech" avec hover (voir docs/missing-components.md, validé par
   Sébastien le 2026-09-16) — scopé à .feature-columns--tech pour ne pas
   affecter les autres pages qui réutilisent .feature-columns__item.card
   (home, /declaration-impots-trading/...). Réutilise les mêmes tokens que
   le hover déjà validé sur l'encart "125 courtiers" (--shadow-faq). */
.feature-columns--tech .card {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.feature-columns--tech .card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-faq);
}

/* Ombre plus marquée sur "C'est quoi DeclarAid ?" (home page), même
   traitement que les encarts du hero /declaration-impots-bourse/ — scopé à
   .feature-columns--elevated pour ne pas changer .card ailleurs. Lever +
   ombre plus prononcée au survol (même mécanique que .feature-columns--tech
   ci-dessus) : des cards cliquables nulle part ailleurs sur la section
   restaient inertes au survol, contrairement au reste de la home. */
.feature-columns--elevated .card {
  box-shadow: var(--shadow-card-marketing);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.feature-columns--elevated .card:hover {
  transform: translateY(-4px);
  box-shadow: rgba(0, 0, 0, 0.35) 0px 12px 28px -6px;
}

/* .card-blog — card de la grille "Derniers articles" (/articles-blog/).
   Fond gris (au lieu du blanc+bordure de .card générique) + ombre pour
   ressortir sur le fond blanc de la page, demandé explicitement par
   Sébastien. Ne réutilise que des tokens déjà validés : pas de nouvelle
   couleur ni de nouvelle ombre. */
.card-blog {
  background: var(--color-background-alt);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card-marketing);
  padding: var(--space-card);
}

.card-blog__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-bottom: var(--space-sm);
}

.card-blog h4 {
  margin: 0 0 var(--space-sm);
}

.card-blog p {
  margin: 0;
  font-size: 13px;
}

/* ---------------------------------------------------------------------
   Page /articles-blog/ — refonte DA "soft minimaliste" (validé Elena,
   2026-10-14, après itération sur maquette interactive). Composants tous
   scopés sous des noms dédiés (.blog-*, .article-tag, .ill-*) pour ne
   JAMAIS toucher .card-blog/.card-blog__badges/.tag, déjà utilisés tels
   quels respectivement sur /sebastien-defrance/ et .broker-table.
   --------------------------------------------------------------------- */

.blog-hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: var(--space-sm);
}

.blog-hero__eyebrow .dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  flex: none;
}

.blog-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--space-lg);
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-xl);
}

.blog-search {
  position: relative;
  flex: 1 1 240px;
  max-width: 320px;
}

.blog-search input {
  width: 100%;
  font: inherit;
  font-size: var(--font-size-body);
  color: var(--color-text);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-xs) 0 var(--space-xs) 24px;
  transition: border-color 0.15s ease;
}

.blog-search input:focus {
  outline: none;
  border-color: var(--color-heading);
}

.blog-search svg {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-tertiary);
}

.blog-filter-tabs {
  display: flex;
  gap: var(--space-lg);
  flex-wrap: wrap;
}

.blog-filter-tab {
  font: inherit;
  font-size: var(--font-size-body);
  font-weight: 500;
  cursor: pointer;
  border: none;
  background: none;
  color: var(--color-text-secondary);
  padding: var(--space-xs) 0;
  position: relative;
  white-space: nowrap;
  transition: color 0.15s ease;
}

.blog-filter-tab::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--color-accent);
  transform: scaleX(0);
  transition: transform 0.18s ease;
}

.blog-filter-tab:hover {
  color: var(--color-heading);
}

.blog-filter-tab.is-active {
  color: var(--color-heading);
  font-weight: 600;
}

.blog-filter-tab.is-active::after {
  transform: scaleX(1);
}

/* Trait actif coloré par catégorie (demande explicite d'Elena, 2026-10-14) —
   "Tous" et "Fiscalité" gardent --color-accent (couleur par défaut ci-dessus). */
.blog-filter-tab[data-filter="crypto"].is-active::after {
  background: var(--color-category-crypto);
}

.blog-filter-tab[data-filter="bourse"].is-active::after {
  background: var(--color-info-text);
}

.blog-empty-state {
  display: none;
  text-align: center;
  padding: var(--space-3xl) 0;
  color: var(--color-text-secondary);
}

.blog-empty-state.is-visible {
  display: block;
}

/* .article-tag — étiquette de catégorie (texte + point coloré) de la
   nouvelle grille. Distinct de .tag (broker-table, 5 couleurs scopées à ce
   composant précis) : ne jamais fusionner les deux. */
.article-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  text-decoration: none;
}

.article-tag .dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  flex: none;
}

.article-tag--fiscalite .dot { background: var(--color-accent); }
.article-tag--crypto .dot { background: var(--color-category-crypto); }
.article-tag--bourse .dot { background: var(--color-info-text); }

/* .blog-card — nouvelle card de la grille "Derniers articles", distincte
   de .card-blog (conservé pour /sebastien-defrance/). */
.blog-card {
  display: flex;
  flex-direction: column;
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-align: left;
  transition: box-shadow 0.2s ease;
}

.blog-card:hover {
  box-shadow: var(--shadow-card-soft);
}

/* Filtre JS (blog-filter.js) : [hidden] seul ne suffit pas à masquer une
   card en display:flex/grid (l'UA stylesheet perd face à .blog-card),
   même mécanique que .broker-table tbody tr[hidden]. */
.blog-card[hidden] {
  display: none;
}

.blog-card__body {
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1;
}

.blog-card__body .heading-style-h4 {
  margin: 0;
}

.blog-card__body h2 a,
.blog-card__body h3 a {
  color: inherit;
  text-decoration: none;
}

.blog-card__body h2 a:hover,
.blog-card__body h3 a:hover {
  text-decoration: underline;
  text-decoration-color: var(--color-accent);
  text-decoration-thickness: 2px;
}

.blog-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.blog-card .text-tertiary {
  margin-top: auto;
  padding-top: var(--space-2xs);
}

/* .blog-card__cover — vignette 16:9 de la grille blog, même ratio/recadrage
   que .press-card__thumb. Toutes les cards utilisent une vraie photo
   (<img>, ajoutées une par une par Elena le 2026-10-14) ; a porté une
   illustration SVG générique par catégorie entre le 2026-10-14 (refonte
   DA) et ce remplacement — voir docs/missing-components.md pour l'historique
   et --color-category-crypto (toujours utilisé par .article-tag/.tag). */
.blog-card__cover {
  position: relative;
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-background-alt);
  overflow: hidden;
}

.blog-card__cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.3s ease;
}

.blog-card:hover .blog-card__cover img,
.blog-card--featured:hover .blog-card__cover img {
  transform: scale(1.04);
}

/* .blog-card--featured — article le plus récent, mis en avant en tête de
   grille. */
.blog-card--featured {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: var(--space-xl);
}

.blog-card--featured .blog-card__cover {
  aspect-ratio: auto;
  height: 100%;
  min-height: 240px;
}

.blog-card--featured .blog-card__body {
  padding: var(--space-xl) var(--space-xl) var(--space-xl) 0;
  justify-content: center;
}

.blog-card--featured .heading-style-h2 {
  margin: 0 0 var(--space-sm);
}

.blog-card--featured .tag-latest {
  color: var(--color-text-tertiary);
  font-size: var(--font-size-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.blog-card--featured .dek {
  color: var(--color-text-secondary);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  margin: 0 0 var(--space-md);
  max-width: 52ch;
}

.blog-card--featured .cta {
  margin-top: var(--space-md);
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-weight: 600;
  font-size: var(--font-size-body);
  color: var(--color-heading);
  text-decoration: none;
}

.blog-card--featured .cta svg { transition: transform 0.15s ease; }
.blog-card--featured:hover .cta svg { transform: translateX(3px); }

@media (max-width: 760px) {
  .blog-card--featured {
    grid-template-columns: 1fr;
  }
  .blog-card--featured .blog-card__cover {
    aspect-ratio: 16 / 9;
    min-height: 0;
  }
  .blog-card--featured .blog-card__body {
    padding: var(--space-lg);
  }
}

.blog-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
}

@media (max-width: 900px) {
  .blog-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .blog-grid { grid-template-columns: 1fr; }
}

/* .blog-newsletter — sobre, sans bloc de couleur plein : deux filets fins
   + champ en soulignement. Branché à Customer.io via
   functions/api/newsletter-subscribe.js (voir docs/missing-components.md) :
   .blog-newsletter__form-col regroupe le form et le message de retour
   (succès/erreur) en colonne, .blog-newsletter__form reste la ligne
   input+bouton. */
.blog-newsletter {
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-2xl) 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  text-align: left;
}

.blog-newsletter__text h3 {
  margin: 0 0 6px;
  color: var(--color-heading);
}

.blog-newsletter__text p {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-body);
  max-width: 40ch;
}

.blog-newsletter__form-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1 1 320px;
  max-width: 420px;
}

.blog-newsletter__form {
  display: flex;
  align-items: flex-end;
  gap: var(--space-md);
}

/* .blog-newsletter__hp — piège à bots (honeypot) : champ réel pour un
   robot qui remplit tout, invisible et inatteignable au clavier pour une
   personne (voir functions/api/newsletter-subscribe.js). */
.blog-newsletter__form .blog-newsletter__hp {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.blog-newsletter__feedback {
  margin: 0;
  min-height: 1.2em;
  font-size: 13px;
}

.blog-newsletter__feedback.is-success {
  color: var(--color-success-text);
}

.blog-newsletter__feedback.is-error {
  color: var(--color-error-text);
}

.blog-newsletter__form input {
  flex: 1;
  font: inherit;
  font-size: var(--font-size-body);
  padding: var(--space-xs) 0;
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text);
}

.blog-newsletter__form input:focus {
  outline: none;
  border-color: var(--color-heading);
}

.blog-newsletter__form button {
  font: inherit;
  font-weight: 600;
  font-size: var(--font-size-body);
  white-space: nowrap;
  cursor: pointer;
  background: none;
  color: var(--color-heading);
  border: none;
  padding: var(--space-xs) 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.blog-newsletter__form button:disabled {
  color: var(--color-text-tertiary);
  cursor: not-allowed;
}

.blog-newsletter__form button:not(:disabled):hover {
  text-decoration: underline;
  text-decoration-color: var(--color-accent);
  text-decoration-thickness: 2px;
}

/* .press-card — mentions presse (/media-presse/). Toute la card est un
   <a> (un seul lien cliquable, cf. demande explicite d'Elena), zone
   visuelle de hauteur fixe avec logo centré (contain, taille plafonnée
   pour une présence homogène quel que soit le format du logo d'origine),
   colonne flex pour que "Lire l'article" (désormais un <span>, plus un
   <a> imbriqué) reste collé en bas même si le titre fait 1 ou 3 lignes.
   Mêmes classes/configuration que pro (déclaraid-pro-web-site),
   --color-accent-secondary n'existe pas dans ce design system : substitué
   par --color-heading (déjà utilisé comme couleur des titres de card). */
.press-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* display:flex ci-dessus écraserait sinon le display:none de l'attribut
   [hidden] (règle UA de priorité la plus basse, qu'une règle auteur, même
   moins spécifique, l'emporte dessus) — utilisé pour masquer sans JS les
   articles au-delà des 12 premiers sur /media-presse/, voir le script en
   bas de page. */
.press-card[hidden] {
  display: none;
}

.press-card:hover {
  transform: translateY(-2px);
  /* Ombre douce déjà utilisée pour le hover /articles-blog/ (refonte DA
     soft, validé Elena 2026-10-14) — pas une nouvelle valeur. */
  box-shadow: var(--shadow-card-soft);
}

.press-card__thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 180px;
  flex: none;
  overflow: hidden;
  background: var(--color-background-alt);
}

.press-card__thumb img {
  display: block;
  max-width: 60%;
  /* 80px (et non 56px) : les logos "wordmark" larges restent de toute façon
     plafonnés par max-width:60% avant d'atteindre cette hauteur (ex. Les
     Echos : ~52px à 60% de largeur) — seuls les logos carrés/portrait
     (ex. Agence BDOR) profitent de la marge supplémentaire. */
  max-height: 80px;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* Podcasts & interviews : vignettes photo (pas des logos de marque, voir
   le HTML), remplissent toute la zone visuelle (comme avant la refonte
   "logo centré" pensée pour la grille Articles de presse uniquement). */
#podcasts-grid .press-card__thumb img {
  max-width: 100%;
  max-height: 100%;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.press-card__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: var(--space-card);
}

.press-card__meta {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0 0 var(--space-xs);
  font-size: 13px;
  /* --color-text-tertiary (#a3a3a3) ne passe pas le contraste AA sur fond
     blanc (~2.3:1) — remplacé par --color-text-secondary (#737373, ~4.7:1),
     déjà utilisé ailleurs comme gris de texte secondaire lisible. */
  color: var(--color-text-secondary);
}

.press-card__outlet {
  font-weight: 700;
  color: var(--color-text);
}

/* Taille de titre de card propre à la page /media-presse/ (17px/600),
   distincte de l'échelle --font-size-h4 (18px/700) partagée par le reste du
   site : propriétés reprises explicitement ici plutôt que via la classe
   partagée .heading-style-h4, pour ne pas en changer le rendu ailleurs. */
.press-card__title {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: 17px;
  font-weight: 600;
}

/* "Lire l'article" (un <span>, toute la card étant déjà le lien) — collé en
   bas de la card grâce à margin-top:auto dans la colonne flex, même si le
   titre au-dessus fait 1 ou 3 lignes selon les cards d'une même rangée. */
.press-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-top: auto;
  padding-top: var(--space-sm);
  font-size: 13px;
  font-weight: 600;
  color: var(--color-heading);
}

.press-card__link svg {
  transition: transform 0.2s ease;
}

.press-card:hover .press-card__link svg {
  transform: translateX(2px);
}

/* .press-tv — vidéo mise en avant (/media-presse/), colonne gauche (58%)
   de .split-section--tv. Bouton play repris de .icon-badge (cercle plein,
   même ombre teintée) agrandi pour un usage lecteur vidéo ;
   --shadow-card-marketing (déjà utilisée pour les cards mises en avant)
   remplace l'ombre verte du brouillon d'origine, hors design system. */
.press-tv {
  width: 100%;
}

.press-tv__player {
  display: block;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-background-dark);
  box-shadow: var(--shadow-card-marketing);
  cursor: pointer;
}

.press-tv__player img,
.press-tv__player iframe {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 0;
}

.press-tv__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 76px;
  height: 76px;
  border-radius: 50%;
  /* Même fond que .btn-primary (bouton "Démarrer" du header) — pas une
     couleur inventée pour l'occasion. */
  background: linear-gradient(180deg, #2b2b2b 0%, var(--color-primary) 100%);
  color: var(--color-primary-text);
  box-shadow: rgba(23, 23, 23, 0.22) 0px 8px 16px -6px;
  display: grid;
  place-items: center;
  transition: transform 0.2s ease;
}

.press-tv__play svg {
  width: 32px;
  height: 32px;
  fill: currentColor;
  margin-left: 3px;
}

.press-tv__player:hover .press-tv__play,
.press-tv__player:focus-visible .press-tv__play {
  transform: scale(1.08);
}

.press-tv__player:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .press-tv__play {
    transition: none;
  }
}

/* .press-tv__title — colonne texte de .split-section--tv (titre H3 22px),
   taille propre à ce bloc, distincte de l'échelle --font-size-h3 (20px)
   partagée par le reste du site. Section toujours sur fond noir (voir
   .press-band--black) : blanc en dur, pas de variante claire à gérer. */
.press-tv__title {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: #fff;
  font-size: 22px;
  font-weight: 700;
}

/* .press-tv__meta / .press-tv__desc — méta et description de la colonne
   texte, gris clair #b5b5b5 donné explicitement par Elena pour ce bloc sur
   fond noir (distinct du rgba(255,255,255,.7) utilisé par .section--dark
   ailleurs sur le site, qui reste la convention par défaut hors de ce
   bloc précis). */
.press-tv__meta {
  margin: 0 0 var(--space-xs);
  font-size: 13px;
  color: #b5b5b5;
}

.press-tv__desc {
  margin: 0 0 var(--space-md);
  line-height: 1.6;
  color: #b5b5b5;
}

/* .press-tv__watch-link — déclenche la même lecture que le clic sur la
   vignette (voir le script en bas de page), simple relais visuel pour les
   utilisateurs qui ne cliquent pas l'image. */
.press-tv__watch-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  /* Padding vertical pour une zone tactile >= 44px (le texte seul ne fait
     que ~26px de haut), sans décaler visuellement le texte grâce à la
     marge négative qui compense. */
  margin: -10px 0;
  padding: 10px 0;
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.press-tv__watch-link:hover {
  opacity: 0.85;
}

/* .press-logos-row — logos "Ils parlent de nous" (ancien hero /media-presse/,
   bloc retiré — règles conservées si réutilisées ailleurs un jour). Logos en
   niveaux de gris, estompés au repos, pleine couleur au survol. */
.press-logos-label {
  margin: 0 0 var(--space-sm);
  font-size: 13px;
  color: var(--color-text-secondary);
}

.press-logos-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xl);
}

.press-logos-row img {
  height: 28px;
  width: auto;
  filter: grayscale(1);
  opacity: 0.7;
  transition: opacity 0.2s ease;
}

.press-logos-row img:hover {
  opacity: 1;
}

/* .press-page-h1 / .press-page-h2 — échelle typographique propre à
   /media-presse/ (44/32 et 28/24), distincte de --font-size-h1/h2
   (50/35 desktop, 34/26 mobile) partagés par le reste du site : classes
   dédiées plutôt que redéfinir les tokens globaux, pour ne pas changer le
   rendu des autres pages. Marge basse réduite : chaque H2 de section est
   maintenant suivi d'une ligne de description (.press-page-lead) qui porte
   elle-même l'écart de 40px vers le contenu. */
.press-page-h1 {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: 44px;
  font-weight: 700;
}

.press-page-h2 {
  margin: 0 0 var(--space-xs);
  font-family: var(--font-family-heading);
  line-height: var(--line-height-heading);
  color: var(--color-heading);
  font-size: 28px;
  font-weight: 700;
}

.press-page-lead {
  margin: 0 0 40px;
  font-size: 17px;
  line-height: 1.6;
  color: var(--color-text-secondary);
}

@media (max-width: 767px) {
  .press-page-h1 {
    font-size: 32px;
  }

  .press-page-h2 {
    font-size: 24px;
  }
}

/* .press-band — bandes de section pleine largeur /media-presse/ (80px
   desktop / 56px mobile, les deux côtés : les bandes se touchent, pas de
   grand vide entre elles, juste la bordure de couleur). .press-band--black
   reprend --color-background-dark (#1f2125), le noir de la section
   "DeclarAid en chiffres" de la home — demande explicite d'Elena, remplace
   --color-primary utilisé dans une première passe. Texte blanc par défaut
   dans la bande ; .press-page-h2 et .press-tv__title gardent leur propre
   couleur foncée par défaut, d'où l'override explicite ci-dessous. */
.press-band {
  padding: 80px 0;
}

@media (max-width: 767px) {
  .press-band {
    padding: 56px 0;
  }
}

/* Sélecteur composé (.press-band.press-band--black) : .section est défini
   plus loin dans ce fichier avec la même spécificité qu'un sélecteur
   .press-band--black seul, et gagnerait sinon par ordre de cascade. */
.press-band.press-band--black {
  background: var(--color-background-dark);
  color: #fff;
}

.press-band--black .press-page-h2 {
  color: #fff;
}

.press-band--black .press-page-lead {
  color: rgba(255, 255, 255, 0.7);
}

/* .btn-primary--invert — "Essayer DeclarAid" sur fond noir (section
   "Vous êtes journaliste ?") : juste blanc + --color-primary inversés,
   aucune nouvelle couleur. */
.btn-primary--invert {
  background: #fff;
  color: var(--color-primary);
}

.btn-primary--invert:hover {
  background: #e5e5e5;
}

/* .card-toc — sommaire d'article de blog */
.card-toc {
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-toc);
  box-shadow: var(--shadow-toc);
  padding: var(--space-card);
}

/* .toc-link — lien du sommaire d'article (dans .card-toc). Surlignage de
   la section en cours de lecture ajouté suite à demande explicite de
   Sébastien (scroll-spy, voir docs/missing-components.md et
   src/js/toc-scrollspy.js) — réutilise uniquement des tokens existants. */
.toc-link {
  display: block;
  padding: var(--space-xs) 0 var(--space-xs) var(--space-xs);
  font-size: 13px;
  line-height: 1.4;
  color: var(--color-text-secondary);
  border-left: 2px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}

li + li > .toc-link {
  margin-top: var(--space-2xs);
}

.toc-link.is-active {
  color: var(--color-heading);
  border-left-color: var(--color-accent);
  font-weight: 700;
}

/* .reveal — utilitaire générique d'apparition au scroll (fondu + léger
   décalage vers le haut), posé par js/scroll-reveal.js sur tout élément
   marqué [data-reveal] (stats, étapes "Comment ça marche ?", cards "Pour
   qui ?"...). La classe n'est ajoutée que par ce script : sans JS, rien
   n'est masqué, tout reste visible par défaut. */
.reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.reveal.is-revealed {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* .card-faq — FAQ, présente sur les pages marketing et en fin d'article de
   blog. Padding non mesuré précisément par le script d'extraction (porté
   par un élément interne non capturé) — valeur approximative. overflow:
   hidden clippe le survol de <summary> (voir details.card-faq > summary
   dans layout.css) aux coins arrondis de la card. */
.card-faq {
  background: var(--color-background);
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-faq);
  padding: var(--space-md);
  overflow: hidden;
}

details.card-faq > summary:hover {
  background: var(--color-background-alt);
}

/* .accordion-group--faq — registre légèrement plus "engineered" que
   .card-faq générique (fin d'article de blog...) : filet fin au lieu de
   l'ombre, marqueur en pastille ronde plutôt qu'un simple caractère +/-,
   accent sur la question active. Scopé pour ne pas affecter .card-faq
   ailleurs sur le site. */
.accordion-group--faq .card-faq {
  border: 1px solid var(--color-border);
  box-shadow: none;
  transition: border-color 0.2s ease;
}

.accordion-group--faq .card-faq:hover {
  border-color: var(--color-text-tertiary);
}

.accordion-group--faq details.card-faq[open] {
  border-color: var(--color-primary);
}

.accordion-group--faq details.card-faq > summary::after {
  content: '+';
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  font-size: 16px;
  font-weight: 400;
  line-height: 1;
  color: var(--color-text-secondary);
  transition: all 0.2s ease;
}

.accordion-group--faq details.card-faq[open] > summary::after {
  content: '\2212';
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-primary-text);
}

/* .card-quote — bloc citation avec avatar (fondateur/expert). Deux
   variantes : blanche avec ombre (défaut) et fond bleu clair (--info,
   réutilise --color-background-info déjà validé). */
.card-quote {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  background: var(--color-background);
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card-marketing);
  padding: var(--space-card);
  /* Texte toujours aligné à gauche, y compris dans un parent centré
     (ex: .content-section) — validé ronde 5. */
  text-align: left;
}

.card-quote--info {
  background: var(--color-background-info);
  box-shadow: none;
}

/* .card-quote--info-strong — variante bleu un peu plus soutenu, scopée à un
   module précis (voir docs/missing-components.md, validé par Sébastien le
   2026-09-17) pour ne pas changer .btn-info ni les autres .card-quote--info
   qui partagent --color-background-info. */
.card-quote--info-strong {
  background: #e8f2ff;
}

.card-quote__avatar {
  flex-shrink: 0;
  width: var(--size-avatar-sm);
  height: var(--size-avatar-sm);
  border-radius: 50%;
  object-fit: cover;
}

/* .card-testimonial — avis client (nom/rôle + titre + citation + étoiles,
   dans cet ordre — pas d'avatar dans le contenu réel du site). Prévue pour
   être utilisée dans un carrousel .testimonial-marquee (voir plus bas). */
.card-testimonial {
  flex: 0 0 320px;
  background: var(--color-background);
  border: none;
  border-radius: var(--radius-md);
  /* Pas d'ombre (ronde 5) : le carrousel doit se fondre dans le fond de la
     page, sans démarcation visuelle entre les cards et la section. */
  box-shadow: none;
  padding: var(--space-card);
}

.card-testimonial__author {
  margin: 0 0 var(--space-sm);
}

.card-testimonial__title {
  margin: 0 0 var(--space-xs);
}

.card-testimonial__quote {
  margin: 0 0 var(--space-sm);
}

.card-testimonial__stars {
  color: var(--color-accent);
  letter-spacing: 2px;
  margin: 0;
}

/* .testimonial-marquee — carrousel à défilement continu (2 rangées, sens
   opposés) avec fondu sur les bords. Le contenu de .testimonial-track doit
   être dupliqué une fois dans le HTML (2 jeux de cards identiques à la
   suite) pour boucler sans coupure — l'animation va de 0 à -50%. */
.testimonial-marquee {
  overflow: hidden;
  min-width: 0;
  -webkit-mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
  mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
}

.testimonial-track {
  display: flex;
  align-items: center;
  width: max-content;
  min-width: 0;
  gap: var(--space-md);
  animation: marquee-left var(--duration-marquee) linear infinite;
}

@keyframes marquee-left {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .testimonial-track {
    animation: none;
  }
}

/* Pause au survol (couvre aussi .logo-marquee-track, qui réutilise cette
   classe) : sans ça, un visiteur ne peut ni lire un témoignage en entier ni
   cliquer un logo presse pendant le défilement continu (aussi un point
   WCAG 2.2.2 — contenu en mouvement qui doit pouvoir être mis en pause). */
.testimonial-marquee:hover .testimonial-track {
  animation-play-state: paused;
}

/* .logo-marquee-track — variante logos (hero /declaration-impot-crowdfunding/,
   hero /declaration-impot-crypto/, section "Comment ça marche ?"
   /declaration-impot-crypto/), réutilise .testimonial-marquee/.testimonial-track
   pour le défilement continu, sans le cadre .card : juste les logos, hauteur
   uniforme + espace un peu plus généreux qu'entre les cards témoignages. */
.logo-marquee-track {
  gap: var(--space-lg);
}

.logo-marquee-track img {
  height: 40px;
  width: auto;
  flex-shrink: 0;
}

/* .press-track — bandeau presse (home, "La presse et les experts parlent de
   DeclarAid"). Hauteur uniforme (20px, réduite de 32px le 2026-10-08 —
   demande explicite d'Elena, "comme chez Stripe" : des wordmarks discrets,
   pas des logos qui dominent le bandeau). Repassé en carrousel défilant le
   même jour (demande explicite d'Elena, "comme avant") après un essai en
   rangée statique — mêmes règles que .press-track pro — scopé pour ne pas
   changer les autres usages de .logo-marquee-track (hero crypto/
   crowdfunding). */
.press-track {
  animation-duration: 40s;
  gap: var(--space-3xl);
}

.press-track img {
  height: 20px;
  width: auto;
}

/* Sous 640px, la même durée que sur desktop défile trop lentement vu
   l'écran plus étroit : durée réduite de moitié, même principe que pro. */
@media (max-width: 640px) {
  .press-track {
    animation-duration: 20s;
  }
}

/* .press-section — bandeau presse isolé (demande explicite d'Elena,
   2026-10-08, "exactement comme sur Stripe") : fond blanc, filet fin
   au-dessus ET en dessous (même --color-border que la bordure sous le
   header), collé de près au hero (faible padding). */
.press-section {
  background: var(--color-background);
  border-top: 1px solid var(--color-border);
  /* border-bottom retirée le 2026-10-08 (demande explicite d'Elena) : la
     bande sombre de .stats-section juste en dessous sert elle-même de
     séparation, la bordure devenait redondante. */
  padding: var(--space-md) 0;
}

/* .press-row — carrousel presse + CTA "Voir les articles" alignés sur une
   même ligne (demande explicite d'Elena, 2026-10-08) : le carrousel garde
   son fondu existant (.testimonial-marquee) plutôt qu'une coupure nette, le
   CTA ne wrap jamais. Repassé à une largeur contenue (max-width 1400,
   calée sur .hero) le même jour — demande explicite d'Elena de réduire le
   défilement pleine page essayé juste avant. */
.press-row {
  display: flex;
  align-items: center;
  gap: var(--space-xl);
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 var(--space-md);
}

.press-row__marquee {
  flex: 1 1 auto;
  min-width: 0;
}

/* Lien plat, sans box/ombre (DA du bandeau : "la donnée porte la structure,
   rien ne la fait flotter dans une boîte", même principe que .trust-band
   et .stats-grid__cta juste en dessous) — accent jaune au survol + flèche
   qui glisse, même micro-interaction que .btn-primary/.btn-info. Revu le
   2026-10-08 (le traitement pilule noire précédent ne correspondait pas à
   la DA de la page, retour explicite d'Elena). */
.press-row__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  flex-shrink: 0;
  white-space: nowrap;
  color: var(--color-text-secondary);
  font-weight: 600;
  font-size: 14px;
  transition: color 0.15s ease;
}

.press-row__cta:hover {
  color: var(--color-accent);
}

.press-row__cta-arrow {
  display: inline-block;
  transition: transform 0.2s ease;
}

.press-row__cta:hover .press-row__cta-arrow {
  transform: translateX(3px);
}

/* Sous 767px, pas assez de place pour une ligne : CTA repassé sous le
   carrousel, centré (comportement d'origine avant .press-row). */
@media (max-width: 767px) {
  .press-row {
    flex-direction: column;
    gap: var(--space-md);
  }
}

/* .tag-cycle — mot/expression qui alterne dans une liste (ex: plateformes
   compatibles sous un titre de catégorie, hero /declaration-impots-bourse/).
   Le JS (js/tag-cycle.js) swap le texte et bascule cette classe pour le
   fondu ; en CSS seul, le mot reste affiché sans alterner (dégradation
   propre, pas de contenu qui disparaît si le JS ne charge pas). */
.tag-cycle {
  display: inline-block;
  color: var(--color-accent);
  font-weight: 700;
  transition: opacity 0.25s ease;
}

.tag-cycle--fade {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .tag-cycle {
    transition: none;
  }
}

/* Sections */

.section {
  padding: var(--space-3xl) 0;
  background: var(--color-background);
}

.section--alt {
  background: var(--color-background-alt);
}

.section--muted {
  background: var(--color-surface-muted);
}

.section--info {
  background: var(--color-background-info);
}

/* .section--dark — "Sécurité et Confidentialité". Couleur validée ronde 3. */
.section--dark {
  background: var(--color-background-dark);
  color: var(--color-text-on-dark);
}

.section--dark h1,
.section--dark h2,
.section--dark h3,
.section--dark h4 {
  color: var(--color-text-on-dark);
}

.section--dark p {
  color: rgba(255, 255, 255, 0.7);
}

/* .table-search — champ de recherche au-dessus de .broker-table (filtre par
   nom de courtier, voir js/broker-search.js). Validé explicitement par
   Sébastien pour /page-courtiers/ (voir docs/missing-components.md). */
.table-search {
  display: block;
  width: 100%;
  max-width: 400px;
  margin: 0 auto var(--space-lg);
  padding: var(--padding-btn-compact);
  font: inherit;
  font-size: var(--font-size-body);
  color: var(--color-text);
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  outline: none;
  transition: border-color 0.2s ease;
}

.table-search:focus {
  border-color: var(--color-tag-cto-bg);
}

/* Icône loupe dans la barre de recherche (voir docs/missing-components.md,
   validé par Sébastien le 2026-09-16) — réutilise --space-sm/md/lg et
   --color-text-tertiary déjà validés, même style d'icône ligne (stroke-width
   2) que le reste du site. */
.table-search-wrap {
  position: relative;
  max-width: 400px;
  margin: 0 auto var(--space-lg);
}

.table-search-wrap .table-search {
  margin: 0;
  padding-left: calc(var(--space-md) + 20px);
}

.table-search__icon {
  position: absolute;
  left: var(--space-sm);
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-tertiary);
  pointer-events: none;
}

.table-search__no-results {
  display: none;
  text-align: center;
  padding: var(--space-lg);
  color: var(--color-text-tertiary);
}

/* .broker-table — tableau des courtiers (/page-courtiers/). En-tête gris,
   lignes zébrées, colonnes Pays/Import centrées — repris du widget
   d'origine, validé explicitement par Sébastien (voir
   docs/missing-components.md). */
.broker-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.broker-table th,
.broker-table td {
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--color-border);
  text-align: left;
}

.broker-table thead tr {
  background: var(--color-secondary-bg);
  font-weight: 600;
  color: var(--color-heading);
}

.broker-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--color-secondary-bg);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.broker-table th:nth-child(2),
.broker-table td:nth-child(2),
.broker-table th:nth-child(3),
.broker-table td:nth-child(3) {
  text-align: center;
}

.broker-table tbody tr:nth-child(even) {
  background: var(--color-background-alt);
}

.broker-table tbody tr[hidden] {
  display: none;
}

/* Hover ergonomique (ronde 6, demande explicite de Sébastien) — réutilise
   --color-background-alt-dark, déjà validé, pour rester au-dessus du zébrage. */
.broker-table tbody tr {
  transition: background-color 0.15s ease;
}

.broker-table tbody tr:hover {
  background: var(--color-background-alt-dark);
}

/* Mobile (<768px, breakpoint canonique du site) : le tableau à 4 colonnes ne
   tient pas à l'écran — au lieu du scroll horizontal (pénible sur mobile),
   chaque ligne devient une mini-carte empilée. Le libellé de chaque colonne
   (data-label, posé sur chaque <td> dans le HTML) réapparaît via ::before à
   la place de l'en-tête, masqué sur ce format. */
@media (max-width: 767px) {
  .broker-table thead {
    display: none;
  }

  .broker-table,
  .broker-table tbody,
  .broker-table tr,
  .broker-table td {
    display: block;
    width: 100%;
  }

  .broker-table tr {
    padding: var(--space-sm) var(--space-md);
    border-bottom: 1px solid var(--color-border);
  }

  .broker-table tbody tr:nth-child(even) {
    background: var(--color-background-alt);
  }

  .broker-table td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    padding: var(--space-2xs) 0;
    border-bottom: none;
    text-align: right;
  }

  .broker-table td::before {
    content: attr(data-label);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--color-text-tertiary);
    text-align: left;
  }

  .broker-table td:first-child {
    font-weight: 700;
    color: var(--color-heading);
    padding-top: 0;
  }

  .broker-table td:first-child::before {
    display: none;
  }
}

/* .tag — pilule de catégorie dans .broker-table (une ou plusieurs par
   ligne). Distinct de .badge (réservé aux cards pricing) : 5 couleurs
   propres à cette colonne, non réutilisables ailleurs sans revalidation. */
.tag {
  display: inline-block;
  padding: 2px 10px;
  margin: 2px;
  border-radius: var(--radius-pill);
  font-size: var(--font-size-xs);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--color-tag-text);
}

.tag--cto { background: var(--color-tag-cto-bg); }
.tag--pea { background: var(--color-tag-pea-bg); }
.tag--crypto { background: var(--color-tag-crypto-bg); }
.tag--crowdfunding { background: var(--color-tag-crowdfunding-bg); }
.tag--compte { background: var(--color-tag-compte-bg); }
