/* ══════════════════════════════════════════════════════════════
   RADAR — mobile.css
   Mobile-first. Base styles target phones; content grows into
   wider viewports via min-width breakpoints only — never the
   reverse.

   Palette/typo "façon Linear" : fond quasi-noir, ladder de surfaces à 4
   niveaux, bordures hairline (pas d'ombre sur le fond sombre), typo Sora
   (substitut libre documenté pour Linear Display/Text), tracking négatif
   sur les titres. Un seul accent chromatique (--accent, vert-citron
   #B6F23A), cohérent avec le principe Linear "no second chromatic color".
   Les couleurs de catégorie (CAT_COLORS, config.js, palette éditoriale)
   ne sont pas l'accent du site, comparable au "richer color-tag palette"
   que Linear réserve à ses propres surfaces produit. Layout system
   (gouttières/rythme/chrome) : voir §"container/section" plus bas. */

:root {
  /* Surface ladder (canvas → surface-4), du plus profond au plus élevé */
  --canvas:            #010102;
  --surface-1:          #0f1011;
  --surface-2:          #141516;
  --surface-3:          #18191a;
  --surface-4:          #191a1b;

  /* Bordures hairline — remplacent les ombres sur fond sombre */
  --hairline:           #23252a;
  --hairline-strong:    #34343a;
  --hairline-tertiary:  #3e3e44;

  /* Texte — ink = couleur de texte par défaut (claire, contrairement à
     l'ancien --ink sombre pré-redesign) */
  --ink:                #f7f8f8;
  --ink-muted:          #d0d6e0;
  --ink-subtle:         #8a8f98;
  --ink-tertiary:       #62666d;

  /* Accent unique — vert-citron #B6F23A demandé explicitement (remplace
     le vert rgb(21,250,19) essayé juste avant). --on-accent reste foncé
     pour le texte posé SUR un remplissage accent plein (pastilles/ronds
     sélectionnés) — trop clair/saturé pour porter du texte blanc lisible. */
  --accent:             #B6F23A;
  --accent-hover:       #C9F56B;
  --accent-focus:       #9DD62B;
  --on-accent:          #0F1117;

  /* Sémantique — seules exceptions à l'accent unique (badges
     ouverture/fermeture), gardées proches du spec Linear (success) et
     assombries pour rester lisibles sur fond sombre (danger, non
     documenté par Linear qui n'a pas ce besoin en marketing) */
  --success:            #1a7f37;
  --danger:             #d92d20;

  /* Neutres et calques transverses (mêmes valeurs qu'avant, nommées) */
  --white: #FFFFFF;
  --map-ink: #1f1f1f;
  --map-ink-muted: #5f6368;
  --map-ink-subtle: #6b6f76;
  --map-line: #eee;
  --map-hover: #f4f4f4;
  --scrim: rgba(0,0,0,0.6);
  --scrim-soft: rgba(0,0,0,0.55);
  --glass: rgba(10,10,12,0.72);
  --glass-soft: rgba(10,10,12,0.6);
  --veil-3: rgba(255,255,255,0.03);
  --veil-4: rgba(255,255,255,0.04);
  --veil-6: rgba(255,255,255,0.06);
  --veil-10: rgba(255,255,255,0.10);
  --shadow-lift: 0 16px 28px rgba(0,0,0,0.45);
  --shadow-pin: 0 3px 8px rgba(0,0,0,0.4);
  --shadow-tip: 0 1px 3px rgba(0,0,0,0.4);
  --map-cluster: rgba(148,163,184,0.85);
  --map-leg: rgba(34,34,34,0.5);
  --shadow-float: 0 2px 6px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2);
  --shadow-drop: rgba(0,0,0,0.4);
  --map-user: #1a73e8;  /* point bleu de ta position, convention des applis de cartes */
  --map-user-halo: rgba(26,115,232,0.25);

  /* Rayons — échelle Linear */
  --r-xs:  4px;
  --r-sm:  6px;
  --r-md:  8px;
  --r-lg:  12px;
  --r-xl:  16px;
  --r-pill: 9999px;

  /* ── Fondations layout : gouttières + rythme vertical des sections ──
     Un seul endroit à modifier pour que ça change partout. */
  --gutter:      16px;
  --section-pad: clamp(3.75rem, 8vw, 6.25rem);
  --stack-gap:   clamp(3rem, 6vw, 4rem);
  --card-gap:    clamp(1rem, 2.5vw, 1.25rem);

  /* Chrome fixe (app bar + onglets + filtre) — mesuré en JS pour que le
     feed ne démarre jamais dessous. */
  /* Valeurs réelles d'emblée (header 56 + onglets 48), recalculées par
     layoutChrome : partir de 0 ferait glisser tout le contenu de 104px au
     démarrage (CLS 0,11). */
  --chrome-h: 104px;
  --filter-bar-top: 104px;
  /* Hauteur de la ligne résumé + Filtres (#m-filter-bar, dans le flux sous
     le chrome), mesurée en JS (renderFilterSummary) : elle gagne une rangée
     quand des pastilles de filtres s'affichent. 64px = rangée seule. */
  --filter-line-h: 64px;
  /* Fondu des bords du bandeau d'onglets (masque alpha, pas une couleur
     affichée) : indique qu'il reste des onglets hors champ. */
  --nav-fade: 32px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  min-height: 100%;
  /* Desactive l'auto-inflation de texte de Safari iOS (elargit
     l'espacement des petits textes, ex. .m-filter-count a 10px) —
     invisible en emulation mobile Chrome/F12, qui ne reproduit pas ce
     comportement specifique a WebKit, seulement sur un vrai iPhone. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  font-family: 'Sora', -apple-system, 'SF Pro Display', system-ui, sans-serif;
  background: var(--canvas);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.05px;
}

button { font-family: inherit; }

/* H1 de la page (backend/seo.py) : lu par Google et les lecteurs d'écran, la
   ligne résumé tient ce rôle à l'écran. */
.m-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Focus clavier (audit P0-02/2.4.7) : un seul anneau accent de
   2px pour tout ce qui n'a pas de règle propre. Le défilement automatique
   vers un élément focalisé s'arrête sous le chrome fixe et au-dessus de la
   pastille Carte (2.4.11, focus non masqué). */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Dialogue focalisé lui-même à l'ouverture au doigt (mobile-modal.js) : pas un contrôle, pas d'anneau. */
[role="dialog"]:focus { outline: none; }
html {
  scroll-padding-top: calc(var(--chrome-h) + 8px);
  scroll-padding-bottom: calc(var(--float-ctl-h) + 32px + env(safe-area-inset-bottom));
}

/* ══════════════════════════════════════════
   APP BAR — fixe, toujours visible. Fond canvas quasi-noir, logo RADAR
   à gauche — SVG inline dans mobile.html (copie de img/radar-logo.svg) :
   le "D" central est un cadran/antenne radar stylisé (cercle + barre
   verticale), en vert accent dans le fichier ; les lettres sont en blanc.
   Le logo est un lien vers l'accueil (rechargement complet = réinitialisation).
   ══════════════════════════════════════════ */
#m-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--gutter);
  background: var(--canvas);
  /* Séparateur blanc 1px entre le header et le bandeau d'onglets,
     dans les 56px (border-box). Repris dans le tiroir
     (.m-drawer-top::after). */
  border-bottom: 1px solid var(--white);
  z-index: 100;
}
/* Logo complet à gauche, menu à droite (façon thecapsuleclub.com). */
.m-logo { display: block; padding: 13px 0; } /* zone de tap de 43 px autour du logo de 17 px */
.m-logo-svg {
  display: block;
  /* Logo B : axe plus court, viewBox 760x247. */
  height: 17px; /* = header de css/legal.css */
  width: auto;
}
/* Actions à droite, icônes 24px. */
.m-header-actions {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
}
.m-header-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  color: var(--ink);
  cursor: pointer;
}
.m-header-btn svg {
  display: block;
}
/* Hauteur commune pastille Carte/Liste et barre de recherche d'adresse.
   --fab-gap : écart identique dernières cartes → pastille → trait du pied de
   page, en bas de la liste. */
:root { --float-ctl-h: 44px; --fab-gap: 32px; }

/* Pastille carte — flottante, centrée en bas, compacte.
   Bordure hairline + ombre pour rester lisible par-dessus les cards et la
   carte ; z-index sous la feuille détail (200) et la connexion (300). */
#m-map-fab {
  position: fixed;
  /* --fab-lift : hauteur visible du pied de page (mobile-view.js), pour
     rester à --fab-gap au-dessus de lui sans jamais le recouvrir. */
  bottom: max(calc(env(safe-area-inset-bottom) + 20px), calc(var(--fab-lift, 0px) + var(--fab-gap)));
  left: 50%;
  transform: translateX(-50%);
  z-index: 150;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--float-ctl-h);
  padding: 0 18px;
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--hairline-strong);
  border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.55);
  font: 600 14px/1 'Sora', sans-serif;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* Trait de l'icône aligné sur l'épaisseur du texte Sora 600 (CSS > attribut,
   vaut aussi pour l'icône tuiles injectée en vue carte). */
#m-map-fab svg { display: block; stroke-width: 2.25; }
#m-map-fab:active { transform: translateX(-50%) scale(0.96); }

/* Tiroir menu (mobile-header.js) — plein écran, entre par la
   droite. Mouvement repris de thecapsuleclub.com : translateX 300ms
   ease-out, rien d'autre (pas de fondu ni de voile). Au-dessus de la
   feuille détail (200), sous la connexion (300). */
#m-drawer {
  position: fixed;
  inset: 0;
  z-index: 250;
  display: flex;
  flex-direction: column;
  height: 100dvh;
  padding: env(safe-area-inset-top) calc(var(--gutter) + env(safe-area-inset-right))
           calc(16px + env(safe-area-inset-bottom)) calc(var(--gutter) + env(safe-area-inset-left));
  background: var(--canvas);
  transform: translateX(100%);
  transition: transform 0.3s ease-out;
  will-change: transform;
  overflow: hidden;
}
#m-drawer.open { transform: translateX(0); }
body.m-drawer-open { overflow: hidden; }
.m-drawer-top {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 56px;
  flex-shrink: 0;
}
/* Même séparateur blanc que #m-header, prolongé bord à bord par-dessus les
   marges latérales du tiroir. */
