/* FICHIER GÉNÉRÉ par `app:assets:bundle-css` — NE PAS ÉDITER À LA MAIN.
   Source : app.imports.css (+ ses @import). Régénérer après toute modif du CSS scindé. */
/*
app.imports.css

QUOI : MANIFESTE d'ordre du CSS du site public — liste des @import des blocs scindés, dans l'ordre EXACT
       de cascade (l'ordre fait partie de l'invariant I4). N'est PAS servi directement.

COMMENT : `app:assets:bundle-css` (backlog P2-2) lit ce manifeste et inline récursivement ses @import
          pour produire la feuille servie `app.css` (bundle, 1 requête au lieu d'une cascade). Éditer ce
          manifeste (ordre) ou les blocs scindés, puis relancer la commande.

OÙ : Consommé par `BundleCssCommand` ; le bundle généré `app.css` est chargé par tous les templates
     publics via asset('styles/app.css').

POURQUOI : Scission §4.2 (aucun fichier source > 500 lignes) + résorption de la cascade @import (P2-2)
           sans bundler Node (I3 préservé).
*/

/*
tokens.css

QUOI : Variables globales :root — palette marine/sable/corail, rayons, ombres, espacements, polices (import Google Fonts) et échelle typo fluide.

COMMENT : Copie telle quelle des lignes 12–81 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/




:root {
    --c-marine: #0a3d62;
    --c-mer: #1e6091;
    --c-mer-clair: #4a90b8;
    --c-sable: #f5e9d4;
    --c-sable-clair: #faf4e8;
    --c-corail: #e07856;
    --c-corail-fonce: #c6603c;
    --c-texte: #2c3e50;
    --c-texte-doux: #5a6b7a;
    --c-blanc: #ffffff;
    /* Retouche contraste 2026-07-10 : bordure sable approfondie (#e8dec8 → #ddcba4)
       pour rendre les délimitations perceptibles sur fond sable et surfaces blanches. */
    --c-bordure: #ddcba4;
    --c-focus: rgba(30, 96, 145, 0.35);

    /* ── Surfaces SÉMANTIQUES (2026-07-17) ────────────────────────────────
       `--c-blanc` reste volontairement BLANC en mode sombre (texte blanc sur pastilles
       colorées) : une surface qui consomme `--c-blanc` doit donc être ré-énumérée à la main
       dans theme-dark.css pour ne pas rester blanche la nuit. Tout oubli = carte blanche en
       mode sombre (bug constaté sur `.contributor-event`).
       Ces deux jetons sont la sortie de secours : ils portent l'INTENTION « surface de
       contenu » et sont remappés en encre par theme-dark.css — un composant qui les consomme
       suit les deux thèmes sans ligne de code supplémentaire. Même logique que les `--adm-*`
       du back-office. À préférer à `--c-blanc` pour tout fond de carte/panneau.
         --c-surface   : carte / panneau posé sur le fond de page
         --c-surface-2 : surface secondaire (encart DANS une carte, champ de formulaire) */
    --c-surface: #ffffff;
    --c-surface-2: var(--c-sable-clair);

    /* Vert « validé » (badge contributeur, statuts). Jeton plutôt que littéral : en vert FONCÉ il
       devient illisible sur l'encre du mode sombre — theme-dark.css le remappe en vert lumineux. */
    --c-succes: #1e5023;

    /* Corail porté par du TEXTE sur une surface. Jeton distinct de `--c-corail-fonce`, qui sert
       aussi de FOND à des pastilles/dégradés à texte blanc : le remapper pour la nuit casserait
       ces aplats. Ici seule la lisibilité du texte compte → theme-dark.css l'éclaircit.
       Corail approfondi (#c6603c → #a8482a) : le corail foncé tombait à 3.7:1 sur le sable clair
       (échec AA) — flagrant sur « Supprimer », un bouton d'action destructive. À #a8482a : 5.3:1
       sur sable, 5.0:1 sur voile corail. */
    --c-corail-texte: #a8482a;

    /* Niveaux de présence de l'annuaire — trois métaux DÉSATURÉS.
       Un doré et un argenté saturés sont inutilisables ici : la pastille porte du texte de
       0.62rem en gras, où AA exige 4.5:1. Les teintes ci-dessous sont donc des versions
       assombries et rabattues vers la charte (le doré tire vers l'ocre, pas vers le jaune ;
       l'argenté vers l'ardoise marine, pas vers le gris neutre) — ce qui les fait cohabiter avec
       le sable et le corail sans ouvrir une quatrième famille chromatique.
       Contrastes mesurés sur leur propre fond : doré 7.0:1, argenté 5.6:1, bleu 5.2:1. */
    --c-niveau-boostee-texte: #7a5c12;
    --c-niveau-boostee-fond: rgba(180, 140, 30, 0.14);
    --c-niveau-preferentielle-texte: #4f5b66;
    --c-niveau-preferentielle-fond: rgba(120, 135, 150, 0.16);
    --c-niveau-normale-texte: #1e6091;
    --c-niveau-normale-fond: rgba(30, 96, 145, 0.10);

    --radius-sm: 8px;
    --radius-md: 14px;
    --radius-lg: 24px;          /* coins arrondis « chirurgicaux » (cartes / bento) */
    --radius-xl: 28px;
    --radius-2xl: 34px;

    /* Ombres douces multi-niveaux (teinte marine pour rester dans la charte) */
    --shadow-xs: 0 1px 2px rgba(10, 61, 98, 0.06);
    --shadow-sm: 0 2px 6px rgba(10, 61, 98, 0.07), 0 1px 2px rgba(10, 61, 98, 0.04);
    --shadow-md: 0 8px 24px rgba(10, 61, 98, 0.10), 0 2px 6px rgba(10, 61, 98, 0.05);
    --shadow-lg: 0 20px 48px rgba(10, 61, 98, 0.15), 0 6px 16px rgba(10, 61, 98, 0.08);
    --shadow-xl: 0 32px 64px -16px rgba(10, 61, 98, 0.28), 0 12px 24px -12px rgba(10, 61, 98, 0.14);
    /* Lueur colorée discrète au survol (cartes premium) */
    --glow-mer: 0 0 0 1px rgba(74, 144, 184, 0.30), 0 18px 40px -18px rgba(30, 96, 145, 0.40);

    --space-xs: 0.5rem;
    --space-sm: 1rem;
    --space-md: 1.5rem;
    --space-lg: 2.5rem;
    --space-xl: 4rem;

    --font-base: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-display: 'Plus Jakarta Sans', var(--font-base);

    /* Échelle typographique fluide (clamp) — un seul réglage du mobile au grand écran */
    --fs-xs:   clamp(0.72rem, 0.69rem + 0.15vw, 0.8rem);
    --fs-sm:   clamp(0.82rem, 0.79rem + 0.18vw, 0.92rem);
    --fs-base: clamp(0.95rem, 0.92rem + 0.20vw, 1.05rem);
    --fs-md:   clamp(1.05rem, 1.00rem + 0.35vw, 1.25rem);
    --fs-lg:   clamp(1.20rem, 1.08rem + 0.65vw, 1.55rem);
    --fs-xl:   clamp(1.50rem, 1.22rem + 1.30vw, 2.15rem);
    --fs-2xl:  clamp(1.90rem, 1.45rem + 2.10vw, 2.95rem);
    --fs-3xl:  clamp(2.30rem, 1.65rem + 3.10vw, 3.70rem);

    /* Verre dépoli (glassmorphism) — fond translucide, filet fin, flou */
    --glass-bg: rgba(255, 255, 255, 0.62);
    --glass-bg-strong: rgba(255, 255, 255, 0.80);
    --glass-border: rgba(255, 255, 255, 0.55);
    --glass-hairline: rgba(10, 61, 98, 0.14); /* 0.08 → 0.14 : retouche contraste 2026-07-10 */
    --glass-blur: 16px;
    --glass-shadow: 0 12px 40px -12px rgba(10, 61, 98, 0.28), 0 2px 8px -2px rgba(10, 61, 98, 0.12);

    /* En-tête flottant translucide (voile marine + flou haut de gamme) */
    --header-bg: linear-gradient(135deg, rgba(10, 61, 98, 0.80) 0%, rgba(30, 96, 145, 0.80) 100%);
    --header-bg-scrolled: linear-gradient(135deg, rgba(9, 52, 84, 0.74) 0%, rgba(26, 84, 128, 0.76) 100%);

    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
    --duration-fast: 150ms;
    --duration-base: 220ms;
    --duration-slow: 360ms;
}


/*
reset.css

QUOI : Réinitialisation de base : box-sizing global, réglages html/body (fond dégradé, flex colonne) et main extensible.

COMMENT : Copie telle quelle des lignes 82–112 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

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

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

body {
    margin: 0;
    font-family: var(--font-base);
    font-size: 16px;
    line-height: 1.6;
    font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
    color: var(--c-texte);
    /* Fond sable clair (rollback 2026-07-16 sur demande — le sable chaud du 2026-07-10 ne
       plaisait pas). Le contraste cartes/fond reste couvert par la bordure et les hairlines
       renforcées ce jour-là (tokens.css --c-bordure, event-card.css), conservées telles quelles. */
    background-color: var(--c-sable-clair);
    background-image:
        radial-gradient(circle at 10% 0%, rgba(74, 144, 184, 0.10) 0%, transparent 45%),
        radial-gradient(circle at 90% 100%, rgba(224, 120, 86, 0.06) 0%, transparent 40%);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body > main {
    flex: 1;
}


/*
a11y.css

QUOI : Accessibilité transverse : focus visible renforcé (WCAG 2.4.7), classe sr-only et neutralisation des animations si prefers-reduced-motion.

COMMENT : Copie telle quelle des lignes 113–194 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

*:focus {
    outline: none;
}
*:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}

/* Règle spécifique WCAG 2.4.7 : focus visible renforcé sur éléments interactifs clés. */
a:focus-visible,
button:focus-visible,
input:focus-visible {
    outline: 3px solid var(--c-mer);
    outline-offset: 2px;
}

/* Masque visuellement un élément tout en le gardant accessible aux lecteurs d'écran. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

::selection {
    background-color: rgba(30, 96, 145, 0.22);
    color: var(--c-marine);
}

[data-reveal] {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
    will-change: opacity, transform;
}
[data-reveal].is-revealed {
    opacity: 1;
    transform: translateY(0);
}

[data-reveal-children] > * {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
}
[data-reveal-children].is-revealed > * {
    opacity: 1;
    transform: translateY(0);
}
[data-reveal-children].is-revealed > *:nth-child(1) { transition-delay: 0ms; }
[data-reveal-children].is-revealed > *:nth-child(2) { transition-delay: 60ms; }
[data-reveal-children].is-revealed > *:nth-child(3) { transition-delay: 120ms; }
[data-reveal-children].is-revealed > *:nth-child(4) { transition-delay: 180ms; }
[data-reveal-children].is-revealed > *:nth-child(5) { transition-delay: 240ms; }
[data-reveal-children].is-revealed > *:nth-child(n+6) { transition-delay: 300ms; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    [data-reveal],
    [data-reveal-children] > * {
        opacity: 1 !important;
        transform: none !important;
    }
    .u-fade-in-up,
    .u-stagger > * {
        animation: none !important;
        opacity: 1 !important;
        transform: none !important;
    }
}


/*
utilities.css

QUOI : Utilitaires réutilisables : verre dépoli (.glass-*), carrousel tactile, apparition douce, système de boutons .btn et conteneur central .container.

COMMENT : Copie telle quelle des lignes 195–338 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================================================
   Utilitaires refonte v2.5.0 — verre dépoli, boutons, carrousels,
   apparition douce. Mobile-first, accessibles, sans framework.
   ============================================================ */

/* ── Verre dépoli (glassmorphism) ── */
.glass-card,
.glass {
    background: var(--glass-bg);
    -webkit-backdrop-filter: saturate(160%) blur(var(--glass-blur));
    backdrop-filter: saturate(160%) blur(var(--glass-blur));
    border: 1px solid var(--glass-border);
    box-shadow: var(--glass-shadow);
    border-radius: var(--radius-lg);
}

.glass--strong {
    background: var(--glass-bg-strong);
}

/* Repli sans backdrop-filter : on opacifie le fond pour préserver la lisibilité. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .glass-card,
    .glass {
        background: var(--glass-bg-strong);
    }
}

/* ── Carrousel tactile horizontal (scrollbar masquée) — tags / catégories mobile ── */
.u-scroll-x {
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.u-scroll-x::-webkit-scrollbar { display: none; }
.u-scroll-x > * {
    scroll-snap-align: start;
    flex: 0 0 auto;
}

/* ── Apparition douce ascendante (CSS pur : état final visible même si l'anim échoue) ── */
@keyframes fade-in-up {
    from { opacity: 0; transform: translateY(18px); }
    to   { opacity: 1; transform: translateY(0); }
}
.u-fade-in-up {
    animation: fade-in-up 620ms var(--ease-out) both;
}
.u-stagger > * {
    animation: fade-in-up 620ms var(--ease-out) both;
}
.u-stagger > *:nth-child(1) { animation-delay: 40ms; }
.u-stagger > *:nth-child(2) { animation-delay: 110ms; }
.u-stagger > *:nth-child(3) { animation-delay: 180ms; }
.u-stagger > *:nth-child(4) { animation-delay: 250ms; }
.u-stagger > *:nth-child(5) { animation-delay: 320ms; }
.u-stagger > *:nth-child(n+6) { animation-delay: 390ms; }

/* ── Système de boutons partagé (.btn) ──
   Défini ici pour que les pages ne chargeant que style.css (newsletter, pied de page)
   disposent d'un bouton premium. Sur les pages admin/soumission, leur CSS dédié
   (chargé APRÈS style.css) prend le relais sans conflit. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    min-height: 46px;
    padding: 0.7rem 1.4rem;
    font-size: 0.95rem;
    font-family: inherit;
    font-weight: 600;
    line-height: 1.2;
    border: 1.5px solid transparent;
    border-radius: 999px;
    cursor: pointer;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-base) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}

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

.btn:active {
    transform: translateY(0);
    box-shadow: var(--shadow-xs);
}

.btn:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: 2px;
}

.btn--primary {
    background: linear-gradient(135deg, var(--c-marine) 0%, var(--c-mer) 100%);
    color: var(--c-blanc);
    border-color: transparent;
}

.btn--primary:hover {
    box-shadow: 0 10px 26px -8px rgba(10, 61, 98, 0.45);
}

.btn--ghost {
    background-color: transparent;
    color: var(--c-texte-doux);
    border-color: var(--c-bordure);
    box-shadow: none;
}

.btn--ghost:hover {
    color: var(--c-marine);
    border-color: var(--c-mer);
    background-color: rgba(30, 96, 145, 0.05);
    box-shadow: none;
}

.container {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding-left: max(var(--space-sm), env(safe-area-inset-left));
    padding-right: max(var(--space-sm), env(safe-area-inset-right));
}

@media (min-width: 640px) {
    .container {
        padding-left: var(--space-md);
        padding-right: var(--space-md);
    }
}



/*
header.css

QUOI : En-tête du site (.site-header) : bandeau sticky translucide (voile marine + flou), logo, tagline et état déroulé .is-scrolled.

COMMENT : Copie telle quelle des lignes 339–498 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

.site-header {
    position: sticky;
    top: 0;
    z-index: 50;
    /* En-tête flottant translucide : voile marine + flou d'arrière-plan haut de gamme. */
    background: var(--header-bg);
    -webkit-backdrop-filter: saturate(155%) blur(12px);
    backdrop-filter: saturate(155%) blur(12px);
    color: var(--c-blanc);
    /* Hauteur affinée (header plus discret) + marge de sécurité encoche/Dynamic Island.
       `max()` garantit un padding de base quand il n'y a pas d'inset (la valeur de repli de
       `env()` ne s'applique qu'aux navigateurs sans support, pas aux écrans sans encoche). */
    padding-block: 0.55rem;
    padding-top: max(0.55rem, env(safe-area-inset-top, 0.55rem));
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), var(--shadow-sm);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    transition: box-shadow var(--duration-base) var(--ease-out),
                background var(--duration-base) var(--ease-out);
}

/* Repli sans flou (très rares navigateurs) : voile marine opaque pour rester lisible. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .site-header {
        background: linear-gradient(135deg, #0a3d62 0%, #1e6091 100%);
    }
}

@media (min-width: 640px) {
    .site-header {
        padding-block: 0.7rem;
        padding-top: max(0.7rem, env(safe-area-inset-top, 0.7rem));
    }
}

/* NE PAS modifier la hauteur (padding/min-height) ici : un header sticky qui
   change de taille au scroll fait osciller la position de défilement autour du
   seuil et provoque un flicker. Seuls les effets visuels sont autorisés. */
.site-header.is-scrolled {
    box-shadow: var(--shadow-md);
    background: var(--header-bg-scrolled);
    -webkit-backdrop-filter: saturate(165%) blur(16px);
    backdrop-filter: saturate(165%) blur(16px);
}

.site-header::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, transparent 70%, rgba(255, 255, 255, 0.04) 100%);
    pointer-events: none;
}

.site-header .container {
    position: relative;
    z-index: 1;
}

.site-header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

.header-brand {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
}

.logo {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    color: var(--c-blanc);
    text-decoration: none;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.45rem;
    letter-spacing: -0.02em;
    transition: opacity var(--duration-fast) var(--ease-out);
}

.logo:hover {
    opacity: 0.92;
}

.logo-mark {
    font-size: 1.8rem;
    color: var(--c-sable);
    transition: transform var(--duration-slow) var(--ease-out-quart);
    display: inline-block;
}

.logo:hover .logo-mark {
    transform: rotate(20deg);
}

.tagline {
    margin: 0;
    font-size: 0.85rem;
    opacity: 0.78;
    font-style: italic;
    letter-spacing: 0.01em;
}

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

.header-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    min-height: 44px;
    padding: 0.55rem 1.15rem;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--c-marine);
    background-color: var(--c-sable);
    border: 1.5px solid transparent;
    border-radius: var(--radius-md);
    text-decoration: none;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    white-space: nowrap;
    transition: background-color var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.header-cta:hover {
    background-color: var(--c-blanc);
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
}

.header-cta:active {
    transform: translateY(0);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.10);
}

@media (min-width: 640px) {
    .header-brand {
        flex-direction: row;
        align-items: baseline;
        gap: 0.85rem;
    }

    .tagline {
        border-left: 1px solid rgba(255, 255, 255, 0.22);
        padding-left: 0.85rem;
    }
}



/*
search.css

QUOI : Zone de recherche de l'accueil : section, panneau blanc, champ avec icône et états focus.

COMMENT : Copie telle quelle des lignes 499–604 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

.search-section {
    padding: var(--space-md) 0 var(--space-sm);
    text-align: center;
}

.search-panel {
    background-color: var(--c-blanc);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-md);
    padding: var(--space-md) var(--space-sm);
    box-shadow: var(--shadow-sm);
    max-width: 880px;
    margin: 0 auto;
}

@media (min-width: 640px) {
    .search-panel {
        border-radius: var(--radius-lg);
        padding: var(--space-lg) var(--space-md);
    }
}

.search-section h1 {
    margin: 0 0 var(--space-md);
    font-family: var(--font-display);
    font-size: clamp(1.8rem, 4.5vw, 2.75rem);
    color: var(--c-marine);
    font-weight: 800;
    letter-spacing: -0.025em;
    line-height: 1.15;
    background: linear-gradient(135deg, var(--c-marine) 0%, var(--c-mer) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.search-wrapper {
    position: relative;
    max-width: 640px;
    margin: 0 auto;
}

.search-icon {
    position: absolute;
    left: var(--space-sm);
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--c-mer);
    opacity: 0.55;
    pointer-events: none;
    transition: opacity var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.search-wrapper:focus-within .search-icon {
    color: var(--c-marine);
    opacity: 1;
}

.search-input {
    width: 100%;
    min-height: 52px;
    padding: 1rem var(--space-sm) 1rem 3rem;
    font-size: 1.05rem;
    font-family: inherit;
    color: var(--c-texte);
    background-color: var(--c-blanc);
    border: 2px solid var(--c-bordure);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    outline: none;
    -webkit-appearance: none;
    appearance: none;
    transition: border-color var(--duration-base) var(--ease-out),
                box-shadow var(--duration-base) var(--ease-out),
                transform var(--duration-base) var(--ease-out);
}

.search-input::placeholder {
    color: var(--c-texte-doux);
    opacity: 0.65;
}

.search-input:hover {
    border-color: var(--c-mer-clair);
}

.search-input:focus,
.search-input:focus-visible {
    border-color: var(--c-mer);
    box-shadow: 0 0 0 4px var(--c-focus), var(--shadow-md);
    outline: none;
    transform: translateY(-1px);
}

.results-count {
    margin: var(--space-md) 0 0;
    font-size: 0.9rem;
    color: var(--c-texte-doux);
    transition: color 0.2s ease;
}



/*
filters.css

QUOI : Pastilles de la barre d'outils (tri, filtres, carte, date, géoloc) et popover de saisie manuelle d'adresse.

COMMENT : Copie telle quelle des lignes 605–1030 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

.search-toolbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.5rem;
    margin: var(--space-md) auto 0;
    min-height: 42px;
    padding: 0.45rem 0.65rem;
    background: linear-gradient(180deg, var(--c-sable-clair) 0%, rgba(250, 244, 232, 0.65) 100%);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-md);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85);
}

@media (min-width: 640px) {
    .search-toolbar {
        max-width: fit-content;
        border-radius: 999px;
    }
}

@media (max-width: 539px) {
    /* Sur mobile, ces pills passent en icône-seule. On masque le libellé VISUELLEMENT
       (pattern .visually-hidden) plutôt qu'avec display:none : le texte reste dans l'arbre
       d'accessibilité, donc le bouton conserve un nom accessible (WCAG 4.1.2, niveau A) et
       le badge de comptage des filtres reste annoncé. Rendu visuel identique à display:none. */
    .toolbar-pill--filters .toolbar-pill__label,
    .toolbar-pill--map .toolbar-pill__label {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }
    .toolbar-pill--filters,
    .toolbar-pill--map {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }
}

