:root {
  /* Dit au navigateur que les DEUX palettes sont supportées -- sans ça,
     Safari ne sait pas quelle couleur UA par défaut (typiquement blanche)
     utiliser pour les zones qu'il peint lui-même AVANT que notre CSS ne
     s'applique (le rebond élastique en est une : c'est une zone hors du
     document, peinte par le moteur de rendu, pas par notre `background`
     sur html/body). `theme-color` (voir les <meta> dans chaque gabarit)
     ne couvre que la barre d'adresse/le chrome du navigateur -- ceci est
     la pièce manquante pour la zone de rebond elle-même. Persistant même
     si le flash n'apparaissait qu'occasionnellement avant : la cause
     réelle, pas juste un symptôme mieux masqué. */
  color-scheme: light dark;
  --bg: #FFFFFF;
  --ink: #0A0A0A;
  --ink-soft: #4A4A4A;
  --line: #0A0A0A;
  --accent: #0047FF;
  --accent-ink: #FFFFFF;
  --invert-bg: #0A0A0A;
  --invert-ink: #FFFFFF;
  --tag-campagne-bg: #0A0A0A;
  --tag-campagne-ink: #FFFFFF;
  --tag-politique-bg: #FFFFFF;
  --tag-politique-ink: #0A0A0A;
  --tag-communique-bg: #FFB800;
  --tag-communique-ink: #1A1400;
  --tag-gouv-bg: #003DA5;
  --tag-gouv-ink: #FFFFFF;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0A0A0A;
    --ink: #F5F5F5;
    --ink-soft: #ADADAD;
    --line: #F5F5F5;
    --accent: #5B8CFF;
    --accent-ink: #0A0A0A;
    --invert-bg: #F5F5F5;
    --invert-ink: #0A0A0A;
    --tag-campagne-bg: #F5F5F5;
    --tag-campagne-ink: #0A0A0A;
    --tag-politique-bg: #0A0A0A;
    --tag-politique-ink: #F5F5F5;
    --tag-communique-bg: #FFB800;
    --tag-communique-ink: #1A1400;
    --tag-gouv-bg: #003DA5;
    --tag-gouv-ink: #FFFFFF;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0A0A0A; --ink: #F5F5F5; --ink-soft: #ADADAD; --line: #F5F5F5;
  --accent: #5B8CFF; --accent-ink: #0A0A0A;
  --invert-bg: #F5F5F5; --invert-ink: #0A0A0A;
  --tag-campagne-bg: #F5F5F5; --tag-campagne-ink: #0A0A0A;
  --tag-politique-bg: #0A0A0A; --tag-politique-ink: #F5F5F5;
  --tag-communique-bg: #FFB800; --tag-communique-ink: #1A1400;
  --tag-gouv-bg: #003DA5; --tag-gouv-ink: #FFFFFF;
}
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #FFFFFF; --ink: #0A0A0A; --ink-soft: #4A4A4A; --line: #0A0A0A;
  --accent: #0047FF; --accent-ink: #FFFFFF;
  --invert-bg: #0A0A0A; --invert-ink: #FFFFFF;
  --tag-campagne-bg: #0A0A0A; --tag-campagne-ink: #FFFFFF;
  --tag-politique-bg: #FFFFFF; --tag-politique-ink: #0A0A0A;
  --tag-communique-bg: #FFB800; --tag-communique-ink: #1A1400;
  --tag-gouv-bg: #003DA5; --tag-gouv-ink: #FFFFFF;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
  /* Le rebond élastique blanc en haut de page sur iOS Safari a déjà été
     "corrigé" trois fois cette session (overscroll-behavior, puis
     theme-color, puis color-scheme, puis un double conteneur de scroll
     html/body figés + .scroll-shell) -- ce dernier essai supprimait bien
     le rebond, mais cassait l'en-tête sticky (glitch de repositionnement
     pendant le scroll, même avec 100dvh) ET le défilement des pages admin
     (qui n'utilisent pas base.html/.scroll-shell -- overflow:hidden sur
     html/body s'appliquait quand même globalement, les rendant
     infernables à faire défiler). Reverti : scroll normal du document,
     conforme au reste du site depuis le début. Ce fond reste la seule
     mesure : imparfait sur l'ombre native du rebond, mais aucune
     régression fonctionnelle. */
  background: var(--bg);
}
body {
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, "Segoe UI", "Helvetica Neue", ui-sans-serif, sans-serif;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

.pixel-mark {
  width: 16px;
  height: 16px;
  fill: currentColor;
  image-rendering: pixelated;
  flex-shrink: 0;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  row-gap: 8px;
  padding: 14px 16px;
  background: var(--invert-bg);
  color: var(--invert-ink);
  border-bottom: 2px solid var(--line);
}
.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 900;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--invert-ink);
}

/* Nom complet en deux lignes, façon manchette de journal : "Revue de" en
   accent bleu au-dessus de "PRESSE" en gras blanc avec une ombre portée
   dure (zéro flou, façon tampon/affiche imprimée -- cohérent avec les
   bordures franches de 2px déjà utilisées partout, jamais d'ombre floue
   ailleurs sur ce site) plutôt qu'un simple remplissage plat, pour
   donner du relief graphique sans sortir de la palette existante. */
