/* ==========================================================================
   Wiinia — feuille de styles applicative
   Tailwind (CDN) gère les utilitaires ; ce fichier regroupe les styles
   personnalisés réutilisables qui étaient auparavant écrits en ligne
   (attributs style="") ou dans des blocs <style> inline.
   ========================================================================== */

/* --- Polices auto-hébergées ----------------------------------------------
   🔁 **Refonte du 2026-09-26 : la typographie du modèle fourni par
   l'utilisateur** (`wiinia-accueil.html`) — Bricolage Grotesque pour les
   titres, Hanken Grotesk pour le texte. Elles remplacent Plus Jakarta Sans.

   ⚠️ Servies depuis NOTRE serveur, jamais depuis Google Fonts : la CSP n'admet
   aucune origine tierce, et la feuille de Google est une ressource BLOQUANTE
   (751 ms mesurés par PageSpeed quand Plus Jakarta en venait). Fichiers tirés
   des paquets `@fontsource-variable/*` 5.3.0, licence OFL-1.1.

   ⚠️ Condition posée par l'utilisateur : « tant que ça ne ralentit pas le
   chargement ». D'où :
     · des polices VARIABLES — un fichier par famille couvre toutes les
       graisses (`font-weight: 200 800`), jamais cinq fichiers statiques ;
     · seuls les deux fichiers `latin` sont préchargés (`_head_assets.html`) ;
       `latin-ext` n'est téléchargé que si un caractère de sa plage apparaît
       réellement (la `unicode-range` fait le tri côté navigateur) ;
     · `font-display: swap` — le texte s'affiche tout de suite dans la police
       de repli, jamais de page blanche en attendant la police.
   Coût mesuré : 76 Ko pour les deux `latin` contre 27 Ko pour Plus Jakarta.

   ⚠️ Les `url()` sont RELATIVES, à dessein : `ManifestStaticFilesStorage` les
   réécrit vers les noms hachés au `collectstatic`. Une URL absolue casserait
   ce mécanisme — et le job `prod-check` de la CI échouerait sur un fichier
   introuvable, ce qui est le comportement voulu.
   ------------------------------------------------------------------------ */