.toolbar-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    min-height: 40px;
    padding: 0.55rem 1rem;
    font-size: 0.825rem;
    font-family: inherit;
    font-weight: 600;
    line-height: 1.2;
    color: var(--c-marine);
    background-color: var(--c-blanc);
    border: 1.5px solid var(--c-bordure);
    border-radius: 999px;
    cursor: pointer;
    text-decoration: none;
    box-shadow: 0 1px 3px rgba(10, 61, 98, 0.06);
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
    animation: toolbar-pill-in 0.25s var(--ease-out);
}

.toolbar-pill:hover {
    border-color: var(--c-mer-clair);
    background-color: #fff;
    box-shadow: 0 3px 10px rgba(10, 61, 98, 0.10);
    transform: translateY(-1px);
}

.toolbar-pill:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: 2px;
}

.toolbar-pill:active {
    transform: translateY(0);
}

.toolbar-pill__icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    stroke-width: 2.2;
    opacity: 0.85;
}

.toolbar-pill__label {
    white-space: nowrap;
}

.toolbar-pill__separator {
    width: 1px;
    height: 14px;
    background-color: var(--c-bordure);
    margin: 0 0.15rem;
}

.toolbar-pill__sub {
    color: var(--c-texte-doux);
    font-weight: 500;
    font-size: 0.78rem;
    white-space: nowrap;
}

.toolbar-pill--geo.is-active {
    background-color: var(--c-marine);
    border-color: var(--c-marine);
    color: var(--c-blanc);
    box-shadow: 0 2px 10px rgba(10, 61, 98, 0.20);
}

.toolbar-pill--geo.is-active:hover {
    background-color: var(--c-mer);
    border-color: var(--c-mer);
}

.toolbar-pill--geo.is-loading {
    opacity: 0.65;
    cursor: progress;
}

.toolbar-pill--geo.is-error {
    color: var(--c-corail);
    border-color: var(--c-corail);
}

/* ===== Saisie manuelle d'adresse (popover) =====
   Wrapper relatif pour ancrer le panneau sous le bouton ; styles compacts cohérents
   avec les autres pills de la toolbar. */
.geo-manual {
    position: relative;
    display: inline-flex;
}

.toolbar-pill--geo-edit .toolbar-pill__icon {
    stroke-width: 2;
}

.geo-manual.is-open .toolbar-pill--geo-edit {
    background-color: var(--c-marine);
    border-color: var(--c-marine);
    color: var(--c-blanc);
}

.geo-manual__panel {
    position: absolute;
    top: calc(100% + 0.5rem);
    left: 0;
    z-index: 30;
    width: min(360px, calc(100vw - 2rem));
    background-color: var(--c-blanc);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-sm);
    animation: toolbar-pill-in 0.18s var(--ease-out);
}

.geo-manual__label {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--c-texte-doux);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: 0 0 0.4rem;
}

.geo-manual__input-wrap {
    position: relative;
    display: flex;
    align-items: center;
}

.geo-manual__input {
    flex: 1;
    width: 100%;
    padding: 0.55rem 0.75rem;
    font: inherit;
    font-size: 0.92rem;
    color: var(--c-texte);
    background-color: var(--c-blanc);
    border: 1.5px solid var(--c-bordure);
    border-radius: var(--radius-sm);
    outline: none;
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.geo-manual__input:focus {
    border-color: var(--c-mer);
    box-shadow: 0 0 0 3px rgba(30, 96, 145, 0.15);
}

.geo-manual__spinner {
    position: absolute;
    right: 0.6rem;
    width: 16px;
    height: 16px;
    border: 2px solid var(--c-bordure);
    border-top-color: var(--c-mer);
    border-radius: 50%;
    animation: geo-manual-spin 0.7s linear infinite;
}

@keyframes geo-manual-spin {
    to { transform: rotate(360deg); }
}

.geo-manual__suggestions {
    list-style: none;
    margin: 0.4rem 0 0;
    padding: 0;
    max-height: 240px;
    overflow-y: auto;
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-sm);
    background-color: var(--c-blanc);
}

.geo-manual__suggestion {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.55rem 0.7rem;
    font-size: 0.88rem;
    color: var(--c-marine);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out);
}

.geo-manual__suggestion + .geo-manual__suggestion {
    border-top: 1px solid var(--c-bordure);
}

.geo-manual__suggestion svg {
    flex-shrink: 0;
    color: var(--c-mer);
}

.geo-manual__suggestion:hover,
.geo-manual__suggestion.is-active {
    background-color: var(--c-sable-clair);
}

.geo-manual__hint {
    margin: 0.5rem 0 0;
    font-size: 0.78rem;
    color: var(--c-texte-doux);
}

@media (max-width: 600px) {
    .geo-manual__panel {
        position: fixed;
        top: auto;
        bottom: 1rem;
        left: 1rem;
        right: 1rem;
        width: auto;
    }
}

.toolbar-pill--filters,
.toolbar-pill--map,
.toolbar-pill--sort {
    position: relative;
}

.toolbar-pill--filters[aria-expanded="true"],
.toolbar-pill--map[aria-expanded="true"],
.toolbar-pill--sort[aria-expanded="true"] {
    background: linear-gradient(135deg, var(--c-marine) 0%, var(--c-mer) 100%);
    border-color: var(--c-marine);
    color: var(--c-blanc);
    box-shadow: 0 2px 10px rgba(10, 61, 98, 0.22);
}

.toolbar-pill--filters[aria-expanded="true"]:hover,
.toolbar-pill--map[aria-expanded="true"]:hover,
.toolbar-pill--sort[aria-expanded="true"]:hover {
    background: linear-gradient(135deg, var(--c-mer) 0%, var(--c-mer-clair) 100%);
    border-color: var(--c-mer);
}

/* Le sous-libellé (valeur de tri courante) passe en blanc quand la pastille est ouverte/active. */
.toolbar-pill--sort[aria-expanded="true"] .toolbar-pill__sub {
    color: rgba(255, 255, 255, 0.85);
}

.toolbar-pill--sort[aria-expanded="true"] .toolbar-pill__separator {
    background-color: rgba(255, 255, 255, 0.4);
}

.toolbar-pill__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    margin-left: 0.1rem;
    padding: 0 0.35rem;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1;
    color: var(--c-blanc);
    background-color: var(--c-corail);
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.toolbar-pill__badge[hidden] {
    display: none;
}

.toolbar-pill--filters[aria-expanded="true"] .toolbar-pill__badge {
    background-color: var(--c-corail);
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.45);
}

.toolbar-pill--date {
    background: linear-gradient(135deg, rgba(74, 144, 184, 0.12) 0%, rgba(10, 61, 98, 0.08) 100%);
    border-color: var(--c-mer-clair);
    color: var(--c-marine);
    cursor: default;
}

.toolbar-pill--date:hover {
    transform: none;
    border-color: var(--c-mer);
}

.toolbar-pill--date .toolbar-pill__icon {
    color: var(--c-mer);
    opacity: 1;
}

@keyframes toolbar-pill-in {
    from { opacity: 0; transform: translateY(-3px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.facet-chips {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    margin: var(--space-sm) auto 0;
    max-width: 900px;
    align-items: stretch;
    padding: var(--space-sm) var(--space-md);
    border-top: 1px solid var(--c-bordure);
    background: linear-gradient(180deg, rgba(245, 233, 212, 0.35) 0%, transparent 100%);
    border-radius: 0 0 var(--radius-md) var(--radius-md);
}

.facet-chips__group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
    align-items: center;
}

.facet-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 36px;
    padding: 0.5rem 0.95rem;
    font-size: 0.8rem;
    font-family: inherit;
    font-weight: 600;
    line-height: 1.2;
    color: var(--c-marine);
    background-color: var(--c-blanc);
    border: 1.5px solid var(--c-bordure);
    border-radius: 999px;
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(10, 61, 98, 0.05);
    transition: background-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}

.facet-chip:hover {
    color: var(--c-marine);
    border-color: var(--c-mer-clair);
    background-color: #fff;
    box-shadow: 0 3px 10px rgba(10, 61, 98, 0.09);
    transform: translateY(-1px);
}

.facet-chip:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: 2px;
}

.facet-chip.is-active {
    background: linear-gradient(135deg, var(--c-marine) 0%, var(--c-mer) 100%);
    color: var(--c-blanc);
    border-color: var(--c-marine);
    font-weight: 700;
    box-shadow: 0 3px 10px rgba(10, 61, 98, 0.22);
    transform: translateY(-1px);
}

.facet-chip.is-active:hover {
    background: linear-gradient(135deg, var(--c-mer) 0%, var(--c-mer-clair) 100%);
    border-color: var(--c-mer);
    color: var(--c-blanc);
}

.facet-chip--category.is-active {
    background: linear-gradient(135deg, var(--c-mer) 0%, var(--c-mer-clair) 100%);
    border-color: var(--c-mer);
}

.facet-chip--free.is-active {
    background: linear-gradient(135deg, var(--c-corail) 0%, var(--c-corail-fonce) 100%);
    border-color: var(--c-corail);
}

.facet-chip--free.is-active:hover {
    background: linear-gradient(135deg, var(--c-corail-fonce) 0%, #b85538 100%);
    border-color: var(--c-corail-fonce);
    color: var(--c-blanc);
}


/*
popovers.css

QUOI : Menus déroulants Tri / Filtres : popovers ancrés, options de tri (radio + coche), cases à cocher catégories et groupes de filtres.

COMMENT : Copie telle quelle des lignes 1031–1331 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ===================== Menus déroulants Tri / Filtres (barre d'outils) ===================== */
.toolbar-dropdown {
    position: relative;
    display: inline-flex;
}

/* Chevron du déclencheur : pivote quand le menu est ouvert. */
.toolbar-pill__caret {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
    margin-left: -0.1rem;
    opacity: 0.6;
    transition: transform 0.22s var(--ease-out), opacity var(--duration-fast) var(--ease-out);
}

.toolbar-pill[aria-expanded="true"] .toolbar-pill__caret {
    transform: rotate(180deg);
    opacity: 0.9;
}

/* Pastille « Filtres » avec sélection active mais menu fermé : accent discret. */
.toolbar-pill--filters.is-active:not([aria-expanded="true"]) {
    border-color: var(--c-marine);
    color: var(--c-marine);
    background: linear-gradient(135deg, rgba(10, 61, 98, 0.07) 0%, rgba(74, 144, 184, 0.06) 100%);
}

/* ----- Le popover ----- */
.dropdown-menu {
    position: absolute;
    top: calc(100% + 0.55rem);
    z-index: 40;
    width: max-content;
    min-width: 248px;
    max-width: min(320px, calc(100vw - 1.5rem));
    padding: 0.45rem;
    text-align: left;
    background-color: var(--c-blanc);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    transform-origin: top center;
    animation: dropdown-in 0.16s var(--ease-out);
}

.dropdown-menu[hidden] {
    display: none;
}

/* Ancrage : le tri s'ouvre vers la droite, les filtres vers la gauche (anti-débordement). */
.dropdown-menu--sort { left: 0; }
.dropdown-menu--filters { right: 0; }

/* Petite flèche optique reliant le popover à son bouton. */
.dropdown-menu::before {
    content: "";
    position: absolute;
    top: -5px;
    width: 10px;
    height: 10px;
    background-color: var(--c-blanc);
    border-left: 1px solid var(--c-bordure);
    border-top: 1px solid var(--c-bordure);
    transform: rotate(45deg);
}

.dropdown-menu--sort::before { left: 1.4rem; }
.dropdown-menu--filters::before { right: 1.4rem; }

@keyframes dropdown-in {
    from { opacity: 0; transform: translateY(-6px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.dropdown-menu__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.3rem 0.55rem 0.45rem;
}

.dropdown-menu__title {
    margin: 0;
    padding: 0.35rem 0.55rem 0.45rem;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--c-texte-doux);
    text-transform: uppercase;
    letter-spacing: 0.07em;
}

.dropdown-menu__head .dropdown-menu__title {
    padding: 0;
}

.dropdown-menu__clear {
    padding: 0.25rem 0.5rem;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--c-mer);
    background: transparent;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}

.dropdown-menu__clear:hover {
    color: var(--c-marine);
    background-color: var(--c-sable-clair);
}

.dropdown-menu__clear[hidden] {
    display: none;
}

/* ----- Options de tri (radio + coche) ----- */
.dropdown-menu__list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.dropdown-option {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    width: 100%;
    padding: 0.55rem 0.55rem;
    font: inherit;
    text-align: left;
    color: var(--c-marine);
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out);
}

.dropdown-option:hover {
    background-color: var(--c-sable-clair);
}

.dropdown-option:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: -2px;
}

.dropdown-option__icon {
    flex-shrink: 0;
    color: var(--c-mer);
}

.dropdown-option__label {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-right: auto;
}

.dropdown-option__name {
    font-size: 0.9rem;
    font-weight: 600;
}

.dropdown-option__hint {
    font-size: 0.73rem;
    font-weight: 500;
    color: var(--c-texte-doux);
}

.dropdown-option__check {
    flex-shrink: 0;
    color: var(--c-mer);
    opacity: 0;
    transform: scale(0.7);
    transition: opacity 0.15s var(--ease-out), transform 0.15s var(--ease-out);
}

.dropdown-option.is-active {
    background-color: rgba(30, 96, 145, 0.08);
}

.dropdown-option.is-active .dropdown-option__icon {
    color: var(--c-marine);
}

.dropdown-option.is-active .dropdown-option__check {
    opacity: 1;
    transform: scale(1);
}

/* Géoloc active → tri par distance : les options manuelles sont neutralisées visuellement. */
.dropdown-menu__list.is-distance-mode .dropdown-option {
    opacity: 0.45;
}

/* ----- Cases à cocher catégories ----- */
.dropdown-menu__options {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 320px;
    overflow-y: auto;
}

.dropdown-check {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    width: 100%;
    padding: 0.5rem 0.55rem;
    font: inherit;
    text-align: left;
    color: var(--c-marine);
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out);
}

.dropdown-check:hover {
    background-color: var(--c-sable-clair);
}

.dropdown-check:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: -2px;
}

.dropdown-check__box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    background-color: var(--c-blanc);
    border: 2px solid var(--c-bordure);
    border-radius: 6px;
    transition: background-color 0.15s var(--ease-out), border-color 0.15s var(--ease-out);
}

.dropdown-check__box svg {
    width: 12px;
    height: 12px;
    color: var(--c-blanc);
    opacity: 0;
    transition: opacity 0.12s var(--ease-out);
}

.dropdown-check__label {
    font-size: 0.9rem;
    font-weight: 600;
}

.dropdown-check.is-active .dropdown-check__box {
    background-color: var(--c-mer);
    border-color: var(--c-mer);
}

.dropdown-check.is-active .dropdown-check__box svg {
    opacity: 1;
}

/* Filtre « Gratuit » coché : la case se teinte en corail pour le distinguer des catégories. */
.dropdown-check--free.is-active .dropdown-check__box {
    background-color: var(--c-corail);
    border-color: var(--c-corail);
}

/* Groupes du menu Filtres : sépare visuellement le bloc « Tarif » des « Catégories »
   (sous-titre discret + filet entre les deux groupes). */
.dropdown-group__label {
    margin: 0;
    padding: 0.3rem 0.55rem 0.35rem;
    font-size: 0.68rem;
    font-weight: 700;
    color: var(--c-texte-doux);
    text-transform: uppercase;
    letter-spacing: 0.07em;
}

.dropdown-group + .dropdown-group {
    margin-top: 0.35rem;
    padding-top: 0.35rem;
    border-top: 1px solid var(--c-bordure);
}

@media (max-width: 539px) {
    /* Sur très petit écran, on n'affiche que l'icône + « Trier » (la valeur courante reste sur la pastille). */
    .toolbar-pill--sort .toolbar-pill__separator,
    .toolbar-pill--sort .toolbar-pill__sub {
        display: none;
    }
}


/*
city-groups.css

QUOI : Affichage groupé ville par ville (accueil par défaut) : sections, bouton « voir plus », spinner et états vides de résultats.

COMMENT : Copie telle quelle des lignes 1332–1586 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ===================== Affichage groupé ville par ville (accueil par défaut) ===================== */
.city-section[hidden],
.events-section[hidden] {
    display: none;
}

.city-block {
    margin-top: var(--space-lg, 2rem);
}

.city-block:first-of-type {
    margin-top: var(--space-md, 1.25rem);
}

.city-block__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    margin-bottom: 0.9rem;
    padding-bottom: 0.5rem;
    border-bottom: 1.5px solid var(--c-bordure);
}

.city-block__title {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--c-marine);
}

.city-block__title .icon-pin {
    width: 18px;
    height: 18px;
    color: var(--c-corail);
    flex-shrink: 0;
}

.city-block__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 0.45rem;
    font-size: 0.78rem;
    font-weight: 700;
    line-height: 1;
    color: var(--c-marine);
    background-color: var(--c-sable, rgba(245, 233, 212, 0.7));
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.city-block__all {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--c-mer);
    text-decoration: none;
    white-space: nowrap;
    transition: color var(--duration-fast) var(--ease-out), gap var(--duration-fast) var(--ease-out);
}

.city-block__all:hover {
    color: var(--c-marine);
    gap: 0.5rem;
}

.city-block__all svg {
    flex-shrink: 0;
}

.city-section__empty {
    margin: var(--space-lg, 2rem) 0;
    text-align: center;
    color: var(--c-texte-doux);
}

.city-section__empty[hidden] {
    display: none;
}

.load-more-wrapper {
    display: flex;
    justify-content: center;
    margin: 2rem 0 0.5rem;
}

/* `.load-more-btn { display: inline-flex }` l'emporterait sur l'attribut `hidden` (display:none de
   l'UA, priorité plus faible qu'un sélecteur de classe). Sans ces règles, le bouton resterait visible
   et cliquable une fois les résultats épuisés. On masque le bouton ET on replie son conteneur. */
.load-more-btn[hidden] {
    display: none;
}

.load-more-wrapper:has(> .load-more-btn[hidden]) {
    display: none;
}

.load-more-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 48px;
    padding: 0.8rem 1.85rem;
    font-size: 0.95rem;
    font-family: inherit;
    font-weight: 600;
    color: var(--c-blanc);
    background: linear-gradient(135deg, var(--c-marine) 0%, var(--c-mer) 100%);
    border: 1px solid var(--c-marine);
    border-radius: 999px;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(10, 61, 98, 0.22);
    transition: background-color var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.load-more-btn:hover {
    background-color: var(--c-mer);
    border-color: var(--c-mer);
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(10, 61, 98, 0.22);
}

.load-more-btn:active {
    transform: translateY(0);
}

.load-more-btn:disabled {
    opacity: 0.6;
    cursor: progress;
    transform: none;
}

.geo-status {
    margin: 0.4rem 0 0;
    font-size: 0.75rem;
    color: var(--c-texte-doux);
    font-style: italic;
    text-align: center;
}

.search-spinner {
    position: absolute;
    right: var(--space-sm);
    top: 50%;
    width: 18px;
    height: 18px;
    margin-top: -9px;
    border: 2px solid var(--c-bordure);
    border-top-color: var(--c-mer);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    pointer-events: none;
}

.search-spinner[hidden] {
    display: none;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

.search-input[aria-busy="true"] {
    padding-right: 3rem;
}


.events-section,
.city-section {
    padding: var(--space-md) 0 var(--space-xl);
}

.events-grid {
    display: grid;
    /* `minmax(0, 1fr)` (et non `1fr` = `minmax(auto, 1fr)`) : sans ça, la taille min-content d'une
       carte — surtout l'image de couverture — empêche la piste de descendre sous sa largeur naturelle,
       la grille déborde alors de son conteneur (overflow mobile) et les tiers se déforment sur desktop. */
    grid-template-columns: minmax(0, 1fr);
    gap: 1.1rem;
}

@media (min-width: 600px) {
    .events-grid {
        gap: var(--space-md);
    }
}

.results-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    text-align: center;
    padding: var(--space-lg) var(--space-md);
    color: var(--c-texte-doux);
    font-size: 1rem;
    background-color: var(--c-sable-clair);
    border-radius: var(--radius-md);
    border: 1px dashed var(--c-bordure);
    animation: results-empty-in 0.3s var(--ease-out);
}

.results-empty[hidden] {
    display: none;
}

.results-empty strong {
    display: block;
    color: var(--c-marine);
    font-size: 1.05rem;
    font-weight: 700;
}

.results-empty__icon {
    font-size: 1.8rem;
    line-height: 1;
    opacity: 0.85;
    margin-bottom: 0.25rem;
}

.results-empty__hint {
    display: block;
    font-size: 0.85rem;
    color: var(--c-texte-doux);
    font-style: italic;
    margin-top: 0.25rem;
}

.results-empty--with-suggestions {
    background-color: rgba(74, 144, 184, 0.06);
    border-color: var(--c-mer-clair);
    border-style: solid;
}

.results-empty--with-suggestions strong {
    color: var(--c-marine);
}

@keyframes results-empty-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}


/*
map.css

QUOI : Carte d'accueil Leaflet : conteneur, pins et marqueurs (dont cluster et « ma position »), popups simples et listes.

COMMENT : Copie telle quelle des lignes 1587–1995 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ---------- Carte d’accueil (Leaflet + pins) ---------- */

.home-map-section {
    margin-bottom: var(--space-lg);
    background-color: var(--c-blanc);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    box-shadow: var(--shadow-xs);
    overflow: hidden;
    max-height: 720px;
    opacity: 1;
    transition: max-height 320ms var(--ease-out),
                opacity 220ms var(--ease-out),
                margin 220ms var(--ease-out),
                padding 220ms var(--ease-out),
                border-color 220ms var(--ease-out);
}

.home-map-section.is-collapsed {
    max-height: 0;
    margin-top: 0;
    margin-bottom: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-color: transparent;
    opacity: 0;
    pointer-events: none;
}

.home-map-section__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-sm);
    margin-bottom: var(--space-sm);
    flex-wrap: wrap;
}

.home-map-section__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    margin: -0.25rem -0.5rem -0.25rem 0;
    color: var(--c-texte-doux);
    background-color: transparent;
    border: 1px solid transparent;
    border-radius: 999px;
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
}

.home-map-section__close:hover {
    color: var(--c-marine);
    background-color: var(--c-sable-clair);
    border-color: var(--c-bordure);
}

.home-map-section__close:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: 2px;
}