.brand-square {
  width: 28px;
  height: 28px;
  background: var(--invert-ink);
  box-shadow: 3px 3px 0 var(--accent);
  flex-shrink: 0;
  margin-right: 3px; /* absorbe le débordement visuel de l'ombre portée */
}
.brand-lines {
  display: flex;
  flex-direction: column;
  line-height: 0.95;
  padding-right: 4px; /* absorbe le débordement visuel de l'ombre portée */
}
.brand-kicker {
  font-weight: 800;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--invert-ink);
}
.brand-word {
  font-weight: 900;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-shadow: 3px 3px 0 var(--accent);
  margin-top: 1px;
}

/* Menu burger -- remplace les boutons "Tendances"/"Régions" séparés
   (2026-08-16) : un seul point d'entrée pour toute la navigation
   secondaire (fil général, national, régions, tendances, à propos), même
   composant `<details>` natif que l'ancien sélecteur de région (pas de
   JS requis pour ouvrir/fermer). */
.burger-menu { position: relative; }
.burger-summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 7px 14px;
  border: 2px solid var(--invert-ink);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--invert-ink);
}
.burger-summary::-webkit-details-marker { display: none; }
.burger-menu[open] .burger-summary { background: var(--invert-ink); color: var(--invert-bg); }
.burger-menu ul {
  position: absolute;
  right: 0;
  top: calc(100% + 2px);
  background: var(--invert-bg);
  border: 2px solid var(--invert-ink);
  list-style: none;
  margin: 0;
  padding: 4px;
  max-height: 60vh;
  overflow-y: auto;
  min-width: 230px;
  z-index: 20;
}
.burger-menu li a, .burger-menu li button {
  display: block;
  width: 100%;
  padding: 9px 10px;
  text-decoration: none;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--invert-ink);
  border-bottom: 1px solid color-mix(in srgb, var(--invert-ink) 20%, transparent);
  background: none;
  border-left: none; border-right: none; border-top: none;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
}
.burger-menu li:last-child a, .burger-menu li:last-child button { border-bottom: none; }
.burger-menu li a:hover, .burger-menu li button:hover { background: var(--accent); color: var(--accent-ink); }

.page {
  max-width: 640px;
  margin: 0 auto;
  padding: 20px 14px 48px;
}

.page-title {
  display: inline-block;
  font-size: 1.5rem;
  font-weight: 900;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin: 8px 4px 20px;
  padding-bottom: 6px;
  border-bottom: 5px solid var(--ink);
  text-wrap: balance;
}

.section-header { margin-bottom: 4px; }

/* Titre (+ eyebrow le cas échéant) + widget "présence médiatique" -- voir
   _presence_widget.html. Sur mobile : toujours empilés, le widget passe
   sur sa propre ligne, aligné à droite (pas de flex ici -- les deux
   enfants sont déjà des éléments de bloc, donc déjà l'un sous l'autre).
   Sur grand écran (voir media query plus bas) : les deux colonnes côte à
   côte, alignées sur leur PREMIÈRE ligne -- "Présence médiatique (24 h)"
   tombe ainsi à la même hauteur que l'eyebrow ("Région" / nom de région),
   pas le grand titre en dessous. */
.page-title-row { display: block; }

.presence-widget { margin: 10px 4px 20px; max-width: 100%; text-align: right; }
.presence-widget-label {
  margin: 0 0 8px;
  font-size: 0.76rem; /* identique à .eyebrow -- même poids visuel */
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}
/* Graphique à barres horizontales -- remplace les anciens "chips" ronds
   (cercles de taille fixe, sans encodage visuel de la grandeur, juste un
   chiffre dedans) par une vraie visualisation comparative : la longueur
   de chaque barre encode le compte relatif au parti le plus présent (voir
   _presence_widget.html, `max_n`). Demande explicite de l'utilisateur
   (2026-08-22) : un widget "plus actuel". */
.presence-chart { display: flex; flex-direction: column; gap: 7px; }
.presence-bar-row {
  display: grid;
  grid-template-columns: 2.6em 1fr 2ch;
  align-items: center;
  gap: 10px;
}
.presence-bar-sigle {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  text-align: left;
}
.presence-bar-track {
  position: relative;
  height: 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  overflow: hidden;
}
.presence-bar-fill {
  position: absolute;
  inset: 0 auto 0 0;
  height: 100%;
  border-radius: 999px;
  transform-origin: left;
}
@media (prefers-reduced-motion: no-preference) {
  .presence-bar-fill { animation: presence-bar-grow 0.5s cubic-bezier(0.22, 1, 0.36, 1) both; }
}
@keyframes presence-bar-grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
.presence-bar-count {
  font-size: 0.85rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}

@media (min-width: 700px) {
  .page-title-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    column-gap: 20px;
  }
  .presence-widget { margin-top: 8px; max-width: 260px; }
}

.calendar {
  margin: 8px 4px 24px;
  border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  border-radius: 20px;
  padding: 20px;
}
.calendar-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 16px;
}
.calendar-nav-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  font-size: 1.05rem;
  line-height: 1;
  text-decoration: none;
  color: var(--ink-soft);
  background: transparent;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.calendar-nav-btn:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.calendar-month {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  text-align: center;
}
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.calendar-dow {
  text-align: center;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--ink-soft);
  padding-bottom: 8px;
}
.calendar-day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  border-radius: 50%;
  font-size: 0.85rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-decoration: none;
  transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.calendar-day:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.calendar-day--muted { visibility: hidden; }