@font-face {
    font-family: 'Hanken Grotesk';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/hanken-grotesk-latin-wght-normal.c501f578bf3f.woff2") format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}
@font-face {
    font-family: 'Hanken Grotesk';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/hanken-grotesk-latin-ext-wght-normal.20672d217485.woff2") format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                   U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                   U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                   U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Bricolage Grotesque';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url("../fonts/bricolage-grotesque-latin-wght-normal.ada540c0e24a.woff2") format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                   U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                   U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                   U+FFFD;
}
@font-face {
    font-family: 'Bricolage Grotesque';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url("../fonts/bricolage-grotesque-latin-ext-wght-normal.b1ae2c312cfb.woff2") format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                   U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                   U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                   U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   JETONS DE COULEUR — la source de vérité de la palette (2026-09-26)
   --------------------------------------------------------------------------
   🚨 **C'est ICI que vivent les couleurs du produit, et nulle part ailleurs.**
   `tailwind.config.js` ne fait que leur donner des noms de classe
   (`bg-surface-100`, `text-ink-500`, `text-accent`) ; il ne contient pas une
   seule valeur de gris, d'encre ou d'accent.

   🔁 **Les valeurs sont celles du modèle fourni par l'utilisateur**
   (`wiinia-accueil.html`, 2026-09-26 : « copie la typographie et les
   couleurs »). Chaque jeton qui en vient le dit en commentaire. Les paliers
   intermédiaires sont interpolés entre deux valeurs du modèle.

   🔁 **Le thème sombre a été RETIRÉ le 2026-09-26, à la demande de
   l'utilisateur** (« retire le mode dark »). Il n'y a plus qu'un bloc. Les
   jetons restent : ils nomment un RÔLE (« la surface d'une carte »), ce qui
   vaut avec ou sans second thème, et ils ont permis de changer toute la
   palette ici en un seul endroit.

   ⚠️ **Les valeurs sont des canaux nus (« 255 255 255 »), pas des couleurs
   CSS.** C'est ce qu'exige Tailwind pour que `bg-surface-100/60` reste valide :
   il compile en `rgb(var(--surface-100) / 0.6)`. Écrire `#ffffff` ici rendrait
   invalide — donc invisible, sans erreur — toute classe portant une opacité.

   ⚠️ `--brand-*` ne figure PAS ici : cette rampe varie par AGENCE, elle est
   posée par `apps/shared/context_processors.brand_style` dans un `<style>` en
   ligne (le HTML est rendu par tenant, le CSS ne l'est pas).
   ========================================================================== */
:root {
    color-scheme: light;

    /* Surfaces — 50 le fond de page, 100 la carte, 200/300 les aplats. */
    --surface-50:  250 250 246;   /* #fafaf6 — modèle `--bg` */
    --surface-100: 255 255 255;   /* #ffffff — modèle `--surface` */
    --surface-200: 241 244 239;   /* #f1f4ef — modèle `--surface-2` */
    --surface-300: 230 235 228;   /* #e6ebe4 */

    /* Traits. */
    --line-100: 234 238 232;      /* #eaeee8 */
    --line-200: 226 231 225;      /* #e2e7e1 — modèle `--line` */
    --line-300: 202 211 203;      /* #cad3cb */

    /* Encre — celle du modèle, un vert-de-gris très sombre. */
    --ink-900: 26 43 40;          /* #1a2b28 — modèle `--ink` */
    --ink-800: 35 53 49;          /* #233531 */
    --ink-700: 46 63 59;          /* #2e3f3b */
    --ink-600: 64 80 76;          /* #40504c */
    --ink-500: 81 98 94;          /* #51625e — modèle `--ink-2`, 6,16 sur le fond */
    /* 🔁 2026-09-30 — le `--ink-3` du modèle (#8a9793) mesurait ~3,0 sur le
       fond : sous le seuil AA de 4,5 pour du texte (relevé par PageSpeed).
       Assombri d'un cran, il reste plus clair que 500 : ~5,1 sur blanc,
       ~4,6 sur surface-200. Mesuré par `tests/test_color_contrast.py`. */
    --ink-400: 100 113 109;       /* #64716d */
    --ink-300: 176 187 183;       /* #b0bbb7 */
    --ink-200: 208 215 212;       /* #d0d7d4 */
    --ink-100: 226 231 225;       /* #e2e7e1 */

    /* Accent — terre de barre. 600/700 sont des APLATS à texte blanc.

       ⚠️ **500 est la terre de barre EXACTE du modèle (`#d9683a`), mais ce
       n'est pas elle qui porte les boutons.** Du blanc sur `#d9683a` mesure
       3,5 : sous le seuil AA de 4,5 pour du texte de 15 px. Les boutons
       prennent donc 600, un cran plus profond (4,67). La terre de barre exacte
       reste partout où elle n'a pas de texte à porter — le point du logo, les
       puces, les barres — et sur le mot mis en avant du titre, qui est du
       GRAND texte (seuil 3,0, elle mesure 3,35). */
    --accent-50:  253 244 239;    /* #fdf4ef */
    --accent-100: 251 233 223;    /* #fbe9df — modèle `--barre-soft` */
    --accent-200: 245 205 184;    /* #f5cdb8 */
    --accent-300: 237 168 134;    /* #eda886 */
    --accent-400: 228 133 96;     /* #e48560 */
    --accent-500: 217 104 58;     /* #d9683a — modèle `--barre` */
    --accent-600: 189 85 42;      /* #bd552a — 4,67 sous du blanc */
    --accent-700: 158 69 34;      /* #9e4522 */
    --accent-800: 116 50 26;      /* #74321a */
    --accent-900: 78 36 19;       /* #4e2413 */

    /* 🚨 L'accent LISIBLE — `text-accent`, pour du TEXTE d'accent. Il doit
       tenir 4,5 sur le fond de page (`#fafaf6`), pas seulement sur blanc :
       600 y tombe à 4,47. D'où ce palier propre. */
    --accent-lisible: 184 82 41;  /* #b85229 — 4,69 sur le fond de page */

    /* Menthe — les panneaux doux du modèle (bloc agences, pastilles). */
    --mint-50:  243 249 246;      /* #f3f9f6 */
    --mint-100: 230 242 236;      /* #e6f2ec — modèle `--mint` */
    --mint-200: 211 232 222;      /* #d3e8de — modèle `--mint-2` */
    --mint-700: 44 110 87;        /* #2c6e57 — modèle `--green`, 5,26 sur menthe */
    --mint-800: 35 90 71;         /* #235a47 — survol des aplats menthe */
}

/* --- Global ------------------------------------------------------------- */
[x-cloak] { display: none !important; }

/* L'attribut `hidden` ne cache RIEN dès qu'une classe pose un `display`.
 *
 * Tailwind déclare pourtant la règle dans son préflight :
 * `[hidden]:where(:not([hidden=until-found])){display:none}`. Mais `:where()`
 * ne pèse rien — la règle vaut (0,1,0), exactement comme `.flex` — et les
 * utilitaires sont écrits APRÈS le préflight dans le même fichier. À égalité
 * de spécificité, c'est le dernier qui gagne : `display:flex` l'emporte, et
 * l'élément reste affiché.
 *
 * 🚨 Ce que ça a coûté : le voile d'envoi du formulaire de bien
 * (`<div id="envoi-en-cours" hidden class="fixed inset-0 … flex …">`)
 * s'affichait dès l'OUVERTURE de la page, par-dessus tout, et interceptait
 * chaque clic. Le formulaire de création paraissait figé sur
 * « Enregistrement… » alors qu'aucune requête n'était partie. Rien ne le
 * signalait : le HTML est correct, le JavaScript aussi, et aucun test ne
 * calcule de CSS — seul un œil devant la page pouvait le voir.
 *
 * Tailwind 4 pose ce `!important` lui-même ; on ne fait que l'avancer. Le
 * `:where(:not([hidden=until-found]))` est conservé tel quel : sans lui, on
 * casserait la révélation d'un contenu replié par la recherche du navigateur.
 */
[hidden]:where(:not([hidden="until-found"])) { display: none !important; }
body { font-family: 'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif; }

/* Indicateur de chargement HTMX (affiché pendant une requête en cours). */
.htmx-indicator { opacity: 0; transition: opacity 150ms ease-in; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* --- Loaders (Étape 2) -------------------------------------------------- */
/* Barre de progression globale en haut de page, pilotée par app.js pour
   TOUTE requête HTMX (filtres, pagination, recherche, favori, message…). */
#wiinia-progress {
    position: fixed;
    top: 0; left: 0;
    height: 3px;
    width: 0;
    background: rgb(var(--accent-600));
    z-index: 9999;
    opacity: 0;
    transition: width 200ms ease, opacity 250ms ease;
    pointer-events: none;
}
#wiinia-progress.is-active { opacity: 1; }

/* Skeleton « shimmer » — placeholder animé pendant le chargement d'une image
   ou d'un bloc. Se met en place via la classe .skeleton. */
@keyframes wiinia-shimmer { 100% { transform: translateX(100%); } }
.skeleton { position: relative; overflow: hidden; background: rgb(var(--surface-300)); }
.skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg,
        rgb(var(--surface-100) / 0) 0,
        rgb(var(--surface-100) / 0.55) 50%,
        rgb(var(--surface-100) / 0) 100%);
    animation: wiinia-shimmer 1.4s infinite;
}

/* Fondu d'apparition des images. IMPORTANT : animation auto-suffisante qui
   FINIT toujours à opacity:1 (fill-mode both) — on ne dépend PAS de JS pour
   révéler l'image, sinon une erreur de chargement la laisserait invisible. */
@keyframes wiinia-fadein { from { opacity: 0.25; } to { opacity: 1; } }
.img-reveal { animation: wiinia-fadein 0.4s ease both; }

/* Boutons HTMX « occupés » : pendant la requête, on grise + curseur attente. */
.htmx-request.btn-busy,
.htmx-request .btn-busy { opacity: 0.55; pointer-events: none; cursor: wait; }

/* ==========================================================================
   SuperAdmin — sidebar (admin/base_admin.html)
   ========================================================================== */
.nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: 13.5px;
    font-weight: 500;
    color: #94a3b8;
    transition: all 150ms;
    cursor: pointer;
    text-decoration: none;
}
.nav-item:hover { background: #1e293b; color: #f1f5f9; }
.nav-item.active {
    background: #1e293b;
    color: #f1f5f9;
    border-left: 2px solid rgb(var(--brand-600));
    padding-left: 10px;
}
.nav-item svg { flex-shrink: 0; opacity: .7; }
/* Sous-menu « Attributs » (2026-09-27) — mêmes teintes que `.nav-item`. */
.nav-sous {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px 6px 40px;
    border-radius: 8px;
    font-size: 13px;
    color: #94a3b8;
}
.nav-sous:hover, .nav-sous.active { background: #1e293b; color: #f1f5f9; }
.nav-sous-nombre { margin-left: auto; font-size: 11px; color: #64748b; font-variant-numeric: tabular-nums; }
.nav-item.active svg { opacity: 1; color: rgb(var(--brand-300)); }

.nav-section {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: #334155;
    padding: 4px 12px;
    margin-top: 8px;
    margin-bottom: 2px;
}
.nav-badge {
    margin-left: auto;
    font-size: 10px;
    font-weight: 700;
    padding: 2px 7px;
    border-radius: 99px;
    background: rgb(var(--brand-600) / 0.18);
    color: rgb(var(--brand-300));
}

.scrollbar-thin::-webkit-scrollbar { width: 4px; }
.scrollbar-thin::-webkit-scrollbar-track { background: transparent; }
.scrollbar-thin::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 99px; }

/* Sidebar agence — scrollbar discrète sur fond sombre */
.sidebar-scroll::-webkit-scrollbar { width: 3px; }
.sidebar-scroll::-webkit-scrollbar-track { background: transparent; }
.sidebar-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 99px; }
.sidebar-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.25); }
/* Firefox */
.sidebar-scroll { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.12) transparent; }