.home-map-section__title {
    margin: 0;
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--c-marine);
}

.home-map-section__hint {
    margin: 0;
    font-size: 0.85rem;
    color: var(--c-texte-doux);
}

.section-heading {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
    margin-bottom: var(--space-md);
}

.section-heading__title {
    margin: 0;
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--c-marine);
}

.section-heading__subtitle {
    margin: 0;
    font-size: 0.88rem;
    color: var(--c-texte-doux);
}

.home-events-map {
    height: 280px;
    width: 100%;
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1px solid var(--c-bordure);
    box-shadow: var(--shadow-sm);
    background: linear-gradient(160deg, #e8f4fc 0%, var(--c-sable-clair) 100%);
    z-index: 0;
}

@media (min-width: 600px) {
    .home-events-map {
        height: 360px;
    }
}

.home-events-map--empty {
    opacity: 0.65;
}

.home-events-map .leaflet-container {
    height: 100%;
    width: 100%;
    font-family: var(--font-base);
}

.home-map-empty {
    margin: var(--space-xs) 0 0;
    font-size: 0.9rem;
    color: var(--c-texte-doux);
    text-align: center;
}

/* Échec de chargement de la carte (Leaflet KO ou tuiles indisponibles) : le canvas est
   grisé (classe --error, ajoutée par home-map.js) et le message explique la cause. */
.home-events-map--error {
    opacity: 0.55;
    filter: grayscale(1);
}

/* `:not([hidden])` : sans lui, `display:flex` (spécificité de classe) l'emporte sur la règle
   native `[hidden]{display:none}` du navigateur → la boîte d'erreur (vide tant que Leaflet charge
   bien) resterait affichée en permanence dès que la section carte est dépliée. */
.home-map-error:not([hidden]) {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    gap: 0.4rem;
    margin: var(--space-sm) 0 0;
    padding: var(--space-sm);
    font-size: 0.9rem;
    line-height: 1.45;
    text-align: center;
    color: var(--c-corail-fonce);
    background-color: #fbe9e7;
    border: 1px solid var(--c-corail);
    border-radius: var(--radius-sm);
}

/* ---------- Pins et marqueurs (home-map-marker*, home-map-user*) ---------- */

.home-map-marker-wrap {
    background: transparent;
    border: none;
}

.home-map-marker {
    position: relative;
    width: 28px;
    height: 28px;
    background: radial-gradient(circle at 35% 35%, var(--c-corail) 0%, var(--c-corail-fonce) 100%);
    border: 3px solid var(--c-blanc);
    border-radius: 50%;
    box-shadow: 0 6px 16px rgba(10, 61, 98, 0.35);
    cursor: pointer;
}

.home-map-marker::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 8px;
    height: 8px;
    background-color: var(--c-blanc);
    border-radius: 50%;
}

/* Pin "cluster" : utilisé quand plusieurs événements partagent les mêmes coordonnées.
   On masque le point central blanc (::after) et on affiche le compteur à la place. */
.home-map-marker--cluster {
    width: 34px;
    height: 34px;
    background: radial-gradient(circle at 35% 30%, var(--c-mer-clair) 0%, var(--c-marine) 100%);
    box-shadow: 0 6px 18px rgba(10, 61, 98, 0.45);
}

.home-map-marker--cluster::after {
    display: none;
}

.home-map-marker__count {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--c-blanc);
    font-family: var(--font-display);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-shadow: 0 1px 2px rgba(10, 61, 98, 0.4);
}

/* Marqueur « Ma position » : point bleu pulsant, convention universelle. */
.home-map-user-wrap {
    background: transparent;
    border: none;
}

.home-map-user-marker {
    position: relative;
    width: 22px;
    height: 22px;
}

.home-map-user-marker__dot {
    position: absolute;
    inset: 4px;
    background: var(--c-mer);
    border: 3px solid var(--c-blanc);
    border-radius: 50%;
    box-shadow: 0 4px 10px rgba(10, 61, 98, 0.45);
    z-index: 2;
}

.home-map-user-marker__pulse {
    position: absolute;
    inset: 0;
    background-color: var(--c-mer);
    border-radius: 50%;
    opacity: 0.45;
    transform-origin: center;
    animation: home-map-user-pulse 2s infinite ease-out;
    z-index: 1;
}

@keyframes home-map-user-pulse {
    0% {
        transform: scale(0.6);
        opacity: 0.55;
    }
    100% {
        transform: scale(2.2);
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .home-map-user-marker__pulse {
        animation: none;
        opacity: 0.3;
    }
}

/* ---------- Popups Leaflet (home-map-popup*) ---------- */

.home-map-popup-wrap .leaflet-popup-content-wrapper {
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    padding: 0;
}

.home-map-popup-wrap .leaflet-popup-content {
    margin: 0;
    min-width: 200px;
}

.home-map-popup-wrap--cluster .leaflet-popup-content {
    min-width: 260px;
}

.home-map-popup {
    padding: var(--space-sm);
}

.home-map-popup__category {
    margin: 0 0 0.2rem;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-mer);
}

.home-map-popup__title {
    margin: 0 0 0.35rem;
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--c-marine);
}

.home-map-popup__meta {
    margin: 0 0 0.6rem;
    font-size: 0.82rem;
    color: var(--c-texte-doux);
}

.home-map-popup__link {
    display: inline-block;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--c-corail-fonce);
    text-decoration: none;
}

.home-map-popup__link:hover {
    text-decoration: underline;
}

/* Popup "cluster" : en-tête + liste d'événements. Le scroll est délégué à Leaflet
   (option `maxHeight` du bindPopup → classe .leaflet-popup-scrolled) pour que la
   popup ne dépasse jamais les dimensions de la carte. */
.home-map-popup--cluster {
    padding: 0;
}

.home-map-popup-wrap--cluster .leaflet-popup-scrolled {
    border-bottom: 1px solid transparent;
}

.home-map-popup__header {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.55rem 0.7rem;
    border-bottom: 1px solid var(--c-bordure);
    background: linear-gradient(180deg, var(--c-sable-clair) 0%, var(--c-blanc) 100%);
    position: sticky;
    top: 0;
    z-index: 1;
}

.home-map-popup__badge {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: radial-gradient(circle at 35% 30%, var(--c-corail) 0%, var(--c-corail-fonce) 100%);
    color: var(--c-blanc);
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.8rem;
    box-shadow: 0 2px 6px rgba(198, 96, 60, 0.35);
}

.home-map-popup__eyebrow {
    margin: 0;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-marine);
    line-height: 1.2;
}

.home-map-popup--cluster .home-map-popup__meta {
    margin: 0.1rem 0 0;
    font-size: 0.78rem;
    line-height: 1.2;
}

.home-map-popup-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.home-map-popup-list__item + .home-map-popup-list__item {
    border-top: 1px solid var(--c-bordure);
}

.home-map-popup-list__link {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.45rem 0.7rem;
    text-decoration: none;
    color: inherit;
    transition: background-color var(--duration-fast) var(--ease-out);
}

.home-map-popup-list__link:hover,
.home-map-popup-list__link:focus-visible {
    background-color: var(--c-sable-clair);
    outline: none;
}

.home-map-popup-list__category {
    font-size: 0.66rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-mer);
}

.home-map-popup-list__title {
    font-size: 0.88rem;
    font-weight: 700;
    line-height: 1.25;
    color: var(--c-marine);
}

.home-map-popup-list__date {
    font-size: 0.76rem;
    color: var(--c-texte-doux);
}

@media (min-width: 768px) {
    .home-events-map {
        height: 420px;
    }
}


/*
event-card.css

QUOI : Carte événement : lien-carte, tuile date « ticket », couleurs par catégorie (data-cat), titre, description et états.

COMMENT : Copie telle quelle des lignes 1996–2318 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

.event-card-link,
.event-card-link:any-link,
.event-card-link:visited,
.event-card-link:hover,
.event-card-link:active,
.event-card-link:focus,
.event-card-link:focus-visible {
    display: block;
    height: 100%;
    color: inherit;
    text-decoration: none;
    -webkit-text-decoration: none;
}

.event-card-link *,
.event-card-link h2,
.event-card-link p,
.event-card-link span {
    text-decoration: none;
}

.event-card-link .event-card__title {
    color: var(--c-marine);
}

.event-card-link .event-card__description {
    color: var(--c-texte-doux);
}

.event-card-link .event-card__ville > span {
    color: var(--c-texte);
}

.event-card-link:hover .event-card__title {
    color: var(--c-mer);
}

.event-card {
    position: relative;
    background-color: var(--c-blanc);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    box-shadow:
        0 1px 2px rgba(10, 61, 98, 0.04),
        0 8px 20px -14px rgba(10, 61, 98, 0.22);
    border: 1px solid rgba(10, 61, 98, 0.15); /* 0.08 → 0.15 : retouche contraste 2026-07-10 */
    border-left: 3px solid var(--cat-color);
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    height: 100%;
    transition: transform var(--duration-base) var(--ease-out),
                box-shadow var(--duration-base) var(--ease-out),
                border-color var(--duration-base) var(--ease-out);
}

.event-card::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, transparent 0%, transparent 60%, rgba(74, 144, 184, 0.04) 100%);
    opacity: 0;
    transition: opacity var(--duration-base) var(--ease-out);
    pointer-events: none;
    border-radius: inherit;
    z-index: 0;
}

.event-card > * {
    position: relative;
    z-index: 1;
}

@media (hover: hover) {
    .event-card:hover {
        transform: translateY(-4px);
        box-shadow:
            0 2px 4px rgba(10, 61, 98, 0.05),
            0 18px 40px -18px rgba(10, 61, 98, 0.32);
        border-color: rgba(74, 144, 184, 0.45);
        border-left-color: var(--cat-color);
    }
}

.event-card:active {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
}

.event-card:hover::before {
    opacity: 1;
}

.secondary-section {
    margin-top: var(--space-lg);
    padding-bottom: var(--space-xl);
}

.secondary-section[hidden] {
    display: none;
}

.secondary-divider {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin: 0 0 var(--space-sm);
}

.secondary-divider__line {
    flex: 1;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--c-bordure), transparent);
}

.secondary-divider__label {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--c-mer);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    white-space: nowrap;
}

.secondary-divider__icon {
    color: var(--c-corail);
    flex-shrink: 0;
}

.secondary-note {
    text-align: center;
    margin: 0 auto var(--space-md);
    max-width: 560px;
    font-size: 0.85rem;
    color: var(--c-texte-doux);
    font-style: italic;
    line-height: 1.5;
}

.events-grid--secondary {
    opacity: 0.92;
}

.event-card--secondary {
    background-color: var(--c-sable-clair);
    border-style: dashed;
    border-color: var(--c-bordure);
    box-shadow: none;
}

.event-card--secondary:hover {
    background-color: var(--c-blanc);
    border-style: solid;
}

.event-card--secondary .event-card__title {
    color: var(--c-mer);
}

.icon-pin {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    stroke-width: 2.2;
    color: var(--c-mer);
}

.event-card__ville {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin: 0;
    font-size: 0.95rem;
    color: var(--c-texte-doux);
    font-weight: 500;
}

.event-card__ville > span {
    color: var(--c-texte);
}

.event-card__distance {
    display: inline-flex;
    align-items: center;
    margin-left: 0.5rem;
    padding: 0.15rem 0.55rem;
    font-size: 0.72rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--c-mer);
    background-color: rgba(30, 96, 145, 0.10);
    border-radius: 999px;
    letter-spacing: 0.2px;
}

.event-card__header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.8rem;
}

/* Tuile date (style « ticket événement ») — ancre visuelle premium de la carte. */
.event-card__date-tile {
    flex-shrink: 0;
    width: 50px;
    border-radius: 12px;
    overflow: hidden;
    text-align: center;
    line-height: 1;
    font-style: normal;
    background-color: var(--c-blanc);
    border: 1px solid var(--c-bordure);
    box-shadow: 0 3px 9px -3px rgba(10, 61, 98, 0.22);
}

/* ----- Couleur par catégorie (galerie « vivante ») -----
   Chaque carte porte data-cat="<slug>" → teinte appliquée à la tuile date, la pastille et le liseré.
   Slugs alignés sur Category (Marché→marche, Conférence→conference, Découverte→decouverte). */
