Barre de navigation mobile commune Groupe KA (KA Tabbar v1)
L ancienne tabbar 3 onglets bord-à-bord devient la pilule flottante du standard ka-ui/nav : Découvrir · Recherche · Villes · Favoris, accent orange, safe-area iOS. Recherche = focus du champ de l accueil (id f-q) ; le fab Filtres remonte au-dessus de la pilule. Stats/Sources restent au menu. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 changed files +345 −39
modified
frontend/src/App.tsx
+25 −15
@@ -6,7 +6,7 @@ | ||
| 6 | 6 | // ============================================================================== |
| 7 | 7 | import { useEffect, useState } from "react"; |
| 8 | 8 | import { createPortal } from "react-dom"; |
| 9 | −import { NavLink, Route, Routes, useLocation } from "react-router-dom"; | |
| 9 | +import { NavLink, Route, Routes, useLocation, useNavigate } from "react-router-dom"; | |
| 10 | 10 | import { AccountProvider, useAccount } from "./account"; |
| 11 | 11 | import { |
| 12 | 12 | fetchAuthConfig, fetchFacets, fetchSources, fetchStats, logout, |
@@ -19,6 +19,7 @@ import { FavProvider } from "./favorites"; | ||
| 19 | 19 | import { LogoIcon } from "./components/Logo"; |
| 20 | 20 | import GroupeKaBadge from "./ka/GroupeKaBadge"; |
| 21 | 21 | import KaFooter from "./ka/KaFooter"; |
| 22 | +import KaTabbar from "./ka/KaTabbar"; | |
| 22 | 23 | import ContactPage from "./pages/Contact"; |
| 23 | 24 | import FavorisPage from "./pages/Favoris"; |
| 24 | 25 | import Home from "./pages/Home"; |
@@ -242,23 +243,32 @@ function Footer() { | ||
| 242 | 243 | ); |
| 243 | 244 | } |
| 244 | 245 | |
| 245 | −/** Barre de navigation inférieure (mobile) — icône active en orange. */ | |
| 246 | +/** Barre de navigation mobile commune Groupe KA (ka-ui/nav — KA Tabbar v1) : | |
| 247 | + Découvrir · Recherche · Villes · Favoris, accent orange signature. */ | |
| 246 | 248 | function MobileTabBar() { |
| 247 | 249 | const location = useLocation(); |
| 248 | − const tabs = [ | |
| 249 | − { to: "/", label: "Rechercher", icon: <IcoSearch size={20} />, on: location.pathname === "/" }, | |
| 250 | − { to: "/stats", label: "Stats", icon: <IcoChart size={20} />, on: location.pathname.startsWith("/stats") }, | |
| 251 | − { to: "/sources", label: "Sources", icon: <IcoFolder size={20} />, on: location.pathname.startsWith("/sources") }, | |
| 252 | − ]; | |
| 250 | + const navigate = useNavigate(); | |
| 251 | + const p = location.pathname; | |
| 252 | + const focusSearch = () => { | |
| 253 | + const go = !document.getElementById("f-q"); | |
| 254 | + if (go) navigate("/"); | |
| 255 | + window.setTimeout(() => { | |
| 256 | + const el = document.getElementById("f-q") as HTMLInputElement | null; | |
| 257 | + if (el) { | |
| 258 | + el.scrollIntoView({ block: "center", behavior: "smooth" }); | |
| 259 | + el.focus({ preventScroll: true }); | |
| 260 | + } | |
| 261 | + }, go ? 140 : 0); | |
| 262 | + }; | |
| 253 | 263 | return ( |
| 254 | − <nav className="tabbar tabbar-3" aria-label="Navigation mobile"> | |
| 255 | − {tabs.map((t) => ( | |
| 256 | − <NavLink key={t.label} to={t.to} className={() => (t.on ? "active" : "")}> | |
| 257 | − {t.icon} | |
| 258 | − {t.label} | |
| 259 | − </NavLink> | |
| 260 | − ))} | |
| 261 | − </nav> | |
| 264 | + <KaTabbar | |
| 265 | + tabs={[ | |
| 266 | + { label: "Découvrir", icon: "compass", to: "/", active: p === "/" || p.startsWith("/resto") }, | |
| 267 | + { label: "Recherche", icon: "search", onPress: focusSearch }, | |
| 268 | + { label: "Villes", icon: "pin", to: "/villes", active: p.startsWith("/ville") }, | |
| 269 | + { label: "Favoris", icon: "heart", to: "/favoris", active: p.startsWith("/favoris") }, | |
| 270 | + ]} | |
| 271 | + /> | |
| 262 | 272 | ); |
| 263 | 273 | } |
| 264 | 274 | |
added
frontend/src/ka/KaTabbar.tsx
+197 −0
@@ -0,0 +1,197 @@ | ||
| 1 | +// KA Tabbar v1 — canonique ka-ui.git/nav/KaTabbar.tsx | |
| 2 | +// Barre de navigation mobile commune Groupe KA (voir TABBAR-STANDARD.md). | |
| 3 | +// Copier dans src/ka/ avec ka-tabbar.css. Apps react-router uniquement ; | |
| 4 | +// pour Next.js (vrai-prix), adapter Link/usePathname — voir le standard. | |
| 5 | +import type { CSSProperties, ReactElement } from "react"; | |
| 6 | +import { Link } from "react-router-dom"; | |
| 7 | +import "./ka-tabbar.css"; | |
| 8 | + | |
| 9 | +export type KaTabIcon = | |
| 10 | + | "compass" | "search" | "heart" | "user" | "map" | "pin" | "tag" | |
| 11 | + | "home" | "grid" | "store" | "basket" | "target" | "trendup" | |
| 12 | + | "receipt" | "book" | "chart" | "calendar"; | |
| 13 | + | |
| 14 | +const ICONS: Record<KaTabIcon, ReactElement> = { | |
| 15 | + compass: ( | |
| 16 | + <> | |
| 17 | + <circle cx="12" cy="12" r="9" /> | |
| 18 | + <path d="m15.5 8.5-2.2 5-4.8 2 2.2-5 4.8-2z" /> | |
| 19 | + </> | |
| 20 | + ), | |
| 21 | + search: ( | |
| 22 | + <> | |
| 23 | + <circle cx="11" cy="11" r="7" /> | |
| 24 | + <path d="m20.5 20.5-4.2-4.2" /> | |
| 25 | + </> | |
| 26 | + ), | |
| 27 | + heart: ( | |
| 28 | + <path d="M12 20.5S4.5 15.8 2.6 11.4A5.3 5.3 0 0 1 12 6.6a5.3 5.3 0 0 1 9.4 4.8C19.5 15.8 12 20.5 12 20.5z" /> | |
| 29 | + ), | |
| 30 | + user: ( | |
| 31 | + <> | |
| 32 | + <circle cx="12" cy="8.2" r="3.7" /> | |
| 33 | + <path d="M4.5 20.2a7.5 7.5 0 0 1 15 0" /> | |
| 34 | + </> | |
| 35 | + ), | |
| 36 | + map: ( | |
| 37 | + <> | |
| 38 | + <path d="M9 4 3.5 6v14L9 18l6 2 5.5-2V4L15 6 9 4z" /> | |
| 39 | + <path d="M9 4v14M15 6v14" /> | |
| 40 | + </> | |
| 41 | + ), | |
| 42 | + pin: ( | |
| 43 | + <> | |
| 44 | + <path d="M12 21s-7-5.6-7-11a7 7 0 0 1 14 0c0 5.4-7 11-7 11z" /> | |
| 45 | + <circle cx="12" cy="10" r="2.6" /> | |
| 46 | + </> | |
| 47 | + ), | |
| 48 | + tag: ( | |
| 49 | + <> | |
| 50 | + <path d="M20.6 13.4 12 22 2 12V2h10l8.6 8.6a2 2 0 0 1 0 2.8z" /> | |
| 51 | + <circle cx="7" cy="7" r="1.6" /> | |
| 52 | + </> | |
| 53 | + ), | |
| 54 | + home: ( | |
| 55 | + <> | |
| 56 | + <path d="m3.5 10.5 8.5-7 8.5 7" /> | |
| 57 | + <path d="M5.5 9v11h13V9" /> | |
| 58 | + </> | |
| 59 | + ), | |
| 60 | + grid: ( | |
| 61 | + <> | |
| 62 | + <rect x="4" y="4" width="7" height="7" rx="1.5" /> | |
| 63 | + <rect x="13" y="4" width="7" height="7" rx="1.5" /> | |
| 64 | + <rect x="4" y="13" width="7" height="7" rx="1.5" /> | |
| 65 | + <rect x="13" y="13" width="7" height="7" rx="1.5" /> | |
| 66 | + </> | |
| 67 | + ), | |
| 68 | + store: ( | |
| 69 | + <> | |
| 70 | + <path d="M4.5 9.5 6 4.5h12l1.5 5" /> | |
| 71 | + <path d="M5 9.5h14V20H5V9.5z" /> | |
| 72 | + <path d="M10 20v-5h4v5" /> | |
| 73 | + </> | |
| 74 | + ), | |
| 75 | + basket: ( | |
| 76 | + <> | |
| 77 | + <path d="M4 9h16l-1.6 10a2 2 0 0 1-2 1.7H7.6a2 2 0 0 1-2-1.7L4 9z" /> | |
| 78 | + <path d="M8.5 9 12 3l3.5 6" /> | |
| 79 | + </> | |
| 80 | + ), | |
| 81 | + target: ( | |
| 82 | + <> | |
| 83 | + <circle cx="12" cy="12" r="8.5" /> | |
| 84 | + <circle cx="12" cy="12" r="4.5" /> | |
| 85 | + <circle cx="12" cy="12" r="0.8" /> | |
| 86 | + </> | |
| 87 | + ), | |
| 88 | + trendup: ( | |
| 89 | + <> | |
| 90 | + <path d="m3.5 16.5 5.5-5.5 3.5 3.5 7.5-7.5" /> | |
| 91 | + <path d="M14.5 7H20v5.5" /> | |
| 92 | + </> | |
| 93 | + ), | |
| 94 | + receipt: ( | |
| 95 | + <> | |
| 96 | + <path d="M6 3.5h12V21l-2-1.4-2 1.4-2-1.4-2 1.4-2-1.4L6 21V3.5z" /> | |
| 97 | + <path d="M9 8h6M9 11.5h6M9 15h4" /> | |
| 98 | + </> | |
| 99 | + ), | |
| 100 | + book: ( | |
| 101 | + <> | |
| 102 | + <path d="M12 6.5C10.5 5 8.5 4.5 5.5 4.5H3.5V19h2c3 0 5 .5 6.5 2 1.5-1.5 3.5-2 6.5-2h2V4.5h-2c-3 0-5 .5-6.5 2z" /> | |
| 103 | + <path d="M12 6.5V21" /> | |
| 104 | + </> | |
| 105 | + ), | |
| 106 | + chart: ( | |
| 107 | + <> | |
| 108 | + <path d="M4 20h16" /> | |
| 109 | + <path d="M7.5 20v-6M12 20V9M16.5 20v-4" /> | |
| 110 | + </> | |
| 111 | + ), | |
| 112 | + calendar: ( | |
| 113 | + <> | |
| 114 | + <rect x="3.5" y="5" width="17" height="15.5" rx="2" /> | |
| 115 | + <path d="M8 3v4M16 3v4M3.5 10h17" /> | |
| 116 | + </> | |
| 117 | + ), | |
| 118 | +}; | |
| 119 | + | |
| 120 | +export function KaTabIco({ name, size = 21 }: { name: KaTabIcon; size?: number }) { | |
| 121 | + return ( | |
| 122 | + <svg | |
| 123 | + width={size} | |
| 124 | + height={size} | |
| 125 | + viewBox="0 0 24 24" | |
| 126 | + fill="none" | |
| 127 | + stroke="currentColor" | |
| 128 | + strokeWidth={1.7} | |
| 129 | + strokeLinecap="round" | |
| 130 | + strokeLinejoin="round" | |
| 131 | + aria-hidden="true" | |
| 132 | + > | |
| 133 | + {ICONS[name]} | |
| 134 | + </svg> | |
| 135 | + ); | |
| 136 | +} | |
| 137 | + | |
| 138 | +export type KaTab = { | |
| 139 | + label: string; | |
| 140 | + icon: KaTabIcon; | |
| 141 | + /** route interne (react-router Link) */ | |
| 142 | + to?: string; | |
| 143 | + /** lien externe (ex. hub univers KA) */ | |
| 144 | + href?: string; | |
| 145 | + /** action locale (ex. ouvrir la recherche) — rend un <button> */ | |
| 146 | + onPress?: () => void; | |
| 147 | + active?: boolean; | |
| 148 | +}; | |
| 149 | + | |
| 150 | +export default function KaTabbar({ | |
| 151 | + tabs, | |
| 152 | + theme = "light", | |
| 153 | + accent, | |
| 154 | + className, | |
| 155 | +}: { | |
| 156 | + tabs: KaTab[]; | |
| 157 | + theme?: "light" | "dark"; | |
| 158 | + /** couleur signature de la marque (défaut : var(--accent) du site) */ | |
| 159 | + accent?: string; | |
| 160 | + className?: string; | |
| 161 | +}) { | |
| 162 | + const cls = | |
| 163 | + "ka-tabbar" + | |
| 164 | + (theme === "dark" ? " ka-tabbar--dark" : "") + | |
| 165 | + (className ? " " + className : ""); | |
| 166 | + const style = accent ? ({ "--ktb-accent": accent } as CSSProperties) : undefined; | |
| 167 | + return ( | |
| 168 | + <nav className={cls} style={style} aria-label="Navigation mobile"> | |
| 169 | + {tabs.map((t) => { | |
| 170 | + const inner = ( | |
| 171 | + <> | |
| 172 | + <KaTabIco name={t.icon} /> | |
| 173 | + {t.label} | |
| 174 | + </> | |
| 175 | + ); | |
| 176 | + const c = t.active ? "active" : ""; | |
| 177 | + if (t.to) | |
| 178 | + return ( | |
| 179 | + <Link key={t.label} to={t.to} className={c} aria-current={t.active ? "page" : undefined}> | |
| 180 | + {inner} | |
| 181 | + </Link> | |
| 182 | + ); | |
| 183 | + if (t.href) | |
| 184 | + return ( | |
| 185 | + <a key={t.label} href={t.href} className={c} target="_blank" rel="noreferrer"> | |
| 186 | + {inner} | |
| 187 | + </a> | |
| 188 | + ); | |
| 189 | + return ( | |
| 190 | + <button key={t.label} type="button" className={c} onClick={t.onPress}> | |
| 191 | + {inner} | |
| 192 | + </button> | |
| 193 | + ); | |
| 194 | + })} | |
| 195 | + </nav> | |
| 196 | + ); | |
| 197 | +} | |
added
frontend/src/ka/ka-tabbar.css
+118 −0
@@ -0,0 +1,118 @@ | ||
| 1 | +/* ============================================================ | |
| 2 | + KA Tabbar v1 — barre de navigation mobile commune Groupe KA | |
| 3 | + Canonique : ka-ui.git/nav/ka-tabbar.css — copiée telle quelle | |
| 4 | + dans chaque app (src/ka/ka-tabbar.css) et importée par le | |
| 5 | + composant KaTabbar. Voir TABBAR-STANDARD.md. | |
| 6 | + | |
| 7 | + Pilule flottante détachée du bas d'écran (langage Immo-Ka), | |
| 8 | + 4-5 onglets, safe-area iOS, thème clair (défaut) ou sombre. | |
| 9 | + Config par app : | |
| 10 | + --ktb-accent couleur signature (trait actif + icône) | |
| 11 | + --ktb-active-label couleur du label actif (défaut : encre) | |
| 12 | + .ka-tabbar--dark variante sombre (fond encre/brand) | |
| 13 | + --ktb-bg fond (surcharge libre, ex. vert profond) | |
| 14 | + ============================================================ */ | |
| 15 | + | |
| 16 | +.ka-tabbar { display: none; } | |
| 17 | + | |
| 18 | +@media (max-width: 760px) { | |
| 19 | + .ka-tabbar { | |
| 20 | + /* thème clair (défaut) — papier off-white légèrement translucide */ | |
| 21 | + --ktb-bg: rgba(250, 249, 245, 0.94); | |
| 22 | + --ktb-ink: var(--ink, #16191a); | |
| 23 | + --ktb-dim: var(--ink-3, #70756d); | |
| 24 | + --ktb-accent: var(--accent, #d6336c); | |
| 25 | + --ktb-line: var(--hairline-strong, rgba(20, 24, 20, 0.16)); | |
| 26 | + --ktb-shadow: 0 10px 30px rgba(20, 24, 20, 0.16), 0 2px 6px rgba(20, 24, 20, 0.07); | |
| 27 | + | |
| 28 | + position: fixed; | |
| 29 | + left: max(12px, env(safe-area-inset-left)); | |
| 30 | + right: max(12px, env(safe-area-inset-right)); | |
| 31 | + bottom: calc(10px + env(safe-area-inset-bottom)); | |
| 32 | + z-index: var(--z-bottombar, 600); | |
| 33 | + display: grid; | |
| 34 | + grid-auto-flow: column; | |
| 35 | + grid-auto-columns: 1fr; | |
| 36 | + max-width: 440px; | |
| 37 | + margin-inline: auto; | |
| 38 | + padding: 5px; | |
| 39 | + background: var(--ktb-bg); | |
| 40 | + -webkit-backdrop-filter: blur(14px) saturate(1.1); | |
| 41 | + backdrop-filter: blur(14px) saturate(1.1); | |
| 42 | + border: 1px solid var(--ktb-line); | |
| 43 | + border-radius: 18px; | |
| 44 | + box-shadow: var(--ktb-shadow); | |
| 45 | + } | |
| 46 | + | |
| 47 | + .ka-tabbar--dark { | |
| 48 | + --ktb-bg: rgba(18, 22, 19, 0.93); | |
| 49 | + --ktb-ink: #f5f3ee; | |
| 50 | + --ktb-dim: rgba(245, 243, 238, 0.62); | |
| 51 | + --ktb-line: rgba(245, 243, 238, 0.16); | |
| 52 | + --ktb-shadow: 0 10px 30px rgba(0, 0, 0, 0.35); | |
| 53 | + } | |
| 54 | + | |
| 55 | + .ka-tabbar a, | |
| 56 | + .ka-tabbar button { | |
| 57 | + appearance: none; | |
| 58 | + background: none; | |
| 59 | + border: 0; | |
| 60 | + cursor: pointer; | |
| 61 | + display: flex; | |
| 62 | + flex-direction: column; | |
| 63 | + align-items: center; | |
| 64 | + justify-content: center; | |
| 65 | + gap: 3px; | |
| 66 | + min-height: 50px; | |
| 67 | + padding: 6px 2px 5px; | |
| 68 | + border-radius: 13px; | |
| 69 | + font-family: var(--font-display, inherit); | |
| 70 | + font-size: 10px; | |
| 71 | + font-weight: 650; | |
| 72 | + letter-spacing: 0.01em; | |
| 73 | + line-height: 1; | |
| 74 | + color: var(--ktb-dim); | |
| 75 | + text-decoration: none; | |
| 76 | + -webkit-tap-highlight-color: transparent; | |
| 77 | + transition: color 0.15s ease; | |
| 78 | + } | |
| 79 | + .ka-tabbar svg { display: block; transition: transform 0.15s ease; } | |
| 80 | + .ka-tabbar a:active svg, | |
| 81 | + .ka-tabbar button:active svg { transform: translateY(1px) scale(0.9); } | |
| 82 | + | |
| 83 | + /* état actif : icône couleur signature, label encre, trait accent */ | |
| 84 | + .ka-tabbar .active { color: var(--ktb-active-label, var(--ktb-ink)); } | |
| 85 | + .ka-tabbar .active svg { color: var(--ktb-accent); animation: ktb-pop 0.25s ease; } | |
| 86 | + .ka-tabbar .active::after { | |
| 87 | + content: ""; | |
| 88 | + width: 16px; | |
| 89 | + height: 2.5px; | |
| 90 | + border-radius: 2px; | |
| 91 | + background: var(--ktb-accent); | |
| 92 | + animation: ktb-under 0.22s ease; | |
| 93 | + } | |
| 94 | + | |
| 95 | + /* le contenu respire sous la pilule (footer compris) */ | |
| 96 | + body:has(.ka-tabbar) { padding-bottom: calc(84px + env(safe-area-inset-bottom)); } | |
| 97 | + | |
| 98 | + /* un CTA sticky de fiche prend le bas d'écran : la pilule s'efface */ | |
| 99 | + body:has(.cta-sticky) .ka-tabbar { display: none; } | |
| 100 | + body:has(.cta-sticky) { padding-bottom: 0; } | |
| 101 | + | |
| 102 | + /* widget KA Agent : dégagé de la pilule (style injecté après le bundle | |
| 103 | + → spécificité doublée pour gagner) */ | |
| 104 | + body:has(.ka-tabbar) .kaa-btn.kaa-btn { bottom: calc(88px + env(safe-area-inset-bottom, 0px)); right: 14px; } | |
| 105 | + body:has(.ka-tabbar) .kaa-panel.kaa-panel:not(.kaa-full) { bottom: calc(154px + env(safe-area-inset-bottom, 0px)); } | |
| 106 | +} | |
| 107 | + | |
| 108 | +@keyframes ktb-under { | |
| 109 | + from { transform: scaleX(0); opacity: 0; } | |
| 110 | +} | |
| 111 | +@keyframes ktb-pop { | |
| 112 | + 40% { transform: translateY(-2px); } | |
| 113 | +} | |
| 114 | + | |
| 115 | +@media (prefers-reduced-motion: reduce) { | |
| 116 | + .ka-tabbar svg { transition: none; } | |
| 117 | + .ka-tabbar .active svg, .ka-tabbar .active::after { animation: none; } | |
| 118 | +} | |
modified
frontend/src/pages/Home.tsx
+1 −0
@@ -180,6 +180,7 @@ export default function Home() { | ||
| 180 | 180 | <label className="f-search"> |
| 181 | 181 | <IcoSearch size={16} /> |
| 182 | 182 | <input |
| 183 | + id="f-q" | |
| 183 | 184 | type="search" |
| 184 | 185 | placeholder={mode === "plats" |
| 185 | 186 | ? "Poutine, tartare, pad thaï, sushi…" |
modified
frontend/src/styles.css
+4 −24
@@ -718,7 +718,7 @@ img { display: block; } | ||
| 718 | 718 | .fab { |
| 719 | 719 | display: flex; align-items: center; gap: 6px; |
| 720 | 720 | position: fixed; left: 50%; transform: translateX(-50%); |
| 721 | − bottom: calc(74px + env(safe-area-inset-bottom)); z-index: 80; | |
| 721 | + bottom: calc(88px + env(safe-area-inset-bottom)); z-index: 80; | |
| 722 | 722 | background: var(--ink); color: var(--accent); border: 1.5px solid var(--ink); |
| 723 | 723 | border-radius: var(--r-ctl); padding: 13px 24px; font-family: var(--font-display); |
| 724 | 724 | font-weight: 700; font-size: 15px; cursor: pointer; |
@@ -737,29 +737,9 @@ img { display: block; } | ||
| 737 | 737 | .mi-thumb { width: 56px; height: 56px; } |
| 738 | 738 | } |
| 739 | 739 | |
| 740 | −/* ================= Barre de navigation inférieure (mobile) ================= */ | |
| 741 | −.tabbar { display: none; } | |
| 742 | −@media (max-width: 760px) { | |
| 743 | − .tabbar { | |
| 744 | − display: grid; | |
| 745 | − position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-bottombar, 600); | |
| 746 | − background: rgba(245, 243, 238, 0.94); | |
| 747 | − backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); | |
| 748 | − border-top: 1.5px solid var(--ink); | |
| 749 | − padding: 6px 4px calc(6px + env(safe-area-inset-bottom)); | |
| 750 | − } | |
| 751 | − .tabbar-3 { grid-template-columns: repeat(3, 1fr); } | |
| 752 | − .tabbar a { | |
| 753 | − display: flex; flex-direction: column; align-items: center; gap: 3px; | |
| 754 | − padding: 5px 2px; min-height: 48px; justify-content: center; | |
| 755 | − font-family: var(--font-mono); font-size: 10px; font-weight: 600; color: var(--ink-3); | |
| 756 | − border-radius: var(--r-ctl); transition: color 0.12s ease; | |
| 757 | − } | |
| 758 | − .tabbar a.active { color: var(--accent-deep); } | |
| 759 | − .tabbar a svg { display: block; } | |
| 760 | − body { padding-bottom: calc(64px + env(safe-area-inset-bottom)); } | |
| 761 | − .ka-footer { padding-bottom: calc(72px + env(safe-area-inset-bottom)); } | |
| 762 | −} | |
| 740 | +/* ============ Barre de navigation inférieure (mobile) ============ | |
| 741 | + KA Tabbar commune (src/ka/ka-tabbar.css) — pilule flottante, | |
| 742 | + accent orange du site via var(--accent). ============ */ | |
| 763 | 743 | |
| 764 | 744 | /* Anti-zoom iOS : les champs doivent faire >= 16px |
| 765 | 745 | (filet global : tokens.css force aussi 16 px au doigt) */ |
| 766 | 746 | |