.m-drawer-top::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: calc(-1 * (var(--gutter) + env(safe-area-inset-left)));
  right: calc(-1 * (var(--gutter) + env(safe-area-inset-right)));
  height: 1px;
  background: var(--white);
}
.m-drawer-top a { display: block; padding: 13px 0; } /* = .m-logo */
.m-drawer-logo { display: block; height: 17px; width: auto; } /* = .m-logo-svg */
/* Croix : icône 20px, zone tactile 44x44 ; marge négative pour caler
   l'icône sur la gouttière, à l'aplomb du menu du header. */
#m-drawer-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: -12px;
  padding: 0;
  background: none;
  border: none;
  color: var(--ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#m-drawer-close svg { display: block; }
#m-drawer-close:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }

/* Seule zone défilante du tiroir (écrans
   très courts) : le compte reste ancré en bas. Espacements en clamp(vh)
   pour que tout tienne sans scroll dès ~600px de haut. */
.m-drawer-nav {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  padding: clamp(24px, 5vh, 40px) 0 clamp(16px, 3vh, 24px);
}
.m-drawer-nav::-webkit-scrollbar { display: none; }
/* Label de section : petit, espacé, gris — repère, pas un titre. */
.m-drawer-label {
  margin: 0 0 clamp(10px, 1.8vh, 16px);
  font-size: 11px;
  font-weight: 500;
  line-height: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-subtle);
}
.m-drawer-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1.6vh, 14px);
}
/* Entrées : capitales, taille suivant la largeur (~24px à 375px), jamais
   coupées ; pas ≈ 36-40px par ligne. Actif = vert, rien d'autre. */
.m-drawer-nav a {
  display: block;
  text-decoration: none;
  padding: 0;
  background: none;
  border: none;
  color: var(--ink);
  font-size: clamp(21px, 6.4vw, 25px);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: opacity 0.2s;
  -webkit-tap-highlight-color: transparent;
}
.m-drawer-nav a:hover { opacity: 0.7; }
.m-drawer-nav a.active { color: var(--accent); opacity: 1; }
.m-drawer-nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
/* Espace perso : un filet fin et de l'air le détachent de « Découvrir ». */
.m-drawer-nav .m-drawer-personal {
  margin-top: clamp(20px, 3.5vh, 32px);
  padding-top: clamp(20px, 3.5vh, 32px);
  border-top: 1px solid var(--hairline-strong);
}

/* Zone compte : liens légers en ligne, ancrés en bas, sous un filet. */
.m-drawer-account {
  flex-shrink: 0;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}
.m-drawer-guest,
.m-drawer-member {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.m-drawer-member[hidden],
.m-drawer-guest[hidden] { display: none; }
.m-drawer-email {
  min-width: 0;
  margin: 0;
  font-size: 13px;
  color: var(--ink-subtle);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Lien texte 14px, hauteur tactile 40px ; « Créer un compte » un cran plus
   visible avec un filet blanc, angles droits comme la page de connexion. */
.m-drawer-link {
  flex-shrink: 0;
  height: 40px;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: opacity 0.2s, background 0.2s, color 0.2s;
  -webkit-tap-highlight-color: transparent;
}
.m-drawer-link:hover { opacity: 0.7; }
.m-drawer-link--outline {
  padding: 0 16px;
  border: 1px solid var(--ink);
}
.m-drawer-link--outline:hover { opacity: 1; background: var(--ink); color: var(--canvas); }
.m-drawer-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  #m-drawer { transition: none; }
}

/* ══════════════════════════════════════════
   GROUPE — onglets (Nouveautés / Ouvertures / Temporaires / Derniers
   jours / Prochainement). L'onglet actif se distingue par un soulignement
   accent + poids de police. Slider horizontal à largeur naturelle (comme
   .m-tab-group juste en dessous, ou le bandeau rouge de TimeOut) plutôt
   que des cases comprimées à largeur égale — les noms longs ("Derniers
   jours") restent lisibles sur un seul mot au lieu d'être écrasés/tronqués.
   Un carrousel "cadran" à 3 cellules fixes + texte glissant est archivé
   dans frontend/reference/mobile-group-tabs-carousel.html (sa logique de
   rotation suppose exactement 3 cellules).
   ══════════════════════════════════════════ */
#m-group-tabs {
  position: fixed;
  top: 56px; left: 0; right: 0;
  height: 48px; /* compact : la place va au contenu */
  display: flex;
  align-items: center;
  gap: 0;
  padding: 0 var(--gutter);
  overflow-x: auto;
  justify-content: safe center;
  scrollbar-width: none;
  z-index: 99;
  /* Un cran au-dessus du canvas (sub-nav, cf. Linear surface-3) avec une
     ligne hairline pour séparer visuellement les deux bandes sans
     recourir à une ombre (bannie sur fond sombre côté Linear). */
  /* Fond un cran plus neutre que --surface-1
     (#0f1011), filet bas un peu plus lisible que --hairline (#23252a),
     textes inactif/actif propres au bandeau (--nav-*). Le filet du haut
     reste la ligne blanche de #m-header. */
  --nav-bg: #111214;
  --nav-line: #2A2C2F;
  --nav-ink: #9A9EA5;
  --nav-ink-active: #FFFFFF;
  background: var(--nav-bg);
  border-bottom: 1px solid var(--nav-line);
}
#m-group-tabs::-webkit-scrollbar { display: none; }
/* Fondu sur le bord où il reste des onglets (classes posées par
   mobile-router.js::updateGroupTabsFade) : l'onglet suivant se devine, sans
   coupure nette au milieu d'un mot. */
#m-group-tabs.m-more-right { -webkit-mask-image: linear-gradient(to right, black calc(100% - var(--nav-fade)), transparent); mask-image: linear-gradient(to right, black calc(100% - var(--nav-fade)), transparent); }
#m-group-tabs.m-more-left { -webkit-mask-image: linear-gradient(to right, transparent, black var(--nav-fade)); mask-image: linear-gradient(to right, transparent, black var(--nav-fade)); }
#m-group-tabs.m-more-left.m-more-right { -webkit-mask-image: linear-gradient(to right, transparent, black var(--nav-fade), black calc(100% - var(--nav-fade)), transparent); mask-image: linear-gradient(to right, transparent, black var(--nav-fade), black calc(100% - var(--nav-fade)), transparent); }
#m-group-tabs:not(.m-aligned) > * { visibility: hidden; }
.m-group-tab {
  flex-shrink: 0;
  text-decoration: none;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--nav-ink);
  font-family: 'Sora', sans-serif;
  font-style: normal;
  /* Casse normale : moins de concurrence avec les titres de rail, mots
     plus courts à lire qu'en majuscules. */
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  white-space: nowrap;
  padding: 0 12px;
  cursor: pointer;
}
.m-group-tab.active {
  color: var(--nav-ink-active);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* ══════════════════════════════════════════
   FILTRES (audit Codex P1-03).

   1. Ligne #m-filter-bar, dans le flux de <main> (pas une 3e barre fixe),
      au-dessus de la liste comme de la carte : bouton « Filtres » (44px)
      puis pastilles des filtres actifs, en scroll horizontal natif si elles
      débordent (le rail déborde jusqu'au bord de l'écran pour laisser voir
      la pastille suivante).
   2. Panneau #m-filter-panel : tiroir qui descend sous les onglets
      (translateY, pas un fondu — demande explicite), sections Période et
      Catégories, action finale « Afficher N idées ». Fermé = `hidden`,
      donc jamais dans l'ordre de Tab. 200ms à l'ouverture comme à la
      fermeture ; sous prefers-reduced-motion, fondu court sans
      glissement.
   ══════════════════════════════════════════ */
#m-filter-bar {
  /* Aligné sur le contenu (gouttière, puis max 1280px centré en desktop).
     16px sous les onglets : la ligne respire au lieu de coller au bandeau. */
  padding: 16px max(var(--gutter), calc((100% - 1280px) / 2)) 4px;
}
.m-filter-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
}
/* Résumé (« 32 nouveautés aujourd’hui ») : information secondaire, sous les
   titres de rail dans la hiérarchie ; deux lignes au plus à 320px. */
.m-feed-summary {
  min-width: 0;
  font-size: 14px;
  line-height: 1.3;
  color: var(--ink-muted);
}
.m-feed-summary-count { color: var(--accent); font-weight: 600; }
/* Pilule 36px, zone tactile portée à 44px par ::before (même règle pour
   les pastilles de filtres). */
#m-filters-trigger {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 14px 0 12px;
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  /* Même corps que le résumé à côté (14px) : à 13px gras le bouton
     paraissait plus haut que la phrase. */
  font: 600 14px/1 'Sora', sans-serif;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#m-filters-trigger::before,
.m-filter-chip::before {
  content: '';
  position: absolute;
  /* Mesuré depuis la boîte de padding (bordure 1px exclue) : 34 + 2×5 = 44. */
  inset: -5px -1px;
}
#m-filters-trigger .m-filters-icon { flex: none; color: var(--accent); }
#m-filters-trigger[aria-expanded="true"] { border-color: var(--accent); }
.m-filters-count { color: var(--accent); }
.m-filters-count[hidden] { display: none; }
/* Pastilles des filtres retirables : rangée défilante sous le résumé. */
.m-filter-chips {
  display: flex;
  gap: 8px;
  /* 12 + 4 de padding + 4 sous le bouton (ligne 44px, pilule 36px) = 20px,
     même écart que bandeau d'onglets → bouton Filtres. */
  margin: 12px 0 0;
  padding: 4px 0;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: none;
}
.m-filter-chips[hidden] { display: none; }
/* Avant le premier rendu d'une URL filtrée : même hauteur que la rangée
   remplie (pastille 36px), sans contenu (classe posée dans mobile.html). */
.m-chips-pending .m-filter-chips[hidden] { display: flex; visibility: hidden; min-height: 44px; }  /* 36 + 2×4 de padding */
.m-filter-chips::-webkit-scrollbar { display: none; }
.m-filter-chips li { flex: none; }
.m-filter-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 10px 0 14px;
  background: transparent;
  color: var(--ink-muted);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  font: 500 13px/1 'Sora', sans-serif;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.m-filter-chip .cat-icon { font-size: 15px; color: var(--ink-subtle); }
.m-filter-clear {
  height: 36px;
  padding: 0 8px;
  background: none;
  border: none;
  color: var(--ink-subtle);
  font: 500 13px/1 'Sora', sans-serif;
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
  cursor: pointer;
}
#m-filters-trigger:focus-visible,
.m-filter-chip:focus-visible,
.m-filter-clear:focus-visible,
#m-filter-panel button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

#m-filter-backdrop {
  position: fixed;
  inset: 0;
  background: var(--scrim-soft);
  z-index: 96;
  opacity: 0;
  transition: opacity 200ms ease;
}
#m-filter-backdrop.visible { opacity: 1; }