.event-card { --cat-color: var(--c-corail); }
.event-card[data-cat="musique"]     { --cat-color: #7c5cc4; }
.event-card[data-cat="sport"]       { --cat-color: #2f9e6b; }
.event-card[data-cat="culture"]     { --cat-color: #3f6cb0; }
.event-card[data-cat="brocante"]    { --cat-color: #a9763f; }
.event-card[data-cat="marche"]      { --cat-color: #6a9a3c; }
.event-card[data-cat="gastronomie"] { --cat-color: #d6603c; }
.event-card[data-cat="famille"]     { --cat-color: #2a9d9d; }
.event-card[data-cat="festival"]    { --cat-color: #d14d9a; }
.event-card[data-cat="atelier"]     { --cat-color: #e08a2e; }
.event-card[data-cat="conference"]  { --cat-color: #5566a8; }
.event-card[data-cat="decouverte"]  { --cat-color: #1e6091; }

.event-card__date-tile-m {
    display: block;
    padding: 0.28rem 0;
    background: linear-gradient(135deg, var(--cat-color) 0%, color-mix(in srgb, var(--cat-color) 72%, #000) 100%);
    color: #fff;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.event-card__date-tile-d {
    display: block;
    padding: 0.32rem 0 0.4rem;
    color: var(--c-marine);
    font-size: 1.3rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

.event-card__heading-text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.3rem;
    min-width: 0;
}

.event-card__category {
    display: inline-block;
    background: linear-gradient(135deg, var(--cat-color) 0%, color-mix(in srgb, var(--cat-color) 72%, #000) 100%);
    color: var(--c-blanc);
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.26rem 0.7rem;
    border-radius: 999px;
    box-shadow: 0 2px 6px -2px color-mix(in srgb, var(--cat-color) 55%, transparent);
}

.event-card__date {
    font-size: 0.82rem;
    color: var(--c-texte-doux);
    font-weight: 600;
}

/* Événement « Incontournable » : tuile date dorée. */
.event-card--featured .event-card__date-tile-m {
    background: linear-gradient(135deg, #d4af37 0%, #b89522 100%);
    color: #3a2e06;
}

.event-card__title {
    margin: 0;
    font-family: var(--font-display);
    font-size: 1.15rem;
    color: var(--c-marine);
    line-height: 1.3;
    font-weight: 700;
    letter-spacing: -0.01em;
    overflow-wrap: anywhere;
}

@media (min-width: 768px) {
    .event-card__title {
        font-size: 1.2rem;
    }
}

.event-card__description {
    margin: var(--space-xs) 0 0;
    font-size: 0.92rem;
    color: var(--c-texte-doux);
    line-height: 1.55;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.event-card[hidden] {
    display: none;
}



/* ----- Couleur par catégorie de LIEU -----
   Même mécanique que pour les événements : la carte porte `data-cat="<slug>"`, la teinte descend
   dans la pastille et le liseré par `--cat-color`. Sans cet attribut, les seize catégories de lieux
   sortaient toutes en corail — la pastille disait le type sans qu'on le voie.

   Les teintes sont choisies pour évoquer leur sujet (le rouge des phares, le vitrail pour le
   religieux, l'ardoise pour les fortifications) ET pour rester lisibles : le blanc sur chacune tient
   au moins 4.5:1 en moyenne sur le dégradé, mesuré sur les deux extrémités. C'est ce qui écarte les
   tons clairs qu'on aurait pu vouloir pour la plage ou la balade. */
.lieu-card[data-cat="plage"]             { --cat-color: #0c7d96; }
.lieu-card[data-cat="falaise"]           { --cat-color: #4a7a92; }
.lieu-card[data-cat="musee"]             { --cat-color: #4a5fa8; }
.lieu-card[data-cat="edifice-religieux"] { --cat-color: #6b4f9e; }
.lieu-card[data-cat="chateau"]           { --cat-color: #8f3f52; }
.lieu-card[data-cat="fortification"]     { --cat-color: #5a6472; }
.lieu-card[data-cat="monument"]          { --cat-color: #8a6a3a; }
.lieu-card[data-cat="phare"]             { --cat-color: #c2452d; }
.lieu-card[data-cat="nature-et-jardin"]  { --cat-color: #3d8a52; }
.lieu-card[data-cat="parc-de-loisirs"]   { --cat-color: #c9497f; }
.lieu-card[data-cat="balade"]            { --cat-color: #5f7d35; }
.lieu-card[data-cat="village"]           { --cat-color: #b06a45; }
.lieu-card[data-cat="point-de-vue"]      { --cat-color: #2f7fa8; }
.lieu-card[data-cat="restaurant"]        { --cat-color: #c85a3c; }
.lieu-card[data-cat="hebergement"]       { --cat-color: #4f5b9c; }
.lieu-card[data-cat="commerce"]          { --cat-color: #8b5a8f; }

/* La pastille du hero d'une fiche lieu prend la teinte de sa catégorie. Elle est codée en corail
   dans `event-detail-main.css`, écrit pour les événements où le hero porte déjà la couleur ailleurs.
   L'ombre suit la teinte : une ombre corail sous une pastille violette se voit. */
.event-hero__category.lieu-card {
    background: linear-gradient(135deg, var(--cat-color) 0%, color-mix(in srgb, var(--cat-color) 72%, #000) 100%);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--cat-color) 45%, transparent);
}

/* Titre de section sur /lieux : un filet de la couleur du groupe, à gauche du libellé. Assez pour
   relier la section à ses cartes, sans transformer le titre en pastille. */
.section-title.lieu-card {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.section-title.lieu-card::before {
    content: "";
    flex: none;
    width: 0.28rem;
    height: 1.1em;
    border-radius: 999px;
    background: var(--cat-color);
}

/*
footer.css

QUOI : Pied de page premium : grille bento (identité, navigation, maillage SEO), réseaux sociaux et barre copyright/mentions.

COMMENT : Copie telle quelle des lignes 2319–2611 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================ Pied de page premium ============================
   Mobile-first STRICT : sections verticales aérées (gap 2rem), cibles tactiles ≥ 48px.
   Desktop ≥768px : grille Bento asymétrique (identité large + 3 colonnes scannables).
   Conserve le voile marine / texte sable de la charte (et le mode sombre plus bas). */
.site-footer {
    position: relative;
    background-color: var(--c-marine);
    color: var(--c-sable);
    margin-top: auto;
    padding-top: var(--space-md);
    padding-bottom: calc(var(--space-md) + env(safe-area-inset-bottom));
    font-size: var(--fs-sm);
    line-height: 1.6;
}

/* Filet lumineux discret en haut du pied de page (finition haut de gamme). */
.site-footer::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 1px;
    background: linear-gradient(90deg, transparent 0%, rgba(245, 233, 212, 0.22) 50%, transparent 100%);
}

/* ── Grille des blocs (vertical mobile, bento desktop) ── */
.site-footer__inner {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.75rem;
    padding-top: var(--space-lg);
    padding-bottom: var(--space-md);
}

/* Maillage SEO (catégories / villes) : masqué sur mobile pour alléger le pied de page,
   réaffiché en bento sur desktop (≥768px) plus bas. */
.site-footer__col--seo {
    display: none;
}

/* Bloc 1 — Identité : marque + accroche touristique + réseaux. */
.site-footer__identity {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.site-footer__logo {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 1.4rem;
    letter-spacing: -0.02em;
    color: var(--c-blanc);
    text-decoration: none;
    transition: opacity var(--duration-fast) var(--ease-out);
}

.site-footer__logo .logo-mark {
    color: var(--c-sable);
    font-size: 1.7rem;
    line-height: 1;
}

.site-footer__logo:hover,
.site-footer__logo:focus-visible {
    opacity: 0.9;
    outline: none;
}

.site-footer__pitch {
    margin: 0;
    max-width: 42ch;
    color: var(--c-sable);
    opacity: 0.74;
    font-size: 0.9rem;
    line-height: 1.65;
}

/* Réseaux sociaux + raccourci newsletter — icônes minimalistes, survol progressif. */
.site-footer__social {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.15rem;
}

.site-footer__social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    color: var(--c-sable);
    background-color: rgba(245, 233, 212, 0.08);
    border: 1px solid rgba(245, 233, 212, 0.16);
    border-radius: 999px;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--duration-base) var(--ease-out),
                background-color var(--duration-base) var(--ease-out),
                border-color var(--duration-base) var(--ease-out),
                transform var(--duration-base) var(--ease-out);
}

.site-footer__social-link svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.site-footer__social-link:hover,
.site-footer__social-link:focus-visible {
    color: var(--c-marine);
    background-color: var(--c-sable);
    border-color: var(--c-sable);
    transform: translateY(-2px);
    outline: none;
}

.site-footer__social-link:focus-visible {
    outline: 2px solid var(--c-sable);
    outline-offset: 3px;
}

/* Blocs 2-4 — colonnes (navigation rapide + maillage SEO catégories/villes). */
.site-footer__heading {
    margin: 0 0 0.6rem;
    font-family: var(--font-display);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--c-sable);
    opacity: 0.95;
}

/* Mobile-first : listes en 2 colonnes serrées → le pied de page ne « mesure plus 10 km ».
   Tous les liens restent visibles (maillage SEO intact). Repasse en 1 colonne en bento desktop. */
.site-footer__list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.1rem 1rem;
}

/* Liens : cible tactile ≥ 44px sur mobile + soulignement linéaire moderne (sans décalage du texte). */
.site-footer__list a {
    position: relative;
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    min-height: 44px;
    padding: 0.15rem 0;
    color: var(--c-sable);
    opacity: 0.82;
    font-size: 0.92rem;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: opacity var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.site-footer__list a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0.7rem;
    height: 1.5px;
    border-radius: 2px;
    background-color: var(--c-sable);
    transform: scaleX(0);
    transform-origin: left center;
    opacity: 0.65;
    transition: transform var(--duration-base) var(--ease-out);
}

.site-footer__list a:hover,
.site-footer__list a:focus-visible {
    opacity: 1;
    outline: none;
}

.site-footer__list a:hover::after,
.site-footer__list a:focus-visible::after {
    transform: scaleX(0.5);
}

.site-footer__list a:focus-visible {
    outline: 2px solid var(--c-sable);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* ── Barre du bas : copyright + mentions légales / accès discrets ── */
.site-footer__bottom {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding-top: var(--space-md);
    border-top: 1px solid rgba(245, 233, 212, 0.16);
}

.site-footer__copy {
    margin: 0;
    font-size: 0.8rem;
    opacity: 0.62;
}

.site-footer__legal {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.1rem 1.1rem;
}

.site-footer__legal a {
    display: inline-flex;
    align-items: center;
    min-height: 48px;
    color: var(--c-sable);
    opacity: 0.72;
    font-size: 0.82rem;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: opacity var(--duration-fast) var(--ease-out);
}

.site-footer__legal a:hover,
.site-footer__legal a:focus-visible {
    opacity: 1;
    text-decoration: underline;
    text-underline-offset: 3px;
    outline: none;
}

.site-footer__legal a:focus-visible {
    outline: 2px solid var(--c-sable);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.site-footer__admin {
    opacity: 0.5;
}

/* ── Desktop ≥768px : grille Bento asymétrique + cibles plus compactes (pointeur fin) ── */
@media (min-width: 768px) {
    .site-footer__inner {
        grid-template-columns: 1.7fr 1fr 1fr 1fr;
        gap: 2.5rem;
        align-items: start;
    }

    /* Réaffiche le maillage SEO masqué sur mobile. */
    .site-footer__col--seo {
        display: block;
    }

    /* Bento : chaque rubrique = sa propre colonne étroite → liste verticale (annule le 2-col mobile). */
    .site-footer__list {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .site-footer__list a {
        min-height: 38px;
        font-size: 0.95rem;
    }

    .site-footer__legal a {
        min-height: 36px;
    }

    .site-footer__bottom {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        gap: 1.5rem;
    }

    .site-footer__legal {
        justify-content: flex-end;
    }
}


/*
events-grid.css

QUOI : Points de rupture responsive de la grille d'événements (2 puis 3 colonnes) et padding de la zone de recherche en desktop.

COMMENT : Copie telle quelle des lignes 2612–2629 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/


@media (min-width: 600px) {
    .events-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 960px) {
    .events-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: var(--space-lg);
    }

    .search-section {
        padding: var(--space-xl) 0 var(--space-lg);
    }
}


/*
event-detail-extras.css

QUOI : Suggestions d'événements similaires en bas de fiche événement.

COMMENT : Copie telle quelle des lignes 2630–2662 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ---------- Suggestions similaires (fiche événement) ---------- */
.event-similar {
    margin-top: var(--space-xl);
    padding-top: var(--space-lg);
    border-top: 1px solid var(--c-bordure);
}

.event-similar__header {
    margin-bottom: var(--space-md);
}

.event-similar__title {
    margin: 0 0 0.4rem;
    font-family: var(--font-display);
    font-size: 1.6rem;
    color: var(--c-marine);
}

.event-similar__subtitle {
    margin: 0;
    color: var(--c-texte-doux);
    font-size: 0.95rem;
}

.event-similar__subtitle::before {
    content: '✨';
    margin-right: 0.4rem;
}

.event-similar__grid {
    margin-top: var(--space-sm);
}


/*
prose.css

QUOI : Pages textuelles (mentions légales, à propos, confidentialité) : gabarit .prose-page et lien retour intégré au hero.

COMMENT : Copie telle quelle des lignes 2663–2799 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/


/* ──────────────────────────────────────────────────
   Pages textuelles (mentions légales, à propos…)
   ────────────────────────────────────────────────── */

.prose-page {
    width: 100%;
    max-width: 720px;
    margin: 0 auto;
    /* Mobile-first : on évite le grand vide en haut ; respiration plus large ≥640px. */
    padding: var(--space-lg) max(var(--space-sm), env(safe-area-inset-left));
}

@media (min-width: 640px) {
    .prose-page {
        padding: var(--space-xl) var(--space-md);
    }
}

.prose-page h1 {
    font-family: var(--font-display);
    font-size: clamp(1.8rem, 4vw, 2.4rem);
    font-weight: 800;
    color: var(--c-marine);
    letter-spacing: -0.025em;
    line-height: 1.15;
    margin: 0 0 var(--space-lg);
    padding-bottom: var(--space-md);
    border-bottom: 2px solid var(--c-bordure);
}

.prose-page h2 {
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--c-marine);
    margin: var(--space-lg) 0 var(--space-xs);
    padding-left: 0.875rem;
    border-left: 3px solid var(--c-corail);
    line-height: 1.3;
}

.prose-page h3 {
    font-size: 1rem;
    font-weight: 600;
    color: var(--c-texte);
    margin: var(--space-md) 0 var(--space-xs);
}

.prose-page p {
    margin: 0 0 1em;
    line-height: 1.75;
    color: var(--c-texte);
}

.prose-page ul,
.prose-page ol {
    margin: 0 0 1.25em;
    padding-left: 1.5rem;
    line-height: 1.75;
    color: var(--c-texte);
}

.prose-page li {
    margin-bottom: 0.5em;
}

.prose-page a:not(.back-link) {
    color: var(--c-mer);
    text-decoration: underline;
    text-decoration-color: rgba(30, 96, 145, 0.35);
    text-underline-offset: 2px;
    transition: color var(--duration-fast), text-decoration-color var(--duration-fast);
}

.prose-page a:not(.back-link):hover {
    color: var(--c-marine);
    text-decoration-color: var(--c-marine);
}

.prose-page strong {
    font-weight: 600;
    color: var(--c-marine);
}

/* Lien retour en bas des pages textuelles + page partenaire.
   TEXTE NU (plus de pastille « badge ») : la pilule blanche à bordure était un badge daté ; le lien
   reprend la couleur de lien du site (`--c-mer`, remappée en bleu lumineux la nuit) et se souligne au
   survol. La flèche « ← » fait partie du libellé traduit. Lisible dans les deux thèmes sans règle
   dédiée : `--c-mer` = #1e6091 sur sable (clair) et #7cb8e2 sur encre (sombre). */
.back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: var(--space-md);
    padding: 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--c-mer);
    background: none;
    border: 0;
    border-radius: 0;
    text-decoration: none;
    box-shadow: none;
    transition: color var(--duration-fast) var(--ease-out),
                gap var(--duration-fast) var(--ease-out);
}

.back-link:hover {
    color: var(--c-marine);
    gap: 0.55rem;
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* Première ligne du hero : libellé (kicker) à gauche, lien retour à droite, alignés sur la même
   ligne — au lieu d'un bouton flottant au-dessus qui créait une bande vide sous l'en-tête. */
/* Lien retour INTÉGRÉ dans le hero marine, en TEXTE NU — plus de pastille « verre ».
   Pourquoi : la pilule (padding + bordure + fond) était large, et sur les heros dont le kicker est
   long (« Publié sous 48h » sur /proposer-un-evenement) elle passait à la ligne sous le kicker au
   lieu de rester dans le coin droit, cassant la ligne unique voulue par `.page-hero__topline`.
   Le lien reprend donc exactement la typographie de `.page-hero__eyebrow` (coin gauche) : les deux
   extrémités de la ligne se répondent, et l'encombrement chute assez pour tenir sur une ligne.
   Le hero étant toujours sombre (thème clair ET sombre), aucun remappage dark n'est nécessaire. */
.page-hero .back-link {
    flex: 0 0 auto;
    margin: 0;
    padding: 0;
    font-size: 0.68rem;
    font-weight: 700;
    /* Interlettrage volontairement plus serré que le kicker (0.16em) : à 0.16em, « ← Retour aux
       événements » mesurait 219px et, avec le kicker à 151px, dépassait la ligne de 345px — le lien
       repassait donc SOUS le kicker. 0.07em conserve la parenté typographique en tenant sur une
       seule ligne. */
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--c-sable);
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}
.page-hero .back-link:hover {
    color: #fff;
    background: none;
    transform: none;
    text-decoration: underline;
    text-underline-offset: 3px;
}


/*
bento.css

QUOI : Grille bento « verre dépoli » générique des pages annexes (cartes, listes à pastilles check, alias .about-*).

COMMENT : Copie telle quelle des lignes 2800–2969 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================ Grille bento « verre dépoli » — système réutilisable ============================
   Pages texte / annexes (À propos, mentions légales, confidentialité, newsletter…) qui ne chargent QUE
   style.css. On déclare ici une grille bento GÉNÉRIQUE réutilisant les tokens --glass-* / --radius-* /
   --space-* et la classe .glass-card. `.about-*` est conservé comme alias de `.bento-*` pour ne pas casser
   la page À propos existante. Mobile-first : 1 colonne ; ≥720px : bento 2 colonnes, carte `--feature`
   en pleine largeur. */
.about-page,
.bento-page {
    width: 100%;
    max-width: 1100px;
    margin: 0 auto;
    padding: var(--space-lg) max(var(--space-sm), env(safe-area-inset-left));
}

@media (min-width: 640px) {
    .about-page,
    .bento-page {
        padding: var(--space-xl) var(--space-md);
    }
}

.about-bento,
.bento-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-md);
}

@media (min-width: 720px) {
    .about-bento,
    .bento-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .about-card--feature,
    .bento-card--feature {
        grid-column: 1 / -1;
    }
}

.about-card,
.bento-card {
    display: flex;
    flex-direction: column;
    padding: var(--space-lg);
    transition: transform var(--duration-base) var(--ease-out),
                box-shadow var(--duration-base) var(--ease-out);
}

.about-card:hover,
.bento-card:hover {
    transform: translateY(-3px);
}

.about-card h2,
.bento-card h2,
.bento-card h3 {
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--c-marine);
    line-height: 1.3;
    margin: 0 0 var(--space-sm);
    padding-left: 0.75rem;
    border-left: 3px solid var(--c-corail);
}

.about-card p,
.bento-card p {
    margin: 0 0 0.9em;
    line-height: 1.75;
    color: var(--c-texte);
}

/* Dernier bloc d'une carte : pas de marge basse résiduelle (p, ul, ol…). */
.about-card p:last-child,
.bento-card > :last-child {
    margin-bottom: 0;
}

.about-card--feature p,
.bento-card--feature p {
    font-size: 1.05rem;
}

.about-card strong,
.bento-card strong {
    font-weight: 600;
    color: var(--c-marine);
}

/* Listes au sein des cartes (page confidentialité). */
.bento-card ul,
.bento-card ol {
    margin: 0 0 0.9em;
    padding-left: 1.25rem;
    line-height: 1.75;
    color: var(--c-texte);
}

.bento-card li {
    margin-bottom: 0.45em;
}

.bento-card li:last-child {
    margin-bottom: 0;
}

/* `:not(.btn)` INDISPENSABLE : cette règle habille les liens de PROSE d'une carte, mais sa
   spécificité (0,2,1) l'emportait sur `.btn--primary` (0,1,0) — tout bouton posé dans une carte
   bento perdait son texte blanc au profit de `--c-mer`, soit du bleu de mer sur le dégradé marine
   du bouton : 3,14:1, sous le seuil WCAG AA (4,5:1). Constaté sur la page 404, mais le défaut
   était générique aux deux thèmes. */
.about-card a:not(.back-link):not(.btn),
.bento-card a:not(.back-link):not(.btn) {
    color: var(--c-mer);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-color: rgba(30, 96, 145, 0.35);
    text-underline-offset: 2px;
    transition: color var(--duration-fast) var(--ease-out),
                text-decoration-color var(--duration-fast) var(--ease-out);
}

.about-card a:not(.back-link):not(.btn):hover,
.bento-card a:not(.back-link):not(.btn):hover {
    color: var(--c-marine);
    text-decoration-color: var(--c-marine);
}

.about-footer,
.bento-footer {
    margin-top: var(--space-lg);
}

/* Liste de bénéfices à pastilles « check » dans une carte bento (page newsletter…). */
.bento-benefits {
    list-style: none;
    margin: var(--space-sm) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

.bento-benefits li {
    display: flex;
    align-items: flex-start;
    gap: 0.65rem;
    margin: 0;
    line-height: 1.5;
    color: var(--c-texte);
}

.bento-benefits li::before {
    content: "✓";
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.45rem;
    height: 1.45rem;
    margin-top: 0.05rem;
    border-radius: 50%;
    background: var(--c-mer);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 700;
    line-height: 1;
}

/* Sombre : --c-mer s'éclaircit (check blanc peu lisible) → on rétablit un bleu franc. */
[data-theme="dark"] .bento-benefits li::before {
    background: #1e6091;
}


/*
event-context-nav.css

QUOI : Navigation contextuelle de la fiche événement (liens précédent/suivant en grille).

COMMENT : Copie telle quelle des lignes 2970–3026 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ---------- Navigation contextuelle (fiche événement) ---------- */
.event-context-nav {
    margin: var(--space-lg) 0;
    display: grid;
    gap: var(--space-sm);
    grid-template-columns: 1fr;
}

@media (min-width: 640px) {
    .event-context-nav {
        grid-template-columns: 1fr 1fr;
    }
}

.event-context-nav__link {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-md);
    background: linear-gradient(135deg, var(--c-blanc) 0%, var(--c-sable-clair) 100%);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: var(--c-texte);
    transition: transform var(--duration-base) var(--ease-out), box-shadow var(--duration-base) var(--ease-out), border-color var(--duration-base) var(--ease-out);
}

.event-context-nav__link:hover,
.event-context-nav__link:focus-visible {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
    border-color: var(--c-mer-clair);
}

.event-context-nav__icon {
    font-size: 1.6rem;
    flex-shrink: 0;
}

.event-context-nav__text {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.event-context-nav__label {
    font-size: 0.85rem;
    color: var(--c-texte-doux);
}

.event-context-nav__text strong {
    font-family: var(--font-display);
    font-size: 1.1rem;
    color: var(--c-marine);
}


/*
hero.css

QUOI : Hero de page unifié (.page-hero) : variante photo, signature « horizon opale », topline kicker, titre écran-titre, rangée méta et tags. Inclut le raccord des pages taxonomie.

COMMENT : Copie telle quelle des lignes 3027–3284 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ---------- Pages taxonomie (catégorie / ville) ---------- */
.taxonomy-page {
    padding-top: var(--space-md);
    padding-bottom: var(--space-xl);
}

/* ============================ Hero de page unifié (.page-hero) ============================
   QUOI : bandeau d'en-tête commun à TOUTES les pages secondaires (ville, catégorie, lieux, à propos,
   newsletter, mentions, partenaire, erreurs). L'accueil garde son hero éditorial propre (home.css).

   COMMENT : modèle = l'ancien hero « photo de ville ». Bloc marine profond, photo de décor optionnelle
   (`.has-photo` + `--hero-bg`) derrière un voile marine ; titre clair surdimensionné (sœur du hero
   d'accueil), kicker « live » pulsant, et la SIGNATURE « horizon opale » (`.page-hero__horizon`) :
   un filet lumineux opalescent en bas du bloc — la lumière d'opale là où la mer rejoint le ciel, ce
   qui donne son nom à la Côte d'Opale. Mobile-first ; enrichi ≥640px.

   POURQUOI : une seule identité d'en-tête sur tout le site, déclinable avec ou sans photo. */
.page-hero {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    margin: 0 0 var(--space-lg);
    padding: clamp(1.5rem, 5vw, 2.75rem) clamp(1.15rem, 3.5vw, 2.5rem) clamp(2rem, 4.5vw, 2.75rem);
    border-radius: var(--radius-xl);
    color: var(--c-blanc);
    /* Dégradé signature « mer → couchant » : marine (haut-gauche, où est le texte) qui chauffe vers le
       corail (bas-droite). Halo bleu en haut-gauche pour ancrer la lisibilité du titre. */
    background:
        radial-gradient(120% 120% at 8% 6%, rgba(74, 144, 184, 0.38) 0%, transparent 52%),
        linear-gradient(140deg, var(--c-marine) 0%, var(--c-mer) 40%, var(--c-corail) 100%);
    box-shadow:
        0 20px 48px -24px rgba(10, 61, 98, 0.55),
        0 4px 12px -6px rgba(10, 61, 98, 0.3);
}

@media (min-width: 640px) {
    .page-hero {
        margin-bottom: var(--space-xl);
    }
}

/* Variante photo : image de décor sous un voile marine → texte lisible (thème clair ET sombre). */
.page-hero.has-photo {
    background:
        linear-gradient(140deg, rgba(10, 61, 98, 0.88) 0%, rgba(30, 96, 145, 0.66) 42%, rgba(224, 120, 86, 0.60) 100%),
        var(--hero-bg);
    background-size: auto, cover;
    background-position: center, center;
    background-repeat: no-repeat;
}

/* Reveal au chargement (neutralisé si mouvement réduit). */
@media (prefers-reduced-motion: no-preference) {
    .page-hero {
        animation: fade-in-up 640ms var(--ease-out) both;
    }
}

/* ── SIGNATURE « horizon opale » : filet lumineux opalescent + bloom doux, posé en bas du bloc.
   Le dégradé bleu pâle → sable → corail évoque la lumière d'opale sur l'horizon de la côte. ── */
.page-hero__horizon {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    border: 0;
    margin: 0;
    pointer-events: none;
    background: linear-gradient(90deg,
        transparent 3%,
        rgba(160, 205, 235, 0.95) 24%,
        rgba(255, 247, 230, 1) 50%,
        rgba(232, 138, 102, 0.95) 76%,
        transparent 97%);
    box-shadow:
        0 0 10px 0 rgba(255, 247, 230, 0.6),
        0 0 26px 2px rgba(160, 205, 235, 0.65),
        0 0 60px 10px rgba(232, 138, 102, 0.32);
    opacity: 1;
}

/* Aube qui monte de l'horizon : voile lumineux dégradé vers le haut, posé juste au-dessus du filet. */
.page-hero__horizon::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 90px;
    background: linear-gradient(to top,
        rgba(190, 214, 234, 0.22) 0%,
        rgba(190, 214, 234, 0.06) 45%,
        transparent 100%);
    pointer-events: none;
}

/* Reflet « opale » qui balaie lentement le filet (gravy — le filet reste lisible sans). */
.page-hero__horizon::after {
    content: "";
    position: absolute;
    top: -1px;
    bottom: -1px;
    width: 26%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.9), transparent);
    filter: blur(2px);
    transform: translateX(-130%);
    pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
    .page-hero__horizon::after {
        animation: opal-glint 7s ease-in-out infinite;
    }
}

@keyframes opal-glint {
    0%       { transform: translateX(-130%); }
    55%, 100% { transform: translateX(460%); }
}

/* Topline : kicker « live » (pastille corail pulsante) + lien retour. */
.page-hero__topline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: clamp(0.9rem, 3vw, 1.4rem);
}

.page-hero__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    margin: 0;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--c-sable);
}

.page-hero__pulse {
    width: 8px;
    height: 8px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--c-corail);
    animation: page-hero-pulse 2.2s ease-out infinite;
}

@keyframes page-hero-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(224, 120, 86, 0.6); }
    70%  { box-shadow: 0 0 0 9px rgba(224, 120, 86, 0); }
    100% { box-shadow: 0 0 0 0 rgba(224, 120, 86, 0); }
}

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

/* Titre surdimensionné « écran-titre » — sœur du hero d'accueil, mais en blanc sur marine. */
.page-hero__title {
    margin: 0;
    font-family: var(--font-display);
    font-size: clamp(1.95rem, 6vw, 3.2rem);
    /* 1.04 rognait les jambages de la dernière ligne des titres qui passent sur 3 lignes en mobile
       (404 : hauteur de contenu 100px pour une boîte de 97px). 1.1 est la valeur spécifiée par
       DESIGN.md §3 pour le niveau Display — on s'aligne sur la spec plutôt que de compenser au cas
       par cas. */
    line-height: 1.1;
    font-weight: 800;
    letter-spacing: -0.035em;
    color: var(--c-blanc);
    text-wrap: balance;
}

/* Accent corail/opale sur un mot-clé du titre (ville, catégorie…). */
.page-hero__accent {
    background: linear-gradient(105deg, #ffd9a8 0%, var(--c-corail) 52%, #8fc4e6 120%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: var(--c-corail);
}

.page-hero__intro {
    margin: clamp(0.7rem, 2vw, 1rem) 0 0;
    max-width: 62ch;
    font-size: var(--fs-md);
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.92);
}

/* Rangée méta : pilule de comptage + actions/filtres éventuels. */
.page-hero__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin-top: clamp(1rem, 3vw, 1.4rem);
}

.page-hero__pill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    padding: 0.4rem 0.9rem;
    background: rgba(255, 255, 255, 0.16);
    border: 1px solid rgba(255, 255, 255, 0.22);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    border-radius: 999px;
    font-size: 0.9rem;
    font-weight: 600;
}

/* Titre de section dans le corps de page (groupes de lieux, « à proximité »…) :
   reprend le display marine, indépendant du hero. */
.section-title {
    margin: 0 0 var(--space-sm);
    font-family: var(--font-display);
    font-size: clamp(1.4rem, 3.5vw, 2rem);
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--c-marine);
}

/* Chapô sous un titre de section. */
.section-lead {
    margin: 0 0 var(--space-md);
    max-width: 60ch;
    color: var(--c-texte-doux);
    font-size: var(--fs-md);
    line-height: 1.6;
}

/* Rangée d'étiquettes (tags d'un lieu) : pastilles qui s'enroulent. Les chips n'ont pas de
   `--cat-color` ici → on leur donne un habillage sable/marine lisible (sinon texte blanc sur fond). */
.lieu-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    margin: 0 0 var(--space-md);
}

.lieu-tags .event-card__category {
    background: var(--c-sable-clair);
    color: var(--c-marine);
    border: 1px solid var(--c-bordure);
    box-shadow: none;
}

[data-theme="dark"] .lieu-tags .event-card__category {
    background: rgba(124, 184, 226, 0.12);
    color: var(--c-mer-clair);
    border-color: rgba(124, 184, 226, 0.28);
}

/* Informations pratiques d'un lieu (adresse, téléphone, site web). Deux colonnes tant qu'il y a la
   place, une seule dès que l'écran se resserre : sous 30rem, un libellé et sa valeur côte à côte
   laissent à la valeur trois caractères par ligne. */
.lieu-infos {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.35rem var(--space-md);
    margin: var(--space-md) 0 0;
    padding-top: var(--space-md);
    border-top: 1px solid var(--c-bordure);
    font-size: var(--fs-md);
}

.lieu-infos dt {
    color: var(--c-texte-doux);
    font-weight: 600;
}

.lieu-infos dd {
    margin: 0;
    color: var(--c-texte);
    /* Une URL longue déborderait de sa colonne : elle doit pouvoir se couper n'importe où. */
    overflow-wrap: anywhere;
}

.lieu-infos a {
    color: var(--c-mer);
    text-decoration: underline;
    text-underline-offset: 0.15em;
}

[data-theme="dark"] .lieu-infos a {
    color: var(--c-mer-clair);
}

@media (max-width: 30rem) {
    .lieu-infos {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .lieu-infos dd {
        margin-bottom: var(--space-sm);
    }
}


/* Mention de provenance d'une fiche reprise d'une source ouverte. Discrète — c'est une obligation
   légale, pas un élément éditorial — mais lisible : un crédit sous le seuil de contraste ne vaut
   pas mieux qu'un crédit absent. */
.lieu-attribution {
    margin: var(--space-md) 0 0;
    padding-top: var(--space-sm);
    border-top: 1px solid var(--c-bordure);
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--c-texte-doux);
}

/*
featured.css

QUOI : Événements « Incontournables » : liseré et tuile date dorés, badge, et contrôles admin de marquage.

COMMENT : Copie telle quelle des lignes 3285–3389 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ---------- Événements « Incontournables » (positionnement préférentiel) ----------
   Identité premium : cadre or feutré, bandeau doré en haut, badge ruban avec
   reflet, halo chaud au hover. Reste dans le langage visuel Opale (marine/corail)
   en n'utilisant l'or que comme accent. */
.event-card--featured {
    border: 1px solid rgba(212, 175, 55, 0.55);
    border-left: 3px solid #d4af37;
    background:
        radial-gradient(circle at 100% 0%, rgba(245, 215, 110, 0.18) 0%, transparent 45%),
        linear-gradient(180deg, #fffaf0 0%, var(--c-blanc) 55%);
    box-shadow:
        0 1px 2px rgba(184, 149, 34, 0.08),
        0 6px 16px rgba(184, 149, 34, 0.10);
    padding-top: calc(var(--space-md) + 0.85rem);
    overflow: hidden;
}

/* Fin liseré doré en haut de la card — accent discret mais immédiatement lisible. */
.event-card--featured::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: linear-gradient(90deg,
        rgba(212, 175, 55, 0) 0%,
        #d4af37 18%,
        #f5d76e 50%,
        #d4af37 82%,
        rgba(212, 175, 55, 0) 100%);
    z-index: 1;
    pointer-events: none;
}

/* Le gradient diagonal du `.event-card::before` reste, mais teinté or au lieu de mer. */
.event-card--featured::before {
    background: linear-gradient(135deg, transparent 0%, transparent 55%, rgba(212, 175, 55, 0.10) 100%);
}

@media (hover: hover) {
    .event-card--featured:hover {
        border-color: #d4af37;
        border-left-color: #b89522;
        box-shadow:
            0 2px 4px rgba(184, 149, 34, 0.12),
            0 14px 32px rgba(184, 149, 34, 0.22),
            0 0 0 1px rgba(212, 175, 55, 0.35);
        transform: translateY(-5px);
    }
}

.event-card__featured-badge {
    position: absolute;
    top: 0.65rem;
    left: 0.85rem;
    display: inline-flex;
    align-items: center;
    gap: 0.32rem;
    padding: 0.34rem 0.75rem;
    font-family: var(--font-display, inherit);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #4a3a08;
    background: linear-gradient(135deg, #f5d76e 0%, #e0bf3a 55%, #c69820 100%);
    border-radius: 999px;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.55),
        inset 0 -1px 0 rgba(122, 91, 12, 0.22),
        0 3px 10px rgba(184, 149, 34, 0.38);
    line-height: 1;
    z-index: 3;
    white-space: nowrap;
}

.event-card__featured-icon {
    display: inline-block;
    font-size: 0.95rem;
    line-height: 0;
    color: #ffffff;
    text-shadow: 0 0 6px rgba(255, 235, 150, 0.9);
    transform: translateY(0.5px);
}

/* ---------- Admin : marquage et toggle « Incontournable » ---------- */
.admin-event-card--featured {
    border-left: 3px solid #d4af37;
    background: linear-gradient(180deg, rgba(212, 175, 55, 0.05) 0%, transparent 50%);
}

.admin-event-card__featured-flag {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.55rem;
    font-size: 0.72rem;
    font-weight: 700;
    color: #5c4708;
    background: linear-gradient(135deg, #f5d76e 0%, #d4af37 100%);
    border-radius: 999px;
    letter-spacing: 0.02em;
}


/*
flash.css

QUOI : Messages flash (succès / erreur) des pages ne chargeant que la feuille principale.

COMMENT : Copie telle quelle des lignes 3390–3410 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ── Messages flash (page newsletter et autres pages ne chargeant que style.css) ── */
.flash {
    padding: var(--space-sm);
    margin-bottom: var(--space-md);
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
    border: 1px solid;
}

.flash--success {
    background-color: #e8f5e9;
    color: #1b5e20;
    border-color: #2e7d32;
}

.flash--error {
    background-color: #fbe9e7;
    color: #b71c1c;
    border-color: var(--c-corail);
}


/*
forms.css

QUOI : Erreurs de validation des formulaires (.form-errors, aria-invalid) et composant d'inscription newsletter (footer, page dédiée, carte bento).

COMMENT : Copie telle quelle des lignes 3411–3631 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ──────────────────────────────────────────────────────────────────────────
   Erreurs de validation des formulaires
   Rendu par le thème global `templates/form/opale_errors.html.twig` :
   `.form-errors` (message + icône) sous chaque champ, et `aria-invalid="true"`
   posé sur le champ fautif → bordure corail. S'applique à TOUS les formulaires
   (front + back-office), `style.css` étant chargé partout où ils apparaissent.
   ────────────────────────────────────────────────────────────────────────── */
.form-errors {
    list-style: none;
    margin: 0.4rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.form-errors__item {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    font-size: 0.85rem;
    font-weight: 600;
    line-height: 1.35;
    color: #b5462a;
}

.form-errors__icon {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    margin-top: 0.08rem;
}

/* Champ invalide : bordure corail + halo, quel que soit le type (input, select, textarea, fichier). */
[aria-invalid="true"] {
    border-color: var(--c-corail) !important;
}

[aria-invalid="true"]:focus {
    box-shadow: 0 0 0 4px rgba(224, 120, 86, 0.18) !important;
}

[data-theme="dark"] .form-errors__item {
    color: #ffb9a8;
}

/* ── Composant d'inscription newsletter (footer + page dédiée) ── */
.newsletter-signup {
    width: 100%;
    max-width: 560px;
    margin: 0 auto;
    text-align: center;
}

.newsletter-signup__title {
    margin: 0 0 0.35rem;
    font-family: var(--font-display);
    font-size: 1.1rem;
    font-weight: 600;
}

.newsletter-signup__intro {
    margin: 0 0 var(--space-sm);
    font-size: 0.9rem;
    opacity: 0.85;
}

/* Label associé mais visuellement masqué : le titre/intro et le placeholder donnent le contexte. */
.newsletter-form__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.newsletter-form__controls {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
}

.newsletter-form__input {
    flex: 1 1 240px;
    min-width: 0;
    min-height: 48px;
    padding: 0.75rem 0.9rem;
    font-size: var(--fs-base);
    color: var(--c-texte);
    background-color: var(--c-sable-clair);
    border: 1.5px solid var(--c-bordure);
    border-radius: var(--radius-md);
    outline: none;
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.newsletter-form__input:hover {
    border-color: var(--c-mer-clair);
}

.newsletter-form__input:focus,
.newsletter-form__input:focus-visible {
    border-color: var(--c-mer);
    box-shadow: 0 0 0 4px var(--c-focus);
    outline: none;
}

.newsletter-form__submit {
    flex: 0 0 auto;
    white-space: nowrap;
}

.newsletter-form__rgpd {
    margin: 0.7rem 0 0;
    font-size: 0.75rem;
    line-height: 1.45;
    opacity: 0.75;
}

.newsletter-form__rgpd a {
    color: inherit;
    text-underline-offset: 2px;
}

.newsletter-form__feedback {
    margin: 0.7rem 0 0;
    font-size: 0.85rem;
    font-weight: 600;
}

.newsletter-form__feedback.is-success {
    color: #1b5e20;
}

.newsletter-form__feedback.is-error {
    color: #b71c1c;
}

/* Contexte footer (fond marine) : éclaircir textes et adapter le retour inline pour le contraste. */
.site-footer__newsletter {
    padding-bottom: var(--space-md);
    margin-bottom: var(--space-sm);
    border-bottom: 1px solid rgba(245, 233, 212, 0.18);
    color: var(--c-sable);
}

.site-footer__newsletter .newsletter-form__feedback.is-success {
    color: #b8f0c0;
}

.site-footer__newsletter .newsletter-form__feedback.is-error {
    color: #ffc9c0;
}

/* Pied de page (fond marine) : le primaire devient corail pour ressortir sur le bleu. */
.site-footer__newsletter .btn--primary {
    background: linear-gradient(135deg, var(--c-corail) 0%, var(--c-corail-fonce) 100%);
    color: #fff;
}

.site-footer__newsletter .btn--primary:hover {
    box-shadow: 0 10px 26px -8px rgba(224, 120, 86, 0.50);
}

/* ── Page newsletter dédiée : le composant d'inscription devient une carte premium ── */
.prose-page .newsletter-signup {
    margin: var(--space-lg) auto var(--space-md);
    padding: clamp(1.5rem, 5vw, 2.5rem);
    background:
        radial-gradient(120% 130% at 0% 0%, rgba(74, 144, 184, 0.12) 0%, transparent 55%),
        linear-gradient(180deg, var(--c-blanc) 0%, var(--c-sable-clair) 100%);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-md);
}

/* Page bento dédiée : l'encart d'inscription devient une carte « action » verre dépoli,
   sœur visuelle des .glass-card (même rayon/filet/ombre), alignée à gauche et pleine largeur
   de sa colonne. Surface un peu plus dense (--glass-bg-strong) pour la distinguer du « pitch ».
   --glass-bg-strong est assez opaque pour rester lisible sans backdrop-filter. */
.bento-page .newsletter-signup {
    width: 100%;
    max-width: none;
    margin: 0;
    padding: var(--space-lg);
    text-align: left;
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: saturate(160%) blur(var(--glass-blur));
    backdrop-filter: saturate(160%) blur(var(--glass-blur));
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--glass-shadow);
}

.prose-page .newsletter-signup__title {
    font-size: var(--fs-lg);
    color: var(--c-marine);
}

/* Titre de l'encart bento : même poids qu'un titre de carte, sans accent de bordure
   (DESIGN.md interdit les border-left/border-right colorés sur cartes/alertes). */
.bento-page .newsletter-signup__title {
    margin: 0 0 var(--space-sm);
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--c-marine);
}

.prose-page .newsletter-signup__intro {
    font-size: var(--fs-base);
}

.bento-page .newsletter-signup__intro {
    font-size: var(--fs-base);
    color: var(--c-texte-doux);
}

.prose-page .newsletter-form__controls,
.bento-page .newsletter-form__controls {
    margin-top: var(--space-sm);
}


/*
form-fields.css

QUOI : Composants de champ réellement partagés entre plusieurs contextes (au-delà d'un simple
       ajustement de valeurs sur un sélecteur déjà scopé par page). Pour l'instant : la bascule à
       choix en pilules (`.field-toggle`), généralisée depuis le sélecteur de mode de date de
       l'événement pour être réutilisable par tout futur formulaire ayant un choix binaire/court.

COMMENT : Deux `<label>` enveloppant chacun un radio natif (masqué visuellement, conservé pour le
clavier/lecteur d'écran) ; l'état coché est porté par `:has(input:checked)`, même motif que
`.toolbar-pill`/`.pub-chip`.

OÙ : Importé par assets/styles/app.imports.css. Utilisé par templates/event/submission.html.twig
     et templates/admin/edit.html.twig (bascule de mode de date).

POURQUOI : docs/superpowers/specs/2026-07-15-refonte-formulaires-publics-design.md.
*/
/* Contrôle segmenté compact : UNE piste recessée (teinte marine à peine visible, comme une
   encoche), les options n'ont ni bordure ni ombre propres — seule l'option cochée reçoit un fond
   marine plein. Largeur égale des deux segments (`flex: 1 1 0`) pour un rendu net et symétrique,
   pas deux boutons de tailles différentes flottant dans la piste. */
.field-toggle {
    display: flex;
    gap: 2px;
    max-width: 22rem;
    padding: 3px;
    margin-bottom: 1rem;
    background-color: rgba(10, 61, 98, 0.055);
    border: 1px solid rgba(10, 61, 98, 0.10);
    border-radius: 999px;
}

/* Spécificité (0,2,0) : bat `.submission-form label` (0,1,1, submission-form.css) quel que soit
   l'ordre de chargement — sans ce préfixe, l'option NON cochée héritait de la couleur marine
   pleine (règle générique de label) au lieu du gris atténué voulu, et les deux segments du
   contrôle semblaient également « actifs », sans hiérarchie visuelle. */
.field-toggle .field-toggle__option {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 0;
    min-height: 38px;
    padding: 0.4rem 0.9rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--c-texte-doux);
    background-color: transparent;
    border: none;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.field-toggle__option input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}

/* form_label() rend un <label> IMBRIQUÉ dans la pilule : les règles de page
   `.submission-form label` (couleur marine, display block, marge) le repeignaient
   par-dessus l'état coché → marine-sur-marine en clair, texte invisible.
   Spécificité (0,2,1) pour battre (0,1,1) quel que soit l'ordre de chargement. */
.field-toggle .field-toggle__option label {
    display: inline;
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    color: inherit;
    cursor: pointer;
}

.field-toggle .field-toggle__option:hover {
    color: var(--c-marine);
}

.field-toggle .field-toggle__option:has(input:checked) {
    color: var(--c-blanc);
    background-color: var(--c-marine);
    box-shadow: 0 1px 3px rgba(10, 61, 98, 0.18);
}

.field-toggle .field-toggle__option:has(input:focus-visible) {
    outline: 2px solid var(--c-mer);
    outline-offset: 2px;
}

/* Mode sombre : --c-marine est remappé en encre CLAIRE (#eaf1f8) → l'état coché
   devenait blanc-sur-blanc (1.1:1, texte invisible). Fond « marque » saturé
   explicite, même motif que les autres pastilles actives du sombre. 8.9:1 vérifié. */
[data-theme="dark"] .field-toggle .field-toggle__option:has(input:checked) {
    color: #ffffff;
    background: linear-gradient(135deg, #0e4d7a 0%, #1e6091 100%);
}

/* Piste : encoche recessée plus visible sur fond de carte sombre. */
[data-theme="dark"] .field-toggle {
    background-color: rgba(0, 0, 0, 0.28);
    border-color: var(--c-bordure);
}

/*
theme-dark.css

QUOI : Mode sombre piloté par [data-theme="dark"] : remappage des tokens et surfaces (encre), fonds marine saturés, cas particuliers (Leaflet, incontournables, flash).

COMMENT : Copie telle quelle des lignes 3632–3796 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================================================
   MODE SOMBRE — piloté par [data-theme="dark"] sur <html>
   ────────────────────────────────────────────────────────────
   L'attribut est posé avant peinture par le script d'init (en-tête
   partagé, présent sur toutes les pages front) :
   - mode "auto"  → résolu selon l'APPAREIL (prefers-color-scheme) ET l'HEURE ;
   - mode "light" / "dark" → choix MANUEL mémorisé (localStorage).
   Une seule source de vérité couvre donc l'automatique ET le manuel.

   Stratégie de couleurs :
   - --c-marine devient une encre CLAIRE → corrige ~46 titres sans toucher au markup.
   - --c-blanc reste BLANC → le texte blanc sur pastilles colorées reste lisible ;
     les surfaces blanches sont, elles, redéfinies en encre.
   - Les rares fonds « marque » (texte blanc) reçoivent un dégradé marine saturé.
   ============================================================ */
:root[data-theme="dark"] {
    color-scheme: dark;

    --c-marine: #eaf1f8;        /* encre claire (titres, libellés) */
    --c-mer: #7cb8e2;           /* accent bleu lumineux */
    --c-mer-clair: #a9d4f0;
    --c-texte: #e7eef6;
    --c-texte-doux: #a3b6c6;
    /* --c-sable reste un sable CLAIR : c'est un accent posé sur l'en-tête / le pied
       (toujours sombres) — le remapper casserait ces accents. */
    --c-sable-clair: #0c1620;   /* fond de page (encre marine) */
    --c-bordure: rgba(160, 196, 224, 0.16);
    --c-focus: rgba(124, 184, 226, 0.50);

    /* Niveaux de présence : les métaux s'inversent (texte clair sur voile sombre). Les valeurs
       claires du mode jour y tomberaient sous le seuil AA — un doré foncé sur fond marine est
       illisible. On remonte donc la luminosité en gardant la même famille de teinte, pour que la
       lecture doré / argenté / bleu reste identique d'un thème à l'autre. */
    --c-niveau-boostee-texte: #e3c169;
    --c-niveau-boostee-fond: rgba(227, 193, 105, 0.16);
    --c-niveau-preferentielle-texte: #c2cedb;
    --c-niveau-preferentielle-fond: rgba(194, 206, 219, 0.14);
    --c-niveau-normale-texte: #9cc8ef;
    --c-niveau-normale-fond: rgba(124, 184, 226, 0.16);

    /* Surfaces sémantiques (cf. tokens.css) : l'équivalent nocturne des surfaces blanches.
       `#16242f` est l'encre surélevée déjà utilisée par la liste `:is(...)` plus bas — les
       jetons portent donc la même valeur, sans nouvelle nuance à maintenir. */
    --c-surface: #16242f;
    --c-surface-2: #1d3242;
    /* Vert lumineux (même valeur que les statuts « publié » du back-office nocturne). */
    --c-succes: #6ee7b7;
    /* Corail éclairci pour rester lisible en TEXTE sur l'encre (le corail foncé y tombe à 3.9:1).
       `--c-corail` / `--c-corail-fonce` restent inchangés : ils portent des aplats à texte blanc. */
    --c-corail-texte: #f0a58a;

    --glass-bg: rgba(18, 30, 41, 0.55);
    --glass-bg-strong: rgba(18, 30, 41, 0.84);
    --glass-border: rgba(160, 196, 224, 0.18);
    --glass-hairline: rgba(160, 196, 224, 0.12);
    --glass-shadow: 0 14px 44px -12px rgba(0, 0, 0, 0.62), 0 2px 8px -2px rgba(0, 0, 0, 0.45);

    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.45);
    --shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.50), 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-md: 0 10px 28px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.40);
    --shadow-lg: 0 24px 56px rgba(0, 0, 0, 0.62), 0 8px 20px rgba(0, 0, 0, 0.45);
    --shadow-xl: 0 36px 72px -16px rgba(0, 0, 0, 0.72), 0 14px 28px -12px rgba(0, 0, 0, 0.5);

    --header-bg: linear-gradient(135deg, rgba(8, 20, 30, 0.78) 0%, rgba(13, 36, 54, 0.80) 100%);
    --header-bg-scrolled: linear-gradient(135deg, rgba(6, 15, 23, 0.84) 0%, rgba(11, 30, 46, 0.86) 100%);
}

[data-theme="dark"] body {
    background-color: var(--c-sable-clair);
    background-image:
        radial-gradient(circle at 10% 0%, rgba(30, 96, 145, 0.22) 0%, transparent 46%),
        radial-gradient(circle at 90% 100%, rgba(224, 120, 86, 0.12) 0%, transparent 42%);
}

/* Le CTA sable conserve un texte marine FONCÉ (le token --c-marine étant désormais clair). */
[data-theme="dark"] .header-cta { color: #0a3d62; }

/* Pied de page : --c-marine y sert de FOND → on rétablit une encre marine profonde. */
[data-theme="dark"] .site-footer { background-color: #0a2030; }

/* ── Surfaces qui étaient blanches → encre surélevée ── */
[data-theme="dark"] :is(
    .search-panel,
    .search-input,
    .geo-manual__input,
    .geo-manual__panel,
    .geo-manual__suggestions,
    .dropdown-menu,
    .dropdown-check__box,
    .home-map-section,
    .event-card,
    .event-card__date-tile,
    .toolbar-pill,
    .facet-chip,
    .newsletter-form__input,
    .field-toggle__option
) {
    background-color: #16242f;
}

[data-theme="dark"] .dropdown-menu::before {
    background-color: #16242f;
}

/* `.back-link` est désormais un lien en TEXTE NU dans tous les contextes (plus aucune pastille « badge » :
   prose.css / submission-form.css / event-detail-main.css). Sa couleur `--c-mer` est déjà remappée en bleu
   lumineux (#7cb8e2) la nuit et son survol `--c-marine` en encre claire → aucun remappage nocturne
   nécessaire, ni pour le lien de bas de page, ni pour ceux intégrés aux heros. */

/* Barre d'outils & encarts qui mélangeaient des sables clairs codés en dur. */
[data-theme="dark"] .search-toolbar {
    background: linear-gradient(180deg, #15242f 0%, rgba(18, 30, 41, 0.6) 100%);
    box-shadow: inset 0 1px 0 rgba(160, 196, 224, 0.06);
}
[data-theme="dark"] .facet-chips {
    background: linear-gradient(180deg, rgba(124, 184, 226, 0.05) 0%, transparent 100%);
}

/* ── Fonds « marque » à texte blanc : dégradé marine saturé explicite ── */
[data-theme="dark"] :is(
    .btn--primary,
    .toolbar-pill--filters[aria-expanded="true"],
    .toolbar-pill--map[aria-expanded="true"],
    .toolbar-pill--sort[aria-expanded="true"],
    .facet-chip.is-active,
    .facet-chip--category.is-active,
    .load-more-btn
) {
    background: linear-gradient(135deg, #0e4d7a 0%, #1e6091 100%);
    color: #fff;
    border-color: #1e6091;
}
[data-theme="dark"] :is(.toolbar-pill--geo.is-active, .geo-manual.is-open .toolbar-pill--geo-edit) {
    background-color: #14517f;
    border-color: #14517f;
    color: #fff;
}
[data-theme="dark"] .home-map-marker--cluster {
    background: radial-gradient(circle at 35% 30%, #3f86b8 0%, #0e3a5c 100%);
}
[data-theme="dark"] .event-context-nav__link {
    background: linear-gradient(135deg, #16242f 0%, #11202b 100%);
}
[data-theme="dark"] .prose-page .newsletter-signup {
    background:
        radial-gradient(120% 130% at 0% 0%, rgba(124, 184, 226, 0.10) 0%, transparent 55%),
        linear-gradient(180deg, #17262f 0%, #0f1c26 100%);
}

/* Hero de page : dégradé marine plus profond en sombre pour rester somptueux. La variante photo
   (`.has-photo`) garde son propre voile (défini plus bas, sans remappage dark nécessaire). */
[data-theme="dark"] .page-hero:not(.has-photo) {
    background:
        radial-gradient(120% 120% at 8% 6%, rgba(74, 144, 184, 0.28) 0%, transparent 52%),
        linear-gradient(140deg, #0a2740 0%, #123c5c 42%, #8a4026 100%);
}

/* Carte « Incontournable » : fond crème codé en dur → encre + or, sinon texte illisible. */
[data-theme="dark"] .event-card--featured {
    background:
        radial-gradient(circle at 100% 0%, rgba(245, 215, 110, 0.12) 0%, transparent 45%),
        linear-gradient(180deg, #1c2433 0%, #16212c 60%);
    border-color: rgba(212, 175, 55, 0.45);
}
[data-theme="dark"] .event-card--featured .event-card__date-tile-d {
    color: #f5d76e;
}

/* Messages flash : on garde des pastilles vives mais sur fonds sombres translucides. */
[data-theme="dark"] .flash--success {
    background-color: rgba(46, 125, 50, 0.18);
    color: #aee3b4;
    border-color: #2e7d32;
}
[data-theme="dark"] .flash--error {
    background-color: rgba(224, 120, 86, 0.18);
    color: #ffb9a8;
    border-color: var(--c-corail);
}

/* La grille Leaflet et ses popups restent sur tuiles claires → on garde un texte foncé localement. */
[data-theme="dark"] :is(.home-map-popup__title, .home-map-popup-list__title) { color: #0a3d62; }
[data-theme="dark"] :is(.home-map-popup__meta, .home-map-popup-list__date) { color: #5a6b7a; }


/*
lieu-filters.css

QUOI : Vitrine Lieux : bandeau de filtres par type en carrousel tactile dans le hero.

COMMENT : Copie telle quelle des lignes 3797–3859 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================================================
   Vitrine Lieux — filtres en carrousel tactile + entrée du hero
   ============================================================ */

/* Le bandeau de filtres par type (dans le hero marine) devient un carrousel
   horizontal à défilement tactile, scrollbar masquée (mobile-first). */
.lieu-filters {
    display: flex;
    gap: 0.5rem;
    margin-top: var(--space-sm);
    margin-right: calc(-1 * var(--space-md));
    padding-bottom: 0.4rem;
    padding-right: var(--space-md);
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.lieu-filters::-webkit-scrollbar { display: none; }

/* Les puces (réutilisent .event-card__category) deviennent de vraies pastilles « verre »
   posées sur le hero marine — défaut translucide, active en pastille pleine. */
.lieu-filters .event-card__category {
    scroll-snap-align: start;
    flex: 0 0 auto;
    background: rgba(255, 255, 255, 0.14);
    border: 1.5px solid rgba(255, 255, 255, 0.28);
    color: #fff;
    text-decoration: none;   /* pastilles de filtre : pas de soulignement « lien » */
    box-shadow: none;
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}
.lieu-filters .event-card__category:hover {
    background: rgba(255, 255, 255, 0.24);
    border-color: rgba(255, 255, 255, 0.5);
    transform: translateY(-1px);
}
.lieu-filters .event-card__category.is-active {
    background: #fff;
    color: #0a3d62;          /* fixe : lisible même quand --c-marine devient clair (dark) */
    border-color: #fff;
}
.lieu-filters .event-card__category:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

@media (min-width: 640px) {
    .lieu-filters {
        flex-wrap: wrap;
        overflow-x: visible;
        margin-right: 0;
        padding-right: 0;
    }
}



/* ══════════════════════════════════════════════════════════════════
   Recherche de lieux — barre de filtres sous le hero

   POURQUOI hors du hero : les filtres y étaient posés en pastilles de verre sur le fond marine, ce
   qui allait tant qu'il y en avait cinq. À dix catégories plus une recherche, une commune et un tri,
   le hero devenait un panneau de commande. La barre descend donc sur le fond de page, où les
   contrôles peuvent être des contrôles.
   ══════════════════════════════════════════════════════════════════ */

.lieu-search {
    margin: 0 0 var(--space-lg);
    padding: var(--space-md);
    background: var(--c-blanc);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

[data-theme="dark"] .lieu-search {
    background: var(--c-surface, rgba(255, 255, 255, 0.04));
}

.lieu-search__row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.75rem var(--space-sm);
}

.lieu-search__field {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-width: 0;
    flex: 1 1 11rem;
}

.lieu-search__field--text {
    flex: 3 1 16rem;
}

.lieu-search__field label {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--c-texte-doux);
}

.lieu-search__input-wrap {
    position: relative;
    display: flex;
    align-items: center;
}

.lieu-search__icon {
    position: absolute;
    left: 0.8rem;
    width: 1rem;
    height: 1rem;
    color: var(--c-texte-doux);
    pointer-events: none;
}

.lieu-search input[type="search"],
.lieu-search select {
    width: 100%;
    min-height: 44px;
    padding: 0.55rem 0.8rem;
    font-family: inherit;
    font-size: 0.95rem;
    color: var(--c-texte);
    background-color: var(--c-sable-clair);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-sm);
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

/* ⚠️ Fond OPAQUE, pas un voile translucide. Chrome peint le panneau déroulant d'un `select` avec le
   fond du contrôle : un blanc à 5 % donnait un panneau presque blanc, sous un texte clair — le menu
   des tris et des communes était illisible dès qu'on l'ouvrait. Un voile convient à un champ posé
   sur une surface connue ; il ne convient pas à un calque flottant dont on ne maîtrise pas le fond. */
[data-theme="dark"] .lieu-search input[type="search"],
[data-theme="dark"] .lieu-search select {
    background-color: var(--c-surface-2, #1d3242);
}

/* Les options n'héritent de rien : sans ces deux lignes, certains navigateurs les rendent avec la
   palette système, indépendamment du thème du site. */
[data-theme="dark"] .lieu-search select option {
    color: var(--c-texte);
    background-color: var(--c-surface-2, #1d3242);
}

/* Le chevron est une image : il ne suit pas `currentColor`. Celui du mode clair est un gris ardoise
   qui disparaît sur fond sombre. */
[data-theme="dark"] .lieu-search select {
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='16'%20height='16'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23a3b6c6'%20stroke-width='2.5'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M6%209l6%206%206-6'/%3E%3C/svg%3E");
}

.lieu-search input[type="search"] {
    padding-left: 2.4rem;
}

.lieu-search input[type="search"]::placeholder {
    /* Le gris par défaut d'un placeholder passe sous 4.5:1 sur le sable ; le ton « texte doux » le tient. */
    color: var(--c-texte-doux);
    opacity: 1;
}

.lieu-search select {
    appearance: none;
    padding-right: 2.3rem;
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='16'%20height='16'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%235a6b7a'%20stroke-width='2.5'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M6%209l6%206%206-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.7rem center;
    background-size: 15px 15px;
}

.lieu-search input[type="search"]:focus,
.lieu-search select:focus {
    outline: none;
    border-color: var(--c-mer);
    box-shadow: 0 0 0 3px rgba(30, 96, 145, 0.18);
}

.lieu-search__actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: 0 0 auto;
}

.lieu-search__geo.is-active {
    border-color: var(--c-mer);
    color: var(--c-mer);
}

.lieu-search__geo.is-error {
    border-color: #b3261e;
    color: #b3261e;
}

.lieu-search__geo[disabled] {
    opacity: 0.6;
    cursor: progress;
}

/* ---- Pastilles de catégorie ---- */

.lieu-cats {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    margin: var(--space-sm) 0 0;
    padding: var(--space-sm) 0 0;
    border: 0;
    border-top: 1px solid var(--c-bordure);
}

.lieu-cat {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.38rem 0.8rem;
    font-size: 0.84rem;
    font-weight: 600;
    color: var(--c-texte);
    text-decoration: none;
    background: var(--c-sable-clair);
    border: 1px solid var(--c-bordure);
    border-radius: 999px;
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

/* Le voile à 5 % et le filet à 16 % passaient le seuil de contraste du TEXTE, mais la pastille
   elle-même ne se détachait plus de la barre : on lisait une liste de mots, pas des boutons. */
[data-theme="dark"] .lieu-cat {
    background: rgba(255, 255, 255, 0.08);
    border-color: rgba(160, 196, 224, 0.28);
}

/* La pastille porte la couleur de sa catégorie par un point, pas par son fond : dix pastilles
   pleines côte à côte se disputeraient l'attention, et celle qui est active ne se verrait plus. */
.lieu-cat__dot {
    width: 0.55rem;
    height: 0.55rem;
    flex: none;
    border-radius: 999px;
    background: var(--cat-color, var(--c-mer));
}

.lieu-cat__count {
    font-size: 0.74rem;
    font-variant-numeric: tabular-nums;
    color: var(--c-texte-doux);
}

.lieu-cat:hover {
    border-color: var(--c-mer-clair);
}

/* ⚠️ Repli en valeur FIXE, pas sur `--c-marine` : ce jeton devient une encre quasi blanche en mode
   sombre, et la pastille « Tous » écrivait alors du blanc sur du blanc. Le bleu de mer du mode clair
   tient 5.6:1 avec du texte blanc dans les deux thèmes. */
.lieu-cat.is-active {
    color: var(--c-blanc);
    background: var(--cat-color, #1e6091);
    border-color: transparent;
}

.lieu-cat.is-active .lieu-cat__dot {
    background: var(--c-blanc);
}

.lieu-cat.is-active .lieu-cat__count {
    color: rgba(255, 255, 255, 0.85);
}

.lieu-cat:focus-visible,
.lieu-search__geo:focus-visible {
    outline: 2px solid var(--c-mer);
    outline-offset: 2px;
}

/* ---- Distance et compteurs ---- */

.lieu-distance {
    flex: none;
    padding: 0.2rem 0.55rem;
    font-size: 0.75rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--c-marine);
    background: var(--c-sable);
    border-radius: 999px;
}

[data-theme="dark"] .lieu-distance {
    color: var(--c-texte);
    background: rgba(255, 255, 255, 0.10);
}

.section-title__count {
    margin-left: 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--c-texte-doux);
}

/* ---- Aucun résultat ---- */

.poi-empty-public {
    padding: var(--space-lg) var(--space-md);
    text-align: center;
    background: var(--c-blanc);
    border: 1px dashed var(--c-bordure);
    border-radius: var(--radius-lg);
}

[data-theme="dark"] .poi-empty-public {
    background: rgba(255, 255, 255, 0.04);
}

.poi-empty-public__title {
    margin: 0 0 0.4rem;
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--c-texte);
}

.poi-empty-public__text {
    margin: 0 auto 1.1rem;
    max-width: 46ch;
    color: var(--c-texte-doux);
    line-height: 1.55;
}

/* ══════════════════════════════════════════════════════════════════
   Carte de lieu — couverture, blason, carte de tête

   POURQUOI : aucune des fiches reprises n'a de photo. La page était cent vingt-sept rectangles de
   texte gris, tous de la même taille : rien n'y donnait envie d'entrer. La couverture abstraite du
   site — déjà employée pour les événements sans visuel — rend à la grille ses couleurs, et la carte
   de tête lui rend un point d'entrée.
   ══════════════════════════════════════════════════════════════════ */

/* Le glyphe de la catégorie, posé en filigrane sur la couverture abstraite. Discret : c'est un
   repère de famille, pas une illustration du lieu — on ne prétend pas montrer ce qu'on n'a pas
   photographié. */
.lieu-card__glyphe {
    position: absolute;
    right: 0.7rem;
    bottom: 0.4rem;
    font-size: clamp(3rem, 9vw, 4.4rem);
    line-height: 1;
    opacity: 0.42;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.35));
    pointer-events: none;
}

/* Pastille de catégorie posée SUR la couverture, en haut à gauche. */
.lieu-card__badge {
    position: absolute;
    top: 0.6rem;
    left: 0.6rem;
    z-index: 2;
}

/* Distance : coin opposé, pour que les deux repères ne se disputent pas la place. */
.lieu-card .lieu-distance {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 2;
    background: rgba(10, 61, 98, 0.82);
    color: var(--c-blanc);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
}

/* Le corps de la carte reprend juste sous la couverture. */
.lieu-card .event-card__title {
    margin-top: 0.15rem;
}

/* ---- Carte de tête ----
   La grille des pages de listing donne déjà deux colonnes à sa première tuile (`bento-shared.css`),
   et sa couverture garde le MÊME format que les autres : sur une page, toutes les images sont au
   même ratio — c'est une convention du site, pas un réglage par page. Le vide que cela laisse à
   côté de la deuxième carte n'est pas un accident à masquer par un format sur mesure : c'est une
   case, et le bento la remplit avec un encart, comme sur l'accueil. */
@media (min-width: 980px) {
    .taxonomy-page .events-grid > .event-card-link:first-child .lieu-card__glyphe {
        font-size: 5.4rem;
    }
}

/* ---- Coût de peinture ----
   La page rend jusqu'à cent vingt-sept couvertures d'un coup, là où une page d'événements en montre
   une douzaine. Deux réglages, mesurés : sans eux, le moteur de rendu de Chrome se bloquait
   plusieurs secondes au défilement.

   1. Le grain de la couverture abstraite est peint en `mix-blend-mode: overlay`. Un mode de fusion
      force une couche de composition PAR élément : douze, c'est gratuit ; cent vingt-sept, non. Le
      grain reste, en simple superposition — la différence de rendu est invisible à l'œil, celle de
      coût ne l'est pas.
   2. Les cartes hors écran ne sont plus peintes du tout. `contain-intrinsic-size` donne au
      navigateur une hauteur présumée, sans quoi la barre de défilement sauterait à chaque carte
      atteinte. */
.lieu-card .event-cover-abstract::after {
    mix-blend-mode: normal;
    opacity: 0.07;
}

.taxonomy-page .events-grid > .event-card-link {
    content-visibility: auto;
    contain-intrinsic-size: auto 420px;
}

/* La première tuile est visible d'emblée et sert de repère de mise en page : la différer ferait
   sauter la grille au chargement, pour rien. */
.taxonomy-page .events-grid > .event-card-link:first-child {
    content-visibility: visible;
}

/* Mention « photo d'un tiers » sur la vignette. Discrète mais lisible : c'est une obligation de
   licence, pas une décoration — un crédit illisible ne vaut pas mieux qu'un crédit absent. */
.lieu-card__credit {
    position: absolute;
    left: 0.6rem;
    bottom: 0.5rem;
    z-index: 2;
    padding: 0.15rem 0.5rem;
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--c-blanc);
    background: rgba(10, 61, 98, 0.78);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    border-radius: 999px;
}

/* ══════════════════════════════════════════════════════════════════
   Fiche d'un lieu — alignée sur la fiche événement
   ══════════════════════════════════════════════════════════════════ */

/* Le glyphe de la catégorie, en filigrane sur la couverture composée du bandeau. Plus grand que sur
   les cartes : le bandeau lui donne la place, et c'est le seul visuel de la page tant qu'aucune
   photo n'est posée. */
.lieu-hero__glyphe {
    position: absolute;
    right: clamp(1rem, 4vw, 3rem);
    bottom: clamp(0.5rem, 2vw, 1.5rem);
    z-index: 1;
    font-size: clamp(4rem, 12vw, 7rem);
    line-height: 1;
    opacity: 0.38;
    filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.4));
    pointer-events: none;
}

/* Caractéristiques du lieu, dans le bandeau à côté de la pastille de catégorie. Habillage « verre »
   posé sur la couverture, comme les puces du hero des pages de listing. */
.lieu-hero__tag {
    display: inline-flex;
    align-items: center;
    padding: 0.26rem 0.7rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--c-blanc);
    background: rgba(255, 255, 255, 0.16);
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 999px;
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
}

/* Bouton d'itinéraire : pleine largeur en pied de panneau, c'est l'action que la page appelle. */
.lieu-itineraire {
    display: flex;
    width: 100%;
    margin-top: var(--space-sm);
}

/* Grille sans carte de tête : toutes les tuiles à la même largeur. La règle de bento
   (`bento-shared.css`) élargit la première tuile de toute grille d'une page de listing — utile là
   où l'on parcourt, déplacé sur une bande de suggestions où rien ne prime. */
@media (min-width: 980px) {
    .taxonomy-page .events-grid--egales > .event-card-link:first-child {
        grid-column: auto;
    }

    .taxonomy-page .events-grid--egales > .event-card-link:first-child .lieu-card .event-card__media {
        aspect-ratio: 4 / 3;
    }
}

/* Le libellé du bouton d'itinéraire porte déjà une flèche dans la traduction ; combinée à l'icône,
   elle faisait passer le texte sur deux lignes dans un bouton pleine largeur. */
.lieu-itineraire {
    text-align: center;
    line-height: 1.3;
}

/* Description complète sur la fiche d'un lieu. `event-card__description` coupe à trois lignes :
   parfait sur une vignette, absurde sur la page qu'on ouvre justement pour la lire. */
.lieu-description {
    max-width: 68ch;
    color: var(--c-texte);
    font-size: var(--fs-md);
    line-height: 1.7;
    text-wrap: pretty;
}

.lieu-description br + br {
    line-height: 2.2;
}

/*
city-combobox.css

QUOI : Moteur de recherche compact à autocomplétion des villes dans le hero marine de la page
       arrondissement (/ou/{slug}). Éléments injectés par assets/js/arrondissement/city-combobox.js.

COMMENT : Le champ est posé sur le hero marine → habillage « verre » blanc translucide (mêmes valeurs
          que .header-search / .lieu-filters). La liste déroulante est un panneau de surface claire
          (jetons sémantiques --c-surface / --c-texte) pour rester lisible en listant des villes, suivi
          des thèmes clair/sombre sans bloc [data-theme] manuel. Corail réservé à l'option active.

OÙ : Importé par assets/styles/app.imports.css (après lieu-filters.css), inliné dans app.css par
     app:assets:bundle-css.

POURQUOI : T10+T12 — remplace les pastilles par un sélecteur cherchable.
*/

/* La liste brute (.lieu-filters) porte display:flex, qui écrase l'attribut [hidden] posé par le JS
   (la règle UA [hidden]{display:none} perd face à une règle auteur display:flex). On rétablit donc
   le masquage pour tout descendant [hidden] du conteneur (liste brute enrichie ET listbox fermée). */
.city-combobox-mount [hidden] {
    display: none;
}

.city-combobox {
    position: relative;
    margin-top: var(--space-sm);
    max-width: 22rem;
}

/* Champ posé sur le hero marine : verre blanc translucide (aligné .header-search). */
.city-combobox__input {
    width: 100%;
    height: 44px;
    padding: 0 0.9rem;
    font: inherit;
    font-size: 0.95rem;
    color: #fff;
    background-color: rgba(255, 255, 255, 0.12);
    border: 1.5px solid rgba(255, 255, 255, 0.28);
    border-radius: var(--radius-md);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
}

.city-combobox__input::placeholder {
    color: rgba(255, 255, 255, 0.7);
}

.city-combobox__input:hover {
    background-color: rgba(255, 255, 255, 0.2);
    border-color: rgba(255, 255, 255, 0.5);
}

.city-combobox__input:focus-visible {
    outline: 2px solid var(--c-sable);
    outline-offset: 2px;
    background-color: rgba(255, 255, 255, 0.2);
}

/* Panneau déroulant : surface claire détachée (lisible pour une liste de villes). Attaché au <body>
   par le JS (échappe à l'overflow:hidden du hero) ; top/left/width sont posés en inline. */
.city-combobox__list {
    position: absolute;
    z-index: 60;
    box-sizing: border-box;
    margin: 0;
    padding: 0.35rem;
    list-style: none;
    max-height: 16rem;
    overflow-y: auto;
    background-color: var(--c-surface);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

/* La liste vit sur <body>, hors du conteneur : son masquage a sa propre règle. */
.city-combobox__list[hidden] {
    display: none;
}

.city-combobox__option {
    padding: 0.55rem 0.7rem;
    border-radius: var(--radius-sm);
    color: var(--c-texte);
    cursor: pointer;
}

/* Corail réservé à l'accent rare : option survolée / surlignée clavier. */
.city-combobox__option.is-active,
.city-combobox__option:hover {
    background-color: var(--c-corail);
    color: #fff;
}

.city-combobox__empty {
    padding: 0.55rem 0.7rem;
    color: var(--c-texte-doux);
    font-size: 0.9rem;
}

/*
bento-shared.css

QUOI : Bento mutualisé accueil + taxonomie : cartes partenaires à hauteur naturelle, pile de sponsors, tuile « lead » élargie.

COMMENT : Copie telle quelle des lignes 3860–3948 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================ Bento mutualisé (accueil + taxonomie) ============================
   Mise en page « bento » partagée par la grille « par ville » de l'accueil ET les pages ville /
   catégorie. Activée par la classe `.events-grid--bento` (posée par `_partials/_bento_grid.html.twig`
   quand il y a ≥ 2 événements et ≥ 1 sponsor) : lead 2/3 + colonne 1/3 (2e event + sponsor empilés),
   et des piles de sponsors qui comblent les trous de la dernière rangée. Source de vérité unique →
   l'accueil et la taxonomie se comportent exactement pareil. */

/* Cartes partenaires : toujours à leur hauteur naturelle, jamais étirées sur la hauteur d'un
   événement voisin (rendu « case sponsor » compact, dans la grille ville comme dans les résultats). */
.events-grid > .partner-cell {
    align-self: start;
}

/* Pile de partenaires : une « case bento » qui occupe une cellule de la grille et empile des sponsors.
   Elle s'étire (align-self par défaut = stretch) sur toute la hauteur de la rangée pour faire EXACTEMENT
   la taille de l'événement voisin ; les sponsors à l'intérieur se partagent cette hauteur (flex: 1). */
.partner-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
}

/* Variante « comblement » des pages arrondissement UNIQUEMENT. Sous 980px la grille passe à une
   colonne : les cases libres n'existent plus, et la pile devient une simple rafale d'encarts à la
   suite des événements. On n'en garde donc qu'un par case — sinon une ville à 3 événements se
   terminerait par 6 entreprises d'affilée sur téléphone.
   Le modificateur est indispensable : `.partner-stack` sert aussi à l'accueil et aux pages ville,
   où la pile est voulue entière en mobile. */
@media (max-width: 979px) {
    .partner-stack--comble .partner-cell:not(:first-child) {
        display: none;
    }
}

/* `flex: 1 1 auto` (base = taille naturelle) : les cartes grandissent pour remplir quand la pile est
   étirée à côté d'un événement, MAIS gardent leur hauteur naturelle quand la pile est seule sur sa
   rangée (sinon, base 0 → les cartes s'effondrent en fines lignes). */
.partner-stack > .partner-cell {
    flex: 1 1 auto;
}

/* Contenu centré : quand une carte s'agrandit pour remplir, le logo + le nom restent équilibrés. */
.partner-stack > .partner-cell .partner-cell__link {
    justify-content: center;
}

/* Colonne droite du bento (2e event + sponsor). Par défaut (mobile/tablette) elle est « transparente »
   (display: contents) : ses enfants coulent normalement dans la grille. Elle ne devient une cellule
   flex à part entière qu'en ≥980px (cf. media query), là où le lead occupe 2 colonnes. */
.lead-aside {
    display: contents;
}

/* Encart « comble-hauteur » (accueil, emplacement `home_lead_aside`) : masqué par défaut — en
   mobile/tablette la colonne n'existe pas visuellement (`display: contents` ci-dessus) et la
   2e carte ne laisse aucun vide ; l'encart n'a de sens qu'en ≥980px (cf. media query). */
.lead-aside > .lead-aside__fill {
    display: none;
}

/* Bento pour les listes (lieux / taxonomie) : 1re tuile « lead » élargie sur grand écran,
   comme l'accueil — donne du rythme et une hiérarchie aux grilles de cartes. */
@media (min-width: 980px) {
    .taxonomy-page .events-grid {
        grid-auto-flow: dense;
    }
    .taxonomy-page .events-grid > .event-card-link:first-child {
        grid-column: span 2;
    }
    .taxonomy-page .events-grid > .event-card-link:first-child .event-card__title {
        font-size: clamp(1.3rem, 2vw, 1.6rem);
    }
    .taxonomy-page .events-grid > .event-card-link:first-child .event-card__description {
        -webkit-line-clamp: 4;
    }

    /* La 2e tuile partage la 1re rangée avec le lead 2/3 : `align-self: start` + `height: auto`
       l'empêchent de s'étirer sur la hauteur du gros visuel (miroir de l'accueil). En mode bento,
       le 2e enfant est `.lead-aside` (pas un `.event-card-link`) → cette règle ne s'y applique pas. */
    .taxonomy-page .events-grid > .event-card-link:nth-child(2) {
        align-self: start;
        height: auto;
    }
    .taxonomy-page .events-grid > .event-card-link:nth-child(2) .event-card {
        height: auto;
    }

    /* Variante « bento » : la colonne de droite (.lead-aside) occupe UNE cellule à côté du lead 2/3
       et s'étire à sa hauteur (grille = stretch). Elle empile le 2e event (hauteur naturelle) puis un
       sponsor qui grandit (flex: 1) pour combler le reste → le sponsor fait pile la hauteur du lead. */
    .events-grid--bento .lead-aside {
        display: flex;
        flex-direction: column;
        gap: var(--space-md);
        min-height: 0;
    }

    .events-grid--bento .lead-aside > .event-card-link,
    .events-grid--bento .lead-aside > .event-card-link .event-card {
        height: auto; /* 2e event : hauteur naturelle (coupe les height:100%) */
    }

    .events-grid--bento .lead-aside > .partner-stack {
        flex: 1 1 auto; /* le sponsor remplit tout l'espace restant sous le 2e event */
        min-height: 0;
    }

    /* Encart « comble-hauteur » (home_lead_aside) : même mécanique que la pile sponsor ci-dessus —
       le conteneur grandit pour que la colonne fasse pile la hauteur du lead, et l'encart s'étire
       avec lui (le lien interne est déjà en height: 100 %, cf. encadres.css). */
    .events-grid--bento .lead-aside > .lead-aside__fill {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }

    .events-grid--bento .lead-aside > .lead-aside__fill .encart-pub {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }

    .events-grid--bento .lead-aside > .lead-aside__fill .encart-pub__link {
        flex: 1 1 auto;
    }
}


/*
encadres.css

QUOI : Encarts partenaires (variantes bento, footer, sidebar) : visuels, étiquette de transparence, adaptations sombre et reduced-motion. Nom de fichier neutre (anti-adblock).

COMMENT : Copie telle quelle des lignes 3949–4213 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ============================ Encarts publicitaires (Publicité) ============================
   Encarts auto-hébergés (entité `Publicite`) rendus par `_partials/_encart_publicite.html.twig`,
   déclinés en variantes `--bento` (tuile accueil), `--footer` (bandeau pied de page) et `--sidebar`
   (back-office). Esthétique 2026 : coins arrondis « chirurgicaux » (tokens `--radius-*`), micro-effet
   au survol (translation + léger scale + zoom doux du visuel), largeur 100 % fluide (jamais de débordement).
   Visibilité INDÉPENDANTE de motion.js (le pied de page apparaît sur des pages sans `motion.js`) :
   l'encart est toujours visible, l'entrée est une animation CSS autonome. Mode sombre via [data-theme="dark"]
   (surface en encre, le reste s'adapte par tokens). */

.encart-pub {
    position: relative;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: linear-gradient(160deg, #f3f7ff 0%, #e8f0fe 100%);
    border: 1px solid rgba(46, 109, 240, 0.16);
    box-shadow: var(--shadow-sm);
    animation: fade-in-up 600ms var(--ease-out) both;
    transition: transform var(--duration-base) var(--ease-out),
                box-shadow var(--duration-base) var(--ease-out);
}

.encart-pub:hover {
    transform: translateY(-3px) scale(1.006);
    box-shadow: var(--shadow-md);
}

.encart-pub__link {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    height: 100%;
    padding: 1rem 1.1rem;
    text-decoration: none;
    color: inherit;
}

/* Étiquette « Publicité » discrète (conformité + transparence), posée en surimpression. */
/* `.encart-pub__tag` (étiquette « Publicité ») retirée le 2026-07-20 sur décision explicite du
   propriétaire du site — cf. commentaire de `_encart_publicite.html.twig`. Règles supprimées
   plutôt que laissées orphelines : plus aucun template n'émet ce nœud. */

.encart-pub__media {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--duration-slow) var(--ease-out), opacity var(--duration-base) var(--ease-out);
}

.encart-pub:hover .encart-pub__media {
    transform: scale(1.05);
    opacity: 0.96;
}

.encart-pub__body {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.encart-pub__name {
    font-family: var(--font-display);
    font-weight: 800;
    color: var(--c-marine);
    font-size: 1.02rem;
    line-height: 1.2;
}

.encart-pub__desc {
    font-size: 0.84rem;
    color: var(--c-texte-doux);
    line-height: 1.35;
}

.encart-pub__cta {
    margin-top: 0.35rem;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--c-mer);
}

.encart-pub__cta svg {
    transition: transform var(--duration-fast) var(--ease-out);
}

.encart-pub:hover .encart-pub__cta svg {
    transform: translateX(3px);
}

/* ---------- Variante BENTO (tuile dédiée dans la grille d'accueil) ---------- */

/* Tuile texte seul : généreuse, centrée, accroche typographique. */
.encart-pub--bento .encart-pub__link:not(.encart-pub__link--media) {
    min-height: 150px;
    justify-content: center;
    padding: 1.4rem 1.5rem;
}

.encart-pub--bento .encart-pub__name {
    font-size: clamp(1.1rem, 2vw, 1.4rem);
}

/* Tuile avec visuel : empilée sur mobile (image 16:9 puis texte), scindée en deux sur desktop. */
.encart-pub--bento .encart-pub__link--media {
    padding: 0;
    gap: 0;
}

.encart-pub--bento .encart-pub__link--media .encart-pub__media {
    aspect-ratio: 16 / 9;
}

.encart-pub--bento .encart-pub__link--media .encart-pub__body {
    padding: 0.95rem 1.2rem 1.2rem;
}

@media (min-width: 720px) {
    .encart-pub--bento .encart-pub__link--media {
        flex-direction: row;
        align-items: stretch;
    }
    .encart-pub--bento .encart-pub__link--media .encart-pub__media {
        width: 46%;
        aspect-ratio: auto;
        min-height: 168px;
        flex: 0 0 auto;
    }
    .encart-pub--bento .encart-pub__link--media .encart-pub__body {
        flex: 1;
        justify-content: center;
    }
}

/* La tuile occupe TOUTE la largeur de la grille Bento dès qu'elle passe en multi-colonnes.
   `1 / -1` = de la première à la dernière ligne de grille → pleine largeur, robuste quel que soit
   le nombre de colonnes (2 dès 560px, 3 dès 980px). */
@media (min-width: 560px) {
    .city-block .events-grid > .encart-pub--bento {
        grid-column: 1 / -1;
    }
}

/* ---------- Bandeau BENTO autonome (pages ville : encart ciblé, AU-DESSUS des événements) ---------- */

/* Rendu du city_bento hors grille (une seule tuile pleine largeur) : on sépare du bloc d'événements
   qui suit. La tuile réutilise telle quelle la variante `.encart-pub--bento` ci-dessus (aucun autre style). */
/* ═══════════ ESPACEMENT UNIFORME DES ENCARTS ═══════════
   RÈGLE : tout encart publicitaire est séparé de ce qui l'entoure par exactement `--encart-gap`,
   au-dessus comme en dessous, sur TOUTES les pages.

   Pourquoi une règle unique plutôt qu'un réglage par page : l'écart réellement perçu ne dépend pas
   que de l'encart, mais aussi de la marge basse du bloc précédent et du padding haut du bloc
   suivant — or ces voisins diffèrent d'une page à l'autre (hero de ville, hero d'accueil, pied de
   page). Compenser au cas par cas donnait 8px ici et 138px là. On IMPOSE donc l'écart en
   neutralisant la contribution des voisins immédiats, au lieu de la subir. */
:root {
    --encart-gap: 2rem;
}

.container > .encart-pub--banner,
.city-bento-highlight,
.site-footer__ad {
    padding-block: 0;
    margin-block: var(--encart-gap);
}

/* Neutralisation des voisins : sans elle, la marge du bloc précédent FUSIONNE avec celle de
   l'encart (collapsing → max des deux, pas 32px) et le padding du bloc suivant s'AJOUTE. */
.page-hero:has(+ .city-bento-highlight),
.page-hero:has(+ .container > .encart-pub--banner) {
    margin-bottom: 0;
}

.city-bento-highlight + .events-section,
.encart-pub--banner + .events-section {
    padding-top: 0;
}

/* Pied de page : `.site-footer__inner` porte 24px de padding-bas qui s'AJOUTENT aux 32px de marge
   de l'encart (un padding ne fusionne pas avec une marge) → 56px au-dessus contre 32px en dessous.
   Défaut constaté sur toutes les pages, l'encart de pied de page étant global. */
.site-footer__inner:has(+ .site-footer__ad) {
    padding-bottom: 0;
}

/* (La respiration verticale des bannières en mobile est réglée plus bas, au niveau de la variante
   `.encart-pub--banner` elle-même — la poser ici serait écrasé par l'ordre source.) */

/* ---------- Variante FOOTER (bandeau partenaire en pied de page) ---------- */

.site-footer__ad {
    margin-bottom: 2rem;
}

.encart-pub--footer {
    border-radius: var(--radius-md);
}

.encart-pub--footer .encart-pub__link {
    flex-direction: row;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1.05rem;
}

.encart-pub--footer .encart-pub__link--media {
    padding: 0.5rem 1.05rem 0.5rem 0.5rem;
}

.encart-pub--footer .encart-pub__media {
    flex: 0 0 auto;
    width: 132px;
    height: 76px;
    border-radius: calc(var(--radius-md) - 6px);
}

.encart-pub--footer .encart-pub__body {
    flex: 1;
}

.encart-pub--footer .encart-pub__cta {
    margin-top: 0.15rem;
}

@media (max-width: 480px) {
    .encart-pub--footer .encart-pub__link {
        flex-wrap: wrap;
    }
    .encart-pub--footer .encart-pub__media {
        width: 100%;
        height: auto;
        aspect-ratio: 16 / 9;
    }
}

/* ---------- Variante SIDEBAR (back-office, colonne étroite) ---------- */

.encart-pub--sidebar {
    border-radius: var(--radius-md);
    margin-bottom: 0.85rem;
}

.encart-pub--sidebar .encart-pub__link {
    padding: 0.7rem 0.8rem;
    gap: 0.35rem;
}

.encart-pub--sidebar .encart-pub__link--media {
    padding: 0;
}

.encart-pub--sidebar .encart-pub__link--media .encart-pub__media {
    aspect-ratio: 16 / 9;
}

.encart-pub--sidebar .encart-pub__link--media .encart-pub__body {
    padding: 0.55rem 0.8rem 0.8rem;
}

.encart-pub--sidebar .encart-pub__name {
    font-size: 0.9rem;
}

.encart-pub--sidebar .encart-pub__desc {
    font-size: 0.78rem;
}

/* ---------- Mode sombre : surface en encre (le blanc codé en dur ne se remappe pas) ---------- */
[data-theme="dark"] .encart-pub {
    background: linear-gradient(160deg, #17262f 0%, #111d26 100%);
    border-color: rgba(160, 196, 224, 0.16);
}

/* ---------- Accessibilité : pas d'animation si l'utilisateur la refuse ---------- */
@media (prefers-reduced-motion: reduce) {
    .encart-pub {
        animation: none;
        transition: none;
    }
    .encart-pub:hover {
        transform: none;
    }
    .encart-pub:hover .encart-pub__media,
    .encart-pub:hover .encart-pub__cta svg {
        transform: none;
    }
}



/* ---------- Visuel AU FORMAT du slot (bannière 728/90, rectangle 6:5, demi-page 1:2) ----------
   Posé quand l'encart possède le visuel de la forme déclarée par son emplacement (cf. Emplacement::format).
   Le visuel est servi ENTIER : aspect-ratio du format (variable --encart-ratio posée par le gabarit) +
   object-fit contain — une création d'annonceur n'est JAMAIS rognée, contrairement au repli mono-image
   historique (cover). Sous 720px, --encart-ratio-mobile prend le relais quand le slot décline un format
   mobile (le <picture> bascule la source en parallèle). */

.encart-pub__picture {
    display: contents; /* l'<img> reste l'enfant direct du lien flex, <picture> n'est qu'un porteur de <source> */
}

.encart-pub__link--media.encart-pub__link--format {
    flex-direction: column;
    align-items: stretch;
    padding: 0;
    gap: 0;
}

.encart-pub__link--media.encart-pub__link--format .encart-pub__media {
    width: 100%;
    height: auto;
    min-height: 0;
    aspect-ratio: var(--encart-ratio, 16 / 9);
    object-fit: contain;
    margin-inline: auto;
}

.encart-pub__link--media.encart-pub__link--format .encart-pub__body {
    padding: 0.95rem 1.2rem 1.2rem;
}

/* Pas de zoom au survol sur un visuel au format : la création déborderait de sa boîte. */
.encart-pub:hover .encart-pub__link--format .encart-pub__media {
    transform: none;
}

@media (max-width: 719px) {
    .encart-pub__link--media.encart-pub__link--format .encart-pub__media {
        aspect-ratio: var(--encart-ratio-mobile, var(--encart-ratio, 16 / 9));
    }
}

/* ---------- Variante BANNER (bannière d'accueil & bandeau fin de page) ----------
   Bandeau discret dans le flux : bannière au RATIO 728/90 étirée à la largeur du contenu (jamais
   bornée en pixels — c'est le ratio qui fait loi, la création s'adapte), tag « Publicité »,
   corps texte réduit à l'essentiel sous le visuel. Sans visuel au format (encart texte ou hérité),
   la carte compacte par défaut s'applique, bornée à la largeur du contenu. */

.encart-pub--banner {
    max-width: 1200px;
    margin: var(--space-sm) auto;
    padding-inline: max(var(--space-sm), env(safe-area-inset-left));
    background: none;
    border: none;
    box-shadow: none;
}

/* Le `padding-inline` ci-dessus sert au bandeau posé DIRECTEMENT dans la page (il lui donne sa
   gouttière). Quand il est déjà dans un `.container`, celui-ci apporte la même gouttière : les deux
   s'additionnaient et rognaient le visuel de 32px. Conséquence mesurée : à ratio 728:90 identique,
   la création de l'accueil tombait à 350×43px là où celle des pages ville faisait 382×47px.
   On neutralise donc le doublon pour que le MÊME ratio rende la MÊME taille partout. */
.container > .encart-pub--banner {
    padding-inline: 0;
}

.encart-pub--banner .encart-pub__link {
    border-radius: var(--radius-md);
}

/* Respiration verticale resserrée sur téléphone. À 43px de haut, une 728:90 encadrée de 16px de
   vide au-dessus ET en dessous affiche 70% de blanc pour 30% de création — d'où l'effet
   « vide / bannière / vide ». 8px sépare encore nettement l'encart du flux sans l'isoler. */
/* (Aucun réglage de marge par variante ici : l'espacement des encarts est centralisé plus haut par
   `--encart-gap`. Ajouter une règle de marge dans ce bloc la ré-écraserait par l'ordre source.) */

/* ---------- Bannière NUE (modificateur --seul, tous slots au format bannière) ----------
   L'encart se réduit à la création cliquable : aucune carte, aucun corps texte — juste l'image
   (ou le GIF) aux coins arrondis du site (--radius-lg, comme les cartes événement) et l'étiquette
   « Publicité » en surimpression. Le gabarit n'émet pas de body dans ce mode. */

.encart-pub.encart-pub--seul {
    background: none;
    border: none;
    box-shadow: none;
    overflow: visible; /* l'arrondi est porté par le visuel lui-même */
}

.encart-pub--seul .encart-pub__link {
    padding: 0;
    gap: 0;
}

.encart-pub--seul .encart-pub__media {
    border-radius: var(--radius-lg);
    transition: box-shadow var(--duration-base) var(--ease-out);
}

/* Survol : pas de translation de carte, juste l'ombre douce du site sur la création. */
.encart-pub.encart-pub--seul:hover {
    transform: none;
    box-shadow: none;
}

.encart-pub--seul:hover .encart-pub__media {
    box-shadow: var(--shadow-md);
}

/* Sur petit écran, la bannière 728:90 ne fait plus que ~45px de haut : un arrondi de carte (24px)
   la transforme en ovale. On redescend à un arrondi proportionné à sa hauteur. */
@media (max-width: 719px) {
    .encart-pub--seul .encart-pub__media {
        border-radius: var(--radius-sm);
    }
}

/*
site-images.css

QUOI : Images de décor (SiteImage) : vignette 16/9 en tête de carte événement, raccord avec la carte « Incontournable ».

COMMENT : Copie telle quelle des lignes 4214–4249 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ──────────────────────────────────────────────────────────────────────────
   Images de décor (SiteImage) : bandeau photo de ville + vignettes de cartes.
   Appended en fin de fichier → l'emporte sur la base `.taxonomy-hero` (y compris
   l'override sombre) à spécificité égale, sans toucher au rendu par défaut.
   ────────────────────────────────────────────────────────────────────────── */

/* Vignette en tête de carte événement : déborde jusqu'aux bords internes
   (marges négatives = padding de la carte), coins supérieurs arrondis. */
.event-card__media {
    position: relative;
    margin: calc(var(--space-md) * -1) calc(var(--space-md) * -1) var(--space-xs);
    /* 4/3 = ratio EXACT du crop admin (image-cropper.js → 800×600). Le conteneur épouse
       le crop : object-fit:cover n'a plus rien à rogner, la vignette montre pile la zone
       sélectionnée par l'admin (correctif « décalage », anciennement 16/9 qui coupait
       ~12 % en haut et en bas de la sélection). */
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    background: var(--c-sable-clair);
}

/* Carte « Incontournable » : son padding-top est majoré de 0.85rem (espace réservé
   au badge). La vignette doit donc remonter d'autant pour affleurer le haut du cadre,
   sinon un jour subsiste entre le bord supérieur et la photo. Le badge (absolute)
   flotte alors par-dessus la vignette — rendu premium voulu. */
.event-card--featured .event-card__media {
    margin-top: calc((var(--space-md) + 0.85rem) * -1);
}

.event-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.event-card__media img.is-placeholder {
    opacity: 0.7;
}

/* ──────────────────────────────────────────────────────────────────────────
   Couverture abstraite générative — fallback « sans photo »
   Remplace l'ancien placeholder gris (site-image-placeholder.svg) par un visuel
   abstrait premium, DÉTERMINISTE par événement (data-variant = event.id % 6, calculé
   à l'identique côté Twig `_carte_evenement` et JS `search/cards.js`).

   COULEUR = celle de la CATÉGORIE : la couverture consomme `--cat-color` (héritée de
   `.event-card`, définie par `data-cat` dans event-card.css) — la même variable qui
   colore la tuile date et la pastille de catégorie. Le visuel « suit » donc la carte :
   chaque catégorie a sa teinte, chaque événement sa composition (6 variantes de blobs).
   Les tons dérivent de `--cat-color` par `color-mix` avec #000/#fff — MÊME langage que le
   dégradé de la tuile date (event-card.css). Repli `--cat-color` = mer si absent (hero).
   Grain fin commun pour un fini mat premium. Cohérent thème clair ET sombre.
   ────────────────────────────────────────────────────────────────────────── */
.event-cover-abstract {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* Tons dérivés de la couleur de catégorie (repli mer pour les contextes sans carte). */
    --cc: var(--cat-color, #1e6091);
    --ab-base: color-mix(in srgb, var(--cc) 82%, #000);
    --ab-deep: color-mix(in srgb, var(--cc) 55%, #000);
    --ab-glow: color-mix(in srgb, color-mix(in srgb, var(--cc) 58%, #fff) 55%, transparent);
    --ab-tint: color-mix(in srgb, var(--cc) 45%, transparent);
    --ab-soft: rgba(255, 255, 255, 0.10);
    --ab-shade: rgba(0, 0, 0, 0.30);
    background-color: var(--ab-base);
    background-repeat: no-repeat;
    overflow: hidden;
    isolation: isolate;
}

/* Grain fin commun (feTurbulence inline) : micro-texture en très faible opacité →
   fini « mat premium », casse le côté plat du dégradé pur. */
.event-cover-abstract::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    opacity: 0.12;
    mix-blend-mode: overlay;
    pointer-events: none;
}

.event-cover-abstract[data-variant="0"] {
    background-image:
        radial-gradient(120% 120% at 78% 18%, var(--ab-glow) 0%, transparent 55%),
        radial-gradient(90% 90% at 12% 92%, var(--ab-soft) 0%, transparent 60%),
        linear-gradient(135deg, var(--ab-deep) 0%, var(--ab-base) 100%);
}
.event-cover-abstract[data-variant="1"] {
    background-image:
        radial-gradient(70% 70% at 22% 24%, var(--ab-glow) 0%, transparent 55%),
        radial-gradient(120% 120% at 86% 82%, var(--ab-tint) 0%, transparent 60%),
        linear-gradient(150deg, var(--ab-base) 0%, var(--ab-deep) 100%);
}
.event-cover-abstract[data-variant="2"] {
    background-image:
        radial-gradient(100% 100% at 50% -8%, var(--ab-glow) 0%, transparent 55%),
        radial-gradient(85% 85% at 50% 118%, var(--ab-shade) 0%, transparent 60%),
        linear-gradient(180deg, var(--ab-base) 0%, var(--ab-deep) 100%);
}
.event-cover-abstract[data-variant="3"] {
    background-image:
        radial-gradient(88% 88% at 84% 86%, var(--ab-soft) 0%, transparent 55%),
        radial-gradient(110% 110% at 8% 8%, var(--ab-glow) 0%, transparent 55%),
        linear-gradient(135deg, var(--ab-deep) 0%, var(--ab-base) 100%);
}
.event-cover-abstract[data-variant="4"] {
    background-image:
        conic-gradient(from 210deg at 32% 30%, var(--ab-glow), transparent 42%),
        radial-gradient(120% 120% at 82% 72%, var(--ab-tint) 0%, transparent 60%),
        linear-gradient(135deg, var(--ab-deep) 0%, var(--ab-base) 100%);
}
.event-cover-abstract[data-variant="5"] {
    background-image:
        radial-gradient(58% 58% at 26% 30%, var(--ab-glow) 0%, transparent 55%),
        radial-gradient(66% 66% at 80% 68%, var(--ab-tint) 0%, transparent 55%),
        linear-gradient(160deg, var(--ab-deep) 0%, var(--ab-base) 100%);
}

@media (prefers-reduced-motion: no-preference) {
    .event-card-link:hover .event-cover-abstract { transform: scale(1.015); }
    .event-cover-abstract { transition: transform var(--duration-base) var(--ease-out); }
}

/* ──────────────────────────────────────────────────────────────────────────
   Pastille « Quick Edit » admin — raccourci d'édition en surimpression
   Discrète (verre marine), n'apparaît QUE pour un admin (rendu conditionnel
   `is_granted('ROLE_ADMIN')` en Twig / `window.OPALE_ADMIN` en JS). Ancrée en haut
   à GAUCHE de la vignette (le bouton like occupe le coin haut-droit). Comme la carte
   est déjà enveloppée dans un <a>, c'est un <button> (interdit d'imbriquer un <a>) :
   la navigation vers le back-office est déléguée en JS (data-quick-edit).
   ────────────────────────────────────────────────────────────────────────── */
.quick-edit {
    position: absolute;
    top: 0.55rem;
    left: 0.55rem;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: none;
    border-radius: 999px;
    background: rgba(10, 61, 98, 0.60);
    -webkit-backdrop-filter: blur(6px) saturate(140%);
    backdrop-filter: blur(6px) saturate(140%);
    color: #fff;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(10, 61, 98, 0.35);
    opacity: 0.88;
    transition: background var(--duration-fast) var(--ease-out),
                opacity var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}
.quick-edit:hover {
    background: var(--c-corail);
    opacity: 1;
    transform: translateY(-1px);
}
.quick-edit:focus-visible {
    outline: 2px solid var(--c-blanc);
    outline-offset: 2px;
    opacity: 1;
}
.quick-edit__icon {
    width: 15px;
    height: 15px;
    stroke-width: 2;
}


/*
like-button.css

QUOI : Bouton « j'aime » (cœur) : variantes pastille de carte et pilule de fiche, pop à la bascule, adaptations sombre.

COMMENT : Copie telle quelle des lignes 4250–4373 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* ──────────────────────────────────────────────────────────────────────────
   Bouton « j'aime » (cœur) — système de favoris
   Deux variantes : `--card` (pastille en surimpression de la vignette) et
   `--detail` (pilule cœur + libellé + compteur sur la fiche événement).
   ────────────────────────────────────────────────────────────────────────── */
.like-btn {
    --like-color: #e6396b;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: none;
    cursor: pointer;
    font: inherit;
    color: var(--c-encre, #1f2a37);
    background: none;
    -webkit-tap-highlight-color: transparent;
}

.like-btn__icon {
    width: 1.25rem;
    height: 1.25rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    transition: transform 0.18s ease, fill 0.18s ease, stroke 0.18s ease;
}

.like-btn.is-liked .like-btn__icon {
    fill: var(--like-color);
    stroke: var(--like-color);
}

/* Petit « pop » à la bascule. */
.like-btn:active .like-btn__icon {
    transform: scale(0.82);
}

/* ── Variante carte : pilule flottante « cœur + nombre de likes » en haut de la vignette ── */
.like-btn--card {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 2;
    gap: 0.3rem;
    padding: 0.3rem 0.6rem;
    border-radius: 999px;
    font-size: 0.82rem;
    font-weight: 700;
    line-height: 1;
    color: #fff;
    background: rgba(15, 23, 42, 0.5);
    backdrop-filter: blur(4px);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    transition: background 0.18s ease, transform 0.18s ease;
}

.like-btn--card:hover {
    background: rgba(15, 23, 42, 0.66);
    transform: scale(1.05);
}

.like-btn--card.is-liked {
    background: #fff;
    color: var(--like-color);
}

.like-btn--card .like-btn__icon {
    width: 1.05rem;
    height: 1.05rem;
}

/* ── Variante fiche : pilule cœur + compteur ──
   Base = MODE CLAIR : la pilule vit dans `.event-info-panel` (fond clair) → encre foncée + fond/bordure
   légèrement teintés sombre, pour rester lisible (avant, `color:#fff` la rendait blanc sur blanc = invisible).
   Le MODE SOMBRE restaure le blanc translucide (override plus bas) ; le FAB mobile impose son propre habillage
   corail (event-detail.css, spécificité supérieure) et n'est donc pas affecté. */
.like-btn--detail {
    padding: 0.55rem 1.1rem;
    border-radius: 999px;
    font-weight: 600;
    color: var(--c-encre, #1f2a37);
    background: rgba(15, 23, 42, 0.06);
    border: 1px solid rgba(15, 23, 42, 0.16);
    backdrop-filter: blur(6px);
    text-decoration: none;
    transition: background 0.18s ease, border-color 0.18s ease;
}

.like-btn--detail:hover {
    background: rgba(15, 23, 42, 0.11);
}

.like-btn--detail.is-liked {
    background: rgba(230, 57, 107, 0.10);
    color: var(--like-color);
    border-color: rgba(230, 57, 107, 0.45);
}

/* Mode sombre : la pilule vit sur un panneau sombre → blanc translucide (habillage d'origine). */
[data-theme="dark"] .like-btn--detail {
    color: #fff;
    background: rgba(255, 255, 255, 0.16);
    border-color: rgba(255, 255, 255, 0.35);
}

[data-theme="dark"] .like-btn--detail:hover {
    background: rgba(255, 255, 255, 0.26);
}

[data-theme="dark"] .like-btn--detail.is-liked {
    background: #fff;
    color: var(--like-color);
    border-color: #fff;
}

.like-btn__count {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.event-hero__actions {
    margin-top: var(--space-sm);
}

/* ──────────────────────────────────────────────────────────────────────────
   Toast d'erreur de favori (créé par likes.js) : message expliquant l'échec
   d'un « j'aime » (réseau KO), sinon l'annulation optimiste serait muette.
   Bas d'écran, discret, auto-masqué ; role="status" pour les lecteurs d'écran.
   ────────────────────────────────────────────────────────────────────────── */
.like-toast {
    position: fixed;
    left: 50%;
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--space-md));
    transform: translate(-50%, 1rem);
    z-index: 1000;
    max-width: min(92vw, 30rem);
    padding: 0.7rem 1rem;
    font-size: 0.9rem;
    line-height: 1.4;
    color: var(--c-blanc);
    background-color: var(--c-corail-fonce);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    opacity: 0;
    pointer-events: none;
    transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out);
}

.like-toast.is-visible {
    opacity: 1;
    transform: translate(-50%, 0);
}

@media (prefers-reduced-motion: reduce) {
    .like-toast {
        transition: opacity 220ms var(--ease-out);
    }

    .like-toast.is-visible {
        transform: translate(-50%, 0);
    }
}


/*
about-extras.css

QUOI : Photo d'illustration de la page « À propos » (slot about_photo).

COMMENT : Copie telle quelle des lignes 4374–4394 de public/css/style.css
          (scission jalon 5 — blocs contigus, zéro réécriture de règle).

OÙ : Importé par assets/styles/app.css, dans l'ordre de cascade du fichier d'origine.

POURQUOI : specs/projet.md §4.2 — aucun fichier CSS > 500 lignes, rendu identique
           au pixel (invariant I4).
*/

/* Photo d'illustration de la page « À propos » (slot about_photo). */
.about-photo {
    margin: 0 0 var(--space-lg);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-md);
}

.about-photo img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

@media (min-width: 700px) {
    .about-photo img {
        aspect-ratio: 16 / 7;
    }
}

/*
pages/entreprise.css

QUOI : Fiche publique d'une entreprise (`entreprise/show.html.twig`) et bloc annuaire des pages ville
       (`_partials/_bloc_annuaire.html.twig`).

COMMENT : Uniquement des tokens (`--c-*`, `--radius-*`, `--shadow-*`, `--space-*`) : le mode sombre
          suit alors sans règle dédiée, puisque `base/theme-dark.css` redéfinit ces mêmes variables.

OÙ : Importé en fin de `app.imports.css` ; le bundle servi est `app.css`, régénéré par
     `php bin/console app:assets:bundle-css`.

POURQUOI : La fiche est volontairement sobre — elle sert les trois niveaux de présence avec le même
           gabarit, la hiérarchie vient du contenu présent (photo, description) et non d'un habillage
           différent.
*/

/* ---------- Fiche entreprise ---------- */

.entreprise {
    max-width: 60rem;
    margin: 0 auto;
    padding: var(--space-lg) var(--space-sm) var(--space-xl);
}

.entreprise__fiche {
    background: var(--c-surface);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-lg);
}

.entreprise__photo {
    display: block;
    width: 100%;
    max-height: 22rem;
    object-fit: cover;
    border-radius: var(--radius-md);
    margin-bottom: var(--space-md);
}

.entreprise__nom {
    margin: 0;
    color: var(--c-marine);
    font-size: clamp(1.6rem, 4vw, 2.25rem);
    line-height: 1.15;
}

.entreprise__soustitre {
    margin: var(--space-xs) 0 0;
    color: var(--c-texte-doux);
    font-size: 1.05rem;
}

.entreprise__themes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    list-style: none;
    margin: var(--space-md) 0 0;
    padding: 0;
}

.entreprise__theme {
    background: var(--c-surface-2);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-sm);
    color: var(--c-texte);
    font-size: 0.85rem;
    padding: 0.25rem 0.7rem;
}

.entreprise__description {
    margin-top: var(--space-md);
    color: var(--c-texte);
    line-height: 1.65;
}

.entreprise__infos {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-xs) var(--space-sm);
    margin: var(--space-md) 0 0;
    padding-top: var(--space-md);
    border-top: 1px solid var(--c-bordure);
}

.entreprise__infos dt {
    color: var(--c-texte-doux);
    font-size: 0.9rem;
}

.entreprise__infos dd {
    margin: 0;
    color: var(--c-texte);
}

.entreprise__infos a {
    color: var(--c-mer);
}

.entreprise__site {
    display: inline-block;
    margin-top: var(--space-md);
    padding: 0.6rem 1.4rem;
    background: var(--c-corail);
    color: var(--c-blanc);
    border-radius: var(--radius-sm);
    font-weight: 600;
    text-decoration: none;
}

.entreprise__site:hover,
.entreprise__site:focus-visible {
    background: var(--c-corail-fonce);
}

.entreprise__retour {
    display: inline-block;
    margin-top: var(--space-md);
    margin-left: var(--space-sm);
    color: var(--c-mer);
    font-size: 0.95rem;
}

/* ---------- Bloc annuaire des pages ville ---------- */

.bloc-annuaire {
    margin-top: var(--space-xl);
    padding-top: var(--space-lg);
    border-top: 1px solid var(--c-bordure);
}

.bloc-annuaire__titre {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    margin: 0 0 var(--space-md);
    color: var(--c-marine);
    font-size: clamp(1.3rem, 3vw, 1.75rem);
}

.bloc-annuaire__compte {
    background: var(--c-surface-2);
    border-radius: var(--radius-sm);
    color: var(--c-texte-doux);
    font-size: 0.85rem;
    font-weight: 500;
    padding: 0.15rem 0.6rem;
}

.bloc-annuaire__filtres {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    margin-bottom: var(--space-md);
}

.bloc-annuaire__filtres input,
.bloc-annuaire__filtres select {
    background: var(--c-surface);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-sm);
    color: var(--c-texte);
    padding: 0.5rem 0.75rem;
}

.bloc-annuaire__filtres button {
    background: var(--c-mer);
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-blanc);
    cursor: pointer;
    font-weight: 600;
    padding: 0.5rem 1.1rem;
}

.bloc-annuaire__liste {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: var(--space-xs);
    list-style: none;
    margin: 0;
    padding: 0;
}

.bloc-annuaire__item a {
    display: block;
    height: 100%;
    background: var(--c-surface);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-md);
    padding: var(--space-sm);
    text-decoration: none;
}

.bloc-annuaire__item a:hover,
.bloc-annuaire__item a:focus-visible {
    box-shadow: var(--shadow-sm);
}

/* Pastille de niveau de présence — même vocabulaire que `.partner-cell__tag` des encarts, pour
   qu'une entreprise se lise pareil dans la grille et dans cette liste. C'est ici que les trois
   niveaux coexistent : la couleur est donc la seule distinction visible, doublée d'un libellé en
   toutes lettres pour les lecteurs d'écran (WCAG 1.4.1). */
.bloc-annuaire__pastille {
    display: inline-block;
    margin-bottom: 0.35rem;
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.15rem 0.45rem;
    border-radius: 999px;
    color: var(--c-niveau-normale-texte);
    background: var(--c-niveau-normale-fond);
}

.bloc-annuaire__pastille--boostee {
    color: var(--c-niveau-boostee-texte);
    background: var(--c-niveau-boostee-fond);
}

.bloc-annuaire__pastille--preferentielle {
    color: var(--c-niveau-preferentielle-texte);
    background: var(--c-niveau-preferentielle-fond);
}

/* Les sponsorisées sont déjà en tête de liste : le rang est porté par l'ordre, pas par un habillage.
   On se contente de poser la surface secondaire du système sous les boostées, ce qui les détache
   sans introduire de signal supplémentaire à décoder. */
.bloc-annuaire__item--boostee a {
    background: var(--c-surface-2);
}

.bloc-annuaire__nom {
    display: block;
    color: var(--c-marine);
    font-weight: 600;
}

.bloc-annuaire__soustitre {
    display: block;
    margin-top: 0.15rem;
    color: var(--c-texte-doux);
    font-size: 0.9rem;
}

.bloc-annuaire__pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    margin-top: var(--space-md);
    color: var(--c-texte-doux);
}

.bloc-annuaire__pagination a {
    color: var(--c-mer);
    font-weight: 600;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Formulaire de contact d'une entreprise

   La section reprend la carte de la fiche (surface, bordure, rayon, ombre teintée)
   pour se lire comme sa suite naturelle, et non comme un bloc rapporté.

   Repliée par défaut : la fiche reste une fiche. Le dépliant natif `<details>` est
   préféré à du JavaScript — il fonctionne sans script, reste accessible au clavier
   et annonce son état aux lecteurs d'écran sans un attribut de plus.
   ───────────────────────────────────────────────────────────────────────────── */
/* La section vit DANS la carte de la fiche : ni fond, ni bordure, ni ombre propres — un filet
   supérieur suffit à la séparer, comme le bloc d'informations juste au-dessus. Lui donner sa
   propre carte produisait deux blocs distincts là où il n'y a qu'une entreprise. */
.entreprise-contact {
    margin-top: var(--space-md);
    padding-top: var(--space-md);
    border-top: 1px solid var(--c-bordure);
}

.entreprise-contact__pli {
    border-radius: var(--radius-md);
    overflow: hidden;
}

/* Le marqueur natif est retiré au profit d'un chevron maîtrisé : `list-style` couvre
   Firefox, le pseudo-élément WebKit couvre Safari et Chrome. */
.entreprise-contact__declencheur {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-sm) 0;
    font-family: var(--font-display, inherit);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--c-marine);
}

.entreprise-contact__declencheur::-webkit-details-marker { display: none; }

.entreprise-contact__declencheur::before {
    content: "";
    width: 0.6rem;
    height: 0.6rem;
    border-right: 2px solid var(--c-mer);
    border-bottom: 2px solid var(--c-mer);
    transform: rotate(-45deg);
    transition: transform 200ms var(--ease-out, ease);
    flex: none;
}

.entreprise-contact__pli[open] .entreprise-contact__declencheur::before {
    transform: rotate(45deg);
}

.entreprise-contact__declencheur:hover { color: var(--c-mer); }

.entreprise-contact__declencheur:focus-visible {
    outline: 3px solid var(--c-focus);
    outline-offset: -3px;
}

.entreprise-contact__intro,
.entreprise-contact__mentions {
    margin: 0 0 var(--space-md);
    color: var(--c-texte-doux);
    font-size: 0.92rem;
    line-height: 1.6;
}

.entreprise-contact__mentions {
    margin-top: var(--space-md);
    margin-bottom: 0;
    font-size: 0.82rem;
}

.entreprise-contact__form {
    padding: 0;
    display: grid;
    gap: var(--space-sm);
}

/* Symfony enveloppe chaque champ dans un `div` : on empile libellé puis contrôle,
   au lieu de l'alignement en ligne du navigateur qui collait l'un à l'autre. */
.entreprise-contact__form > div {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.entreprise-contact__form label {
    font-weight: 600;
    color: var(--c-marine);
    font-size: 0.95rem;
}

.entreprise-contact__form input[type="text"],
.entreprise-contact__form input[type="email"],
.entreprise-contact__form input[type="tel"],
.entreprise-contact__form textarea {
    width: 100%;
    padding: 0.7rem 0.85rem;
    font: inherit;
    color: var(--c-texte);
    background: var(--c-sable-clair);
    border: 1px solid var(--c-bordure);
    border-radius: var(--radius-sm);
    transition: border-color 160ms var(--ease-out, ease), box-shadow 160ms var(--ease-out, ease);
}

.entreprise-contact__form textarea {
    resize: vertical;
    min-height: 9rem;
}

.entreprise-contact__form input:focus,
.entreprise-contact__form textarea:focus {
    outline: none;
    border-color: var(--c-mer);
    box-shadow: 0 0 0 3px var(--c-focus);
}

/* Case de consentement : case d'abord, libellé à côté — l'ordre vient du gabarit, qui rend le
   widget avant son libellé. Sans quoi `form_row` place le libellé en tête et rejette la case en
   fin de ligne, à plusieurs centimètres du texte qu'elle valide. */
.entreprise-contact__consentement {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: var(--space-xs);
}

.entreprise-contact__form input[type="checkbox"] {
    margin-top: 0.25rem;
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--c-mer);
    flex: none;
}

.entreprise-contact__consentement label {
    font-weight: 400;
    color: var(--c-texte);
    font-size: 0.92rem;
    line-height: 1.5;
}

.entreprise-contact__form .form-help,
.entreprise-contact__form .help-text,
.entreprise-contact__form small {
    color: var(--c-texte-doux);
    font-size: 0.85rem;
}

.entreprise-contact__form ul,
.entreprise-contact__form .form-error-message {
    margin: 0.15rem 0 0;
    padding-left: 1.1rem;
    color: var(--c-corail-texte);
    font-size: 0.85rem;
}

.entreprise-contact__form button[type="submit"] {
    justify-self: start;
    margin-top: var(--space-xs);
    padding: 0.8rem 1.6rem;
    font: inherit;
    font-weight: 700;
    color: var(--c-blanc);
    background: linear-gradient(135deg, var(--c-marine) 0%, var(--c-mer) 100%);
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: transform 160ms var(--ease-out, ease), box-shadow 160ms var(--ease-out, ease);
}

.entreprise-contact__form button[type="submit"]:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
}

.entreprise-contact__form button[type="submit"]:focus-visible {
    outline: 3px solid var(--c-focus);
    outline-offset: 2px;
}

/* Honeypot : invisible d'un humain, mais laissé dans le flux du document — un
   `display: none` est justement ce que certains robots détectent pour l'éviter. */
.entreprise-contact__form .honeypot-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

@media (max-width: 640px) {
    .entreprise-contact__form button[type="submit"] { justify-self: stretch; text-align: center; }
}