/* Bande qui défile au doigt, sans barre grise du navigateur (téléphone). Sur
   ordinateur, ne pas s'en servir pour masquer un défilement : sans pavé
   tactile, la souris n'aurait plus aucun moyen d'atteindre la suite. */
.defile-sans-barre { scrollbar-width: none; }
.defile-sans-barre::-webkit-scrollbar { display: none; }

/* Icône de navigation (taille fixe 18px) */
.nav-icon { width: 18px; height: 18px; }

/* ==========================================================================
   Back-office agence — sidebar (manage/base_manage.html)
   ========================================================================== */
.sidebar-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-radius: 10px;
    font-size: 13.5px;
    font-weight: 500;
    transition: all 0.15s;
    color: rgba(255, 255, 255, 0.55);
    text-decoration: none;
}
.sidebar-link:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.sidebar-link.active { color: #fff; background: rgb(var(--brand-600)); }
.sidebar-link.disabled { color: rgba(255, 255, 255, 0.25); cursor: default; }
.sidebar-link svg { width: 16px; height: 16px; flex-shrink: 0; }

.sidebar-section {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.25);
    padding: 16px 12px 4px;
}

/* Menus repliables (accordéon Alpine) ----------------------------------- */
/* Le bouton parent réutilise .sidebar-link ; on neutralise les styles bouton. */
.sidebar-group-toggle {
    width: 100%;
    border: 0;
    background: transparent;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
}
.sidebar-group-toggle .chevron {
    margin-left: auto;
    width: 14px;
    height: 14px;
    opacity: 0.5;
    transition: transform 0.18s;
}
.sidebar-group-toggle[aria-expanded="true"] .chevron { transform: rotate(180deg); }
/* Pastille affichée sur le parent quand le groupe est replié. */
.sidebar-group-badge {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 6px;
    border-radius: 99px;
    background: rgb(var(--accent-600));
    color: #fff;
    font-size: 10px;
    font-weight: 700;
}
/* Quand le groupe est déplié, on masque la pastille du PARENT (le compteur
   réapparaît sur la sous-ligne Messagerie, qui est alors visible). */