/* Sous les onglets (z-index 99) : en glissant, le panneau semble sortir de
   dessous le bandeau. `top` posé en JS (layoutChrome) ; 104px = défaut
   avant mesure (header 56 + onglets 48). */
#m-filter-panel {
  position: fixed;
  top: 104px;
  left: 0; right: 0;
  z-index: 97;
  display: flex;
  flex-direction: column;
  background: var(--surface-1);
  border-bottom: 1px solid var(--hairline);
  box-shadow: var(--shadow-lift);
  max-height: calc(100dvh - var(--filter-bar-top) - 16px);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateY(-100%);
  transition: transform 200ms cubic-bezier(0.2, 0, 0, 1);
}
#m-filter-panel[hidden] { display: none; }
#m-filter-panel.open { transform: translateY(0); }
#m-filter-panel > * {
  width: 100%;
  max-width: 560px;
  margin-inline: auto;
}
.m-filter-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 6px 0 var(--gutter);
}
.m-filter-head h2 {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.2px;
  color: var(--ink);
}
#m-filter-close {
  flex: none;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: var(--r-pill);
  color: var(--ink);
  cursor: pointer;
}
#m-filter-close svg { display: block; }
.m-filter-section { padding: 12px var(--gutter) 4px; }
.m-filter-section[hidden] { display: none; }
.m-filter-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.m-filter-section-title {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--ink-subtle);
}
.m-filter-section-hint { font-size: 12px; color: var(--ink-subtle); }

/* Période : trois choix textuels de même largeur. Choisi = fond accent ET
   coche (pas la couleur seule). */
.m-period-options {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}
.m-period-option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 8px;
  background: var(--surface-2);
  color: var(--ink-muted);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  font: 500 14px/1 'Sora', sans-serif;
  white-space: nowrap;
  cursor: pointer;
}
.m-period-option .cat-icon { flex: none; }
.m-period-count { font-size: 12px; color: var(--ink-subtle); }
.m-period-option.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.m-period-option.active .m-period-count { color: var(--on-accent); }
@media (max-width: 359px) {
  .m-period-option { gap: 4px; padding: 0 4px; font-size: 13px; }
}

/* Catégories — rond icône + libellé + compteur, grille 2x4 paginée à
   balayer (scroll-snap par page). Aucune icône sans son libellé. Choisie =
   rond accent ET coche devant le libellé ; sans événement = estompée et
   désactivée, libellé conservé. */
.m-cat-list {
  display: flex;
  flex-direction: row;
  overflow-x: auto;
  padding: 16px 0 12px;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
}
.m-cat-page {
  display: flex;
  justify-content: center;
  flex: 0 0 100%;
  scroll-snap-align: start;
}
.m-cat-grid {
  display: grid;
  /* Colonnes fluides (audit P0-03) : 4×66 + 3×20 = 324px ne
     tiennent pas dans les 288px disponibles à 320px (4e rond coupé). */
  grid-template-columns: repeat(4, minmax(0, 66px));
  column-gap: clamp(8px, (100vw - 296px) / 3, 20px);
  row-gap: 16px;
}
.m-cat-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 66px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.m-cat-tab-circle {
  position: relative; /* ancre .m-filter-count */
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  border: 1px solid var(--hairline);
  font-size: 22px;
  line-height: 1;
  /* Un <button> a son propre `color` UA, non hérité du body : sans ça les
     icônes (stroke="currentColor") seraient noires sur fond sombre. */
  color: var(--ink);
}
.m-cat-tab-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-subtle);
  text-align: center;
  line-height: 1.25;
}
.m-cat-tab-check { color: var(--accent); margin-right: 2px; }
.m-cat-tab.active .m-cat-tab-circle {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--on-accent);
}
.m-cat-tab.active .m-cat-tab-label {
  font-weight: 600;
  color: var(--ink);
}
.m-cat-tab-disabled {
  cursor: default;
  opacity: 0.5;
}
/* Compteur posé sur le rond (catégories), chevauchant son bord en haut à
   droite ; bordure couleur du panneau pour le détourer. */
.m-filter-count {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 20px;
  padding: 2px 4px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  text-align: center;
  border: 1.5px solid var(--surface-1);
  white-space: nowrap;
}
.m-cat-tab.active .m-filter-count {
  background: var(--ink);
  color: var(--canvas);
}

/* Actions, collées au bas du panneau même quand il défile. */
.m-filter-actions {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px var(--gutter) calc(12px + env(safe-area-inset-bottom));
  background: var(--surface-1);
  border-top: 1px solid var(--hairline);
}
#m-filter-reset {
  flex: none;
  min-height: 44px;
  padding: 0 12px;
  background: none;
  border: none;
  color: var(--ink-muted);
  font: 500 14px/1 'Sora', sans-serif;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
#m-filter-reset[hidden] { display: none; }
#m-filter-apply {
  flex: 1;
  min-height: 48px;
  background: var(--accent);
  color: var(--on-accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  font: 600 15px/1 'Sora', sans-serif;
  cursor: pointer;
}
#m-filter-apply:disabled {
  background: var(--surface-2);
  border-color: var(--hairline-strong);
  color: var(--ink-subtle);
  cursor: default;
}
#m-filter-panel button:focus-visible { outline-offset: 2px; }
/* La pastille Carte (z-index 150) passerait par-dessus « Afficher N idées »
   sur petit écran : masquée tant que le panneau est affiché. */
body:has(#m-filter-panel:not([hidden])) #m-map-fab { visibility: hidden; }
#m-filter-apply:focus-visible { outline-color: var(--white); }

@media (prefers-reduced-motion: reduce) {
  #m-filter-panel,
  #m-filter-panel.open {
    transform: none;
    opacity: 0;
    transition: opacity 80ms linear;
  }
  #m-filter-panel.open { opacity: 1; }
  #m-filter-backdrop { transition: opacity 80ms linear; }
}

/* ══════════════════════════════════════════
   CONTAINER / SECTION — le système réutilisable
   ══════════════════════════════════════════
   Chaque <section> a ce padding horizontal (gouttières) et vertical
   (rythme d'espacement) ; le .container à l'intérieur centre le
   contenu, grandit jusqu'à 1280px, et empile ses enfants avec un
   gap uniforme. Aucune largeur ni hauteur fixe en dessous. */
section {
  padding: var(--section-pad) var(--gutter);
}
.container {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--stack-gap);
}

main {
  padding-top: var(--chrome-h);
}
/* Vues de l'app : au moins un écran. Avant le squelette du fil, le pied de
   page s'affichait vers 300px puis glissait sous l'écran (CLS 0,11 à 0,14).
   Pas sur les pages servies par le serveur, déjà complètes au chargement. */
body:not(.m-page-event) main { min-height: 100vh; }
/* Le premier rail suit directement la ligne résumé + Filtres.
   En bas : la place de la pastille Carte, qui s'arrête au-dessus du pied de
   page, avec le même écart (--fab-gap) au-dessus et en dessous d'elle. */
#m-feed-section {
  padding-top: 24px;  /* aération résumé → premier rail */
  padding-bottom: calc(var(--float-ctl-h) + 2 * var(--fab-gap));
}
/* Rails : 4px de bande de défilement sous les cartes, retirés pour garder
   le même écart cartes → pastille. */
#m-feed-section:has(#m-rails:not([hidden])) {
  padding-bottom: calc(var(--float-ctl-h) + 2 * var(--fab-gap) - 4px);
}

/* Carte : tout l'espace sous la ligne Filtres (qui reste au-dessus d'elle,
   mêmes filtres qu'en liste). */
/* 12px sous la ligne (4px de marge propre) : même écart de 16px au-dessus
   de la carte qu'entre les onglets et la ligne. */
#m-map-section {
  position: relative;
  padding: 0;
  margin-top: 12px;
  height: calc(100dvh - var(--chrome-h) - var(--filter-line-h) - 12px);
}
#m-map-canvas {
  width: 100%;
  height: 100%;
}
/* Carte sans repère (audit P1-06) : message centré sur la carte
   et retour à la liste, au-dessus des tuiles, loin de la loupe (en haut) et
   de la pastille Carte (en bas). */
.m-map-empty {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: min(300px, calc(100% - 2 * var(--gutter)));
  justify-items: center;
  text-align: center;
}
/* Carte flottante commune (carte vide, fenêtres .m-dialog). */
.m-map-empty, .m-dialog-card {
  display: grid;
  gap: 14px;
  padding: 20px;
  background: var(--surface-1);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
}
.m-map-empty[hidden] { display: none; }
/* Chargement de MapLibre (à la demande). */
.m-map-loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  margin: 0;
  padding: 10px 16px;
  max-width: calc(100% - 2 * var(--gutter));
  background: var(--surface-1);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  color: var(--ink-muted);
  font-size: 14px;
  text-align: center;
}  /* display:grid primerait sur hidden */
.m-map-empty p { font-size: 14px; line-height: 1.4; color: var(--ink); }
#m-map-empty-back, #m-geo-help-retry {
  min-height: 44px;
  padding: 0 20px;
  background: var(--accent);
  color: var(--on-accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  font: 600 14px/1 'Sora', sans-serif;
  cursor: pointer;
}
#m-map-empty-back:focus-visible, #m-geo-help-retry:focus-visible { outline-color: var(--white); }
/* Me localiser (mobile-geo.js) : en bas à droite, à la hauteur de la
   pastille Carte/Liste centrée, même îlot clair que la recherche. */
#m-map-locate {
  position: absolute;
  right: 16px;
  bottom: max(calc(env(safe-area-inset-bottom) + 20px), var(--fab-gap));
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--float-ctl-h);
  height: var(--float-ctl-h);
  padding: 0;
  border: none;
  border-radius: 50%;
  font-size: 18px;
  cursor: pointer;
}
#m-map-locate[hidden] { display: none; }
#m-map-locate:disabled { color: var(--map-ink-subtle); cursor: progress; }
.m-map-user-dot {
  width: 16px;
  height: 16px;
  background: var(--map-user);
  border: 3px solid var(--white);
  border-radius: 50%;
  box-shadow: 0 0 0 6px var(--map-user-halo);
}
/* Marqueurs carte (mobile-map-markers.js) : photo ronde, bulle compteur
   pour un lieu partagé, info-bulle titre + lieu au survol. La racine est
   positionnée par MapLibre (transform) : l'effet de survol va sur le disque. */