.calendar-day--inactive { color: var(--ink-soft); opacity: 0.35; }
.calendar-day--today { box-shadow: inset 0 0 0 1.5px var(--accent); }
.calendar-day--selected {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: none;
}
.calendar-hint {
  margin: 16px 4px 0;
  font-size: 0.85rem;
  color: var(--ink-soft);
  text-align: center;
}
.calendar-hint a { color: var(--accent); text-decoration: none; }
.calendar-hint a:hover { text-decoration: underline; }

.eyebrow {
  margin: 8px 4px 2px;
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  text-decoration: none;
}
.eyebrow a { text-decoration: none; }
.eyebrow a:hover { color: var(--accent); }

.feed {
  display: flex;
  flex-direction: column;
}

.card {
  background: var(--bg);
  color: var(--ink);
  border-top: 2px solid var(--ink);
  padding: 16px 4px;
  transition: background 0.1s ease, color 0.1s ease;
}
.feed .card:last-child { border-bottom: 2px solid var(--ink); }

.card-meta-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  row-gap: 6px;
  margin-bottom: 8px;
}

.tag-group { display: flex; align-items: center; flex-wrap: wrap; row-gap: 6px; gap: 8px; }

.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 9px;
  border: 2px solid var(--ink);
}
.tag-campagne { background: var(--tag-campagne-bg); color: var(--tag-campagne-ink); }
.tag-politique { background: var(--tag-politique-bg); color: var(--tag-politique-ink); }

/* Palier de gouvernement (provincial/fédéral) -- voir moteur/relevance_llm.py.
   Icônes SVG locales (fleur de lys / feuille d'érable, voir
   _palier_icons.html) -- placées après le texte du tag (flex order),
   `currentColor` pour toujours matcher la couleur du texte du tag plutôt
   qu'une couleur fixe. */
.tag-palier { display: inline-flex; }
.tag-palier-icon {
  width: 1.15em;
  height: 1.15em;
  color: inherit;
}

.tag-langue {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  padding: 2px 7px;
  border: 1.5px solid var(--ink-soft);
  color: var(--ink-soft);
}

.parti-dots { display: flex; align-items: center; gap: 4px; }
.parti-dot {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex-shrink: 0;
}

.card-date-group {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
}

