Mobile : overlay du menu sorti du header (fix backdrop-filter/fixed), import tokens.css layer(base), verrou scroll, viewport-fit=cover, stat-chips tactiles, ka-agent corrigé
- src/app/layout.tsx : overlay #gk-menu déplacé hors du <header> (le backdrop-blur re-parentait le position:fixed → menu inutilisable entre 768 et 1280 px) ; export viewport (device-width, initial-scale 1, viewport-fit cover) ; header sur var(--z-header), overlay sur var(--z-modal), nav scrollable. - src/app/globals.css : @import "../ka/tokens.css" layer(base) (socle mobile commun : échelle z-index, anti-zoom iOS 16 px, overflow-x clip → fin du débordement horizontal du ticker) ; grain dupliqué retiré (un seul grain) ; pilotage CSS pur du menu (body:has(#gk-menu:checked)) + gel du scroll ; zone tactile ≥44 px des a.stat-chip (::after inset -6px). - src/ka/tokens.css : socle mobile obligatoire (z-index communs, --vh100, .ka-bottombar, .ka-scroll-lock, .ka-menu/.ka-overlay, filet anti-zoom iOS). - public/ka-agent.js : safe-areas, textarea 16 px, plein écran ≤640 px + verrou. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 changed files +209 −58
modified
public/ka-agent.js
+25 −7
@@ -17,18 +17,19 @@ | ||
| 17 | 17 | |
| 18 | 18 | /* ---------- styles ---------- */ |
| 19 | 19 | var css = ` |
| 20 | − .kaa-btn{position:fixed;right:18px;bottom:18px;z-index:2147483000;width:58px;height:58px; | |
| 20 | + .kaa-btn{position:fixed;right:18px;bottom:calc(18px + env(safe-area-inset-bottom,0px));z-index:2147483000;width:58px;height:58px; | |
| 21 | 21 | border-radius:50%;border:2px solid var(--ink,#141814);background:var(--accent,#d9f26b); |
| 22 | 22 | color:var(--on-accent,#141814);font:700 17px/1 "Space Grotesk",system-ui,sans-serif; |
| 23 | 23 | cursor:pointer;box-shadow:4px 4px 0 rgba(20,24,20,.85);display:flex;align-items:center; |
| 24 | − justify-content:center;transition:transform .15s;padding-bottom:env(safe-area-inset-bottom,0)} | |
| 24 | + justify-content:center;transition:transform .15s} | |
| 25 | 25 | .kaa-btn:hover{transform:translate(-2px,-2px)} |
| 26 | − .kaa-panel{position:fixed;right:16px;bottom:86px;z-index:2147483001;width:min(392px,calc(100vw - 24px)); | |
| 27 | − height:min(580px,calc(100dvh - 110px));display:none;flex-direction:column;background:var(--paper,#f5f3ee); | |
| 26 | + .kaa-panel{position:fixed;right:16px;bottom:calc(86px + env(safe-area-inset-bottom,0px));z-index:2147483001;width:min(392px,calc(100vw - 24px)); | |
| 27 | + height:min(580px,calc(100vh - 110px));height:min(580px,calc(100dvh - 110px));display:none;flex-direction:column;background:var(--paper,#f5f3ee); | |
| 28 | 28 | border:2px solid var(--ink,#141814);border-radius:14px;box-shadow:8px 8px 0 rgba(20,24,20,.85); |
| 29 | 29 | overflow:hidden;font-family:Inter,system-ui,sans-serif} |
| 30 | 30 | .kaa-panel.kaa-open{display:flex} |
| 31 | − .kaa-panel.kaa-full{right:0;bottom:0;width:100vw;height:100dvh;border-radius:0;border:0;box-shadow:none} | |
| 31 | + .kaa-panel.kaa-full{right:0;bottom:0;width:100vw;height:100vh;height:100dvh;border-radius:0;border:0;box-shadow:none} | |
| 32 | + html.kaa-lock,html.kaa-lock body{overflow:hidden!important;overscroll-behavior:none} | |
| 32 | 33 | .kaa-head{display:flex;align-items:center;gap:9px;padding:11px 14px;background:var(--ink,#141814); |
| 33 | 34 | color:var(--paper,#f5f3ee);flex:none} |
| 34 | 35 | .kaa-head b{font:700 15px "Space Grotesk",system-ui,sans-serif;letter-spacing:-.02em} |
@@ -53,7 +54,7 @@ | ||
| 53 | 54 | border-radius:999px;background:var(--surface-2,#faf9f5)} |
| 54 | 55 | .kaa-in{display:flex;gap:8px;padding:11px;border-top:2px solid var(--ink,#141814);background:#fff;flex:none; |
| 55 | 56 | padding-bottom:calc(11px + env(safe-area-inset-bottom,0))} |
| 56 | − .kaa-in textarea{flex:1;resize:none;min-height:44px;max-height:110px;padding:11px 12px;font:13.5px/1.4 Inter,system-ui,sans-serif; | |
| 57 | + .kaa-in textarea{flex:1;resize:none;min-height:44px;max-height:110px;padding:10px 12px;font:16px/1.4 Inter,system-ui,sans-serif; | |
| 57 | 58 | border:1.5px solid var(--ink,#141814);border-radius:9px;background:var(--paper,#f5f3ee);outline:none} |
| 58 | 59 | .kaa-in button{min-width:52px;min-height:44px;border:1.5px solid var(--ink,#141814);border-radius:9px; |
| 59 | 60 | background:var(--ink,#141814);color:var(--accent,#d9f26b);font:700 16px "Space Grotesk",system-ui;cursor:pointer} |
@@ -142,15 +143,32 @@ | ||
| 142 | 143 | } |
| 143 | 144 | |
| 144 | 145 | /* ---------- ouverture / plein écran ---------- */ |
| 146 | + /* Sur téléphone : panneau plein écran d'office + scroll d'arrière-plan | |
| 147 | + verrouillé tant qu'il est ouvert (html.kaa-lock). Pas de focus clavier | |
| 148 | + forcé au tap (le clavier ne s'ouvre que si l'utilisateur touche le champ). */ | |
| 149 | + var mobileMq = window.matchMedia("(max-width: 640px)"); | |
| 150 | + var fineInput = window.matchMedia("(hover: hover) and (pointer: fine)"); | |
| 151 | + function syncLock() { | |
| 152 | + var open = panel.classList.contains("kaa-open"); | |
| 153 | + if (open && mobileMq.matches) panel.classList.add("kaa-full"); | |
| 154 | + document.documentElement.classList.toggle( | |
| 155 | + "kaa-lock", open && panel.classList.contains("kaa-full")); | |
| 156 | + } | |
| 145 | 157 | btn.addEventListener("click", function () { |
| 146 | 158 | panel.classList.toggle("kaa-open"); |
| 147 | − if (panel.classList.contains("kaa-open")) { render(); ta.focus(); } | |
| 159 | + if (panel.classList.contains("kaa-open")) { | |
| 160 | + render(); | |
| 161 | + if (fineInput.matches) ta.focus(); | |
| 162 | + } | |
| 163 | + syncLock(); | |
| 148 | 164 | }); |
| 149 | 165 | panel.querySelector(".kaa-x2").addEventListener("click", function () { |
| 150 | 166 | panel.classList.remove("kaa-open", "kaa-full"); |
| 167 | + syncLock(); | |
| 151 | 168 | }); |
| 152 | 169 | panel.querySelector(".kaa-x1").addEventListener("click", function () { |
| 153 | 170 | panel.classList.toggle("kaa-full"); |
| 171 | + syncLock(); | |
| 154 | 172 | scroll(); |
| 155 | 173 | }); |
| 156 | 174 | |
modified
src/app/globals.css
+44 −10
@@ -9,6 +9,10 @@ | ||
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | 11 | @import "tailwindcss"; |
| 12 | +/* Socle commun Groupe KA (tokens + socle mobile). ⚠️ layer(base) OBLIGATOIRE : | |
| 13 | + sans lui, l'import écrase les utilities Tailwind v4 (modèle : vrai-prix). | |
| 14 | + Les redéfinitions locales ci-dessous restent hors layer et gagnent. */ | |
| 15 | +@import "../ka/tokens.css" layer(base); | |
| 12 | 16 | |
| 13 | 17 | :root { |
| 14 | 18 | --paper: #f5f3ee; |
@@ -67,19 +71,36 @@ body { | ||
| 67 | 71 | -webkit-font-smoothing: antialiased; |
| 68 | 72 | } |
| 69 | 73 | |
| 70 | −/* Grain de film pleine page — jamais de z-index sur body > * (position: relative | |
| 71 | − seulement) pour ne pas casser les utilitaires z-* de Tailwind. */ | |
| 72 | −body::before { | |
| 73 | − content: ""; | |
| 74 | +/* Grain de film pleine page : fourni par ../ka/tokens.css (body::before) — | |
| 75 | + la copie locale a été retirée pour éviter le double grain (plus sombre). | |
| 76 | + Jamais de z-index sur body > * (position: relative seulement, via tokens). */ | |
| 77 | + | |
| 78 | +/* ---------- Menu mobile plein écran (overlay HORS du header) ---------- | |
| 79 | + L'overlay est un enfant direct de <body> : un position:fixed descendant du | |
| 80 | + header (backdrop-blur) se re-parentait et ne couvrait que le header. | |
| 81 | + Piloté 100 % CSS par la checkbox #gk-menu restée dans le header. */ | |
| 82 | +.gk-mobile-overlay { | |
| 83 | + display: none; | |
| 74 | 84 | position: fixed; |
| 75 | 85 | inset: 0; |
| 76 | − pointer-events: none; | |
| 77 | − opacity: 0.35; | |
| 78 | − z-index: 9999; | |
| 79 | − background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.06'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E"); | |
| 86 | + z-index: var(--z-modal); | |
| 80 | 87 | } |
| 81 | −body > * { | |
| 82 | − position: relative; | |
| 88 | +body:has(#gk-menu:checked) .gk-mobile-overlay { | |
| 89 | + display: flex; | |
| 90 | +} | |
| 91 | +/* Menu ouvert : gel du scroll d'arrière-plan */ | |
| 92 | +html:has(#gk-menu:checked) { | |
| 93 | + overflow: hidden; | |
| 94 | +} | |
| 95 | +/* Desktop (nav complète visible) : jamais d'overlay, même si la case est | |
| 96 | + restée cochée après un redimensionnement. */ | |
| 97 | +@media (min-width: 1280px) { | |
| 98 | + .gk-mobile-overlay { | |
| 99 | + display: none !important; | |
| 100 | + } | |
| 101 | + html:has(#gk-menu:checked) { | |
| 102 | + overflow: auto; | |
| 103 | + } | |
| 83 | 104 | } |
| 84 | 105 | |
| 85 | 106 | h1, |
@@ -678,6 +699,19 @@ h4 { | ||
| 678 | 699 | box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.12); |
| 679 | 700 | } |
| 680 | 701 | |
| 702 | +/* Cible tactile ≥44 px des pilules cliquables (33 px rendus + 2×6 px de zone | |
| 703 | + étendue) — sans changer le rendu. */ | |
| 704 | +@media (pointer: coarse) { | |
| 705 | + a.stat-chip { | |
| 706 | + position: relative; | |
| 707 | + } | |
| 708 | + a.stat-chip::after { | |
| 709 | + content: ""; | |
| 710 | + position: absolute; | |
| 711 | + inset: -6px; | |
| 712 | + } | |
| 713 | +} | |
| 714 | + | |
| 681 | 715 | .pulse-dot { |
| 682 | 716 | position: relative; |
| 683 | 717 | width: 8px; |
modified
src/app/layout.tsx
+53 −41
@@ -1,5 +1,5 @@ | ||
| 1 | 1 | // Auteur : Simon-Pierre Boucher — contact@spboucher.ai |
| 2 | −import type { Metadata } from "next"; | |
| 2 | +import type { Metadata, Viewport } from "next"; | |
| 3 | 3 | import { Inter, JetBrains_Mono, Space_Grotesk } from "next/font/google"; |
| 4 | 4 | import UserButton from "./UserButton"; |
| 5 | 5 | import { getLiveMetrics, fmtDec, fmtInt, fmtPlus, type LiveMetrics } from "@/lib/live"; |
@@ -42,6 +42,13 @@ export const metadata: Metadata = { | ||
| 42 | 42 | }, |
| 43 | 43 | }; |
| 44 | 44 | |
| 45 | +// viewport-fit=cover : nécessaire pour les safe-areas iOS (encoche/barre home). | |
| 46 | +export const viewport: Viewport = { | |
| 47 | + width: "device-width", | |
| 48 | + initialScale: 1, | |
| 49 | + viewportFit: "cover", | |
| 50 | +}; | |
| 51 | + | |
| 45 | 52 | const NAV_ITEMS = [ |
| 46 | 53 | { href: "/#trouve-ka", label: "Moteur" }, |
| 47 | 54 | { href: "/#flux", label: "L’agrégation" }, |
@@ -110,7 +117,7 @@ export default async function RootLayout({ | ||
| 110 | 117 | <body |
| 111 | 118 | className={`${spaceGrotesk.variable} ${inter.variable} ${jetbrains.variable} bg-paper text-ink`} |
| 112 | 119 | > |
| 113 | − <header className="sticky top-0 z-[1100] border-b-2 border-ink bg-paper md:bg-[rgba(245,243,238,0.88)] md:backdrop-blur-xl"> | |
| 120 | + <header className="sticky top-0 z-[var(--z-header)] border-b-2 border-ink bg-paper md:bg-[rgba(245,243,238,0.88)] md:backdrop-blur-xl"> | |
| 114 | 121 | <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4 sm:px-6"> |
| 115 | 122 | <a href="#" className="group" aria-label="Groupe KA — accueil"> |
| 116 | 123 | <Wordmark /> |
@@ -155,49 +162,54 @@ export default async function RootLayout({ | ||
| 155 | 162 | > |
| 156 | 163 | ☰ |
| 157 | 164 | </label> |
| 158 | − <div className="fixed inset-0 z-[3000] hidden flex-col bg-paper peer-checked:flex"> | |
| 159 | − <div className="flex h-16 items-center justify-between border-b-2 border-ink px-4"> | |
| 160 | − <Wordmark /> | |
| 161 | − <label | |
| 162 | − htmlFor="gk-menu" | |
| 163 | − className="flex h-11 w-11 cursor-pointer items-center justify-center rounded-md border-[1.5px] border-ink text-[18px] shadow-[3px_3px_0_rgba(20,24,20,0.25)] select-none" | |
| 164 | − > | |
| 165 | − ✕ | |
| 166 | − </label> | |
| 167 | − </div> | |
| 168 | − <nav aria-label="Menu mobile" className="px-6 pt-8"> | |
| 169 | − <ul> | |
| 170 | − {NAV_ITEMS.map((item, i) => ( | |
| 171 | − <li | |
| 172 | − key={item.href} | |
| 173 | − className="border-b border-dashed border-[rgba(20,24,20,0.25)]" | |
| 174 | − > | |
| 175 | − <a | |
| 176 | − href={item.href} | |
| 177 | − className="gk-display flex items-baseline gap-4 py-5 text-[29px] font-bold tracking-[-0.03em] uppercase" | |
| 178 | − > | |
| 179 | − <span className="gk-mono text-[12px] font-bold text-green"> | |
| 180 | − 0{i + 1} | |
| 181 | − </span> | |
| 182 | − {item.label} | |
| 183 | − </a> | |
| 184 | − </li> | |
| 185 | − ))} | |
| 186 | − <li className="pt-6"> | |
| 187 | − <a | |
| 188 | − href="mailto:contact@groupe-ka.com" | |
| 189 | − className="btn btn-primary w-full" | |
| 190 | − > | |
| 191 | − contact@groupe-ka.com | |
| 192 | − </a> | |
| 193 | − </li> | |
| 194 | − </ul> | |
| 195 | − </nav> | |
| 196 | − </div> | |
| 197 | 165 | </div> |
| 198 | 166 | </div> |
| 199 | 167 | </header> |
| 200 | 168 | |
| 169 | + {/* Overlay du menu mobile — HORS du header (le backdrop-blur du header | |
| 170 | + re-parentait le position:fixed → menu inutilisable 768-1280 px). | |
| 171 | + Piloté en CSS pur : body:has(#gk-menu:checked) .gk-mobile-overlay | |
| 172 | + (globals.css) — les <label htmlFor="gk-menu"> marchent à distance. */} | |
| 173 | + <div className="gk-mobile-overlay flex-col bg-paper"> | |
| 174 | + <div className="flex h-16 items-center justify-between border-b-2 border-ink px-4"> | |
| 175 | + <Wordmark /> | |
| 176 | + <label | |
| 177 | + htmlFor="gk-menu" | |
| 178 | + className="flex h-11 w-11 cursor-pointer items-center justify-center rounded-md border-[1.5px] border-ink text-[18px] shadow-[3px_3px_0_rgba(20,24,20,0.25)] select-none" | |
| 179 | + > | |
| 180 | + ✕ | |
| 181 | + </label> | |
| 182 | + </div> | |
| 183 | + <nav aria-label="Menu mobile" className="min-h-0 flex-1 overflow-y-auto px-6 pt-8 pb-10"> | |
| 184 | + <ul> | |
| 185 | + {NAV_ITEMS.map((item, i) => ( | |
| 186 | + <li | |
| 187 | + key={item.href} | |
| 188 | + className="border-b border-dashed border-[rgba(20,24,20,0.25)]" | |
| 189 | + > | |
| 190 | + <a | |
| 191 | + href={item.href} | |
| 192 | + className="gk-display flex items-baseline gap-4 py-5 text-[29px] font-bold tracking-[-0.03em] uppercase" | |
| 193 | + > | |
| 194 | + <span className="gk-mono text-[12px] font-bold text-green"> | |
| 195 | + 0{i + 1} | |
| 196 | + </span> | |
| 197 | + {item.label} | |
| 198 | + </a> | |
| 199 | + </li> | |
| 200 | + ))} | |
| 201 | + <li className="pt-6"> | |
| 202 | + <a | |
| 203 | + href="mailto:contact@groupe-ka.com" | |
| 204 | + className="btn btn-primary w-full" | |
| 205 | + > | |
| 206 | + contact@groupe-ka.com | |
| 207 | + </a> | |
| 208 | + </li> | |
| 209 | + </ul> | |
| 210 | + </nav> | |
| 211 | + </div> | |
| 212 | + | |
| 201 | 213 | <div className="ticker" aria-hidden="true"> |
| 202 | 214 | <div className="ticker-inner"> |
| 203 | 215 | {[0, 1].map((dup) => ( |
modified
src/ka/tokens.css
+87 −0
@@ -69,7 +69,24 @@ | ||
| 69 | 69 | |
| 70 | 70 | /* Cible tactile minimale */ |
| 71 | 71 | --touch: 44px; |
| 72 | + | |
| 73 | + /* ---- Échelle z-index COMMUNE (obligatoire — aucune valeur arbitraire) ---- | |
| 74 | + grain de film body::before = 9999 (pointer-events:none, toujours au-dessus, | |
| 75 | + purement visuel). Tout composant interactif se place sous 9999 : */ | |
| 76 | + --z-content: 1; /* contenu positionné ordinaire */ | |
| 77 | + --z-sticky: 300; /* éléments sticky de contenu (sous-nav…) */ | |
| 78 | + --z-header: 500; /* header du site */ | |
| 79 | + --z-bottombar: 600; /* tab bar / barre d'action basse */ | |
| 80 | + --z-dropdown: 700; /* menus déroulants (au-dessus des barres) */ | |
| 81 | + --z-overlay: 800; /* voile sous modale/panneau */ | |
| 82 | + --z-modal: 900; /* modales, panneaux de filtres, galeries */ | |
| 83 | + --z-toast: 950; /* notifications */ | |
| 84 | + | |
| 85 | + /* ---- Hauteur de viewport DYNAMIQUE (barre du navigateur mobile qui se | |
| 86 | + replie au scroll) : toujours var(--vh100), JAMAIS 100vh en dur. ---- */ | |
| 87 | + --vh100: 100vh; | |
| 72 | 88 | } |
| 89 | +@supports (height: 100dvh) { :root { --vh100: 100dvh; } } | |
| 73 | 90 | |
| 74 | 91 | /* ---- Socle ---- */ |
| 75 | 92 | * { box-sizing: border-box; } |
@@ -227,3 +244,73 @@ body > * { position: relative; } | ||
| 227 | 244 | /* ---- Utilitaires responsive ---- */ |
| 228 | 245 | .only-mobile { display: initial; } .only-desktop { display: none; } |
| 229 | 246 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } |
| 247 | + | |
| 248 | +/* ============================================================================= | |
| 249 | + SOCLE MOBILE OBLIGATOIRE (2026-08-19) — règles communes aux 13 sites. | |
| 250 | + RÈGLES pour tout composant futur : | |
| 251 | + · TAP, jamais :hover, pour ouvrir un menu (le survol n'existe pas au doigt) ; | |
| 252 | + le :hover ne sert qu'aux effets décoratifs, sous @media (hover: hover). | |
| 253 | + · var(--vh100) (dvh) et JAMAIS 100vh pour tout élément calé sur le viewport. | |
| 254 | + · position:fixed exige qu'AUCUN ancêtre n'ait transform/filter/perspective/ | |
| 255 | + will-change/backdrop-filter — sinon monter l'élément à la racine (body). | |
| 256 | + · Barre basse fixe = .ka-bottombar + classe has-bottombar sur <body>. | |
| 257 | + · Menu/panneau ouvert = .ka-scroll-lock sur <html> (scroll arrière-plan gelé). | |
| 258 | + · z-index : uniquement l'échelle --z-* ci-dessus. | |
| 259 | + · Zones tactiles ≥ var(--touch) (44 px), champs ≥ 16 px (zoom iOS). | |
| 260 | + ========================================================================== */ | |
| 261 | + | |
| 262 | +/* Anti-zoom iOS : au doigt, aucun champ sous 16 px (le focus d'un champ <16 px | |
| 263 | + déclenche un zoom automatique de page sur Safari iOS). !important assumé : | |
| 264 | + c'est un filet d'accessibilité, un champ plus petit casse le zoom quoi | |
| 265 | + qu'il arrive — ne s'applique qu'aux écrans tactiles. */ | |
| 266 | +@media (hover: none) and (pointer: coarse) { | |
| 267 | + input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), | |
| 268 | + select, textarea { font-size: max(16px, 1em) !important; } | |
| 269 | +} | |
| 270 | + | |
| 271 | +/* Zone tactile du badge Groupe KA étendue à ≥44 px au doigt, sans changer | |
| 272 | + son rendu (débord de la zone cliquable via pseudo-élément). */ | |
| 273 | +@media (pointer: coarse) { | |
| 274 | + .gk-badge { position: relative; } | |
| 275 | + .gk-badge::after { content: ""; position: absolute; inset: -8px; } | |
| 276 | +} | |
| 277 | + | |
| 278 | +/* Hauteurs viewport dynamiques */ | |
| 279 | +.h-viewport { height: 100vh; height: 100dvh; } | |
| 280 | +.min-h-viewport { min-height: 100vh; min-height: 100dvh; } | |
| 281 | + | |
| 282 | +/* Barre fixe basse fiable : safe-area iPhone incluse, immunisée contre les | |
| 283 | + transforms accidentels, et compensation d'espace via body.has-bottombar. */ | |
| 284 | +.ka-bottombar { | |
| 285 | + position: fixed; left: 0; right: 0; bottom: 0; | |
| 286 | + z-index: var(--z-bottombar); | |
| 287 | + padding-bottom: env(safe-area-inset-bottom, 0px); | |
| 288 | + background: var(--surface); | |
| 289 | + border-top: 1.5px solid var(--ink); | |
| 290 | + transform: none !important; filter: none !important; | |
| 291 | +} | |
| 292 | +body.has-bottombar { padding-bottom: calc(var(--bottombar-h, 64px) + env(safe-area-inset-bottom, 0px)); } | |
| 293 | + | |
| 294 | +/* Verrou de scroll d'arrière-plan (menu mobile, modale, panneau de filtres | |
| 295 | + ouverts) : ajouter/retirer .ka-scroll-lock sur <html>. */ | |
| 296 | +html.ka-scroll-lock, html.ka-scroll-lock body { overflow: hidden !important; overscroll-behavior: none; } | |
| 297 | + | |
| 298 | +/* Menu déroulant de référence : au-dessus de tout contenu (cartes Mapbox | |
| 299 | + incluses), défilement interne avec élan, jamais plus haut que l'écran. */ | |
| 300 | +.ka-menu { | |
| 301 | + position: absolute; z-index: var(--z-dropdown); | |
| 302 | + min-width: 180px; | |
| 303 | + max-height: min(60vh, 420px); max-height: min(60dvh, 420px); | |
| 304 | + overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; | |
| 305 | + background: var(--surface); border: 1.5px solid var(--ink); | |
| 306 | + border-radius: var(--r-card); box-shadow: var(--shadow-off-mid); | |
| 307 | +} | |
| 308 | +.ka-menu a, .ka-menu button, .ka-menu label, .ka-menu [role="menuitem"], .ka-menu [role="option"] { | |
| 309 | + display: flex; align-items: center; min-height: var(--touch); | |
| 310 | + padding: 10px 14px; width: 100%; text-decoration: none; | |
| 311 | +} | |
| 312 | +/* Voile plein écran sous une modale / un panneau */ | |
| 313 | +.ka-overlay { | |
| 314 | + position: fixed; inset: 0; z-index: var(--z-overlay); | |
| 315 | + background: rgba(20, 24, 20, 0.45); | |
| 316 | +} | |
| 230 | 317 | |