.sidebar-group-toggle[aria-expanded="true"] .sidebar-group-badge { display: none; }
.sidebar-sub {
    margin: 2px 0 2px 18px;
    padding-left: 12px;
    border-left: 1px solid rgba(255, 255, 255, 0.1);
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.sidebar-sublink {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.5);
    text-decoration: none;
    transition: all 0.15s;
}
.sidebar-sublink:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.sidebar-sublink.active { color: #fff; background: rgb(var(--brand-600)); }

/* ==========================================================================
   Lien d'évitement (skip-link) — premier élément focusable des 3 layouts
   ========================================================================== */
/* Permet à un utilisateur au clavier ou au lecteur d'écran de sauter la
   navigation — jusqu'à une trentaine de liens dans la sidebar du back-office —
   pour atteindre directement le contenu.

   ⚠️ SURTOUT PAS `sr-only` ici, malgré l'idiome répandu. `sr-only` sort
   l'élément de l'écran en position absolue ; quand il reçoit le focus, le
   navigateur fait défiler le document pour l'amener dans la vue. Or le <body>
   du back-office est `overflow-hidden h-screen` : c'est <html> qui défile, et
   tout le contenu passe au-dessus du viewport — la page devient blanche. C'est
   exactement le bug déjà payé sur l'input fichier du formulaire de bien
   (cf. CLAUDE.md, « Input fichier masqué »).

   L'élément reste donc positionné, juste AU-DESSUS du viewport, et redescend
   au focus : rien à faire défiler, on est déjà en haut. */
.skip-link {
    position: absolute;
    top: -4rem;
    left: 0.75rem;
    z-index: 100;
    padding: 0.5rem 1rem;
    border-radius: 0.5rem;
    background: rgb(var(--brand-600));
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: top 0.15s;
}
.skip-link:focus {
    top: 0.75rem;
    outline: 2px solid #fff;
    outline-offset: 2px;
}

/* ==========================================================================
   Formulaires (properties/property_form.html)
   ========================================================================== */
.form-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: rgb(var(--ink-700));
    margin-bottom: 5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.form-input,
.form-select {
    width: 100%;
    border: 1.5px solid rgb(var(--line-200));
    border-radius: 10px;
    padding: 9px 12px;
    font-size: 14px;
    background: rgb(var(--surface-100));
    color: rgb(var(--ink-900));
    transition: border-color 0.15s;
    outline: none;
}
.form-input:focus,
.form-select:focus {
    border-color: rgb(var(--brand-600));
    box-shadow: 0 0 0 3px rgb(var(--brand-600) / 0.12);
}

.card {
    background: rgb(var(--surface-100));
    border-radius: 16px;
    border: 1px solid rgb(var(--line-100));
    padding: 20px;
}
.card-title {
    font-size: 13px;
    font-weight: 700;
    color: rgb(var(--ink-900));
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid rgb(var(--line-100));
}

/* ── Sections de formulaire repliables (native <details>, zéro JS) ──────────
   Le formulaire de bien est long : chaque groupe est repliable pour limiter le
   scroll. `<details>` garde les champs dans le DOM même replié → ils restent
   soumis avec le formulaire. Accessible au clavier nativement. */
details.card > summary.card-title {
    cursor: pointer;
    list-style: none;                 /* retire le triangle par défaut */
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 0;                 /* replié : pas de gros vide sous le titre */
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}
details.card[open] > summary.card-title {
    margin-bottom: 16px;              /* ouvert : espace avant le contenu */
}
details.card > summary.card-title::-webkit-details-marker { display: none; }  /* Safari */
/* Chevron indicateur (pivote à l'ouverture). */
details.card > summary.card-title::after {
    content: "";
    flex: none;
    margin-left: auto;                            /* pousse le chevron à droite */
    width: 8px;
    height: 8px;
    border-right: 2px solid rgb(var(--ink-400));
    border-bottom: 2px solid rgb(var(--ink-400));
    transform: rotate(45deg) translateY(-2px);   /* chevron vers le bas */
    transition: transform 0.2s ease;
}
details.card[open] > summary.card-title::after {
    transform: rotate(-135deg) translateY(-2px); /* chevron vers le haut */
}
summary.card-title:hover { color: rgb(var(--accent-lisible)); }
summary.card-title:focus-visible {
    outline: 2px solid rgb(var(--brand-600));
    outline-offset: 2px;
    border-radius: 6px;
}

/* ==========================================================================
   Carte Leaflet — confinement du z-index
   --------------------------------------------------------------------------
   Leaflet pose des z-index internes ÉLEVÉS : tuiles/panes 400, contrôles
   (zoom, attribution) et popups jusqu'à 1000. Sans stacking context sur le
   conteneur, ces valeurs se comparent GLOBALEMENT → la carte passe au-dessus
   des modales de l'app (contact `z-[60]`, lightbox `z-50`). `isolation:isolate`
   crée un contexte d'empilement local : tout le sous-arbre carte reste confiné
   sous les couches applicatives. (Constaté en navigateur : modale de contact
   masquée par la carte.)
   ========================================================================== */
.leaflet-container {
    isolation: isolate;
    z-index: 0;
}

/* Survol aux couleurs de l'agence — remplace deux paires de gestionnaires JS.
 *
 * La couleur d'une agence n'est connue qu'au rendu : elle ne peut pas vivre
 * dans une classe Tailwind, qui est figée au build. Elle passait donc par un
 * `onmouseover="this.style…"` — c'est-à-dire par du JavaScript en ligne, pour
 * un effet purement décoratif. Les deux variables ci-dessous sont posées dans
 * l'attribut `style` de l'élément, et c'est le CSS qui fait le survol.
 *
 * ⚠️ Gain réel au passage, et il n'est pas cosmétique : la couleur était
 * interpolée DANS une chaîne JavaScript (`this.style.background='…'`). Une
 * valeur contenant une apostrophe cassait le gestionnaire — au mieux. Ici elle
 * n'atteint plus aucun interpréteur JavaScript.
 */
.survol-marque              { background-color: var(--survol-repos); }
.survol-marque:hover,
.survol-marque:focus-visible { background-color: var(--survol-actif); }

/* ==========================================================================
   LE CADRE DU MODÈLE (`wiinia-accueil.html`) — accueil et bandeaux de page
   --------------------------------------------------------------------------
   🔁 2026-09-26. Ce qu'un utilitaire Tailwind écrirait mal : le cadre arrondi
   (anneau blanc + ombre portée en un seul `box-shadow`), le voile qui efface
   la scène sous le texte, et les pastilles vitrées de l'accueil.

   `.cadre-scene` / `.voile-scene` servent l'accueil ET les bandeaux des autres
   pages (fiche agence, « Tous les biens », « Qui sommes-nous ») : ils
   s'appelaient `accueil-*`, un nom qui mentait dès leur deuxième usage.
   `.accueil-puce`, `.accueil-etiquette`, `.accueil-losange` restent propres à
   l'accueil.

   ⚠️ Le fond `#eaf4f8` et l'ombre teintée sont LITTÉRAUX : ce sont les
   couleurs de la scène dessinée, pas celles de l'interface — les passer en
   jetons les ferait changer le jour où l'on retouche la palette du site, sans
   que la scène suive.
   ========================================================================== */
/* ==========================================================================
   `.conteneur` — contenu FLUIDE (2026-09-27, demande de l'utilisateur).
   Pleine largeur, marges latérales proportionnelles à l'écran : 16 px sur un
   téléphone, jusqu'à 72 px sur un grand écran. Remplace la colonne centrée
   `max-w-7xl mx-auto px-4` (1 280 px), qui laissait deux bandes vides sur
   tout écran plus large.
   ⚠️ Les blocs de TEXTE gardent leur largeur de lecture (`max-w-2xl/3xl`) :
   une ligne de 200 caractères ne se lit pas.
   ========================================================================== */
.conteneur {
    width: 100%;
    margin-inline: auto;
    padding-inline: clamp(16px, 4vw, 72px);
}

.cadre-scene {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    border-radius: 28px;
    background: #eaf4f8;
    box-shadow: 0 0 0 6px rgb(var(--surface-100)),
                0 30px 60px -34px rgba(44, 110, 87, 0.35);
}
@media (max-width: 520px) {
    .cadre-scene { border-radius: 22px; }
}
/* Le voile : opaque sous le texte à gauche, transparent sur la villa à droite,
   et un second dégradé qui remonte du bas pour asseoir la carte de recherche. */
.voile-scene {
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        linear-gradient(90deg, rgb(var(--surface-50) / 0.94) 0%,
                               rgb(var(--surface-50) / 0.78) 36%,
                               rgb(var(--surface-50) / 0) 60%),
        linear-gradient(0deg,  rgb(var(--surface-50) / 0.9) 0%,
                               rgb(var(--surface-50) / 0) 26%);
}
/* Sur un écran étroit le texte occupe toute la largeur : le voile doit le
   couvrir en entier, sinon il se lit sur la villa. */
@media (max-width: 860px) {
    .voile-scene {
        background:
            linear-gradient(180deg, rgb(var(--surface-50) / 0.94) 0%,
                                    rgb(var(--surface-50) / 0.82) 55%,
                                    rgb(var(--surface-50) / 0) 78%),
            linear-gradient(0deg,   rgb(var(--surface-50) / 0.9) 0%,
                                    rgb(var(--surface-50) / 0) 26%);
    }
}
.vitre {
    background: rgb(255 255 255 / 0.8);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}
/* 🚨 2026-09-29 — PAS de `display` ici : app.css est chargé APRÈS tailwind.css,
   donc à spécificité égale un `display:flex` battait `hidden` — les pastilles
   « ordinateur seulement » (`hidden lg:flex`) s'affichaient sur téléphone, par-
   dessus les chiffres du héros. L'affichage se décide dans le gabarit
   (`tests/test_css_hidden.py`). */
.accueil-puce {
    position: absolute;
    align-items: center;
    gap: 10px;
    padding: 9px 14px 9px 9px;
    border-radius: 14px;
    background: rgb(255 255 255 / 0.78);
    color: rgb(var(--ink-900));
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    font-size: 13.5px;
    font-weight: 500;
    box-shadow: 0 10px 30px -14px rgba(26, 43, 40, 0.3);
    animation: accueil-flotte 6s ease-in-out infinite;
}
/* L'étiquette de prix : la petite pointe qui la rattache à la maison. */
.accueil-etiquette::after {
    content: "";
    position: absolute;
    left: 22px;
    bottom: -7px;
    width: 14px;
    height: 14px;
    background: inherit;
    transform: rotate(45deg);
    border-radius: 0 0 4px 0;
}
@keyframes accueil-flotte { 50% { transform: translateY(-6px); } }
@media (prefers-reduced-motion: reduce) {
    .accueil-puce { animation: none; }
}
/* Les puces en losange de la liste des agences. */
.accueil-losange::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 2px;
    background: rgb(var(--accent-500));
    transform: rotate(45deg);
    flex: none;
    margin-top: 0.55em;
}

