/* style-flash.css
   Styles dédiés à la page /en-direct/ (timeline d'actualité crypto en temps
   réel). Loadé async via base.njk uniquement quand page.url == "/en-direct/".

   Le feed est rendu en JS depuis /api/flash-news.json. Le CSS gère le layout
   timeline + la pulse animation "EN DIRECT" + le visuel des entries.
*/

/* ============================================
   HERO — titre + pulse + status
   ============================================ */
.flash-hero {
  padding-bottom: 20px;
}

.flash-title {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* Pulse rouge à gauche du titre — visuel "live" (urgence/breaking news) */
.flash-pulse {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #ef4444;
  box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
  animation: flash-pulse-ring-red 2s ease-out infinite;
  flex-shrink: 0;
}

@keyframes flash-pulse-ring-red {
  0%   { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); }
  70%  { box-shadow: 0 0 0 12px rgba(239, 68, 68, 0); }
  100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
}

/* Pulse gold pour les dots des entries (1er + nouvelles) — cohérent avec
   la couleur du dot lui-même (var(--accent) = #FFCB73 en dark theme). */
@keyframes flash-pulse-ring-gold {
  0%   { box-shadow: 0 0 0 0 rgba(255, 203, 115, 0.7); }
  70%  { box-shadow: 0 0 0 12px rgba(255, 203, 115, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 203, 115, 0); }
}

/* Status bar sous le titre : "EN DIRECT · N brèves · mis à jour il y a Xs" */
.flash-status {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  padding: 6px 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.flash-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #ef4444;
  animation: flash-pulse-ring-red 2s ease-out infinite;
  flex-shrink: 0;
}
.flash-status-label {
  color: var(--accent);
  font-weight: 700;
}
.flash-status-meta {
  color: var(--text-muted);
  letter-spacing: 0;
  text-transform: none;
  font-family: var(--font-sans);
  font-size: 12px;
}

/* ============================================
   FEED — timeline d'entries
   ============================================ */
.flash-section {
  max-width: 780px;
  margin: 0 auto;
  padding: 32px 0 64px;
}

/* CTA Telegram (v79d → v79g) : pill compacte centrée, fond transparent
   au repos. Plus moderne / discret que l'ancien encart pleine-largeur.
   Icône Telegram bleu brand (#229ED9), texte gris muted.
   v79g : sticky en haut sous le header au scroll + état .is-stuck
   piloté par JS (cf. IntersectionObserver sur sentinel dans en-direct.njk)
   qui repeind tout en bleu Telegram pour faire ressortir la pill quand
   le contenu défile derrière. Hover : même bleu Telegram. */
/* v150 Lot D — Toolbar Variant C "segmented all-in-one"
   Container principal qui regroupe : segmented impact + search + icônes
   Telegram/Archives en 1 seule ligne (vs avant : Telegram pill sticky + PWA +
   filtres sur 2 niveaux). Réduit le bruit visuel x4.
   Le container a son bg-secondary + border, les enfants (filter-bar +
   search-wrap) gardent leur bg-card → effet "segmented" naturel. */