.card-date {
  font-size: 0.76rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

.card-heure {
  font-size: 0.68rem;
  color: var(--ink-soft);
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

.card-title {
  display: block;
  font-size: 1.05rem;
  font-weight: 700;
  text-decoration: none;
  color: var(--ink);
  text-wrap: balance;
  margin-bottom: 6px;
}
.card-title:hover { color: var(--accent); }

.card-resume {
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin: 0 0 10px;
}

.card-meta-bottom {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 0.82rem;
}

.card-source { color: var(--ink-soft); font-weight: 700; text-transform: uppercase; font-size: 0.74rem; letter-spacing: 0.03em; }

/* Communiqué de parti -- volontairement pas la couleur du parti concerné
   (ce serait lu comme un endossement) : ambre distinct, texte toujours
   explicite ("COMMUNIQUÉ"), jamais juste une pastille de couleur. Placé
   avant le titre (pas dans card-meta-bottom) pour prévenir la lecture
   avant même le titre. Sans contour -- le bloc de couleur pleine suffit
   à se démarquer, un contour noir cassait l'intégration visuelle. */
.badge-communique, .badge-gouv {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  padding: 6px 10px;
  font-weight: 800;
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  line-height: 1.4;
}
.badge-communique { background: var(--tag-communique-bg); color: var(--tag-communique-ink); }
.badge-gouv { background: var(--tag-gouv-bg); color: var(--tag-gouv-ink); }
.badge-communique-icon, .badge-gouv-icon {
  flex-shrink: 0;
  font-size: 1.7em;
  line-height: 1;
}
.badge-gouv-icon {
  display: flex;
  align-items: center;
}
.badge-gouv-icon svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}
.badge-communique-text, .badge-gouv-text { display: block; }
/* Nom du parti plus petit/moins gras que "COMMUNIQUÉ DE PRESSE" -- certains
   noms officiels sont longs ("Équipe [chef] - [parti]") et débordaient sur
   3 lignes en gardant le même poids que le libellé principal. Même poids
   réduit pour "Gouvernement du Québec" sous "COMMUNICATION
   GOUVERNEMENTALE", par cohérence. */
.badge-communique-parti, .badge-gouv-sub { font-size: 0.82em; font-weight: 600; letter-spacing: 0.01em; }

/* Lien "Voir la couverture" (voir /couverture/{id}) -- affiché seulement
   sous un communiqué qui a vraiment au moins un article lié, voir
   db.communiques_avec_couverture(). */
.lien-couverture {
  display: inline-block;
  margin: 0 0 10px;
  font-size: 0.82rem;
  font-weight: 800;
  text-decoration: none;
  color: var(--accent);
  border-bottom: 2px solid var(--accent);
}
.lien-couverture:hover { opacity: 0.75; }

/* /couverture/{id} -- le communiqué à l'origine du fil, mis en évidence
   par un simple cadre accentué (pas un nouveau composant de carte, juste
   un contenant autour de la carte déjà réutilisée). */
.couverture-origine {
  border: 2px solid var(--accent);
  padding: 4px;
  margin-bottom: 32px;
}

.card-ridings { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; }
.riding-badge {
  text-decoration: none;
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 600;
  border-bottom: 2px solid var(--accent);
}
.riding-badge:hover { color: var(--accent); }

.scroll-sentinel {
  padding: 24px 0;
  text-align: center;
}
.scroll-loading {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
/* htmx ne fournit pas ce CSS lui-même -- juste la classe .htmx-request
   pendant que la requête est en vol -- l'affichage conditionnel est à
   définir ici. */
.htmx-indicator { opacity: 0; transition: opacity 0.15s ease; }
.htmx-request.htmx-indicator, .htmx-request .htmx-indicator { opacity: 1; }

.empty {
  color: var(--ink-soft);
  font-size: 0.92rem;
  padding: 40px 8px;
  text-align: center;
  border-top: 2px solid var(--line);
  border-bottom: 2px solid var(--line);
}

.site-footer {
  margin-top: 24px;
  padding: 28px 16px 36px;
  background: var(--invert-bg);
  color: var(--invert-ink);
  font-size: 0.78rem;
  text-align: center;
}
.site-footer p { max-width: 640px; margin: 0 auto; }
/* `p.` pour égaler la spécificité de ".site-footer p" ci-dessus (sinon
   celle-ci gagne malgré l'ordre d'écriture et écrase margin-top ici). */
p.site-footer-copyright {
  margin-top: 10px;
  color: color-mix(in srgb, var(--invert-ink) 65%, transparent);
  font-size: 0.7rem;
}

@media (min-width: 700px) {
  .page-title { font-size: 1.9rem; }
  .card { padding: 20px 6px; }
}

/* Vue grand écran -- voir base.html (`page_class` par template) : seules
   les pages de fil (accueil/circonscription/région) reçoivent
   `.page--feed`, pas /tendances ni l'admin, pour ne pas forcer une grille
   de cartes sur du contenu qui n'en est pas. En dessous de ce seuil,
   AUCUNE règle ici ne s'applique -- la vue mobile reste exactement celle
   d'avant ce changement (empilement vertical simple, voir `.feed` et
   `.card` plus haut). */
@media (min-width: 900px) {
  .page--feed { max-width: 1280px; }

  /* Le calendrier (/par-date) n'a pas de raison de s'étirer sur toute la
     largeur de .page--feed (1280px) -- une grille de 7 colonnes y devient
     énorme et illisible. Reste compact, largeur d'un vrai sélecteur de
     date, comme sur mobile. Demande explicite de l'utilisateur
     (2026-08-22). */
  .calendar { max-width: 380px; }

  /* En-tête aligné sur la même largeur que le contenu en dessous plutôt
     que de s'étirer bord à bord sur un écran large -- sinon le logo et
     la nav se retrouvent isolés chacun dans un coin, avec un grand vide
     entre les deux. */
  .site-header { max-width: 1280px; margin: 0 auto; }

  .feed {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    align-items: start;
    gap: 18px;
  }
  /* Bordure complète plutôt que le simple `border-top` empilé du mobile
     (voir .card plus haut) -- une grille a besoin d'un contour par carte
     pour rester lisible, un trait du haut seul ne délimiterait rien entre
     deux cartes côte à côte. */
  .card {
    border: 2px solid var(--ink);
    border-top: 2px solid var(--ink);
    padding: 18px;
    display: flex;
    flex-direction: column;
    height: 100%;
  }
  /* Résumé tronqué à 4 lignes -- longueur de titre/résumé très variable
     d'un article à l'autre, sans quoi les cartes d'une même rangée
     auraient des hauteurs très inégales (visuellement un des points
     faibles d'une grille par rapport à un simple empilement). */
  .card-resume {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* Pousse la source/les circonscriptions en bas de carte, pour que ce
     bloc s'aligne d'une carte à l'autre dans une même rangée même quand
     le titre/résumé au-dessus n'a pas la même hauteur. */
  .card-meta-bottom { margin-top: auto; padding-top: 10px; }

  .scroll-sentinel { grid-column: 1 / -1; }
}

/* Vue "lecteur" (voir reader.py/routes/admin.py::lire) -- typographie
   pensée pour la lecture longue (largeur contrainte, interligne généreux),
   volontairement différente du reste du site (UI dense, majuscules,
   bordures franches) : ici le contenu prime, pas le chrome. */
.reader-page { max-width: 680px; }
.reader-title {
  font-size: 1.7rem;
  font-weight: 800;
  line-height: 1.25;
  margin: 8px 0 24px;
  text-wrap: balance;
}
.reader-body {
  font-size: 1.08rem;
  line-height: 1.7;
  color: var(--ink);
}
.reader-body p { margin: 0 0 1.2em; }
.reader-body h2, .reader-body h3, .reader-body h4 {
  font-weight: 800;
  line-height: 1.3;
  margin: 1.6em 0 0.6em;
}
.reader-body h2 { font-size: 1.3rem; }
.reader-body h3 { font-size: 1.15rem; }
.reader-body img, .reader-body figure { max-width: 100%; height: auto; }
.reader-body figure { margin: 1.4em 0; }
.reader-body figcaption { font-size: 0.85rem; color: var(--ink-soft); margin-top: 6px; }
.reader-body a { color: var(--accent); text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.reader-body blockquote {
  margin: 1.4em 0;
  padding-left: 16px;
  border-left: 3px solid var(--accent);
  color: var(--ink-soft);
  font-style: italic;
}
.reader-body ul, .reader-body ol { padding-left: 1.4em; margin: 0 0 1.2em; }
.reader-body li { margin-bottom: 0.4em; }

/* -- à propos -- */
.about-body {
  max-width: 640px;
  font-size: 1.02rem;
  line-height: 1.65;
  color: var(--ink);
}
.about-body p { margin: 0 0 1.2em; }
.about-body a { color: var(--accent); text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.about-body h2.section-title { margin-top: 28px; }
.license-notice {
  font-size: 0.86rem;
  color: var(--ink-soft);
  padding: 12px 14px;
  border: 2px solid var(--line);
}
.about-version {
  margin: 32px 0 0;
  padding-top: 14px;
  border-top: 2px solid var(--line);
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

/* -- page /regions (index dédié, plus le sous-menu du burger) -- */
/* Pas de border-top ici (contrairement à avant 2026-08-16) -- avec le
   border-bottom déjà sur chaque <li> ci-dessous, un border-top sur la
   liste suivante juste après créait une double ligne collée au bord
   inférieur de la liste précédente. Le margin-bottom seul suffit à
   marquer la différence de groupe (ex. National / Abitibi-Témiscamingue),
   sans doubler le trait. */
.regions-list {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
}
.regions-list li { border-bottom: 2px solid var(--line); }
.regions-list a {
  display: block;
  padding: 14px 4px;
  text-decoration: none;
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
}
.regions-list a:hover { color: var(--accent); }

/* Deux colonnes sur grand écran -- en dessous de ce seuil, aucune règle
   ici ne s'applique, la vue mobile reste l'empilement simple d'avant. */
@media (min-width: 700px) {
  .regions-list {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    column-gap: 32px;
  }
  /* "Portée nationale" toujours en dernier, colonne de droite -- même
     avec un nombre pair de régions (qui remplirait sinon exactement 8
     rangées et placerait cet élément seul en bas à gauche de la 9e). */
  .regions-list-national { grid-column: 2; }
}

/* -- admin -- */

.admin-nav { display: flex; align-items: center; gap: 14px; }

.admin-analyse-apercu {
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.86rem;
  color: var(--ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.link-button {
  background: none;
  border: none;
  color: var(--invert-ink);
  font-size: 0.8rem;
  cursor: pointer;
  text-decoration: underline;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.admin-login-page {
  max-width: 340px;
  padding-top: 15vh;
}
.admin-login-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 16px;
}
.admin-login-form input {
  padding: 11px 12px;
  border: 2px solid var(--ink);
  background: var(--bg);
  color: var(--ink);
  font-size: 1rem;
  border-radius: 0;
}
.admin-login-form button, .admin-page button, .admin-page input[type="submit"] {
  padding: 11px 16px;
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--ink);
  color: var(--bg);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.86rem;
  cursor: pointer;
}
.admin-login-form button:hover, .admin-page button:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.admin-error { color: var(--accent); font-size: 0.88rem; font-weight: 700; }

.admin-page { max-width: 700px; width: 100%; }
/* Catalogue de sources (voir moteur/sync_sources_catalogue.py) : ~190
   lignes de référence en lecture seule, pas un flux de triage -- un
   tableau dense sert mieux la recherche visuelle qu'une pile de cartes
   mobile-first, d'où ce gabarit plus large réservé à cette page. */
.admin-page--wide { max-width: 1080px; }

.admin-filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.admin-filters select, .admin-filters input {
  padding: 11px 10px;
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--bg);
  color: var(--ink);
  font-size: 0.95rem;
}
.admin-filters input[type="search"] { flex: 1; min-width: 160px; }
.admin-filters button {
  padding: 11px 16px;
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--ink);
  color: var(--bg);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.admin-filters button:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
@media (max-width: 480px) {
  .admin-filters { flex-direction: column; }
  .admin-filters select, .admin-filters input, .admin-filters button { width: 100%; }
}

/* Poll htmx (voir admin_dashboard.html, #admin-article-list) : ce point
   confirme que la liste se rafraîchit toute seule, sans qu'il faille
   recharger la page pour voir arriver un nouvel article. Pulse pendant
   la requête (classe htmx-request, ajoutée/retirée automatiquement par
   htmx sur l'élément ciblé) -- désactivé si l'utilisateur préfère moins
   de mouvement. */
.admin-live-indicator {
  display: inline-flex;
  align-items: center;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  padding: 0 6px;
}
.admin-filters:has(+ #admin-article-list.htmx-request) .admin-live-indicator,
.admin-count:has(+ #admin-journal-list.htmx-request) .admin-live-indicator {
  color: var(--accent);
}
@media (prefers-reduced-motion: no-preference) {
  #admin-article-list.htmx-request, #admin-journal-list.htmx-request { animation: admin-live-pulse 0.6s ease-in-out; }
}
@keyframes admin-live-pulse {
  0% { opacity: 1; }
  50% { opacity: 0.6; }
  100% { opacity: 1; }
}

.admin-count {
  margin: 0 4px 6px;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
}

/* Liste de triage admin -- cartes plutôt qu'un tableau : sur mobile, un
   tableau force soit un défilement horizontal (titres coupés,
   inutilisable) soit un texte minuscule -- une carte empilée garde le
   titre entier lisible et les boutons d'action assez grands pour le
   pouce. */
.admin-review-list { display: flex; flex-direction: column; }
.admin-review-card {
  border-top: 2px solid var(--ink);
  padding: 14px 4px 16px;
}
.admin-review-list .admin-review-card:last-child { border-bottom: 2px solid var(--ink); }

/* Journal du moteur (/admin/journal) -- une ligne (ou quelques-unes, voir
   .journal-resume) par passage, dans l'esprit d'un log Unix (police à
   chasse fixe, horodatage absolu aligné en colonne) plutôt qu'une phrase
   en français -- demande explicite de l'utilisateur (2026-08-22). Pas des
   cartes : chaque entrée est un texte court (voir moteur/crawl.py), une
   liste compacte reste lisible même sur mobile ici, contrairement aux
   articles. */
.journal-list { display: flex; flex-direction: column; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
/* Horodatage empilé au-dessus du résumé plutôt que côte à côte -- une
   colonne de largeur fixe (19ch, "AAAA-MM-JJ HH:MM:SS") à côté d'un
   résumé DÉJÀ multi-ligne (voir .journal-resume) écrasait le texte sur
   mobile (constaté par l'utilisateur, capture d'écran, 2026-08-22). Un
   seul agencement pour tous les écrans plutôt qu'une variante par
   largeur -- l'entrée est de toute façon déjà multi-ligne. */
.journal-entry {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 4px;
  border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  font-size: 0.82rem;
}
.journal-list .journal-entry:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 14%, transparent); }
.journal-heure {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.journal-resume { color: var(--ink-soft); white-space: pre-line; }
.journal-entry--vide .journal-resume { font-style: italic; }

.admin-review-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}

.admin-review-title {
  display: block;
  font-size: 1.02rem;
  font-weight: 700;
  text-decoration: none;
  color: var(--ink);
  text-wrap: balance;
}
.admin-review-title:hover { color: var(--accent); }

.admin-review-resume {
  margin: 6px 0 0;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.admin-review-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 12px;
  font-size: 0.78rem;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.admin-review-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
/* form comme item flex direct (flex:1, base 140px) -- le bouton dedans
   n'a besoin que de remplir 100% de cette largeur, pas d'être lui-même
   un item flex d'un flex imbriqué : évite un double niveau de flex
   fragile pour un résultat identique. */
.admin-review-actions form { margin: 0; flex: 1 1 140px; }

/* `button.` (pas juste `.admin-btn-valider`) pour égaler la spécificité
   de la règle générique ".admin-page button" plus haut dans ce fichier
   (0,1,1) -- sans ça, cette dernière gagne malgré l'ordre d'écriture et
   écrase silencieusement fond/couleur/padding ici (bug constaté : les
   deux boutons rendaient identiques, plus larges que prévu). */
button.admin-btn-valider, button.admin-btn-masquer {
  display: block;
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  border: 2px solid var(--ink);
  border-radius: 0;
  font-weight: 800;
  font-size: 0.88rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  cursor: pointer;
  white-space: nowrap;
}
button.admin-btn-valider { background: var(--ink); color: var(--bg); }
button.admin-btn-valider:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
button.admin-btn-masquer { background: var(--bg); color: var(--ink); }
button.admin-btn-masquer:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

.admin-review-actions a.admin-btn-editer {
  flex: 1 1 100%;
  order: 3;
  margin-top: 2px;
  text-align: center;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  text-decoration: none;
}
.admin-review-actions a.admin-btn-editer:hover { color: var(--accent); }

@media (min-width: 480px) {
  .admin-review-actions a.admin-btn-editer { flex: 0 0 auto; order: 0; margin-top: 0; margin-left: auto; }
}

.statut-badge {
  display: inline-block;
  padding: 2px 9px;
  border: 2px solid var(--ink);
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
}
.statut-valide { background: var(--ink); color: var(--bg); }
.statut-masque { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.statut-en_attente { background: var(--bg); color: var(--ink); }

.admin-duplicate-warning {
  margin: 8px 0;
  padding: 8px 12px;
  border: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--ink);
  font-size: 0.82rem;
  line-height: 1.5;
}
.admin-duplicate-warning a { color: var(--accent); font-weight: 700; }

/* Tableau du catalogue de sources -- voir le commentaire sur
   .admin-page--wide : contenu de référence, pas d'actions, la densité
   d'information prime sur la cible tactile. */
.admin-table-wrap { overflow-x: auto; border: 2px solid var(--line); }
.admin-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; white-space: nowrap; }
.admin-table th, .admin-table td { padding: 8px 12px; text-align: left; border-bottom: 1px solid color-mix(in srgb, var(--line) 25%, transparent); }
.admin-table th {
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  background: color-mix(in srgb, var(--ink) 5%, var(--bg));
  position: sticky;
  top: 0;
}
.admin-table tbody tr:last-child td { border-bottom: none; }
.admin-table tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.admin-table td { color: var(--ink); }
.admin-table a { color: var(--ink); }
.admin-table a:hover { color: var(--accent); }
.admin-source-note { white-space: normal; min-width: 220px; color: var(--ink-soft); }

.statut-source-integre, .statut-source-scrape_html { background: var(--ink); color: var(--bg); }
.statut-source-a_valider { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.statut-source-pas_de_flux, .statut-source-pas_de_flux_utile { background: var(--bg); color: var(--ink-soft); border-color: var(--ink-soft); }
.statut-source-ferme { background: var(--bg); color: var(--ink-soft); border-color: var(--ink-soft); text-decoration: line-through; }

/* Tableau de bord des sources, en cartes empilées (voir le commentaire
   sur .admin-review-list plus haut -- même raisonnement mobile-first,
   demande explicite de l'utilisateur 2026-08-18 de pouvoir consulter ça
   facilement sur téléphone). Remplace l'ancien gabarit en tableau large. */
.source-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 18px;
}
.source-stat-chip {
  display: flex;
  align-items: baseline;
  gap: 5px;
  padding: 5px 10px;
  border: 2px solid var(--line);
  font-size: 0.76rem;
  color: var(--ink-soft);
}
.source-stat-chip b { color: var(--ink); font-size: 0.9rem; }

.source-section { margin-top: 26px; }
.source-section-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 0.95rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 0 4px 4px;
  padding-top: 12px;
  border-top: 2px solid var(--ink);
}
.source-section-title .count { color: var(--ink-soft); font-weight: 500; text-transform: none; letter-spacing: normal; font-size: 0.82rem; }

.source-card {
  border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  padding: 11px 4px;
}
.source-card:first-child { border-top: none; }

.source-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.source-name { font-weight: 700; font-size: 0.92rem; color: var(--ink); text-decoration: none; }
.source-name:hover { color: var(--accent); }
.source-fiab { flex-shrink: 0; font-size: 0.78rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.source-type { display: block; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-soft); margin-top: 2px; }

/* Média multi-canaux (ex. Radio-Canada) : <details> natif, pas de JS. */
details.source-media summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
details.source-media summary::-webkit-details-marker { display: none; }
.source-media-chip {
  font-size: 0.68rem;
  font-weight: 800;
  color: var(--accent-ink);
  background: var(--accent);
  padding: 2px 8px;
  margin-left: 8px;
}
.source-canaux { padding: 8px 0 2px 14px; border-left: 2px solid color-mix(in srgb, var(--line) 60%, transparent); margin: 8px 0 0 2px; }
.source-canal-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 5px 0 5px 8px; }
.source-canal-row a { font-size: 0.84rem; color: var(--ink-soft); text-decoration: none; }
.source-canal-row a:hover { color: var(--accent); }

.source-note { font-size: 0.82rem; color: var(--ink-soft); margin: 5px 4px 0 0; line-height: 1.4; }
.source-region { font-size: 0.76rem; color: var(--ink-faint, var(--ink-soft)); margin-top: 2px; }

.source-contact {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.source-contact a, .source-contact span {
  font-size: 0.74rem;
  text-decoration: none;
  padding: 3px 9px;
  border: 1.5px solid var(--line);
  color: var(--ink-soft);
}
.source-contact a { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.source-contact a:hover { background: var(--accent); color: var(--accent-ink); }

.admin-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 5px 14px;
  font-size: 0.86rem;
  margin: 16px 0 22px;
  padding: 14px 16px;
  border: 2px solid var(--line);
}
.admin-meta dt { color: var(--ink-soft); font-weight: 700; text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.03em; }
.admin-meta dd { margin: 0 0 4px; }

.admin-edit-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 560px;
}
.admin-edit-form label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-soft); margin-top: 10px; }
.admin-edit-form input, .admin-edit-form textarea, .admin-edit-form select {
  padding: 10px 11px;
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--bg);
  color: var(--ink);
  font-size: 0.92rem;
  font-family: inherit;
}
.admin-edit-form button { margin-top: 16px; align-self: flex-start; }

.admin-field-hint {
  margin: 2px 0 0;
  font-size: 0.74rem;
  color: var(--ink-soft);
  text-transform: none;
}

.admin-multiselect {
  min-height: 140px;
}

/* Spécificité au-dessus de ".admin-edit-form label" (majuscules/petit) --
   les libellés de case à cocher doivent rester lisibles en casse normale,
   alignés avec leur case plutôt qu'empilés en étiquette de champ. */
.admin-edit-form label.admin-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 0;
  padding: 4px 2px;
  font-size: 0.88rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink);
  cursor: pointer;
}
.admin-edit-form label.admin-checkbox input[type="checkbox"] {
  width: auto;
  flex-shrink: 0;
  accent-color: var(--accent);
}
.admin-edit-form label.admin-checkbox-standalone {
  margin-top: 10px;
  border: 2px solid var(--ink);
  padding: 10px 12px;
}