.m-map-pin {
  --m-pin-scale: 1;
  --m-pin-d: calc(var(--m-pin-size, 24px) * var(--m-pin-scale));
  width: var(--m-pin-d);
  height: var(--m-pin-d);
  cursor: pointer;
}
.m-map-pin-disc {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
  border: 2px solid var(--accent); /* vert accent */
  box-shadow: var(--shadow-pin);
  background: var(--surface-3);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.2s ease;
}
.m-map-pin-disc img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.m-map-pin-noimg {
  color: var(--white);
  font-size: calc(var(--m-pin-d) * 0.5);
  line-height: 1;
}
.m-map-pin-group .m-map-pin-disc {
  background: var(--map-cluster);
  /* Chiffre sombre sur la bulle gris clair (blanc = 2,2:1 seulement). */
  color: var(--map-ink);
  font-weight: 700;
  font-size: max(10px, calc(var(--m-pin-d) * 0.35));
}
.m-map-pin-open .m-map-pin-disc { opacity: 0.35; }
.m-map-spider-legs {
  pointer-events: none;
  overflow: visible;
}
.m-map-spider-legs line {
  stroke: var(--map-leg);
  stroke-width: 1.5;
}
.m-map-tip {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 8px;
  border-radius: 6px;
  background: var(--white);
  color: var(--map-ink);
  box-shadow: var(--shadow-tip);
  font-family: 'Sora', sans-serif;
  font-size: 12px;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
}
.m-map-tip div + div { margin-top: 2px; }
.m-map-tip-title { font-weight: 600; }
.m-map-tip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--white);
}
@media (hover: hover) {
  .maplibregl-marker:hover { z-index: 5; }
  .m-map-pin:hover .m-map-pin-disc { transform: scale(1.05); }
  .m-map-pin:hover .m-map-tip { display: block; }
}
.m-map-search-pin {
  font-size: 32px;
  line-height: 1;
  color: var(--danger);
  filter: drop-shadow(0 2px 3px var(--shadow-drop));
}
/* En haut de la carte : le bas est pris par #m-map-fab.
   À 16px du haut et du bord gauche de la carte. */
#m-map-search-wrap {
  position: absolute;
  top: 16px;  /* même retrait que la gouttière gauche */
  /* Bord gauche fixe (celui de la barre centrée dépliée) : repliée, la
     loupe reste à la même place et la barre s'étire vers la droite. */
  left: max(16px, calc(50% - 210px));
  width: min(calc(100% - 32px), 420px);
  transition: width 0.25s ease;
  display: flex;
  align-items: center;
  gap: 10px;
  border-radius: var(--r-pill);
  height: var(--float-ctl-h);
  /* 13 + 18 (loupe) + 13 = --float-ctl-h : cercle parfait une fois replié. */
  padding: 0 13px;
}
/* Îlot clair commun aux contrôles posés sur la carte. */
#m-map-search-wrap, #m-map-locate {
  background: var(--white);
  color: var(--map-ink);
  box-shadow: var(--shadow-float);
}
#m-map-search-wrap.is-collapsed {
  width: var(--float-ctl-h);
  overflow: hidden;
}
#m-map-search-wrap svg {
  flex: none;
  color: var(--map-ink-muted);
}
/* Toute la pastille (44px) est la cible, pas seulement la loupe de 18px. */
#m-map-search-btn {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--float-ctl-h);
  height: var(--float-ctl-h);
  margin-left: -13px;
  background: none;
  border: none;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
}
#m-map-search-wrap:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
#m-map-search-btn:focus-visible, #m-map-search:focus-visible { outline: none; }
#m-map-search {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font-family: inherit;
  font-size: 16px;
  color: var(--map-ink);
}
#m-map-search::placeholder {
  color: var(--map-ink-subtle);
}
#m-map-suggestions {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  list-style: none;
  background: var(--white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-float);
  overflow: hidden;
  max-height: 260px;
  overflow-y: auto;
}
#m-map-suggestions li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  cursor: pointer;
  border-bottom: 1px solid var(--map-line);
}
#m-map-suggestions li:last-child {
  border-bottom: none;
}
#m-map-suggestions li:hover {
  background: var(--map-hover);
}
#m-map-suggestions li svg {
  flex: none;
  color: var(--map-ink-muted);
}
#m-map-suggestions li span {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
#m-map-suggestions li strong {
  font-size: 14px;
  font-weight: 600;
  color: var(--map-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#m-map-suggestions li small {
  font-size: 12px;
  color: var(--map-ink-subtle);
}

/* ══════════════════════════════════════════
   FEED — grille 12 colonnes, cards en li
   ══════════════════════════════════════════ */
.m-card-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--card-gap);
}
.m-card-grid[hidden] { display: none; }
/* Rapprochement intro → contenu : seulement quand l'intro est affichée
   (état « chargement impossible »), sinon le contenu ouvre la section. */
#m-feed-intro:not([hidden]) ~ .m-card-grid,
#m-feed-intro:not([hidden]) ~ #m-rails {
  margin-top: calc(28px - var(--stack-gap) - 12px);
}

#m-rails[hidden] { display: none; }
#m-rails {
  display: flex;
  flex-direction: column;
  gap: clamp(32px, 9vw, 40px);
}
.m-rail-section {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.m-rail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.m-rail-title {
  /* Nom et compteur centrés sur le même axe horizontal : les deux boîtes
     de texte sont rognées à la hauteur des capitales (text-box), donc
     centrer les boîtes centre les lettres elles-mêmes. */
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: 'Sora', sans-serif;
  /* Échelle typo : sous le hero. */
  font-size: clamp(19px, 5.1vw, 22px);
  line-height: 1.1;
  letter-spacing: -0.3px;
  font-weight: 600;
  color: var(--ink);
  margin: 0;
  padding: 0;
}
/* Compteur du rail : petite pastille accent à côté du nom, sans
   deux-points ; le nom reste l'élément fort. */
.m-rail-name { text-box: trim-both cap alphabetic; }
/* Titre-lien vers la page catégorie : même rendu que le titre, 44px de
   cible, soulignement seulement au survol. */
.m-rail-link {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  color: inherit;
  text-decoration: none;
  border-radius: var(--r-sm);
}
.m-rail-link:hover .m-rail-name { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
/* Chiffres centrés dans la pastille : boîte rognée cap → ligne de base,
   même marge au-dessus et au-dessous. */
.m-rail-count {
  display: inline-block;
  text-box: trim-both cap alphabetic;
  padding: 6px 7px;
  border-radius: var(--r-pill);
  background: var(--veil-6);
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: 0;
}
/* État « chargement impossible » (mobile-load-error.js) : le
   titre et le sous-titre de l'intro portent le message, ici seulement
   l'action de reprise. */
.m-load-error { display: flex; justify-content: center; padding: 8px 0 32px; }
.m-load-error-retry {
  min-height: 44px;
  padding: 0 24px;
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.m-load-error-retry:disabled { opacity: 0.6; cursor: default; }

/* « Voir les 30 derniers jours (N) » sous une liste courte sur 7 jours (mobile-feed.js). */
.m-wider-period:not([hidden]) { display: flex; justify-content: center; margin: 24px 0 8px; }
.m-wider-period a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 24px;
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}
.m-wider-period a:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }
.m-load-error-retry:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }

.m-rail-blurb {
  margin: 0;
  font-size: 14px;
  line-height: 1.35;
  color: var(--ink-muted);
}
.m-rail {
  display: flex;
  align-items: stretch;
  gap: var(--card-gap);
  overflow-x: auto;
  padding: 0 0 4px;
  scroll-snap-type: x proximity;
  scrollbar-color: var(--accent) transparent;
}
.m-rail::-webkit-scrollbar {
  height: 4px;
}
.m-rail::-webkit-scrollbar-track {
  background: transparent;
}
.m-rail::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 2px;
}
/* Amorce d'une 3e carte (audit P1-02) : sur mobile, le rail
   déborde dans la gouttière droite jusqu'au bord de l'écran et les deux
   cartes cèdent --rail-peek chacune à moitié ; on voit ainsi
   --rail-peek + --gutter de la carte suivante, ce qui suggère le scroll. Le
   padding droit ramène la dernière carte sur l'alignement de la gouttière.
   Annulé à partir de 560px (3 puis 4 cartes, breakpoints plus bas). */
.m-rail {
  --rail-peek: 12px;
  margin-right: calc(-1 * var(--gutter));
  padding-right: var(--gutter);
}
.m-rail .m-card {
  grid-column: unset;
  flex: 0 0 calc((100% - 2 * var(--card-gap) - var(--rail-peek)) / 2);
  width: calc((100% - 2 * var(--card-gap) - var(--rail-peek)) / 2);
  scroll-snap-align: start;
}
/* Titre + description à gauche, « Voir tout » centré sur ce bloc. */
.m-rail-heading {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* « Voir tout → » : lien
   texte discret à droite du titre, zone tactile 44px de haut. */
.m-rail-more {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
  padding-left: 8px;
  color: var(--ink-muted);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
}
.m-rail-more .cat-icon { font-size: 15px; color: var(--accent); }
.m-rail-more:hover { color: var(--ink); }
.m-rail-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }
/* Phrase de catégorie : utile sur grand écran, retirée sur mobile où elle
   repousse le contenu. */
@media (max-width: 559px) {
  .m-rail-blurb { display: none; }
}

.m-page-chevron {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  cursor: pointer;
}
.m-page-chevron:disabled { color: var(--ink-subtle); opacity: 0.3; cursor: default; }
.m-page-chevron-left svg { display: block; }
.m-page-chevron-right svg { display: block; }

.m-page-nav-bottom[hidden] { display: none; }
.m-page-nav-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* Même distance (36px) que sous-titre → 1ère card (voir #m-feed-section
     .m-card-grid juste au-dessus) : ici pas de padding-bottom équivalent à
     celui de #m-feed-intro à soustraire, juste le gap flex du conteneur
     (--stack-gap) à ramener à 36px. */
  margin-top: calc(36px - var(--stack-gap));
}

.m-card {
  position: relative;
  grid-column: span 6;
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
}
/* Bouton principal de la card (audit P0-02) : couvre toute la
   card, nommé par le titre ; le logo source et le favori passent au-dessus
   (z-index 2). Anneau de focus dessiné à l'intérieur de la card : l'overflow
   des rails le couperait s'il débordait. */