.flash-toolbar-c {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 auto 12px;
  max-width: 980px;
  padding: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 999px;
  flex-wrap: wrap;
}
.flash-toolbar-c > * { margin: 0; }
.flash-toolbar-c-actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.flash-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
.flash-icon-btn:hover,
.flash-icon-btn:focus-visible {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  outline: none;
}
.flash-icon-btn--tg:hover,
.flash-icon-btn--tg:focus-visible {
  color: #229ED9;
  border-color: #229ED9;
  background: color-mix(in srgb, #229ED9 10%, transparent);
}
.flash-icon-btn svg { display: block; }

/* Secondary row : filtres avancés (Catégorie + Sentiment) + PWA install
   (révélé conditionnellement par JS). Ligne discrète sous la toolbar
   principale, alignée à gauche pour ne pas attirer l'attention. */
.flash-toolbar-c-secondary {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 auto 24px;
  max-width: 980px;
  padding: 0 8px;
  flex-wrap: wrap;
}
.flash-toolbar-c-secondary-label {
  font-size: 11px;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
}

/* Mobile (mai 2026, user feedback "alléger sur smartphone") :
   - Recherche full-text + filtres avancés (Catégorie/Sentiment) MASQUÉS
     (usages desktop power-user, peu utilisés en mobile)
   - Filtre d'importance CONSERVÉ (essentiel)
   - Telegram + Archives CENTRÉS + COLORISÉS (Telegram bleu officiel #229ED9,
     Archives gold accent #FFCB73) pour CTA visible
   - Boutons aggrandis 32→40px pour tap targets confortables. */
@media (max-width: 720px) {
  .flash-toolbar-c {
    border-radius: 14px;
    padding: 8px;
    justify-content: center;
    gap: 12px;
  }
  /* Masque la recherche full-text sur mobile */
  .flash-toolbar-c .flash-search-wrap {
    display: none;
  }
  /* Masque la rangée secondaire (Catégorie + Sentiment + PWA install).
     PWA install conditionnel reste accessible via le menu share natif iOS/Android. */
  .flash-toolbar-c-secondary {
    display: none;
  }
  /* Actions Telegram + Archives : centrées, plus grandes, coloriées */
  .flash-toolbar-c-actions {
    margin-left: 0;
    gap: 10px;
  }
  .flash-icon-btn {
    width: 40px;
    height: 40px;
  }
  /* Telegram = bleu officiel (#229ED9) en permanence sur mobile.
     Contraste fond bleu → texte blanc. */
  .flash-icon-btn--tg {
    color: #ffffff;
    background: #229ED9;
    border-color: #229ED9;
  }
  .flash-icon-btn--tg:hover,
  .flash-icon-btn--tg:focus-visible {
    color: #ffffff;
    background: #1c8bc0;
    border-color: #1c8bc0;
  }
  /* Archives = accent gold (cohérent avec le branding cryptoactu) */
  .flash-icon-btn:not(.flash-icon-btn--tg) {
    color: #0a0a0f;
    background: var(--accent, #FFCB73);
    border-color: var(--accent, #FFCB73);
  }
  .flash-icon-btn:not(.flash-icon-btn--tg):hover,
  .flash-icon-btn:not(.flash-icon-btn--tg):focus-visible {
    color: #0a0a0f;
    background: #ffd98a;
    border-color: #ffd98a;
  }
}

/* Petits écrans (≤480px iPhone SE → 13/14/15 plain) : forcer le wrap
   filter-bar ligne 1 (centrée) + actions Telegram/Archives ligne 2
   (centrées). Évite le clipping quand la filter-bar + 2 boutons ne tiennent
   pas sur une seule ligne dans la pill toolbar.
   Sélecteur compound `.flash-toolbar-c .flash-feed-filter-bar` requis pour
   battre la spécificité de .flash-feed-filter-bar { width: fit-content }
   définie plus bas dans le fichier (source order conflict). */
@media (max-width: 480px) {
  .flash-toolbar-c .flash-feed-filter-bar {
    flex: 1 1 100%;
    width: 100%;
    justify-content: center;
  }
  .flash-toolbar-c .flash-toolbar-c-actions {
    flex: 1 1 100%;
    justify-content: center;
  }
}

/* =================================================================
   Filtre Impact (mai 2026) — chips Tous / Majeur (N)
   ================================================================= */

/* Barre de chips au-dessus de la timeline (sous la pill Telegram + PWA).
   2 boutons toggle : Tous (défaut) / Impact majeur. Filtrage purement CSS
   via body[data-flash-filter="majeur"] — préserve le DOM intact pour le
   lazy reveal sentinel et la persistence sessionStorage. */
/* v150 Lot D — .flash-feed-toolbar legacy supprimé.
   AVANT (v134) : 1 toolbar centré qui contenait PWA + Search + Impact +
   Cat + Sent. APRÈS (v150 Variant C) : 2 toolbars distinctes,
   .flash-toolbar-c (main) + .flash-toolbar-c-secondary (filtres avancés).
   Markup mis à jour dans src/en-direct.njk. */

.flash-feed-filter-bar {
  display: inline-flex;
  gap: 6px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px;
  width: fit-content;
  max-width: 100%;
}
.flash-feed-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  background: transparent;
  border: none;
  border-radius: 999px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.4;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
  white-space: nowrap;
}
.flash-feed-filter-chip:hover {
  color: var(--text-primary);
}
.flash-feed-filter-chip.is-active {
  background: var(--bg-primary);
  color: var(--text-primary);
}
/* Variant Majeur active : accent orange cohérent avec
   .flash-entry-impact--majeur (#fb923c). */
.flash-feed-filter-chip[data-filter-value="majeur"].is-active {
  color: #fb923c;
}
.flash-feed-filter-count {
  display: inline-block;
  min-width: 18px;
  padding: 0 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  font-weight: 700;
  text-align: center;
  line-height: 18px;
}
.flash-feed-filter-chip.is-active .flash-feed-filter-count {
  background: rgba(251, 146, 60, 0.14);
  color: #fb923c;
}
.flash-feed-filter-chip[data-filter-value="all"].is-active .flash-feed-filter-count {
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-primary);
}
.flash-feed-filter-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fb923c;
  flex-shrink: 0;
}