/* -- nav / tendances -- */

.main-nav { display: flex; align-items: center; gap: 10px; }
.nav-link {
  padding: 7px 14px;
  border: 2px solid var(--invert-ink);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--invert-ink);
  text-decoration: none;
}
.nav-link:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* flex-wrap plutôt qu'une rangée figée -- avant ce correctif, 4 boutons
   dans une seule rangée sans wrap forçaient soit le TEXTE à casser sur
   deux lignes ("7 JOURS" -> "7"/"JOURS", hauteurs inégales entre boutons),
   soit carrément à déborder hors de l'écran sur mobile (le dernier bouton
   "TOUT" coupé à droite, repéré par l'utilisateur par capture d'écran,
   2026-08-14). `flex: 1 1 auto` + `min-width` : 2 boutons par rangée sur
   mobile étroit, les 4 sur une seule rangée dès que l'espace le permet --
   toujours des rectangles pleine hauteur, jamais de texte qui déborde de
   sa boîte. */
.periode-nav { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 4px 20px; }
.periode-lien {
  flex: 1 1 auto;
  min-width: 92px;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 7px 14px;
  border: 2px solid var(--ink);
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  text-decoration: none;
  text-align: center;
  white-space: nowrap;
  color: var(--ink);
}
.periode-lien:hover { border-color: var(--accent); color: var(--accent); }
.periode-lien.actif { background: var(--ink); color: var(--bg); }