/* Diaporama du bloc agences : un zoom lent sur la photo affichée, pour que le
   panneau vive entre deux changements. Coupé sous `prefers-reduced-motion`. */
@keyframes diapo-zoom { from { transform: scale(1); } to { transform: scale(1.08); } }
.diapo-zoom { animation: diapo-zoom 9s ease-out infinite alternate; }
@media (prefers-reduced-motion: reduce) {
    .diapo-zoom { animation: none; }
}

/* ==========================================================================
   COHÉRENCE DU SITE AVEC LE MODÈLE (2026-09-26)
   « tu dois mettre à jour toutes les pages du projet avec le même design »
   ========================================================================== */
/* Les titres prennent la police d'affichage du modèle, PARTOUT — back-office
   compris — comme le fait le modèle (`h1,h2,h3{font-family:var(--display)}`).
   Une seule règle plutôt que `font-display` écrit sur 300 titres : un titre
   ajouté demain l'aura sans qu'on y pense. Le resserrement des lettres suit :
   Bricolage est dessinée pour être serrée dans les grandes tailles. */
h1, h2, h3 {
    font-family: 'Bricolage Grotesque', 'Hanken Grotesk', system-ui, sans-serif;
    letter-spacing: -0.02em;
}
/* Le fond d'un panneau qui porte la scène dessinée (connexion) : le ciel de
   la scène, pour qu'aucune bande ne se voie pendant le chargement. */
.scene-fond { background: #eaf4f8; }
/* Voile vertical : le texte se lit en haut sur le ciel éclairci, la villa et
   la rue restent visibles en bas. */
.voile-haut {
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg, rgb(var(--surface-50) / 0.95) 0%,
                                        rgb(var(--surface-50) / 0.86) 48%,
                                        rgb(var(--surface-50) / 0) 78%);
}