.m-card-open {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  background: none;
  border: 0;
  border-radius: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.m-card-open:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.m-card-visual {
  position: relative;
  width: 100%;
  /* Hauteur FIXE (pas aspect-ratio) — demande explicite après retour
     utilisateur : les posters offi.fr (théâtre/cinéma), très hauts en
     format natif, produisaient des cards visiblement plus grandes que les
     autres (ex. les cards Ville de Paris/Exposition Alula) malgré
     object-fit:cover, l'aspect-ratio proportionnel ne suffisait pas à
     garantir une taille identique perçue. Une hauteur en dur élimine toute
     variation, quels que soient la source/le format de l'image ou la
     largeur de la card (1/2/3 colonnes selon le breakpoint). */
  /* 168px : plus de contenu par écran, images toujours dominantes. */
  height: 168px;
  /* Noir pur (--canvas), pas --surface-2 (gris) — même fond que le body et
     la carte MapLibre, demande explicite (partie haute de la card, visible
     quand il n'y a pas de photo). */
  background: var(--canvas);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 40px;
}
.m-card-visual img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* offi_* uniquement (POSTER_SOURCES, mobile-app.js) : image entière visible
   (contain) sur un fond flouté agrandi de la même photo, plutôt qu'une
   simple couleur plate, pour combler l'espace laissé vide par le contain
   dans une boîte quasi carrée. Les autres sources ne portent pas ces
   classes et gardent .m-card-visual img (cover) sans changement. */
.m-card-visual-poster { overflow: hidden; }
/* Expo Time Out sans visuel (repérage provisoire) : fond vert d'accent, libellé
   catégorie en noir pour rester lisible dessus. */
.m-card-visual-flag { background: var(--accent); }
.m-card-visual-bg {
  position: absolute;
  inset: 0;
  /* contain, pas cover : même cadrage que .m-card-visual-fg (même image,
     même boîte, même règle de fit) — le flou prolonge donc les vraies
     couleurs de bord de l'affiche. Avec cover, ce calque échantillonnait
     une bande horizontale du milieu vertical de l'image (recadrage
     totalement différent de ce qu'affiche le contain au premier plan),
     d'où des couleurs de flou sans rapport avec les bords réels visibles. */
  background-size: contain;
  background-position: center;
  filter: blur(20px);
  /* scale() nettement > 1 : contain laisse un déficit sur l'axe non
     contraint (jusqu'à ~50% selon le ratio de l'affiche, voir les échantillons
     offi.fr testés) — il faut agrandir suffisamment pour que ce calque
     déborde de la boîte sur CET axe aussi, sinon la couleur de fond plate
     (var(--surface-2)) resterait visible dans les coins. */
  transform: scale(1.5);
}
/* Spécificité (0,2,0) > .m-card-visual img (0,1,1) : passe cette image en
   contain sans toucher à la règle de base dont dépendent toutes les autres
   sources. position:relative (pas static) est nécessaire pour que cette
   image soit peinte dans le même groupe d'empilement que .m-card-visual-bg/
   .m-card-tag/.m-card-source-badge (tous position:absolute) — sinon, en
   restant "static", elle serait peinte AVANT eux quel que soit l'ordre DOM,
   et le fond flou (positionné) la recouvrirait entièrement. */
.m-card-visual-poster .m-card-visual-fg {
  position: relative;
  object-fit: contain;
  /* Masque en dégradé radial : l'image nette reste pleinement opaque au
     centre puis s'estompe vers ses bords, qui se fondent progressivement
     dans .m-card-visual-bg (flou) en dessous, plutôt qu'une jonction nette
     entre les deux calques. Taille explicite "75% 75%" (pas un mot-clé
     ellipse closest-side/farthest-corner) : les pourcentages sont relatifs
     à la distance centre→bord sur chaque axe, ce qui permet de régler le
     diamètre de l'ellipse indépendamment des stops de couleur — ajustée à
     l'œil avec black 50%/transparent 70% (calculés sur ce rayon
     explicite). */
  -webkit-mask-image: radial-gradient(75% 75% at center, black 50%, transparent 70%);
  mask-image: radial-gradient(75% 75% at center, black 50%, transparent 70%);
}
/* Matchs (tout le sport, jamais de logo de club) : "PSG vs. MAR" centré sur le fond noir du
   visuel, "vs." en retrait. Carte : 24 px (barème, nom sur carte sans photo),
   tient dans une carte en 2 colonnes ; fiche : taille des titres de page. */
.m-card-matchup,
.m-detail-matchup {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--ink);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.m-detail-matchup {
  gap: 12px;
  font-size: clamp(28px, 7.5vw, 34px);
}
/* Photo du sport en fond (sports-cat/), voile sombre pour garder "PSG vs.
   MAR" lisible sur une photo chargée (filet de but, mêlée). */
.m-card-matchup--photo,
.m-detail-matchup--photo {
  background:
    linear-gradient(rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.45)),
    var(--matchup-photo) center / cover no-repeat;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
}
.m-card-matchup--photo .m-card-matchup-vs,
.m-detail-matchup--photo .m-detail-matchup-vs { color: rgba(255, 255, 255, 0.75); }
.m-card-matchup-vs,
.m-detail-matchup-vs {
  color: var(--ink-subtle);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
}
/* Rail = une seule catégorie, déjà nommée dans son titre : l'icône
   haut-gauche y est redondante (audit P1-05) et chevaucherait le
   badge de droite (« Fin demain »...) sur les cartes étroites. Gardée dans
   les grilles, qui peuvent mélanger plusieurs catégories. */
.m-rail .m-card-tag-emoji { display: none; }
/* Grilles : icône en bas-gauche du visuel, en face du logo source — en
   haut, le badge haut-droit (« Fin aujourd'hui »...) la recouvrirait sur
   les cartes étroites. */
.m-card-grid .m-card-tag-emoji { top: auto; bottom: 10px; }
.m-card-tag {
  position: absolute;
  top: 8px; left: 8px;
  /* Chip translucide sombre + hairline plutôt qu'un chip clair — reste
     lisible sur n'importe quelle photo tout en restant dans le langage
     sombre du reste de l'app (le clair "flottant sur photo" de l'ancien
     design tranchait avec le fond noir partout ailleurs). */
  background: var(--glass);
  border: 1px solid var(--hairline-strong);
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.4px;
  padding: 4px 9px;
  border-radius: var(--r-pill);
  color: var(--ink);
  /* .m-card-tag-right affiche un texte à casse normale ("Demain", "3
     mois") : les majuscules forcées gardent les badges de même taille
     apparente (sinon l'écriture normale laisse un espace visuel qui donne
     l'impression d'un badge plus petit). */
  text-transform: uppercase;
}
/* Badge catégorie de la grille — emoji seul, sans libellé texte (retour
   utilisateur : le nom de catégorie encombre le badge). Émoji agrandi
   pour rester lisible une fois le texte 8px retiré ; le reste (fond,
   bordure, position) vient toujours de .m-card-tag. La feuille détail
   (#m-detail-tag) fait de même. */
.m-card-tag-emoji {
  font-size: 13px;
  padding: 5px 8px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--accent);
}
/* Sous-catégorie (radar_category_1, établissements SIRENE) : puce blanche
   dans la fiche détail seulement (pas sur le visuel des cards), devant les
   badges de durée (#m-detail-timing), même gabarit que .m-timing-*. */
.m-detail-subcat {
  font-size: 10px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  background: var(--white);
  color: var(--canvas);
}
/* Duplicat de .m-card-tag (même gabarit), même alignement (top:10px) mais
   côté droit — couleurs reprises de l'ancienne .m-timing-closing (rouge)
   pour signaler visuellement "ferme dans". */
/* Badge de durée : casse normale et pilule resserrée — moins de masse
   qu'en capitales (« FIN AUJOURD’HUI »), toujours lisible
   (blanc sur --danger/--success, ≥ 4,5:1). */
.m-card-tag-right {
  left: auto;
  right: 8px;
  font-size: 10px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  padding: 3px 7px;
  background: var(--danger);
  border-color: var(--danger);
  color: var(--white);
}
.m-card-tag-right-open {
  background: var(--success);
  border-color: var(--success);
  color: var(--white);
}
.m-card-source-badge {
  /* Bas-droit du visuel. */
  position: absolute;
  /* 10px du bord : la zone tactile de 44px (::before, -10px) tient alors
     entière dans la card, qui couperait ce qui dépasse. */
  bottom: 10px; right: 10px;
  z-index: 2;
  /* 24px : une métadonnée, pas un bouton. Zone tactile
     portée à 44px par ::before (lien vers la source). */
  width: 24px; height: 24px;
  border-radius: var(--r-xs);
  overflow: hidden;
  /* Fond blanc gardé volontairement : ce sont des logos de marque externes
     (SortirAParis, LeBonbon...) conçus pour un fond clair, pas un élément
     de chrome de l'app — les repeindre casserait leur lisibilité. */
  background: var(--white);
  border: none;
}
/* cover, sans padding : le rectangle du badge est volontairement un peu
   plus petit que le carré du logo, qui le remplit entièrement — ses coins
   débordent donc légèrement et se font tronquer par le bord arrondi
   (overflow:hidden) du badge. */
/* object-fit:contain (pas cover) : la plupart des logos sources sont
   quasi carrés donc ça ne change rien pour eux, mais le logo TMDB est un
   logotype large (viewBox ~273x35) qu'un cover découperait illisible. */