.section-title {
  font-size: 0.9rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin: 0 4px 12px;
}

.chart-section { margin-bottom: 32px; }

.bar-chart { display: flex; flex-direction: column; gap: 10px; }
.bar-row { display: grid; grid-template-columns: 64px 1fr 36px; align-items: center; gap: 10px; }
.bar-label { font-size: 0.82rem; font-weight: 800; text-align: right; }
.bar-track { background: transparent; border: 2px solid var(--line); height: 20px; }
.bar-fill { height: 100%; min-width: 3px; }
.bar-count { font-size: 0.82rem; font-variant-numeric: tabular-nums; font-weight: 700; }

/* Tableaux croisés (/admin/statistiques) -- overflow-x sur le
   conteneur, jamais sur la page : un tableau média×parti ou jour×sujet
   dépasse facilement la largeur d'un écran de téléphone. */
.stats-table-wrap { overflow-x: auto; margin: 0 0 32px; border: 2px solid var(--line); }
.stats-table { border-collapse: collapse; width: 100%; font-size: 0.8rem; white-space: nowrap; }
.stats-table th, .stats-table td { padding: 8px 10px; text-align: right; }
.stats-table th:first-child, .stats-table td:first-child { text-align: left; }
.stats-table thead th {
  border-bottom: 2px solid var(--ink);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: 0.7rem;
}
.stats-table tbody tr + tr { border-top: 1px solid var(--line); }
.stats-table td { font-variant-numeric: tabular-nums; }
.stats-table td.stats-total { font-weight: 800; }
.stats-table-empty-cell { color: var(--ink-soft); }