/* Note : le centrage du filter-bar est désormais géré par .flash-feed-toolbar
   (parent flex justify-content: center). L'ancienne rule .flash-section >
   .flash-feed-filter-bar a été retirée v97 (toolbar wrapper introduit). */

/* Filtrage : quand body[data-flash-filter="majeur"], masque toutes les
   .flash-entry qui n'ont PAS data-impact="Majeur". CSS-only = zéro coût JS
   à chaque scroll + 0 re-flow visible. */
body[data-flash-filter="majeur"] .flash-entry:not([data-impact="Majeur"]) {
  display: none;
}

/* =================================================================
   Lot A (v134) — Toolbar unifiée : Search + Impact + Cat + Sent (1 ligne)
   v134 refonte : tout regroupé dans .flash-feed-toolbar (pas de toolbar
   secondaire). 4 contrôles compacts wrapent naturellement en mobile.
   ================================================================= */

/* Search input (flex: 1 pour grow dans la toolbar) */
.flash-search-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 12px;
  flex: 1 1 240px;
  min-width: 200px;
  max-width: 340px;
  transition: border-color 0.16s ease;
}
.flash-search-wrap:focus-within { border-color: var(--accent, #FFCB73); }
.flash-search-icon {
  flex-shrink: 0;
  color: var(--text-muted);
  margin-right: 8px;
}
.flash-search-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 13px;
  padding: 6px 0;
}
.flash-search-input::placeholder {
  color: var(--text-muted);
  opacity: 0.85;
}
/* v134 : masque la croix native webkit/blink (Safari, Chrome) pour
   éviter le double-clear avec notre bouton custom. Le bouton custom
   apparaît seulement quand input non-vide (JS toggle hidden). */
.flash-search-input::-webkit-search-cancel-button,
.flash-search-input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}
.flash-search-clear {
  flex-shrink: 0;
  margin-left: 6px;
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.flash-search-clear:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-primary);
}
[data-theme="light"] .flash-search-clear:hover {
  background: rgba(0, 0, 0, 0.06);
}

/* Select wrap (mutualisé Catégorie + Sentiment) — style pill compact
   cohérent avec les chips Impact (même hauteur, même border-radius). */