.m-card-source-badge img { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: inherit; }
a.m-card-source-badge { overflow: visible; }
a.m-card-source-badge::before { content: ''; position: absolute; inset: -10px; }
a.m-card-source-badge:focus-visible { outline-offset: 2px; }
.m-card-body { padding: 10px 12px 12px; }
.m-card-title {
  font-size: 13px; /* 14px jugé trop gros */
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.1px;
  color: var(--ink);
  margin-bottom: 4px;
  /* Plafonné à 2 lignes — même raison que .editorial-card-title (desktop,
     main.css) : un titre long (ex. offi.fr) grandissait toute la card,
     cassant l'uniformité de la grille. min-height réserve TOUJOURS la
     place de 2 lignes (1.35 × 2), même pour un titre court sur 1 seule
     ligne — sinon le repère 📍 qui suit n'est pas à la même hauteur d'une
     card à l'autre (retour utilisateur, cf. screenshot). */
  min-height: 2.7em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.m-card-footer {
  display: flex;
  align-items: center;
  min-height: 24px;
}
.m-card-postal {
  font-size: 12px;
  color: var(--white);
  /* Nom de lieu des expos (voir cardHtml) : une seule ligne, tronquée
     avant le logo source plutôt que de le pousser hors de la card. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.m-card-meta { display: flex; gap: 8px; flex-wrap: wrap; }
.m-timing-open, .m-timing-closing, .m-timing-upcoming {
  font-size: 10px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: var(--r-pill);
}
.m-timing-open    { background: var(--success); color: var(--white); }
.m-timing-closing { background: var(--danger); color: var(--white); }
/* Fin dans 4 à 30 jours : neutre ; le rouge reste aux 3 derniers jours (mobile-timing.js). */
.m-timing-calm, .m-card-tag-right-calm { background: var(--surface-3); border-color: var(--hairline-strong); color: var(--white); box-shadow: inset 0 0 0 1px var(--hairline-strong); }
/* Même vert que le badge haut-droit de la card (.m-card-tag-right-open),
   pas l'accent clair — demande explicite. */
.m-timing-upcoming { background: var(--success); color: var(--white); }

.m-card-empty { opacity: 0.7; cursor: default; }
.m-card-empty-visual {
  font-size: 12px;
  color: var(--ink-muted);
  text-align: center;
  padding: 0 24px;
  line-height: 1.4;
  background: var(--veil-3);
}
/* Sous-squelettes de la card vide — mêmes classes de positionnement que les
   vrais éléments (.m-card-tag/.m-card-tag-right pour top-left/top-right),
   juste vidées de contenu et grisées. Sélecteur composé pour primer sur le
   fond/texte de .m-card-tag et .m-card-tag-right (rouge) sans dépendre de
   l'ordre des règles. */
.m-card-tag.m-card-empty-tag {
  width: 46px;
  height: 17px;
  padding: 0;
  background: var(--veil-6);
  border-color: transparent;
  color: transparent;
}
.m-card-empty-title {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.m-card-empty-line,
.m-card-empty-postal {
  height: 11px;
  border-radius: var(--r-xs);
  background: var(--veil-6);
}
.m-card-empty-line:last-child { width: 60%; }
.m-card-empty-postal { width: 34%; }

/* Intro — titre (suivi du nombre d'événements, ex. "Sur le radar
   aujourd'hui: 80") + sous-texte, au-dessus de la grille
   (mobile-app.js::renderFeedIntro), contenu changeant par onglet actif.
   Inspiré du site mobile OFFI (demande explicite) : réserve environ la
   moitié de l'écran par défaut en dessous du chrome fixe (--chrome-h,
   mesuré en JS), contenu centré verticalement dans cet espace. Fait partie
   du flux normal du document (pas de position fixed/sticky) donc défile et
   disparaît naturellement avec le reste du feed — aucune logique JS de
   scroll nécessaire pour ça. */
#m-feed-intro[hidden] { display: none; }
#m-feed-intro {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  gap: clamp(8px, 2.4vw, 12px);
  padding-top: 6px;  /* ex-clamp(12px, 4vw, 20px), avant la ligne Filtres */
  text-align: center;
  padding-bottom: 12px;
}
.m-feed-intro-title {
  font-family: 'Sora', sans-serif;
  /* Interligne resserré ; le plus gros texte de la page. */
  font-size: clamp(22px, 6.2vw, 27px);
  font-weight: 600;
  letter-spacing: -0.5px;
  color: var(--ink);
  line-height: 1.05;
  /* #m-feed-intro est en flex column + align-items:center (pas stretch) :
     le <h2> se dimensionne à son contenu (shrink-to-fit), pas à la largeur
     du conteneur — sans max-width, un titre "limite" (ex. "Repéré
     aujourd'hui: 58") peut tomber pile sous le seuil de retour à la ligne
     et s'afficher en UNE ligne quasi bord-à-bord, visuellement différent
     des titres qui wrappent proprement sur 2 lignes équilibrées (ex. "Sur
     le radar aujourd'hui") ou qui tiennent clairement sur une ligne courte
     (ex. "Ouvert aujourd'hui: 0") — repéré sur l'onglet Temporaires,
     retour utilisateur direct. max-width force un rythme plus prévisible
     (repasse en 2 lignes équilibrées dès que ça approche la limite) sans
     affecter les titres nettement courts ou longs.
     Décalage vertical du bloc désormais géré par `padding-top` sur
     #m-feed-intro (voir plus haut, plus de margin-top négatif ici — le
     `justify-content:flex-start` remplace le centrage, donc plus besoin de
     "tricher" avec une marge négative pour remonter le bloc). */
  /* min(330px, 92%) force le retour à la ligne pour un titre à la limite
     (ex. "Repéré aujourd'hui: 58" ≈ 342px en une ligne, quasi bord-à-bord
     — signalé par retour utilisateur sur l'onglet Temporaires) sans
     forcer le wrap des titres nettement plus courts (ex. "Ouvert
     aujourd'hui: 0" ≈ 319px, reste sur 1 ligne) ; ("ch" essayé d'abord —
     écarté : le "0" du Poppins 700 est presque aussi large qu'un em dans
     cette police, 20ch ≈ 360px ne contraignait quasiment rien en
     pratique). text-wrap:balance (Chromium/Safari récents, dégrade
     proprement en wrap normal sinon) choisit un point de coupure équilibré
     plutôt que de laisser un mot isolé sur la 2e ligne (ex. "Ouvert
     aujourd'hui:" / "0" tout seul — repéré en testant un premier seuil
     plus bas). */
  max-width: min(330px, 92%);
  text-wrap: balance;
}
.m-feed-intro-count {
  /* Nombre en accent plein, sans contour/ombre 3D — l'effet "brutalist"
     d'origine n'a pas d'équivalent Linear ; l'accent seul suffit à le
     distinguer du reste du titre. */
  color: var(--accent);
  -webkit-text-stroke: 0;
  text-shadow: none;
}
.m-feed-intro-subtitle {
  font-size: 14px;
  color: var(--ink-muted);
  max-width: 34ch;
  line-height: 1.35;
  min-height: 2.7em;
}

.m-page-indicator {
  font-size: 12px;
  color: var(--ink-subtle);
  text-align: center;
}

/* Squelettes de chargement — réutilise le gabarit de la card "vide"
   (.m-card-empty-visual/.m-card-empty-tag/.m-card-empty-title, voir
   renderFeed) plutôt qu'un squelette dupliqué : mêmes tailles/positions,
   juste un shimmer animé à la place du grisé statique. */
/* Titre de rail du squelette d'accueil : même hauteur que
   l'en-tête réel (zone de 44px du lien « Voir tout »). */
.m-skeleton-rail .m-rail-header { min-height: 44px; }
.m-skeleton-title { width: 132px; height: 22px; border-radius: var(--r-sm); }
.m-skeleton .m-card-empty-visual,
.m-skeleton .m-card-empty-tag,
.m-skeleton .m-card-empty-line,
.m-skeleton .m-card-empty-postal,
.m-skeleton-title,
.m-card-visual-loading {
  background: linear-gradient(90deg, var(--veil-4) 25%, var(--veil-10) 50%, var(--veil-4) 75%);
  background-size: 200% 100%;
  animation: m-shimmer 1.4s infinite;
}
@media (prefers-reduced-motion: reduce) {
  .m-skeleton .m-card-empty-visual, .m-skeleton .m-card-empty-tag,
  .m-skeleton .m-card-empty-line, .m-skeleton .m-card-empty-postal,
  .m-skeleton-title, .m-card-visual-loading { animation: none; }
}
@keyframes m-shimmer {
  0%   { background-position:  200% 0; }
  100% { background-position: -200% 0; }
}

/* ══════════════════════════════════════════
   FEUILLE DÉTAIL — plein écran, glisse depuis le bas
   ══════════════════════════════════════════ */
#m-detail-backdrop {
  position: fixed; inset: 0;
  background: var(--scrim);
  z-index: 200;
  opacity: 0;
  transition: opacity 0.25s ease;
}
#m-detail-backdrop.visible { opacity: 1; }

#m-detail-sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  top: 6vh;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-bottom: none;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  z-index: 201;
  /* Cadre fixe : le défilement est dans #m-detail-scroll. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(100%);
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
}
#m-detail-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding-bottom: env(safe-area-inset-bottom, 20px);
}
#m-detail-sheet.open { transform: translateY(0); }

/* Fermeture : icône Lucide, 44px, posée sur la feuille elle-même (hors du
   conteneur qui défile) : toujours visible et à sa place en défilant. En
   sticky dans la zone qui défile, Safari iOS peut décaler sa zone de
   toucher sur le visuel-lien. */
#m-detail-close {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--glass-soft);
  color: var(--white);
  cursor: pointer;
}
#m-detail-close svg { display: block; }

/* #m-detail-sheet est un <aside>, pas un <section> — la règle
   "section { padding: ... var(--gutter) }" (gouttières) ne s'applique
   donc jamais à lui, et .container seul n'a pas de padding horizontal
   propre (il ne fait que centrer/limiter la largeur). Sans ce padding
   explicite, titre/timing/adresse/lien étaient collés aux bords de la
   feuille. */
.m-detail-body { padding: 20px var(--gutter) 0; gap: 16px; }

#m-detail-visual {
  display: block;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  background: var(--surface-2);
  font-size: 104px;
  cursor: pointer;
  overflow: hidden;
  /* Lien : pas de soulignement ni couleur de lien sur un visuel texte (matchup foot). */
  text-decoration: none;
  color: inherit;
}
#m-detail-visual-media { position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
.m-detail-visual-bg {
  position: absolute;
  inset: 0;
  background-size: contain;
  background-position: center;
  filter: blur(20px);
  transform: scale(1.5);
}
.m-detail-visual-fg {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: contain;
  -webkit-mask-image: radial-gradient(75% 75% at center, black 50%, transparent 70%);
  mask-image: radial-gradient(75% 75% at center, black 50%, transparent 70%);
}
/* Icône de catégorie seule, même rendu que .m-card-tag-emoji. */
#m-detail-tag {
  position: absolute;
  bottom: 12px; left: 14px;
  background: var(--glass);
  border: 1px solid var(--hairline-strong);
  font-size: 16px;
  line-height: 1;
  padding: 6px 10px;
  border-radius: var(--r-pill);
  color: var(--accent);
}
#m-detail-source-badge {
  position: absolute;
  bottom: 14px; right: 14px;
  width: 40px; height: 40px;
  border-radius: var(--r-lg);
  overflow: hidden;
  /* Fond blanc gardé volontairement, même raison que .m-card-source-badge
     plus haut — logo de marque externe, pas du chrome d'app. */
  background: var(--white);
  border: 1px solid var(--white);
}
#m-detail-source-badge img { display: block; width: 100%; height: 100%; object-fit: contain; }