.analyses-section { margin-bottom: 24px; }
.analyse-card {
  border-top: 2px solid var(--ink);
  padding: 14px 4px;
}
.feed .analyse-card:last-child, .analyses-section .analyse-card:last-child { border-bottom: 2px solid var(--ink); }
.analyse-date { font-size: 0.78rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 6px; }
.analyse-nb { font-weight: 400; text-transform: none; color: var(--ink-soft); letter-spacing: normal; }
.analyse-texte { font-size: 0.92rem; margin: 0; color: var(--ink); }

/* /en-direct -- timeline d'entrées façon "quelqu'un nous parle", sans
   prétendre à un avatar humain : le pixel-mark déjà utilisé comme
   signature du site ailleurs (en-têtes) sert de repère visuel constant.
   Mise en page avatar+message (comme une conversation), mais sans coins
   arrondis ni bulle à queue -- reste dans le langage brutaliste du reste
   du site plutôt que d'emprunter l'esthétique messagerie grand public. */
.pulse-intro { color: var(--ink-soft); font-size: 0.92rem; margin: 0 4px 24px; }

.pulse-timeline { display: flex; flex-direction: column; }
.pulse-entry { display: grid; grid-template-columns: 32px 1fr; gap: 14px; }
.pulse-entry + .pulse-entry { margin-top: 4px; }

.pulse-mark-col { display: flex; flex-direction: column; align-items: center; }
.pulse-mark {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  color: var(--bg);
}
.pulse-mark .pixel-mark { width: 14px; height: 14px; }
.pulse-thread { flex: 1; width: 2px; background: var(--line); margin: 4px 0; min-height: 24px; }

.pulse-content { padding-bottom: 28px; }
.pulse-bubble {
  border: 2px solid var(--ink);
  padding: 14px 16px;
}
.pulse-type-tag {
  display: inline-block;
  margin-bottom: 8px;
  padding: 2px 8px;
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--ink);
  color: var(--bg);
}
.pulse-texte { margin: 0; font-size: 1rem; line-height: 1.5; color: var(--ink); }
.pulse-meta {
  margin: 8px 2px 0;
  font-size: 0.74rem;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.pulse-meta time { font-weight: 800; color: var(--ink); text-transform: none; letter-spacing: normal; }