.flash-select-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 10px 4px 12px;
  font-size: 12.5px;
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color 0.16s ease;
  white-space: nowrap;
}
.flash-select-wrap:hover { border-color: var(--border-strong); }
.flash-select-wrap:focus-within { border-color: var(--accent, #FFCB73); }
.flash-select-label {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}
.flash-select {
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  padding: 5px 18px 5px 2px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  /* Custom chevron en pure CSS (2 linear-gradient forment un triangle bas) */
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 8px) 50%, calc(100% - 4px) 50%;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}

/* Filtre additionnel (search + cat + sent) : JS toggle .is-filtered-out
   sur les .flash-entry. Combiné avec le CSS-only impact filter ci-dessus
   via union des deux sélecteurs. */
.flash-entry.is-filtered-out { display: none; }

/* =================================================================
   Lot C (v138) — Auto-link discret dans titre + résumé
   ================================================================= */
/* Liens internes auto-générés (Bitcoin → /coin/bitcoin/, MiCA → /glossaire/mica/,
   Binance → /avis/binance/, etc.). Style discret pour ne pas saturer
   visuellement le feed : sous-ligné fin couleur héritée, hover en accent.
   Bénéfice UX : indique au lecteur qu'un mot pointe vers une page approfondie
   sans casser le flux de lecture. */
.flash-entry-autolink {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--text-muted);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  transition: color 0.15s ease, text-decoration-color 0.15s ease;
}
.flash-entry-autolink:hover {
  color: var(--accent, #FFCB73);
  text-decoration-color: var(--accent, #FFCB73);
  text-decoration-thickness: 2px;
}
/* v164 (user request) — Titres H2 : liens en couleur accent gold, sans
   soulignement par défaut (le titre est déjà gras + grand, le soulignement
   permanent saturait visuellement). Soulignement uniquement au hover pour
   signaler l'interactivité.
   Override `color: inherit` du parent .flash-entry-autolink → accent gold
   visible direct, et override `text-decoration: underline` → none. */
.flash-entry-title .flash-entry-autolink {
  color: var(--accent, #FFCB73);
  text-decoration: none;
}
.flash-entry-title .flash-entry-autolink:hover,
.flash-entry-title .flash-entry-autolink:focus-visible {
  color: var(--accent, #FFCB73);
  text-decoration: underline;
  text-decoration-color: var(--accent, #FFCB73);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

/* Empty state quand TOUS les filtres combinés masquent toutes les entries */
.flash-no-results {
  padding: 32px 16px;
  text-align: center;
  color: var(--text-muted);
  font-size: 14px;
  background: var(--bg-card);
  border: 1px dashed var(--border);
  border-radius: 12px;
  margin: 16px auto;
  max-width: 480px;
}
.flash-no-results p { margin: 0 0 12px; }
.flash-reset-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: 999px;
  padding: 6px 16px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.flash-reset-btn:hover {
  border-color: var(--accent, #FFCB73);
  color: var(--accent, #FFCB73);
}

/* Mobile <=480px : règles legacy retirées (mai 2026, user feedback
   "alléger sur smartphone").
   - .flash-search-wrap est maintenant hidden via @media 720px (cf. plus haut)
   - .flash-feed-filter-bar { flex: 1 1 auto } entrait en conflit avec le
     nouveau `flex: 1 1 100%` qui force le wrap centré des actions
     Telegram+Archives sous la filter-bar à 480px. */

/* v164 — .flash-faq-* CSS retire. La FAQ /en-direct/ utilise desormais
   le pattern partage .conv-faq-* (cf. style.css ~ligne 8961), comme
   toutes les autres pages outils (convertisseur, halving, ETF, F&G, etc.).
   Source unique de verite = pas de double maintenance ni de drift visuel. */

/* Empty state filtre : si toutes les entries sont cachées par le filtre,
   afficher un message via pseudo-élément sur le <ol>. Détecté via une rule
   CSS-only :has() (Chrome/Edge/Safari ≥ 105, Firefox 121+ → 95 %+ support
   en 2026). Fallback gracieux : zone vide silencieuse sur vieux Firefox. */
@supports selector(:has(.flash-entry)) {
  body[data-flash-filter="majeur"] .flash-feed:not(:has(.flash-entry[data-impact="Majeur"])) {
    position: relative;
    min-height: 120px;
  }
  body[data-flash-filter="majeur"] .flash-feed:not(:has(.flash-entry[data-impact="Majeur"]))::after {
    content: "Aucune news Majeure dans le buffer. Désactive le filtre ou attends qu'une news à fort impact remonte.";
    display: block;
    text-align: center;
    padding: 40px 20px;
    color: var(--text-muted);
    font-size: 13.5px;
    line-height: 1.55;
    max-width: 520px;
    margin: 0 auto;
  }
}

/* =================================================================
   PWA "Add to home screen" — bouton install + iOS modal
   ================================================================= */

/* Pill cohérente avec .flash-telegram-cta (même format, accent gold).
   Désormais flex-item d'un .flash-feed-toolbar parent → pas de margin
   auto, le parent gère le spacing via gap + justify-content: center.
   Hidden par défaut côté HTML, révélée par JS quand beforeinstallprompt
   fire OU sur iOS Safari OU sur desktop installable. */
.pwa-install-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  margin: 0;
  padding: 7px 16px 7px 14px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
/* Show only when JS révèle (suppr du `hidden` attr) */
.pwa-install-cta[hidden] { display: none !important; }
.pwa-install-cta:hover {
  color: var(--accent);
  border-color: var(--accent-border);
  background: var(--accent-soft);
}
.pwa-install-cta:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.pwa-install-cta:disabled {
  opacity: 0.5;
  cursor: progress;
}
.pwa-install-icon {
  color: var(--accent);
  flex-shrink: 0;
}

/* iOS modal : Apple ne fournit pas d'API d'install programmatique
   donc on affiche des instructions visuelles "Partager → Sur l'écran
   d'accueil". Modal centré viewport, backdrop semi-opaque + blur.
   Dismiss : click backdrop, click ×, click "Compris", touche Escape. */
.pwa-ios-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  animation: pwa-modal-fade-in 0.2s ease-out;
}
.pwa-ios-modal[hidden] { display: none !important; }
@keyframes pwa-modal-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
.pwa-ios-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(8, 8, 12, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  cursor: pointer;
}
.pwa-ios-modal-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 380px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 24px 22px 22px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
  animation: pwa-card-pop 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.1);
}
@keyframes pwa-card-pop {
  from { transform: translateY(8px) scale(0.97); opacity: 0; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}
.pwa-ios-modal-close {
  position: absolute;
  top: 10px;
  right: 12px;
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  border-radius: 6px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s, background 0.15s;
}
.pwa-ios-modal-close:hover {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.04);
}
.pwa-ios-modal-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.pwa-ios-modal-title {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 28px 6px 0;
  letter-spacing: -0.005em;
  line-height: 1.25;
}
.pwa-ios-modal-pitch {
  font-size: 13.5px;
  color: var(--text-muted);
  margin: 0 0 18px;
  line-height: 1.5;
}
.pwa-ios-modal-steps {
  list-style: none;
  padding: 0;
  margin: 0 0 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.pwa-ios-modal-steps li {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13.5px;
  color: var(--text-body);
  line-height: 1.45;
  padding: 12px 14px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.pwa-ios-modal-steps strong {
  color: var(--text-primary);
  font-weight: 600;
}
.pwa-ios-modal-step-num {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast, #0a0a0f);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  display: grid;
  place-items: center;
}
.pwa-ios-modal-share {
  display: inline-block;
  vertical-align: middle;
  color: var(--accent);
  margin: 0 2px;
}
.pwa-ios-modal-ok {
  width: 100%;
  padding: 11px 16px;
  background: var(--accent);
  color: var(--accent-contrast, #0a0a0f);
  border: none;
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  letter-spacing: 0.01em;
  transition: filter 0.15s;
}
.pwa-ios-modal-ok:hover {
  filter: brightness(1.06);
}
.pwa-ios-modal-ok:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Light theme overrides : backdrop plus clair, steps plus contrastés */
[data-theme="light"] .pwa-ios-modal-backdrop {
  background: rgba(26, 26, 23, 0.55);
}
[data-theme="light"] .pwa-ios-modal-card {
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.18);
}
[data-theme="light"] .pwa-ios-modal-close:hover {
  background: rgba(0, 0, 0, 0.04);
}

.flash-feed {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.flash-feed-loading,
.flash-feed-empty {
  padding: 40px 24px;
  text-align: center;
  color: var(--text-muted);
  font-size: 14.5px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 8px;
}

.flash-feed-footer {
  margin-top: 28px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--text-faint);
  text-align: center;
  font-family: var(--font-sans);
}

/* Lazy loading sentinel (mai 2026 v4) : div vide observé par IntersectionObserver.
   Dès qu'il entre dans le viewport (avec une marge de précharge de 600px), le
   JS révèle les 20 entries suivantes du buffer. Remplace l'ancien bouton
   "Voir plus" + hint texte — UX infinite scroll automatique. Taille 1px pour
   l'observer (non-zero), invisible visuellement. */
.flash-feed-sentinel {
  height: 1px;
  width: 100%;
  pointer-events: none;
}

/* ============================================
   ENTRY — chaque brève dans la timeline
   ============================================ */
.flash-entry {
  position: relative;
  padding: 22px 0 22px 20px;
  border-left: 2px solid var(--border);
  margin-left: 12px;
  transition: border-left-color 0.18s ease;
  /* v150 Lot D Phase 7 — scroll-margin-top offset le sticky header (~80px)
     + ticker (~32px) + marge confort quand l'user arrive via permalink
     #flash-<id> (copy button). Sans ça, l'entry ciblée se retrouve
     partiellement masquée derrière le header. */
  scroll-margin-top: 120px;
}

/* Animation "new entry" — déclenchée par .flash-entry-new ajoutée en JS
   quand un ID jamais vu apparaît dans le poll. Combine 3 effets :
   1. Slide depuis le haut (translateY -16px → 0)
   2. Fade-in (opacity 0 → 1)
   3. Highlight gold qui s'efface en 2.5s (box-shadow + bg subtil)

   Les animations ne se répètent pas (1 seul cycle), donc même si le
   render re-applique la classe, c'est invisible côté UX. Pour les
   anciennes entries (déjà vues), pas de classe = aucune animation. */
@keyframes flash-entry-slide-in {
  0%   { opacity: 0; transform: translateY(-16px); }
  100% { opacity: 1; transform: translateY(0); }
}

.flash-entry-new {
  /* Animation slide-in + fade-in uniquement. Le trait vertical et le bg
     restent neutres. Seul le dot ::before passe en gold + pulse (cf.
     overrides ci-dessous) pour signaler "nouvelle entry". */
  animation: flash-entry-slide-in 600ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
/* Petit indicateur visuel temporaire pour les entries nouvelles : un dot
   doré qui pulse pendant l'animation. Override le dot neutre par défaut
   des entries autres que :first-child. */
.flash-entry-new:not(:first-child)::before,
.flash-entry-new:first-child::before {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--bg-primary), 0 0 0 5px var(--accent-border);
  animation: flash-pulse-ring-gold 2s ease-out 3;
}

/* Le 1er entry (le plus récent) : juste un dot accent gold + pulse,
   le trait vertical reste neutre (cohérence avec les autres entries). */
.flash-entry:first-child::before {
  content: "";
  position: absolute;
  left: -7px;
  top: 26px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--bg-primary), 0 0 0 5px var(--accent-border);
  animation: flash-pulse-ring-gold 2s ease-out infinite;
}

/* Entries suivantes : petit dot neutre */
.flash-entry:not(:first-child)::before {
  content: "";
  position: absolute;
  left: -5px;
  top: 30px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--bg-card);
  border: 1.5px solid var(--border-strong);
}

.flash-entry-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.flash-entry-time {
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 600;
}

/* Tags Signal + Impact (mai 2026 v3) : 2 micro-infos marché en mode inline
   ticker (pas de pilule pleine, juste dot + label + value) pour rester
   discrets sur les états normaux. Les états "alerte" (Majeur, Baissier)
   reçoivent un highlight subtil pour attirer l'œil sans crier.

   Vocabulaire + couleurs alignés avec les widgets articles (.ai-meta) :
   - Signal : Haussier (vert) / Neutre (gris) / Baissier (rouge)
   - Impact : Majeur (orange vif) / Modéré (jaune) / Mineur (gris) */
.flash-entry-tag {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  /* Pas de pill background par défaut — restera flat sur les états normaux.
     Les alerts (Majeur, Baissier) ajoutent leur propre fond via leurs variants. */
}
.flash-entry-tag-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  background: currentColor;
  align-self: center;
}
.flash-entry-tag-label {
  color: var(--text-faint);
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: 0;
}
.flash-entry-tag-value {
  color: var(--text-body);
  font-weight: 700;
}

/* Signal variants (couleur appliquée au dot + value) */
.flash-entry-signal--haussier { color: #22c55e; }
.flash-entry-signal--haussier .flash-entry-tag-value { color: #22c55e; }
.flash-entry-signal--baissier { color: #ef4444; }
.flash-entry-signal--baissier .flash-entry-tag-value { color: #ef4444; }
.flash-entry-signal--neutre   { color: var(--text-muted); }
.flash-entry-signal--neutre   .flash-entry-tag-value { color: var(--text-muted); }

/* Impact variants — Majeur reçoit le traitement "alerte" : couleur plus
   saturée + chip background subtil + uppercase pour vraiment ressortir
   dans la timeline. Modéré et Mineur restent flat. */
.flash-entry-impact--mineur { color: var(--text-faint); }
.flash-entry-impact--mineur .flash-entry-tag-value { color: var(--text-faint); }
.flash-entry-impact--modere { color: var(--accent); }
.flash-entry-impact--modere .flash-entry-tag-value { color: var(--accent); }
.flash-entry-impact--majeur {
  color: #fb923c;
  padding: 1px 7px;
  border-radius: 3px;
  background: rgba(251, 146, 60, 0.12);
  border: 1px solid rgba(251, 146, 60, 0.35);
}
.flash-entry-impact--majeur .flash-entry-tag-value {
  color: #fb923c;
  text-transform: uppercase;
  font-weight: 800;
  letter-spacing: 0.06em;
}

/* FR-event entries (mai 2026 v66 → v79b → v79c) : la signalisation FR-event
   se fait désormais par swap d'emoji en tête de titre (l'emoji catégorie
   est remplacé par 🇫🇷 quand entry.is_fr === true, cf. src/en-direct.njk
   v79c). Aucune CSS dédiée nécessaire — le drapeau hérite de la taille
   et de l'alignement de l'emoji titre. La classe .flash-entry--fr est
   toujours appliquée côté JS pour hook futur. */

/* Signal Haussier et Baissier reçoivent le même traitement chip d'alerte
   (mai 2026 v4) : background coloré + border + uppercase bold sur la value.
   Haussier vert (signal positif fort), Baissier rouge (signal négatif fort).
   Cohérent visuellement avec Impact Majeur (orange). Le Signal Neutre reste
   flat (et est caché par le JS quand sentiment === "Neutre"). */
.flash-entry-signal--haussier {
  padding: 1px 7px;
  border-radius: 3px;
  background: rgba(34, 197, 94, 0.10);
  border: 1px solid rgba(34, 197, 94, 0.32);
}
.flash-entry-signal--haussier .flash-entry-tag-value {
  text-transform: uppercase;
  font-weight: 800;
  letter-spacing: 0.06em;
}
.flash-entry-signal--baissier {
  padding: 1px 7px;
  border-radius: 3px;
  background: rgba(239, 68, 68, 0.10);
  border: 1px solid rgba(239, 68, 68, 0.32);
}
.flash-entry-signal--baissier .flash-entry-tag-value {
  text-transform: uppercase;
  font-weight: 800;
  letter-spacing: 0.06em;
}

.flash-entry-title {
  margin: 0 0 8px;
  font-family: var(--font-serif);
  font-size: 19px;
  line-height: 1.28;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.012em;
}

.flash-entry-summary {
  margin: 0 0 12px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-body);
}

.flash-entry-footer {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--text-faint);
}

/* v149 → v150 Lot D Phase 5 — Bouton copy permalink (à droite des tags meta).
   Avant : seulement sur /archives/YYYY/MM/DD/. v150 Phase 5 : ajouté aussi sur
   /en-direct/ avec URL pointant vers l'archive permanente (pas l'anchor
   éphémère #flash-X dans /en-direct/). Style partagé entre les 2 pages. */
.flash-entry-copy {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: auto;
  padding: 3px 8px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.flash-entry-copy:hover,
.flash-entry-copy:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
  outline: none;
}
.flash-entry-copy.is-copied {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: var(--accent);
  color: var(--accent);
}
.flash-entry-copy.is-copied .flash-entry-copy-label::after { content: " ✓"; }
.flash-entry-copy svg { flex-shrink: 0; }
@media (max-width: 480px) {
  .flash-entry-copy-label { display: none; }
}

.flash-entry-source-label {
  font-family: var(--font-mono);
  letter-spacing: 0.05em;
}
.flash-entry-source {
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 1px dotted var(--border-strong);
  transition: color 0.15s ease;
}
.flash-entry-source:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.flash-entry-cta {
  margin-left: auto;
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  font-size: 12.5px;
  transition: color 0.15s ease;
}
.flash-entry-cta:hover {
  color: var(--text-primary);
}

/* ============================================
   RESPONSIVE
   ============================================ */
/* v150 Lot D — ancienne media query .flash-telegram-cta top:120px supprimée
   (la pill Telegram sticky n'existe plus, remplacée par icon-btn dans la
   toolbar Variant C). */

@media (max-width: 640px) {
  .flash-section {
    padding: 24px 0 48px;
  }
  .flash-entry {
    padding: 18px 0 18px 16px;
    margin-left: 8px;
  }
  .flash-entry-title {
    font-size: 17px;
  }
  .flash-entry-summary {
    font-size: 13.5px;
  }
  .flash-entry-footer {
    font-size: 11.5px;
  }
  .flash-entry-cta {
    margin-left: 0;
    margin-top: 4px;
    width: 100%;
  }
}