#m-detail-title { font-size: 20px; font-weight: 600; letter-spacing: -0.4px; line-height: 1.3; color: var(--ink); }
.m-detail-timing { display: flex; gap: 8px; flex-wrap: wrap; }
.m-detail-field { font-size: 14px; line-height: 1.6; color: var(--ink-muted); }
#m-detail-real-description { text-align: justify; }
/* Description sport sur 3 lignes (journée / affiche / horaire, voir
   SportsEvent.to_radar_event) : les \n doivent rester des retours à la ligne. */
#m-detail-real-description.is-sport { white-space: pre-line; }
#m-detail-map-wrap { width: 100%; aspect-ratio: 1 / 1; border-radius: var(--r-xl); overflow: hidden; border: 1px solid var(--hairline); }
#m-detail-map { width: 100%; height: 100%; }
/* Boutons + / − de la mini-carte (MapLibre, 29px d'office) : cibles de 44px. */
#m-detail-map .maplibregl-ctrl-group button { width: 44px; height: 44px; }
/* Distance et « M'y rendre » au-dessus de la mini-carte (mobile-geo.js) :
   deux boutons texte accent, sans cadre. */
#m-detail-route { display: flex; align-items: center; gap: 12px; font-size: 14px; color: var(--ink-muted); }
#m-detail-route[hidden], #m-detail-walk:has(#m-detail-dist:empty) { display: none; }
#m-detail-walk { display: inline-flex; align-items: center; gap: 6px; }
#m-detail-locate, #m-detail-route-btn {
  min-height: 44px;
  padding: 0;
  background: none;
  border: none;
  color: var(--accent);
  font: 600 14px/1.3 'Sora', sans-serif;
  text-align: left;
  cursor: pointer;
}
#m-detail-locate:disabled { color: var(--ink-muted); cursor: default; }
/* Lien vers la source (Site officiel…), même voix que « M'y rendre ». */
#m-detail-source-link a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--accent);
  font: 600 14px/1.3 'Sora', sans-serif;
  text-decoration: none;
}
#m-detail-source-link a:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }
#m-detail-route-btn { flex: none; margin-left: auto; display: inline-flex; align-items: center; gap: 6px; }
#m-detail-route .cat-icon { font-size: 18px; }
/* Fenêtres centrées (aide localisation, M'y rendre) : au-dessus de la fiche
   (200) et du tiroir (250), sous la connexion (300). */
.m-dialog {
  position: fixed;
  inset: 0;
  z-index: 280;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: var(--scrim);
}
.m-dialog[hidden] { display: none; }
.m-dialog-card {
  width: min(360px, 100%);
  color: var(--ink);
  font-size: 14px;
  line-height: 1.5;
}
.m-dialog-title { font-size: 20px; font-weight: 600; }
.m-dialog-card p { color: var(--ink-muted); }
.m-dialog-actions { display: flex; justify-content: flex-end; gap: 12px; }
.m-dialog-cancel {
  min-height: 44px;
  padding: 0 20px;
  background: none;
  color: var(--ink);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  font: 600 14px/1 'Sora', sans-serif;
  cursor: pointer;
}
#m-geo-help-steps { display: grid; gap: 10px; margin: 0; padding-left: 20px; }
/* Applis de navigation : icône officielle (App Store) + nom. */
/* Fenêtre compacte, titre centré. */
.m-route-card { width: min(260px, 100%); gap: 12px; padding: 16px; }
.m-route-card .m-dialog-title { font-size: 16px; text-align: center; }
#m-route-apps { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
#m-route-apps a {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 6px 10px;
  background: var(--surface-2);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-md);
  color: var(--ink);
  font: 600 14px/1 'Sora', sans-serif;
  text-decoration: none;
}
/* Coins d'icône d'appli iOS (≈ 22 % du côté). */
#m-route-apps img { width: 28px; height: 28px; border-radius: 6px; }
/* Cinéma : bande-annonce YouTube à la place de la carte — même traitement
   visuel (coins arrondis, hairline) que #m-detail-map-wrap, ratio 16:9
   (format vidéo) plutôt que 1:1 (format carte). */
#m-detail-trailer-wrap { width: 100%; aspect-ratio: 16 / 9; border-radius: var(--r-xl); overflow: hidden; border: 1px solid var(--hairline); }
#m-detail-trailer { width: 100%; height: 100%; border: 0; }
#m-detail-link { font-size: 14px; font-weight: 600; color: var(--accent); text-decoration: none; }
/* ══════════════════════════════════════════
   BREAKPOINTS — mobile grows up, never shrinks down
   ══════════════════════════════════════════ */
@media (min-width: 560px) {
  .m-rail { margin-right: 0; padding-right: 0; }   /* pas d'amorce de carte */
  .m-card { grid-column: span 4; }   /* 3×N grid */
  .m-rail .m-card { flex-basis: calc((100% - 2 * var(--card-gap)) / 3); width: calc((100% - 2 * var(--card-gap)) / 3); }
}
@media (min-width: 900px) {
  .m-card { grid-column: span 3; }   /* 4×N grid */
  .m-rail .m-card { flex-basis: calc((100% - 3 * var(--card-gap)) / 4); width: calc((100% - 3 * var(--card-gap)) / 4); }
  /* Ancré au bord droit (audit P0-04) : avec left:50% la fiche
     dépasserait de 48px à 1024px (fermeture hors écran) et flotterait à 1280+. */
  #m-detail-sheet { left: auto; right: 0; width: min(560px, 100vw); transform: translate(100%, 0); top: 0; border-radius: 0; }
  #m-detail-sheet.open { transform: translate(0, 0); }
}

/* Mention "Image d'illustration" sur les images de repli (fallbackLabelHtml,
   utils.js) : en bas à gauche du visuel, sans capter les clics (la card/la
   photo détail restent cliquables). */
.m-visual-fallback-label {
  position: absolute;
  left: 8px;
  bottom: 8px;
  z-index: 1;
  pointer-events: none;
  white-space: nowrap;
  /* Fond sombre translucide : lisible sur toute photo (un blanc à 35 % sans
     fond plafonne à 3:1), discret. */
  background: var(--glass);
  color: var(--ink-muted);
  font-size: 10px;
  font-weight: 500;
  padding: 3px 7px;
  border-radius: var(--r-xs);
}
/* Grille multi-catégories : l'icône de catégorie occupe le coin bas-gauche. */
.m-card-grid:not(.m-card-grid--single) .m-visual-fallback-label { left: 46px; max-width: calc(100% - 86px); overflow: hidden; text-overflow: ellipsis; }
.m-card-grid--single .m-card-tag-emoji { display: none; }
/* Fiche : à droite de l'icône de catégorie (#m-detail-tag), centrée sur elle. */
#m-detail-visual .m-visual-fallback-label { left: 60px; bottom: 18px; }

/* ══════════════════════════════════════════
   PAGE DE CONNEXION — plein écran
   par-dessus l'app sur /connexion (mobile-login.js). Logo + fermer en haut,
   formulaire aligné à gauche : titre, Google, séparateur « ou », email,
   mot de passe (œil), bouton accent.
   ══════════════════════════════════════════ */
/* Mesures calées sur beesly.fr/login : contenu en haut (pas
   centré), titre 30px. Rectangles à angles droits et 38px de haut (champs,
   Google, bouton), à la demande de l'utilisateur. Plein écran à toutes les largeurs
   (formulaire limité à 400px), un seul défilement : celui de #m-login, le
   body étant bloqué dessous. Marges 24px + safe areas (encoche, barre iOS). */
#m-login {
  --login-secondary: #a9aeb7; /* entre --ink-subtle et --ink-muted : lisible sur noir */
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  padding: env(safe-area-inset-top) calc(24px + env(safe-area-inset-right))
           calc(32px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left));
  background: var(--canvas);
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
}
#m-login[hidden] { display: none; }
body.m-login-open { overflow: hidden; }
.m-login-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  padding: 8px 0;
}
.m-login-logo {
  display: block;
  height: 17px; /* 20px avec l'ancien logo, même taille de lettres (logo B) */
  width: auto;
}
/* Simple icône (22px), zone de clic 44x44 ; marge négative pour aligner
   le X sur le bord droit du formulaire. */
#m-login-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: -11px;
  background: none;
  border: none;
  color: var(--login-secondary);
  cursor: pointer;
  transition: color 0.15s;
}
#m-login-close:hover { color: var(--ink); }
#m-login-close:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; color: var(--ink); }
#m-login-form {
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  padding-top: 32px;
  display: flex;
  flex-direction: column;
}
#m-login-form h1 {
  margin: 0 0 8px;
  font-size: 30px;
  font-weight: 500;
  letter-spacing: -0.75px;
  line-height: 36px;
  color: var(--ink);
}
.m-login-lead {
  margin: 0 0 32px;
  font-size: 16px;
  line-height: 24px;
  color: var(--login-secondary);
}
.m-login-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 38px;
  padding: 0 16px;
  background: none;
  border: 1px solid var(--hairline-strong);
  border-radius: 0;
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.m-login-google svg { width: 18px; height: 18px; }
.m-login-google:hover { background: var(--surface-1); border-color: var(--hairline-tertiary); }
.m-login-sep {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 24px 0 27px;
  font-size: 12px;
  line-height: 16px;
  color: var(--login-secondary);
}
.m-login-sep::before,
.m-login-sep::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hairline);
}
#m-login-form label {
  margin-bottom: 11px;
  font-size: 14px;
  font-weight: 500;
  line-height: 18px;
  color: var(--ink-muted);
}
.m-login-pass-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 11px;
}
#m-login-form .m-login-pass-head label { margin-bottom: 0; }
#m-login-forgot {
  padding: 0;
  background: none;
  border: none;
  font-size: 12px;
  line-height: 16px;
  color: var(--login-secondary);
  cursor: pointer;
}
#m-login-forgot:hover { color: var(--ink); }
#m-login-form input {
  width: 100%;
  height: 38px;
  margin-bottom: 21px;
  padding: 0 12px;
  background: none;
  border: 1px solid var(--hairline-strong);
  border-radius: 0;
  color: var(--ink);
  font: inherit;
  font-size: 14px; /* < 16px : iOS zoome au focus, choix assumé */
  transition: border-color 0.15s, box-shadow 0.15s;
}
/* Inscription : prénom et nom côte à côte ; année de naissance en liste
   déroulante (roue native sur mobile), mêmes dimensions que les champs. */
.m-login-names {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.m-login-field { min-width: 0; }
#m-login-form select {
  width: 100%;
  height: 38px;
  margin-bottom: 21px;
  padding: 0 12px;
  background: var(--canvas);
  border: 1px solid var(--hairline-strong);
  border-radius: 0;
  color: var(--ink);
  color-scheme: dark;
  font: inherit;
  font-size: 14px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
#m-login-form select:invalid { color: var(--login-secondary); }
#m-login-form select:focus,
#m-login-form input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(182, 242, 58, 0.12);
}
.m-login-pass { position: relative; }
.m-login-pass input { padding-right: 40px !important; }
#m-login-eye {
  position: absolute;
  top: 0;
  right: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 38px;
  background: none;
  border: none;
  color: var(--login-secondary);
  cursor: pointer;
}
#m-login-eye svg { width: 16px; height: 16px; }
#m-login-eye:hover { color: var(--ink); }
#m-login-eye .m-eye-off,
#m-login-eye[aria-pressed="true"] .m-eye-open { display: none; }
#m-login-eye[aria-pressed="true"] .m-eye-off { display: block; }
/* Erreur : texte clair et filet rouge — le rouge seul en
   texte sur le noir ne fait que 4,3:1. */
#m-login-error {
  margin: -8px 0 16px;
  padding-left: 10px;
  border-left: 3px solid var(--danger);
  font-size: 13px;
  line-height: 18px;
  color: var(--ink);
}
#m-login-error[hidden] { display: none; }
/* Règles du mot de passe, mode inscription seulement (mobile-login.js). */
#m-login-hint {
  margin: -8px 0 16px;
  font-size: 13px;
  line-height: 18px;
  color: var(--login-secondary);
}
#m-login-hint[hidden] { display: none; }
/* Blocs masqués selon le mode de la page (mobile-login.js) :
   même disposition en colonne que #m-login-form, dont ils enveloppent les
   champs (sinon les labels inline perdent leur marge du bas). */
#m-login-alt,
#m-login-email-field,
#m-login-pass-field,
#m-login-pass2-field,
#m-login-profile-fields,
.m-login-field {
  display: flex;
  flex-direction: column;
}
#m-login-alt[hidden],
#m-login-email-field[hidden],
#m-login-pass-field[hidden],
#m-login-pass2-field[hidden],
#m-login-profile-fields[hidden],
#m-login-switch-line[hidden],
#m-login-submit[hidden] { display: none; }
/* Message après envoi d'un lien (inscription, renvoi, mot de passe oublié)
   ou lien invalide ; « Renvoyer l'email » en bouton secondaire, comme Google. */
#m-login-notice {
  margin: 0 0 24px;
  font-size: 15px;
  line-height: 22px;
  color: var(--ink);
}
#m-login-notice[hidden] { display: none; }
#m-login-resend {
  height: 38px;
  margin-bottom: 8px;
  padding: 0 16px;
  background: none;
  border: 1px solid var(--hairline-strong);
  border-radius: 0;
  color: var(--ink);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
#m-login-resend:hover { background: var(--surface-1); border-color: var(--hairline-tertiary); }
#m-login-resend:disabled { opacity: 0.6; cursor: default; }
#m-login-resend[hidden] { display: none; }
#m-login-submit {
  height: 38px;
  padding: 0 16px;
  background: var(--accent);
  border: none;
  border-radius: 0;
  color: var(--on-accent);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(182, 242, 58, 0.10);
  transition: background 0.15s;
}
#m-login-submit:hover { background: var(--accent-hover); }
#m-login-submit:disabled { opacity: 0.6; cursor: default; }
/* Lien texte sous l'action principale (comme beesly.fr/login). */
.m-login-signup-line {
  margin: 24px 0 0;
  text-align: center;
  font-size: 14px;
  line-height: 20px;
  color: var(--login-secondary);
}
#m-login-signup {
  padding: 0;
  background: none;
  border: none;
  font: inherit;
  font-weight: 500;
  color: var(--accent);
  cursor: pointer;
}
#m-login-signup:hover { color: var(--accent-hover); text-decoration: underline; }

/* Bouton enregistrer (mobile-saved.js) — en bas à droite du
   footer de carte, poussé par margin-left:auto. Zone de clic agrandie par
   padding + marge négative sans décaler la ligne. */
.m-bookmark {
  /* Au-dessus du bouton principal de la card ; 18 + 2×13 = 44px de cible,
     marges négatives pour ne pas grandir la card. */
  position: relative;
  z-index: 2;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  /* -12px à droite et en bas : la card (overflow:hidden, 12px de padding)
     couperait le dernier pixel de la cible. */
  margin: -13px -12px -12px auto;
  padding: 13px;
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  color: var(--ink);
  font-size: 18px;
  line-height: 0;
  cursor: pointer;
}
.m-bookmark .cat-icon { vertical-align: 0 !important; }
.m-bookmark.is-saved { color: var(--danger); }
.m-bookmark.is-saved .cat-icon { fill: currentColor; }
.m-card-footer .m-card-postal { margin-right: 8px; }

/* Fiche détail : bouton enregistrer à droite du titre. */
.m-detail-title-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  /* Place de la croix (#m-detail-close, 44px à 12px du bord) : fiche défilée,
     elle recouvrait Enregistrer et un tap destiné au favori fermait la fiche. */
  padding-right: 48px;
}
.m-detail-title-row #m-detail-title { flex: 1; min-width: 0; }
/* Partager à gauche du bookmark, mêmes taille et zone de clic. */
.m-detail-actions { display: flex; align-items: center; margin: -6px -8px 0 0; }
#m-detail-bookmark .m-bookmark { font-size: 24px; margin: 0; padding: 10px; }
/* Le dessin share-2 remplit ~21,5 des 24 unités du viewBox contre ~19,4 pour
   bookmark : icône réduite au prorata (24 × 19,4/21,5) pour que les deux
   aient la même hauteur visible, padding élargi d'autant (zone de clic
   inchangée, 40px). */
.m-share {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 11.2px;  /* 21,6 + 2×11,2 = 44px de cible */
  min-width: 44px;
  min-height: 44px;
  background: none;
  border: none;
  color: var(--ink);
  font-size: 21.6px;
  line-height: 0;
  cursor: pointer;
}
.m-share .cat-icon { vertical-align: 0 !important; stroke-width: 1.667; } /* 1.5 × 24/21,6 : même épaisseur de trait que le bookmark */

/* Badge « Terminé » des favoris dont la date de fin est passée
   (mobile-timing.js::endedText). */
.m-card-tag-right-ended { background: var(--surface-3); color: var(--ink-subtle); border-color: var(--hairline-strong); }
.m-timing-ended {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  color: var(--ink-subtle);
  font-size: 12px;
  font-weight: 600;
}

/* ══════════════════════════════════════════
   PIED DE PAGE — sous la liste, trait léger pleine largeur
   au-dessus (repris de beesly.fr) ; copyright + liens légaux. Caché en vue
   carte (plein écran). La pastille Carte remonte au-dessus de lui.
   ══════════════════════════════════════════ */
#m-footer {
  border-top: 1px solid var(--hairline);
  padding: 24px var(--gutter) calc(24px + env(safe-area-inset-bottom));
  color: var(--ink-subtle);
  font-size: 13px;
}
body:has(#m-map-section:not([hidden])) #m-footer { display: none; }
.m-footer-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 8px 24px;
  max-width: 1280px;
  margin: 0 auto;
}
.m-footer-inner nav { display: flex; gap: 20px; }
.m-footer-inner a { color: var(--ink-subtle); text-decoration: none; padding: 6px 0; } /* zone de clic ≥ 24 px */
.m-footer-inner a:hover { color: var(--ink); }
@media (max-width: 559px) {
  .m-footer-inner { flex-direction: column; text-align: center; }
}

/* ══════════════════════════════════════════
   PAGE ÉVÉNEMENT ET LIEU (/evenement/…, /lieu/… ; backend/seo.py::event_page,
   plan SEO lot 4) — l'article servi par le serveur remplace le fil ; l'app
   reste chargée derrière (en-tête, menu, onglets). Quitter la page pour une
   vue rend le fil (mobile-router.js::leaveServerPage).
   ══════════════════════════════════════════ */
body.m-page-event #m-filter-bar,
body.m-page-event #m-feed-section,
body.m-page-event #m-map-section,
body.m-page-event #m-map-fab { display: none; }
.m-event-page {
  max-width: 720px;
  margin: 0 auto;
  padding: 20px var(--gutter) 40px;
  color: var(--ink);
}
.m-event-crumbs {
  margin-bottom: 16px;
  font-size: 13px;
  color: var(--ink-subtle);
}
.m-event-crumbs a { color: inherit; text-decoration: none; }
.m-event-crumbs a:hover { text-decoration: underline; }
.m-event-banner {
  display: inline-block;
  margin: 0 0 16px;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--surface-3);
  font-size: 13px;
  font-weight: 600;
}
.m-event-img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  margin-bottom: 20px;
  border-radius: 12px;
  background: var(--surface-2);
}
.m-event-page h1 {
  margin: 0 0 8px;
  font-size: clamp(24px, 6vw, 34px);
  font-weight: 700;
  line-height: 1.15;
}
.m-event-meta { margin: 0 0 4px; font-size: 15px; color: var(--ink-muted); }
.m-event-badge {
  display: inline-block;
  margin-right: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--success);
  color: var(--white);
  font-size: 12px;
  font-weight: 600;
}
.m-event-dates { margin: 0 0 20px; font-size: 15px; font-weight: 600; }
.m-event-desc { margin: 0 0 24px; font-size: 16px; line-height: 1.55; }
.m-event-page h2 { margin: 28px 0 10px; font-size: 18px; font-weight: 600; }
.m-event-address { margin: 0 0 8px; color: var(--ink-muted); }
.m-event-source,
.m-event-more { color: var(--accent); font-weight: 600; text-decoration: none; }
.m-event-source:hover,
.m-event-more:hover { text-decoration: underline; }
.m-event-related { margin: 0; padding: 0; list-style: none; }
.m-event-related li { padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.m-event-related a { color: var(--ink); font-weight: 500; text-decoration: none; }
.m-event-related a:hover { text-decoration: underline; }
.m-event-related span { display: block; margin-top: 2px; font-size: 13px; color: var(--ink-subtle); }

/* Liste servie par le serveur avec une vue (backend/seo.py::_list_block, plan
   SEO lot 6) : pour les robots sans JavaScript ; l'app la remplace. */
.m-js .m-server-list { display: none; }
.m-server-list { padding: 16px var(--gutter); color: var(--ink); }
.m-server-list li { margin: 8px 0; }
.m-server-list a { color: var(--ink); }
.m-server-list span { color: var(--ink-subtle); font-size: 13px; }
