Harmonisation ka-ui : accent safran, badge Groupe KA, KaFooter, page /contact
- Vendorisation du package ka-ui (frontend/src/ka/) : tokens.css, ecosystem.json, KaFooter.tsx, GroupeKaBadge.tsx - tokens.css importé avant styles.css ; styles.css réduit à l accent SAFRAN (--accent #f08c00, soft #fdeed7, deep #b96a00, on-accent #141814) + alias --lime, socle dupliqué retiré, classes métier passées sur var(--accent) - Header : badge « Un service Groupe KA » (desktop >= 900 px + menu mobile) - Footer : KaFooter commun (contacts, avertissement, écosystème) + rangée de nav locale et crédit OpenStreetMap au-dessus - Nouvelle page /contact (infos d ecosystem.json, lien hub, création de compte KA ID -> groupe-ka.com/connexion) + prefixe SPA « contact » dans web.py - SEO : titre « Resto-Ka — Un service Groupe KA » Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
10 changed files +604 −165
modified
frontend/index.html
+1 −1
@@ -8,7 +8,7 @@ | ||
| 8 | 8 | <head> |
| 9 | 9 | <meta charset="UTF-8" /> |
| 10 | 10 | <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> |
| 11 | − <title>Resto-Ka — Tous les restaurants du Québec, menus et prix</title> | |
| 11 | + <title>Resto-Ka — Un service Groupe KA</title> | |
| 12 | 12 | <meta name="description" content="Resto-Ka agrège les restaurants du Québec avec leurs menus complets et leurs prix réels — comparables, cherchables, dans les 17 régions. Chaque resto, chaque plat, chaque prix." /> |
| 13 | 13 | <meta name="theme-color" content="#ffffff" /> |
| 14 | 14 | <meta name="mobile-web-app-capable" content="yes" /> |
modified
frontend/src/App.tsx
+23 −32
@@ -15,6 +15,9 @@ import { | ||
| 15 | 15 | IcoChart, IcoCompass, IcoFolder, IcoPlate, IcoSearch, |
| 16 | 16 | } from "./components/Icons"; |
| 17 | 17 | import { LogoIcon } from "./components/Logo"; |
| 18 | +import GroupeKaBadge from "./ka/GroupeKaBadge"; | |
| 19 | +import KaFooter from "./ka/KaFooter"; | |
| 20 | +import ContactPage from "./pages/Contact"; | |
| 18 | 21 | import FavorisPage from "./pages/Favoris"; |
| 19 | 22 | import Home from "./pages/Home"; |
| 20 | 23 | import RestoPage from "./pages/Resto"; |
@@ -122,6 +125,7 @@ const NAV_LINKS = [ | ||
| 122 | 125 | { to: "/", label: "Restos", icon: <IcoPlate size={17} />, end: true }, |
| 123 | 126 | { to: "/stats", label: "Stats", icon: <IcoChart size={17} />, end: false }, |
| 124 | 127 | { to: "/sources", label: "Sources", icon: <IcoFolder size={17} />, end: false }, |
| 128 | + { to: "/contact", label: "Contact", icon: <IcoCompass size={17} />, end: false }, | |
| 125 | 129 | ]; |
| 126 | 130 | |
| 127 | 131 | function Header() { |
@@ -144,6 +148,7 @@ function Header() { | ||
| 144 | 148 | Resto-<span className="ka">Ka</span> |
| 145 | 149 | <span className="brand-tag">Chaque resto, chaque plat, chaque prix.</span> |
| 146 | 150 | </NavLink> |
| 151 | + <span className="hdr-badge"><GroupeKaBadge /></span> | |
| 147 | 152 | <nav className="nav" aria-label="Navigation principale"> |
| 148 | 153 | {NAV_LINKS.map((l) => ( |
| 149 | 154 | <NavLink key={l.to} to={l.to} end={l.end} |
@@ -179,6 +184,7 @@ function Header() { | ||
| 179 | 184 | <span className="mm-arrow" aria-hidden="true">→</span> |
| 180 | 185 | </NavLink> |
| 181 | 186 | ))} |
| 187 | + <div className="mm-badge"><GroupeKaBadge /></div> | |
| 182 | 188 | <div className="mm-foot"> |
| 183 | 189 | Agrégateur indépendant — menus et prix captés aux sources, chaque |
| 184 | 190 | fiche renvoie au resto original et affiche le contexte du prix. |
@@ -191,43 +197,27 @@ function Header() { | ||
| 191 | 197 | ); |
| 192 | 198 | } |
| 193 | 199 | |
| 200 | +/** Rangée de nav locale + footer commun Groupe KA (ka-ui/KaFooter). */ | |
| 194 | 201 | function Footer() { |
| 195 | 202 | return ( |
| 196 | − <footer className="footer"> | |
| 197 | − <div className="container"> | |
| 198 | − <div className="fbrand"> | |
| 199 | − <LogoIcon size={32} dark /> | |
| 200 | − Resto-<span className="ka">Ka</span> | |
| 201 | − </div> | |
| 202 | − <div className="fbaseline"> | |
| 203 | − Chaque resto, chaque plat, <span className="hl">chaque prix.</span> | |
| 204 | − </div> | |
| 205 | − <div className="frow"> | |
| 206 | − <div> | |
| 207 | − <b>Agrégateur indépendant</b> des restaurants du Québec — menus | |
| 208 | − complets et prix réels dans les 17 régions. Les menus proviennent | |
| 209 | − des plateformes de commande des restos eux-mêmes ; chaque prix est | |
| 210 | − étiqueté selon son contexte (salle, emporter, livraison) et chaque | |
| 211 | − fiche renvoie à la source originale. | |
| 212 | − </div> | |
| 213 | − <div> | |
| 214 | − Resto-Ka est une plateforme du{" "} | |
| 215 | − <a href="https://www.groupe-ka.com" target="_blank" rel="noopener noreferrer"> | |
| 216 | − <b>Groupe KA</b> | |
| 217 | − </a>{" "} | |
| 218 | − — contact :{" "} | |
| 219 | − <a href="mailto:contact@groupe-ka.com">contact@groupe-ka.com</a> | |
| 203 | + <> | |
| 204 | + <div className="prefoot"> | |
| 205 | + <div className="container prefoot-inner"> | |
| 206 | + <nav className="prefoot-nav" aria-label="Navigation du pied de page"> | |
| 207 | + {NAV_LINKS.map((l) => ( | |
| 208 | + <NavLink key={l.to} to={l.to} end={l.end}>{l.label}</NavLink> | |
| 209 | + ))} | |
| 210 | + <NavLink to="/favoris">Favoris</NavLink> | |
| 211 | + </nav> | |
| 212 | + <div className="prefoot-osm"> | |
| 213 | + Données de découverte ©{" "} | |
| 214 | + <a href="https://www.openstreetmap.org/copyright" target="_blank" | |
| 215 | + rel="noopener noreferrer">contributeurs OpenStreetMap</a> (ODbL) | |
| 220 | 216 | </div> |
| 221 | 217 | </div> |
| 222 | − <div className="fmono"> | |
| 223 | − © {new Date().getFullYear()} Groupe KA — Resto-Ka | |
| 224 | − {" · "} | |
| 225 | − Données de découverte ©{" "} | |
| 226 | − <a href="https://www.openstreetmap.org/copyright" target="_blank" | |
| 227 | − rel="noopener noreferrer">contributeurs OpenStreetMap</a> (ODbL) | |
| 228 | − </div> | |
| 229 | 218 | </div> |
| 230 | − </footer> | |
| 219 | + <KaFooter siteId="resto-ka" /> | |
| 220 | + </> | |
| 231 | 221 | ); |
| 232 | 222 | } |
| 233 | 223 | |
@@ -262,6 +252,7 @@ export default function App() { | ||
| 262 | 252 | <Route path="/favoris" element={<FavorisPage />} /> |
| 263 | 253 | <Route path="/stats" element={<StatsPage />} /> |
| 264 | 254 | <Route path="/sources" element={<SourcesPage />} /> |
| 255 | + <Route path="/contact" element={<ContactPage />} /> | |
| 265 | 256 | <Route |
| 266 | 257 | path="*" |
| 267 | 258 | element={ |
added
frontend/src/ka/GroupeKaBadge.tsx
+21 −0
@@ -0,0 +1,21 @@ | ||
| 1 | +// Auteur : Simon-Pierre Boucher — contact@spboucher.ai | |
| 2 | +// ka-ui/react/GroupeKaBadge.tsx — badge « Un service Groupe KA », cliquable | |
| 3 | +// vers le hub. À placer dans le header de chaque site (à côté du logo de la | |
| 4 | +// marque). Variante dark pour les surfaces encre. Zone de protection : le | |
| 5 | +// padding interne du badge suffit ; ne rien coller à moins de 8 px du badge. | |
| 6 | +export default function GroupeKaBadge({ dark = false }: { dark?: boolean }) { | |
| 7 | + return ( | |
| 8 | + <a | |
| 9 | + className={`gk-badge${dark ? " gk-badge--dark" : ""}`} | |
| 10 | + href="https://www.groupe-ka.com" | |
| 11 | + target="_blank" | |
| 12 | + rel="noopener noreferrer" | |
| 13 | + aria-label="Un service Groupe KA — visiter le portail de l'écosystème" | |
| 14 | + > | |
| 15 | + Un service | |
| 16 | + <b> | |
| 17 | + Groupe<span className="ka">KA</span> | |
| 18 | + </b> | |
| 19 | + </a> | |
| 20 | + ); | |
| 21 | +} | |
added
frontend/src/ka/KaFooter.tsx
+91 −0
@@ -0,0 +1,91 @@ | ||
| 1 | +// Auteur : Simon-Pierre Boucher — contact@spboucher.ai | |
| 2 | +// ka-ui/react/KaFooter.tsx — footer commun Groupe KA (fond encre), à vendorer | |
| 3 | +// dans chaque app Vite/React sous frontend/src/ka/. Consomme ecosystem.json : | |
| 4 | +// toute modification des infos de contact se fait dans ka-ui/ecosystem.json | |
| 5 | +// puis se resynchronise sur les 12 sites (voir ka-ui/README.md). | |
| 6 | +import eco from "./ecosystem.json"; | |
| 7 | + | |
| 8 | +type LocalLink = { label: string; href: string }; | |
| 9 | + | |
| 10 | +export default function KaFooter({ | |
| 11 | + siteId, | |
| 12 | + localLegal = [], | |
| 13 | +}: { | |
| 14 | + /** id du site courant dans ecosystem.json (ex. "resto-ka") */ | |
| 15 | + siteId: string; | |
| 16 | + /** pages légales PROPRES au site (ex. /conditions), affichées avant celles du hub */ | |
| 17 | + localLegal?: LocalLink[]; | |
| 18 | +}) { | |
| 19 | + const year = new Date().getFullYear(); | |
| 20 | + const site = eco.sites.find((s) => s.id === siteId); | |
| 21 | + const others = eco.sites.filter((s) => s.id !== siteId); | |
| 22 | + return ( | |
| 23 | + <footer className="ka-footer" id="contact"> | |
| 24 | + <div className="container"> | |
| 25 | + <a | |
| 26 | + className="wordmark" | |
| 27 | + href={eco.hub.url} | |
| 28 | + target={siteId === "groupe-ka" ? undefined : "_blank"} | |
| 29 | + rel="noopener noreferrer" | |
| 30 | + aria-label="Groupe KA — le portail de l'écosystème" | |
| 31 | + > | |
| 32 | + Groupe <span className="ka">KA</span> | |
| 33 | + </a> | |
| 34 | + <p className="desc"> | |
| 35 | + {eco.org.tagline} Des centaines de connecteurs lisent les sites à la | |
| 36 | + source, normalisent la donnée et se resynchronisent seuls.{" "} | |
| 37 | + {site ? ( | |
| 38 | + <> | |
| 39 | + <b style={{ color: "var(--paper)" }}>{site.wordmark}</b> —{" "} | |
| 40 | + {site.tagline} — est un service Groupe KA. | |
| 41 | + </> | |
| 42 | + ) : null} | |
| 43 | + </p> | |
| 44 | + <p className="notice"> | |
| 45 | + <b>{eco.org.disclaimer}</b> | |
| 46 | + </p> | |
| 47 | + <ul className="sites"> | |
| 48 | + {others.map((s) => ( | |
| 49 | + <li key={s.id}> | |
| 50 | + <a href={`https://${s.domain}`} target="_blank" rel="noopener noreferrer"> | |
| 51 | + {s.wordmark} | |
| 52 | + </a> | |
| 53 | + </li> | |
| 54 | + ))} | |
| 55 | + {eco.extraFooterLinks.map((l) => ( | |
| 56 | + <li key={l.href}> | |
| 57 | + <a href={l.href} target="_blank" rel="noopener noreferrer"> | |
| 58 | + {l.label} | |
| 59 | + </a> | |
| 60 | + </li> | |
| 61 | + ))} | |
| 62 | + </ul> | |
| 63 | + <div className="contacts"> | |
| 64 | + {eco.contacts.map((c) => ( | |
| 65 | + <p key={c.email} style={{ margin: 0 }}> | |
| 66 | + <a href={`mailto:${c.email}`}>{c.email}</a> | |
| 67 | + <span>{c.role}</span> | |
| 68 | + </p> | |
| 69 | + ))} | |
| 70 | + </div> | |
| 71 | + <p className="legal"> | |
| 72 | + © {year} {eco.org.copyrightHolder} | |
| 73 | + {localLegal.map((l) => ( | |
| 74 | + <span key={l.href}> | |
| 75 | + {" · "} | |
| 76 | + <a href={l.href}>{l.label}</a> | |
| 77 | + </span> | |
| 78 | + ))} | |
| 79 | + {eco.legal.map((l) => ( | |
| 80 | + <span key={l.href}> | |
| 81 | + {" · "} | |
| 82 | + <a href={l.href} target="_blank" rel="noopener noreferrer"> | |
| 83 | + {l.label} | |
| 84 | + </a> | |
| 85 | + </span> | |
| 86 | + ))} | |
| 87 | + </p> | |
| 88 | + </div> | |
| 89 | + </footer> | |
| 90 | + ); | |
| 91 | +} | |
added
frontend/src/ka/ecosystem.json
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +{ | |
| 2 | + "org": { | |
| 3 | + "name": "Groupe KA", | |
| 4 | + "legalName": "Groupe KA — Simon-Pierre Boucher", | |
| 5 | + "tagline": "Holding québécois d'agrégateurs de produits et services entièrement automatisés.", | |
| 6 | + "disclaimer": "Groupe KA est un agrégateur de contenu : nous ne vendons rien, ne louons rien et ne sommes partie à aucune transaction.", | |
| 7 | + "copyrightHolder": "Groupe KA — Simon-Pierre Boucher" | |
| 8 | + }, | |
| 9 | + "hub": { | |
| 10 | + "url": "https://www.groupe-ka.com", | |
| 11 | + "loginPath": "/connexion", | |
| 12 | + "signupNote": "La création de compte KA ID se fait sur le hub groupe-ka.com ; chaque site délègue sa connexion via /api/auth/ka/login." | |
| 13 | + }, | |
| 14 | + "contacts": [ | |
| 15 | + { "email": "contact@groupe-ka.com", "role": "Projets, partenariats & données" }, | |
| 16 | + { "email": "info@groupe-ka.com", "role": "Médias & questions générales" }, | |
| 17 | + { "email": "admin@groupe-ka.com", "role": "Légal, vie privée & Loi 25" } | |
| 18 | + ], | |
| 19 | + "legal": [ | |
| 20 | + { "label": "Conditions d'utilisation", "href": "https://www.groupe-ka.com/conditions" }, | |
| 21 | + { "label": "Politique de confidentialité", "href": "https://www.groupe-ka.com/confidentialite" }, | |
| 22 | + { "label": "Protection des renseignements personnels (Loi 25)", "href": "https://www.groupe-ka.com/loi-25" }, | |
| 23 | + { "label": "Transparence des robots d'indexation", "href": "https://www.groupe-ka.com/bots" } | |
| 24 | + ], | |
| 25 | + "sites": [ | |
| 26 | + { "id": "groupe-ka", "wordmark": "Groupe KA", "domain": "www.groupe-ka.com", "accent": "#d9f26b", "accentSoft": "#f0f9d2", "accentDeep": "#123f2e", "onAccent": "#141814", "tagline": "Le portail de l'écosystème ·Ka" }, | |
| 27 | + { "id": "trouve-ka", "wordmark": "Trouve·Ka", "domain": "www.trouve-ka.com", "accent": "#1c7ed6", "accentSoft": "#e7f2fd", "accentDeep": "#14508f", "onAccent": "#ffffff", "tagline": "Le moteur de recherche du web québécois" }, | |
| 28 | + { "id": "lou-ka", "wordmark": "Lou·Ka", "domain": "www.lou-ka.com", "accent": "#ff6a00", "accentSoft": "#fff1e6", "accentDeep": "#cc5500", "onAccent": "#ffffff", "tagline": "Tous les logements à louer" }, | |
| 29 | + { "id": "immo-ka", "wordmark": "Immo·Ka", "domain": "www.immo-ka.com", "accent": "#e23744", "accentSoft": "#fbe0e2", "accentDeep": "#a8232e", "onAccent": "#ffffff", "tagline": "Toutes les propriétés à vendre" }, | |
| 30 | + { "id": "vrai-prix", "wordmark": "Vrai-Prix", "domain": "www.vrai-prix.com", "accent": "#ff5148", "accentSoft": "#ffe3e0", "accentDeep": "#9e2a25", "onAccent": "#ffffff", "tagline": "La valeur réelle de chaque propriété" }, | |
| 31 | + { "id": "auto-ka", "wordmark": "Auto·Ka", "domain": "www.auto-ka.com", "accent": "#ff5a2a", "accentSoft": "#ffe8de", "accentDeep": "#cc3f16", "onAccent": "#ffffff", "tagline": "Les voitures usagées du Québec" }, | |
| 32 | + { "id": "fabri-ka", "wordmark": "Fabri·Ka", "domain": "www.fabri-ka.com", "accent": "#c4532e", "accentSoft": "#f7e3da", "accentDeep": "#a94525", "onAccent": "#ffffff", "tagline": "Les produits fabriqués au Québec" }, | |
| 33 | + { "id": "food-ka", "wordmark": "Food·Ka", "domain": "www.food-ka.com", "accent": "#1f9d55", "accentSoft": "#e2f5ea", "accentDeep": "#157a40", "onAccent": "#ffffff", "tagline": "Les prix d'épicerie, suivis à la source" }, | |
| 34 | + { "id": "resto-ka", "wordmark": "Resto·Ka", "domain": "www.resto-ka.com", "accent": "#f08c00", "accentSoft": "#fdeed7", "accentDeep": "#b96a00", "onAccent": "#141814", "tagline": "Chaque resto, chaque plat, chaque prix" }, | |
| 35 | + { "id": "sorti-ka", "wordmark": "Sorti·Ka", "domain": "www.sorti-ka.com", "accent": "#d6336c", "accentSoft": "#fbe0eb", "accentDeep": "#a12551", "onAccent": "#ffffff", "tagline": "Toutes les sorties, dans les 17 régions" }, | |
| 36 | + { "id": "crea-ka", "wordmark": "Créa·Ka", "domain": "www.crea-ka.com", "accent": "#7048e8", "accentSoft": "#ece5fc", "accentDeep": "#5433b8", "onAccent": "#ffffff", "tagline": "Les créateurs d'ici, tous leurs liens" }, | |
| 37 | + { "id": "api-ka", "wordmark": "API·Ka", "domain": "www.api-ka.com", "accent": "#3b5bdb", "accentSoft": "#e4eafb", "accentDeep": "#2b44a8", "onAccent": "#ffffff", "tagline": "La donnée de l'écosystème, par API" } | |
| 38 | + ], | |
| 39 | + "extraFooterLinks": [ | |
| 40 | + { "label": "ValoPlex", "href": "https://www.valoplex.com" }, | |
| 41 | + { "label": "Ka2", "href": "https://www.ka2.bot" }, | |
| 42 | + { "label": "Ka4", "href": "https://www.ka4.bot" }, | |
| 43 | + { "label": "Ka6", "href": "https://www.ka6.bot" } | |
| 44 | + ] | |
| 45 | +} | |
added
frontend/src/ka/tokens.css
+229 −0
@@ -0,0 +1,229 @@ | ||
| 1 | +/* ----------------------------------------------------------------------------- | |
| 2 | + Auteur : Simon-Pierre Boucher — contact@spboucher.ai | |
| 3 | + Fichier : ka-ui/tokens.css — SOURCE CANONIQUE (repo ka-ui sur spbgit) | |
| 4 | + Desc. : Design system commun GROUPE KA « éditorial sharp » — tokens + socle | |
| 5 | + de composants partagés par les 12 plateformes. Chaque site importe ce | |
| 6 | + fichier PUIS surcharge uniquement son accent (voir accents.css). | |
| 7 | + | |
| 8 | + · Typo : display Space Grotesk / texte Inter / micro-étiquettes JetBrains Mono | |
| 9 | + · Palette: papier #f5f3ee · encre #141814 · vert profond #1c5c41 + ACCENT du site | |
| 10 | + · Signature : bordures encre 1,5–2 px + ombres décalées dures, grain de film, | |
| 11 | + surlignés d'accent inclinés (néo-brutalisme raffiné) | |
| 12 | + · Breakpoints communs : 360 / 768 / 1024 / 1440 px (mobile-first) | |
| 13 | + · Zones tactiles ≥ 44×44 px, safe-areas iOS, typographie fluide (clamp) | |
| 14 | +----------------------------------------------------------------------------- */ | |
| 15 | + | |
| 16 | +:root { | |
| 17 | + /* ---- Palette fixe Groupe KA (identique sur les 12 sites) ---- */ | |
| 18 | + --paper: #f5f3ee; | |
| 19 | + --surface: #ffffff; | |
| 20 | + --surface-2: #faf9f5; | |
| 21 | + --ink: #141814; | |
| 22 | + --ink-2: #4d5551; | |
| 23 | + --ink-3: #8b928c; | |
| 24 | + --line: rgba(20, 24, 20, 0.14); | |
| 25 | + --line-strong: rgba(20, 24, 20, 0.85); | |
| 26 | + --green: #1c5c41; | |
| 27 | + --green-deep: #123f2e; | |
| 28 | + --amber: #e8a33d; | |
| 29 | + --amber-soft: #fdf3e2; | |
| 30 | + --danger: #b3423a; | |
| 31 | + --danger-soft: #fbe9e7; | |
| 32 | + | |
| 33 | + /* ---- ACCENT du site — SEULES variables surchargées par marque ---- */ | |
| 34 | + --accent: #d9f26b; /* défaut : lime Groupe KA */ | |
| 35 | + --accent-soft: #f0f9d2; | |
| 36 | + --accent-deep: #123f2e; | |
| 37 | + --on-accent: var(--ink); /* couleur du texte posé SUR l'accent */ | |
| 38 | + | |
| 39 | + /* alias rétro-compatibles (les satellites historiques utilisent --lime) */ | |
| 40 | + --lime: var(--accent); | |
| 41 | + --lime-soft: var(--accent-soft); | |
| 42 | + | |
| 43 | + /* ---- Géométrie sharp ---- */ | |
| 44 | + --r-card: 10px; | |
| 45 | + --r-ctl: 6px; | |
| 46 | + --r-pill: 999px; | |
| 47 | + | |
| 48 | + /* ---- Ombres décalées — la signature du groupe ---- */ | |
| 49 | + --shadow-flat: 0 1px 2px rgba(20, 24, 20, 0.05); | |
| 50 | + --shadow-off: 6px 6px 0 var(--ink); | |
| 51 | + --shadow-off-soft: 8px 8px 0 rgba(20, 24, 20, 0.08); | |
| 52 | + --shadow-off-mid: 4px 4px 0 rgba(20, 24, 20, 0.18); | |
| 53 | + | |
| 54 | + /* ---- Typographie ---- */ | |
| 55 | + --font-display: "Space Grotesk", system-ui, sans-serif; | |
| 56 | + --font-body: "Inter", system-ui, sans-serif; | |
| 57 | + --font-mono: "JetBrains Mono", ui-monospace, monospace; | |
| 58 | + | |
| 59 | + /* Échelle fluide (mobile-first, clamp) */ | |
| 60 | + --fs-h1: clamp(30px, 3.2vw + 18px, 54px); | |
| 61 | + --fs-h2: clamp(23px, 1.8vw + 14px, 34px); | |
| 62 | + --fs-h3: clamp(17px, 0.9vw + 12px, 22px); | |
| 63 | + --fs-body: 15px; | |
| 64 | + --fs-small: 13px; | |
| 65 | + | |
| 66 | + /* Espacements */ | |
| 67 | + --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; | |
| 68 | + --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; | |
| 69 | + | |
| 70 | + /* Cible tactile minimale */ | |
| 71 | + --touch: 44px; | |
| 72 | +} | |
| 73 | + | |
| 74 | +/* ---- Socle ---- */ | |
| 75 | +* { box-sizing: border-box; } | |
| 76 | +html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; } | |
| 77 | +body { | |
| 78 | + margin: 0; | |
| 79 | + background: var(--paper); | |
| 80 | + color: var(--ink); | |
| 81 | + font-family: var(--font-body); | |
| 82 | + font-size: var(--fs-body); | |
| 83 | + line-height: 1.55; | |
| 84 | + -webkit-font-smoothing: antialiased; | |
| 85 | + padding-bottom: env(safe-area-inset-bottom); | |
| 86 | + overflow-x: clip; /* jamais de débordement horizontal */ | |
| 87 | +} | |
| 88 | +img, svg, video { max-width: 100%; height: auto; display: block; } | |
| 89 | +h1, h2, h3, h4 { font-family: var(--font-display); letter-spacing: -0.03em; margin: 0 0 0.5em; } | |
| 90 | +h1 { font-size: var(--fs-h1); line-height: 1.06; } | |
| 91 | +h2 { font-size: var(--fs-h2); line-height: 1.15; } | |
| 92 | +h3 { font-size: var(--fs-h3); line-height: 1.25; } | |
| 93 | +a { color: inherit; } | |
| 94 | +::selection { background: var(--accent); color: var(--on-accent); } | |
| 95 | +:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; } | |
| 96 | + | |
| 97 | +/* Grain de film pleine page — ⚠️ jamais de z-index sur body > * (position: | |
| 98 | + relative seulement), sinon les utilitaires z-* sont écrasés. */ | |
| 99 | +body::before { | |
| 100 | + content: ""; | |
| 101 | + position: fixed; inset: 0; pointer-events: none; opacity: 0.35; z-index: 9999; | |
| 102 | + 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"); | |
| 103 | +} | |
| 104 | +body > * { position: relative; } | |
| 105 | + | |
| 106 | +/* ---- Conteneur commun ---- */ | |
| 107 | +.container { max-width: 1152px; margin: 0 auto; padding: 0 var(--sp-4); } | |
| 108 | +@media (min-width: 768px) { .container { padding: 0 var(--sp-5); } } | |
| 109 | + | |
| 110 | +/* ---- Micro-typographie signature ---- */ | |
| 111 | +.kicker { | |
| 112 | + display: inline-flex; align-items: center; gap: 10px; | |
| 113 | + font-family: var(--font-mono); font-size: 11.5px; font-weight: 500; | |
| 114 | + text-transform: uppercase; letter-spacing: 0.12em; color: var(--green); | |
| 115 | +} | |
| 116 | +.kicker::before { content: ""; width: 22px; height: 2px; background: var(--green); } | |
| 117 | +.klabel { | |
| 118 | + font-family: var(--font-mono); font-size: 10px; font-weight: 700; | |
| 119 | + text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-3); | |
| 120 | +} | |
| 121 | +.hl { | |
| 122 | + display: inline-block; background: var(--accent); color: var(--on-accent); | |
| 123 | + border-radius: 8px; padding: 0 10px 2px; transform: rotate(-1deg); | |
| 124 | +} | |
| 125 | +.outline-txt { color: transparent; -webkit-text-stroke: 2px var(--ink); } | |
| 126 | + | |
| 127 | +/* ---- Boutons (cible ≥ 44 px) ---- */ | |
| 128 | +.btn { | |
| 129 | + display: inline-flex; align-items: center; justify-content: center; gap: 8px; | |
| 130 | + min-height: var(--touch); padding: 10px 18px; | |
| 131 | + font-family: var(--font-display); font-weight: 700; font-size: 14px; | |
| 132 | + border: 1.5px solid var(--ink); border-radius: var(--r-ctl); | |
| 133 | + background: var(--surface); color: var(--ink); | |
| 134 | + cursor: pointer; text-decoration: none; | |
| 135 | + transition: transform 0.15s, box-shadow 0.15s, background 0.15s, color 0.15s; | |
| 136 | +} | |
| 137 | +.btn:hover { transform: translate(-2px, -2px); box-shadow: var(--shadow-off-mid); } | |
| 138 | +.btn:active { transform: translate(0, 0); box-shadow: none; } | |
| 139 | +.btn:disabled { opacity: 0.45; pointer-events: none; } | |
| 140 | +.btn-primary { background: var(--ink); color: var(--accent); } | |
| 141 | +.btn-primary:hover { background: var(--accent-deep); } | |
| 142 | +.btn-accent { background: var(--accent); color: var(--on-accent); } | |
| 143 | +.btn-ghost { background: transparent; border-color: var(--line); } | |
| 144 | +.btn-ghost:hover { border-color: var(--ink); } | |
| 145 | + | |
| 146 | +/* ---- Cartes ---- */ | |
| 147 | +.card { | |
| 148 | + background: var(--surface); border: 1.5px solid var(--ink); | |
| 149 | + border-radius: var(--r-card); box-shadow: var(--shadow-off-soft); | |
| 150 | + overflow: hidden; | |
| 151 | +} | |
| 152 | +.card-hover { transition: transform 0.15s, box-shadow 0.15s; } | |
| 153 | +.card-hover:hover { transform: translate(-2px, -2px); box-shadow: var(--shadow-off); } | |
| 154 | + | |
| 155 | +/* ---- Chips / badges ---- */ | |
| 156 | +.chip { | |
| 157 | + display: inline-flex; align-items: center; gap: 6px; | |
| 158 | + padding: 4px 11px; font-family: var(--font-mono); font-size: 11px; font-weight: 700; | |
| 159 | + text-transform: uppercase; letter-spacing: 0.06em; | |
| 160 | + border: 1.5px solid var(--ink); border-radius: var(--r-pill); | |
| 161 | + background: var(--surface); color: var(--ink); | |
| 162 | +} | |
| 163 | +.chip-accent { background: var(--accent); color: var(--on-accent); } | |
| 164 | +.chip-soft { background: var(--accent-soft); border-color: var(--line); } | |
| 165 | + | |
| 166 | +/* ---- Champs ---- */ | |
| 167 | +.input, .select, .textarea { | |
| 168 | + width: 100%; min-height: var(--touch); padding: 10px 14px; | |
| 169 | + font: inherit; color: var(--ink); background: var(--surface); | |
| 170 | + border: 1.5px solid var(--ink); border-radius: var(--r-ctl); | |
| 171 | +} | |
| 172 | +.input:focus, .select:focus, .textarea:focus { | |
| 173 | + outline: none; box-shadow: 3px 3px 0 var(--accent); border-color: var(--ink); | |
| 174 | +} | |
| 175 | +.input::placeholder { color: var(--ink-3); } | |
| 176 | + | |
| 177 | +/* ---- Badge « Un service Groupe KA » (header, cliquable → hub) ---- */ | |
| 178 | +.gk-badge { | |
| 179 | + display: inline-flex; align-items: center; gap: 7px; | |
| 180 | + min-height: 30px; padding: 3px 10px 4px; | |
| 181 | + font-family: var(--font-mono); font-size: 10px; font-weight: 700; | |
| 182 | + letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; | |
| 183 | + border: 1.5px solid var(--ink); border-radius: var(--r-pill); | |
| 184 | + background: var(--surface); color: var(--ink-2); white-space: nowrap; | |
| 185 | +} | |
| 186 | +.gk-badge b { | |
| 187 | + font-family: var(--font-display); font-size: 12px; letter-spacing: -0.02em; | |
| 188 | + text-transform: none; color: var(--ink); | |
| 189 | +} | |
| 190 | +.gk-badge b .ka { | |
| 191 | + display: inline-block; background: var(--ink); color: var(--accent); | |
| 192 | + border-radius: 5px; padding: 0 5px 1px; margin-left: 3px; transform: rotate(-2deg); | |
| 193 | +} | |
| 194 | +.gk-badge:hover .ka { transform: rotate(0); } | |
| 195 | +.gk-badge--dark { background: transparent; border-color: rgba(245,243,238,0.4); color: rgba(245,243,238,0.75); } | |
| 196 | +.gk-badge--dark b { color: var(--paper); } | |
| 197 | + | |
| 198 | +/* ---- Footer commun (fond encre — voir react/KaFooter.tsx) ---- */ | |
| 199 | +.ka-footer { margin-top: var(--sp-8); background: var(--ink); padding: 44px 0; font-size: 13px; color: rgba(245,243,238,0.75); } | |
| 200 | +.ka-footer a { text-decoration: none; } | |
| 201 | +.ka-footer .wordmark { font-family: var(--font-display); font-weight: 700; font-size: 30px; letter-spacing: -0.04em; color: var(--paper); text-decoration: none; } | |
| 202 | +.ka-footer .wordmark .ka { color: var(--accent); } | |
| 203 | +.ka-footer .desc { max-width: 640px; margin-top: var(--sp-4); } | |
| 204 | +.ka-footer .notice { max-width: 640px; margin-top: var(--sp-4); border-left: 2px solid var(--accent); padding-left: var(--sp-4); } | |
| 205 | +.ka-footer .notice b { color: var(--paper); } | |
| 206 | +.ka-footer .sites { list-style: none; display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 26px 0 0; padding: 0; font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; } | |
| 207 | +.ka-footer .sites a { color: rgba(245,243,238,0.65); } | |
| 208 | +.ka-footer .sites a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; } | |
| 209 | +.ka-footer .contacts { display: grid; gap: 14px 32px; border-top: 1px solid rgba(245,243,238,0.15); margin-top: 30px; padding-top: 26px; } | |
| 210 | +@media (min-width: 768px) { .ka-footer .contacts { grid-template-columns: repeat(3, 1fr); } } | |
| 211 | +.ka-footer .contacts a { font-family: var(--font-mono); font-weight: 700; font-size: 12px; color: rgba(245,243,238,0.85); } | |
| 212 | +.ka-footer .contacts a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; } | |
| 213 | +.ka-footer .contacts span { display: block; margin-top: 3px; font-size: 11px; color: rgba(245,243,238,0.5); } | |
| 214 | +.ka-footer .legal { margin-top: 30px; font-family: var(--font-mono); font-size: 11px; color: rgba(245,243,238,0.45); } | |
| 215 | +.ka-footer .legal a { color: inherit; } | |
| 216 | +.ka-footer .legal a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; } | |
| 217 | + | |
| 218 | +/* ---- Tableaux → cartes empilées sous 768 px ---- */ | |
| 219 | +.tbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; } | |
| 220 | +@media (max-width: 767px) { | |
| 221 | + .tbl-stack thead { display: none; } | |
| 222 | + .tbl-stack tr { display: block; border: 1.5px solid var(--ink); border-radius: var(--r-card); margin-bottom: var(--sp-3); background: var(--surface); } | |
| 223 | + .tbl-stack td { display: flex; justify-content: space-between; gap: var(--sp-3); padding: 8px 12px; border: 0; } | |
| 224 | + .tbl-stack td::before { content: attr(data-label); font-family: var(--font-mono); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-3); } | |
| 225 | +} | |
| 226 | + | |
| 227 | +/* ---- Utilitaires responsive ---- */ | |
| 228 | +.only-mobile { display: initial; } .only-desktop { display: none; } | |
| 229 | +@media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } | |
modified
frontend/src/main.tsx
+1 −0
@@ -7,6 +7,7 @@ import React from "react"; | ||
| 7 | 7 | import ReactDOM from "react-dom/client"; |
| 8 | 8 | import { BrowserRouter } from "react-router-dom"; |
| 9 | 9 | import App from "./App"; |
| 10 | +import "./ka/tokens.css"; // socle ka-ui (importé AVANT styles.css) | |
| 10 | 11 | import "./styles.css"; |
| 11 | 12 | |
| 12 | 13 | ReactDOM.createRoot(document.getElementById("root")!).render( |
added
frontend/src/pages/Contact.tsx
+75 −0
@@ -0,0 +1,75 @@ | ||
| 1 | +// ============================================================================== | |
| 2 | +// Author: Simon-Pierre Boucher <contact@spboucher.ai> | |
| 3 | +// File: pages/Contact.tsx | |
| 4 | +// Desc: Page Contact — infos officielles du Groupe KA tirées de | |
| 5 | +// ka-ui/ecosystem.json (courriels + rôles, avertissement, hub, | |
| 6 | +// écosystème des sites), composants du design system éditorial sharp. | |
| 7 | +// ============================================================================== | |
| 8 | +import { useEffect } from "react"; | |
| 9 | +import eco from "../ka/ecosystem.json"; | |
| 10 | + | |
| 11 | +export default function ContactPage() { | |
| 12 | + useEffect(() => { | |
| 13 | + document.title = "Contact — Resto-Ka — Un service Groupe KA"; | |
| 14 | + window.scrollTo(0, 0); | |
| 15 | + return () => { document.title = "Resto-Ka — Un service Groupe KA"; }; | |
| 16 | + }, []); | |
| 17 | + | |
| 18 | + const sites = eco.sites.filter((s) => s.id !== "resto-ka"); | |
| 19 | + | |
| 20 | + return ( | |
| 21 | + <div className="contact-page container"> | |
| 22 | + <span className="kicker">Contact</span> | |
| 23 | + <h1> | |
| 24 | + Écrire au <span className="hl">Groupe KA</span> | |
| 25 | + </h1> | |
| 26 | + <p className="sub"> | |
| 27 | + Resto-Ka est un service du Groupe KA. Toutes les demandes — projets, | |
| 28 | + médias, données, légal — passent par les adresses officielles du groupe. | |
| 29 | + </p> | |
| 30 | + | |
| 31 | + <div className="contact-grid"> | |
| 32 | + {eco.contacts.map((c) => ( | |
| 33 | + <div className="contact-card" key={c.email}> | |
| 34 | + <span className="role">{c.role}</span> | |
| 35 | + <a className="mail" href={`mailto:${c.email}`}>{c.email}</a> | |
| 36 | + </div> | |
| 37 | + ))} | |
| 38 | + </div> | |
| 39 | + | |
| 40 | + <p className="contact-note"> | |
| 41 | + <b>{eco.org.disclaimer}</b> Chaque fiche de Resto-Ka renvoie au | |
| 42 | + restaurant original et affiche le contexte de chaque prix. | |
| 43 | + </p> | |
| 44 | + | |
| 45 | + <div className="contact-hub"> | |
| 46 | + <a className="btn btn-primary" href={eco.hub.url} target="_blank" rel="noopener noreferrer"> | |
| 47 | + Visiter le hub groupe-ka.com → | |
| 48 | + </a> | |
| 49 | + <a | |
| 50 | + className="btn" | |
| 51 | + href={`${eco.hub.url}${eco.hub.loginPath}`} | |
| 52 | + target="_blank" | |
| 53 | + rel="noopener noreferrer" | |
| 54 | + > | |
| 55 | + Créer un compte KA ID | |
| 56 | + </a> | |
| 57 | + <span className="hint">{eco.hub.signupNote}</span> | |
| 58 | + </div> | |
| 59 | + | |
| 60 | + <h2>L'écosystème ·Ka</h2> | |
| 61 | + <p className="sub" style={{ marginBottom: 0 }}>{eco.org.tagline}</p> | |
| 62 | + <ul className="contact-sites"> | |
| 63 | + {sites.map((s) => ( | |
| 64 | + <li key={s.id}> | |
| 65 | + <a href={`https://${s.domain}`} target="_blank" rel="noopener noreferrer" | |
| 66 | + title={s.tagline}> | |
| 67 | + <span className="dot" style={{ background: s.accent }} aria-hidden="true" /> | |
| 68 | + {s.wordmark} | |
| 69 | + </a> | |
| 70 | + </li> | |
| 71 | + ))} | |
| 72 | + </ul> | |
| 73 | + </div> | |
| 74 | + ); | |
| 75 | +} | |
modified
frontend/src/styles.css
+117 −131
@@ -2,89 +2,29 @@ | ||
| 2 | 2 | Author: Simon-Pierre Boucher <contact@spboucher.ai> |
| 3 | 3 | File: styles.css |
| 4 | 4 | Desc: Identité visuelle Resto·Ka — système de design « éditorial sharp » |
| 5 | − du GROUPE KA (identité partagée avec groupe-ka.com, Lou·Ka, Vrai-Prix, | |
| 6 | − ValoPlex) : | |
| 7 | − · Typo display Space Grotesk / texte Inter / micro-étiquettes JetBrains Mono | |
| 8 | − · Palette : papier #f5f3ee · encre #141814 · vert profond #1c5c41 · | |
| 9 | − lime électrique #d9f26b · ambre #e8a33d | |
| 10 | − · Signature : bordures encre 1,5 px + ombres décalées dures (néo-brutalisme | |
| 11 | − raffiné), grain de film pleine page, surlignés lime inclinés | |
| 5 | + du GROUPE KA. Le socle (palette fixe, typo, grain, boutons, cartes…) | |
| 6 | + vient de ka-ui (src/ka/tokens.css, importé AVANT ce fichier) ; ici on | |
| 7 | + ne définit que l'ACCENT SAFRAN de Resto-Ka + les classes métier. | |
| 8 | + · Accent Resto-Ka : safran #f08c00 (soft #fdeed7 · deep #b96a00) | |
| 12 | 9 | · Baseline : « Chaque resto, chaque plat, chaque prix. » |
| 13 | 10 | · 100 % adaptatif mobile (safe-areas iOS) |
| 14 | 11 | ----------------------------------------------------------------------------- */ |
| 15 | 12 | :root { |
| 16 | − /* palette officielle Groupe KA (éditorial sharp) */ | |
| 17 | − --paper: #f5f3ee; | |
| 18 | − --surface: #ffffff; | |
| 19 | − --surface-2: #faf9f5; | |
| 20 | − --ink: #141814; | |
| 21 | − --ink-2: #4d5551; | |
| 22 | − --ink-3: #8b928c; | |
| 23 | − --line: rgba(20, 24, 20, 0.14); | |
| 24 | − --line-strong: rgba(20, 24, 20, 0.85); | |
| 25 | − --green: #1c5c41; | |
| 26 | − --green-deep: #123f2e; | |
| 27 | − --lime: #d9f26b; | |
| 28 | − --lime-soft: #f0f9d2; | |
| 29 | − --amber: #e8a33d; | |
| 30 | − --amber-soft: #fdf3e2; | |
| 31 | − --danger: #b3423a; | |
| 32 | − --danger-soft: #fbe9e7; | |
| 33 | − | |
| 34 | − /* géométrie sharp : petits rayons, bordures franches */ | |
| 35 | − --r-card: 10px; | |
| 36 | − --r-ctl: 6px; | |
| 37 | − --r-pill: 999px; | |
| 38 | − | |
| 39 | − /* ombres décalées dures — la signature du groupe */ | |
| 40 | − --shadow-flat: 0 1px 2px rgba(20, 24, 20, 0.05); | |
| 41 | − --shadow-off: 6px 6px 0 var(--ink); | |
| 42 | − --shadow-off-soft: 8px 8px 0 rgba(20, 24, 20, 0.08); | |
| 43 | − --shadow-off-mid: 4px 4px 0 rgba(20, 24, 20, 0.18); | |
| 44 | − | |
| 45 | − --font-display: "Space Grotesk", system-ui, sans-serif; | |
| 46 | − --font-body: "Inter", system-ui, sans-serif; | |
| 47 | − --font-mono: "JetBrains Mono", ui-monospace, monospace; | |
| 48 | −} | |
| 49 | − | |
| 50 | −* { box-sizing: border-box; } | |
| 51 | −html { scroll-behavior: smooth; } | |
| 52 | −body { | |
| 53 | − margin: 0; | |
| 54 | − background: var(--paper); | |
| 55 | − color: var(--ink); | |
| 56 | − font-family: var(--font-body); | |
| 57 | − font-size: 15px; | |
| 58 | − line-height: 1.55; | |
| 59 | − -webkit-font-smoothing: antialiased; | |
| 60 | − padding-bottom: env(safe-area-inset-bottom); | |
| 61 | −} | |
| 62 | −/* grain de film pleine page (signature Groupe KA) */ | |
| 63 | −body::before { | |
| 64 | − content: ""; | |
| 65 | − position: fixed; inset: 0; pointer-events: none; opacity: 0.35; z-index: 9999; | |
| 66 | − 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"); | |
| 67 | −} | |
| 68 | −#root { position: relative; z-index: 1; } | |
| 69 | − | |
| 70 | −h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.03em; margin: 0; } | |
| 71 | −a { color: inherit; text-decoration: none; } | |
| 72 | −button { font-family: inherit; } | |
| 73 | −img { display: block; } | |
| 74 | −::selection { background: var(--lime); color: var(--ink); } | |
| 75 | −:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; } | |
| 13 | + /* ACCENT du site — seule surcharge de tokens.css (ka-ui) */ | |
| 14 | + --accent: #f08c00; | |
| 15 | + --accent-soft: #fdeed7; | |
| 16 | + --accent-deep: #b96a00; | |
| 17 | + --on-accent: #141814; | |
| 76 | 18 | |
| 77 | −/* surligné lime incliné — la marque des titres du groupe */ | |
| 78 | −h1 .hl, h2 .hl, .fbaseline .hl { | |
| 79 | − display: inline-block; background: var(--lime); color: var(--ink); | |
| 80 | − border-radius: 8px; padding: 0 10px 2px; transform: rotate(-1deg); | |
| 81 | −} | |
| 82 | −.kicker { | |
| 83 | − display: inline-flex; align-items: center; gap: 10px; | |
| 84 | − font-family: var(--font-mono); font-size: 11.5px; font-weight: 500; | |
| 85 | − text-transform: uppercase; letter-spacing: 0.12em; color: var(--green); | |
| 19 | + /* alias rétro-compatibles (le code historique utilise --lime) */ | |
| 20 | + --lime: var(--accent); | |
| 21 | + --lime-soft: var(--accent-soft); | |
| 86 | 22 | } |
| 87 | −.kicker::before { content: ""; width: 22px; height: 2px; background: var(--green); } | |
| 23 | + | |
| 24 | +h1, h2, h3, h4 { font-weight: 700; margin: 0; } | |
| 25 | +a { text-decoration: none; } | |
| 26 | +button { font-family: inherit; } | |
| 27 | +img { display: block; } | |
| 88 | 28 | |
| 89 | 29 | .container { max-width: 1240px; margin: 0 auto; padding: 0 24px; } |
| 90 | 30 | @media (max-width: 640px) { .container { padding: 0 16px; } } |
@@ -105,7 +45,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 105 | 45 | .brand .logo-ico { margin-right: 11px; flex: none; } |
| 106 | 46 | /* « Ka » en pastille lime inclinée — même geste que la carte membre Groupe KA */ |
| 107 | 47 | .brand .ka { |
| 108 | − background: var(--lime); color: var(--ink); | |
| 48 | + background: var(--accent); color: var(--ink); | |
| 109 | 49 | padding: 1px 6px 3px; border-radius: 5px; margin-left: 3px; |
| 110 | 50 | display: inline-block; transform: rotate(-2deg); |
| 111 | 51 | } |
@@ -122,8 +62,8 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 122 | 62 | color: var(--ink); transition: all 0.13s ease; min-height: 38px; |
| 123 | 63 | display: inline-flex; align-items: center; |
| 124 | 64 | } |
| 125 | −.nav a:hover { background: var(--lime); border-color: var(--ink); } | |
| 126 | −.nav a.active { background: var(--ink); color: var(--lime); border-color: var(--ink); } | |
| 65 | +.nav a:hover { background: var(--accent); border-color: var(--ink); } | |
| 66 | +.nav a.active { background: var(--ink); color: var(--accent); border-color: var(--ink); } | |
| 127 | 67 | |
| 128 | 68 | /* ================= Ticker ================= */ |
| 129 | 69 | .ticker { |
@@ -134,7 +74,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 134 | 74 | } |
| 135 | 75 | .ticker-track { display: inline-flex; gap: 0; animation: ticker 40s linear infinite; will-change: transform; } |
| 136 | 76 | .ticker span { padding: 0 26px; position: relative; } |
| 137 | −.ticker span::after { content: "◆"; position: absolute; right: -6px; color: var(--lime); opacity: 0.9; font-size: 8px; top: 3px; } | |
| 77 | +.ticker span::after { content: "◆"; position: absolute; right: -6px; color: var(--accent); opacity: 0.9; font-size: 8px; top: 3px; } | |
| 138 | 78 | @keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } } |
| 139 | 79 | @media (prefers-reduced-motion: reduce) { |
| 140 | 80 | .ticker-track { animation: none; } |
@@ -159,7 +99,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 159 | 99 | .stat-chip b { color: var(--ink); font-weight: 700; font-family: var(--font-display); } |
| 160 | 100 | .stat-chip .pulse { |
| 161 | 101 | width: 8px; height: 8px; border-radius: 50%; background: var(--green); |
| 162 | − box-shadow: 0 0 0 4px var(--lime-soft); animation: pulse 2.4s ease infinite; | |
| 102 | + box-shadow: 0 0 0 4px var(--accent-soft); animation: pulse 2.4s ease infinite; | |
| 163 | 103 | } |
| 164 | 104 | @keyframes pulse { 50% { box-shadow: 0 0 0 7px rgba(28, 92, 65, 0.18); } } |
| 165 | 105 | |
@@ -172,7 +112,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 172 | 112 | .benefit-ico { |
| 173 | 113 | width: 42px; height: 42px; border: 1.5px solid var(--ink); border-radius: var(--r-ctl); |
| 174 | 114 | flex: none; display: flex; align-items: center; justify-content: center; |
| 175 | − background: var(--lime); color: var(--ink); box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.15); | |
| 115 | + background: var(--accent); color: var(--ink); box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.15); | |
| 176 | 116 | } |
| 177 | 117 | .benefit-ico.blue { background: var(--surface); color: var(--green); } |
| 178 | 118 | .benefit-label { |
@@ -234,12 +174,12 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 234 | 174 | .pills { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 2px; } |
| 235 | 175 | .pill { |
| 236 | 176 | display: inline-flex; align-items: center; gap: 7px; |
| 237 | − border: 1.5px solid var(--ink); background: var(--lime); color: var(--ink); | |
| 177 | + border: 1.5px solid var(--ink); background: var(--accent); color: var(--ink); | |
| 238 | 178 | border-radius: var(--r-ctl); padding: 5px 12px; font-size: 12.5px; font-weight: 700; |
| 239 | 179 | font-family: var(--font-display); cursor: pointer; |
| 240 | 180 | box-shadow: 2px 2px 0 rgba(20, 24, 20, 0.2); transition: all 0.12s ease; |
| 241 | 181 | } |
| 242 | −.pill:hover { background: var(--ink); color: var(--lime); } | |
| 182 | +.pill:hover { background: var(--ink); color: var(--accent); } | |
| 243 | 183 | .pill-x { font-size: 10px; opacity: 0.65; } |
| 244 | 184 | .pill-clear { background: var(--surface); border-color: var(--danger); color: var(--danger); } |
| 245 | 185 | .pill-clear:hover { background: var(--danger); color: #fff; } |
@@ -255,10 +195,10 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 255 | 195 | transition: background 0.15s ease, transform 0.1s ease, box-shadow 0.1s ease; |
| 256 | 196 | } |
| 257 | 197 | .btn:active { transform: translate(2px, 2px); box-shadow: none !important; } |
| 258 | −.btn-primary { background: var(--ink); color: var(--lime); box-shadow: var(--shadow-off-mid); } | |
| 198 | +.btn-primary { background: var(--ink); color: var(--accent); box-shadow: var(--shadow-off-mid); } | |
| 259 | 199 | .btn-primary:hover { background: var(--green-deep); } |
| 260 | 200 | .btn-ghost { background: transparent; color: var(--ink); } |
| 261 | −.btn-ghost:hover { background: var(--lime); } | |
| 201 | +.btn-ghost:hover { background: var(--accent); } | |
| 262 | 202 | .btn:disabled { opacity: 0.4; cursor: default; } |
| 263 | 203 | |
| 264 | 204 | /* ================= Chips ================= */ |
@@ -270,8 +210,8 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 270 | 210 | cursor: pointer; font-family: var(--font-display); white-space: nowrap; min-height: 40px; |
| 271 | 211 | box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.1); transition: all 0.13s ease; |
| 272 | 212 | } |
| 273 | −.chip:hover { background: var(--lime-soft); transform: translate(-1px, -1px); box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.15); } | |
| 274 | −.chip.on { background: var(--ink); color: var(--lime); box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.3); } | |
| 213 | +.chip:hover { background: var(--accent-soft); transform: translate(-1px, -1px); box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.15); } | |
| 214 | +.chip.on { background: var(--ink); color: var(--accent); box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.3); } | |
| 275 | 215 | |
| 276 | 216 | /* ================= Results ================= */ |
| 277 | 217 | .results-head { display: flex; align-items: baseline; gap: 14px; margin: 28px 0 18px; } |
@@ -297,7 +237,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 297 | 237 | display: flex; align-items: center; justify-content: center; height: 100%; |
| 298 | 238 | color: var(--ink-3); |
| 299 | 239 | background: |
| 300 | − radial-gradient(circle at 82% 20%, var(--lime-soft) 0, transparent 46%), | |
| 240 | + radial-gradient(circle at 82% 20%, var(--accent-soft) 0, transparent 46%), | |
| 301 | 241 | radial-gradient(circle at 12% 85%, var(--amber-soft) 0, transparent 42%), |
| 302 | 242 | var(--surface-2); |
| 303 | 243 | } |
@@ -310,10 +250,10 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 310 | 250 | background: rgba(255, 255, 255, 0.95); color: var(--ink); |
| 311 | 251 | box-shadow: 2px 2px 0 rgba(20, 24, 20, 0.25); |
| 312 | 252 | } |
| 313 | −.badge.type { background: var(--ink); color: var(--lime); } | |
| 253 | +.badge.type { background: var(--ink); color: var(--accent); } | |
| 314 | 254 | .badge.right { left: auto; right: 10px; } |
| 315 | 255 | /* badge de contexte de prix — TOUJOURS visible (un prix sans contexte est inutilisable) */ |
| 316 | −.badge.ctx-takeout, .ctx-pill.ctx-takeout { background: var(--lime); color: var(--ink); } | |
| 256 | +.badge.ctx-takeout, .ctx-pill.ctx-takeout { background: var(--accent); color: var(--ink); } | |
| 317 | 257 | .badge.ctx-dine-in, .ctx-pill.ctx-dine-in { background: var(--green); color: #fff; } |
| 318 | 258 | .badge.ctx-delivery, .ctx-pill.ctx-delivery { background: var(--amber); color: var(--ink); } |
| 319 | 259 | .card-body { padding: 15px 17px; display: flex; flex-direction: column; gap: 6px; flex: 1; } |
@@ -327,7 +267,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 327 | 267 | .source-tag { |
| 328 | 268 | font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; |
| 329 | 269 | text-transform: uppercase; letter-spacing: 0.06em; color: var(--green); |
| 330 | − background: var(--lime-soft); border: 1px solid rgba(28, 92, 65, 0.35); | |
| 270 | + background: var(--accent-soft); border: 1px solid rgba(28, 92, 65, 0.35); | |
| 331 | 271 | border-radius: var(--r-ctl); padding: 3px 9px; |
| 332 | 272 | overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60%; |
| 333 | 273 | } |
@@ -345,7 +285,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 345 | 285 | min-height: 46px; |
| 346 | 286 | } |
| 347 | 287 | .mode-toggle button + button { border-left: 1.5px solid var(--ink); } |
| 348 | −.mode-toggle button.on { background: var(--ink); color: var(--lime); } | |
| 288 | +.mode-toggle button.on { background: var(--ink); color: var(--accent); } | |
| 349 | 289 | |
| 350 | 290 | /* ================= Résultats par plat ================= */ |
| 351 | 291 | .dish-list { |
@@ -397,8 +337,8 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 397 | 337 | box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.1); |
| 398 | 338 | transition: all 0.12s ease; |
| 399 | 339 | } |
| 400 | −.pg-btn:hover:not(:disabled) { background: var(--lime-soft); } | |
| 401 | −.pg-btn.on { background: var(--ink); color: var(--lime); } | |
| 340 | +.pg-btn:hover:not(:disabled) { background: var(--accent-soft); } | |
| 341 | +.pg-btn.on { background: var(--ink); color: var(--accent); } | |
| 402 | 342 | .pg-btn:disabled { opacity: 0.35; cursor: default; box-shadow: none; } |
| 403 | 343 | .pg-btn:active:not(:disabled) { transform: translate(2px, 2px); box-shadow: none; } |
| 404 | 344 | .pg-ellipsis { color: var(--ink-3); font-family: var(--font-mono); padding: 0 4px; } |
@@ -442,7 +382,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 442 | 382 | .panel .price { |
| 443 | 383 | font-family: var(--font-display); font-size: 30px; font-weight: 700; |
| 444 | 384 | letter-spacing: -0.03em; color: var(--ink); |
| 445 | − background: var(--lime); border-radius: 8px; padding: 0 10px 2px; | |
| 385 | + background: var(--accent); border-radius: 8px; padding: 0 10px 2px; | |
| 446 | 386 | display: inline-block; transform: rotate(-1deg); |
| 447 | 387 | } |
| 448 | 388 | .panel h1 { font-size: 23px; margin: 10px 0 2px; } |
@@ -455,11 +395,11 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 455 | 395 | .klabel { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-3); font-weight: 700; margin-bottom: 8px; } |
| 456 | 396 | .amenity-row { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 20px; } |
| 457 | 397 | .amenity { |
| 458 | − background: var(--lime-soft); color: var(--green); font-size: 12px; font-weight: 600; | |
| 398 | + background: var(--accent-soft); color: var(--green); font-size: 12px; font-weight: 600; | |
| 459 | 399 | border: 1px solid rgba(28, 92, 65, 0.35); border-radius: var(--r-ctl); padding: 4px 11px; |
| 460 | 400 | } |
| 461 | 401 | .cta { |
| 462 | − display: block; text-align: center; background: var(--ink); color: var(--lime); | |
| 402 | + display: block; text-align: center; background: var(--ink); color: var(--accent); | |
| 463 | 403 | font-weight: 700; font-family: var(--font-display); border: 1.5px solid var(--ink); |
| 464 | 404 | border-radius: var(--r-ctl); padding: 14px; min-height: 48px; |
| 465 | 405 | box-shadow: var(--shadow-off-mid); transition: all 0.14s ease; |
@@ -479,7 +419,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 479 | 419 | font-family: var(--font-display); font-size: 12.5px; font-weight: 700; color: var(--ink-2); |
| 480 | 420 | } |
| 481 | 421 | .view-toggle button + button { border-left: 1.5px solid var(--ink); } |
| 482 | −.view-toggle button.on { background: var(--ink); color: var(--lime); } | |
| 422 | +.view-toggle button.on { background: var(--ink); color: var(--accent); } | |
| 483 | 423 | .ctx-toggle { margin-bottom: 14px; } |
| 484 | 424 | |
| 485 | 425 | .menu-block { display: flex; flex-direction: column; gap: 16px; } |
@@ -490,7 +430,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 490 | 430 | } |
| 491 | 431 | .ctx-banner b { font-family: var(--font-display); font-size: 14px; } |
| 492 | 432 | .ctx-banner span { font-family: var(--font-mono); font-size: 11px; color: var(--ink-2); } |
| 493 | −.ctx-banner.ctx-takeout { background: var(--lime-soft); } | |
| 433 | +.ctx-banner.ctx-takeout { background: var(--accent-soft); } | |
| 494 | 434 | .ctx-banner.ctx-takeout b { color: var(--green); } |
| 495 | 435 | .ctx-banner.ctx-dine-in { background: var(--surface); } |
| 496 | 436 | .ctx-banner.ctx-dine-in b { color: var(--green-deep); } |
@@ -507,7 +447,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 507 | 447 | padding: 14px 18px; text-align: left; |
| 508 | 448 | } |
| 509 | 449 | .menu-section-head h3 { font-size: 17px; color: var(--ink); letter-spacing: -0.02em; flex: 1; } |
| 510 | −.menu-section-head:hover { background: var(--lime-soft); } | |
| 450 | +.menu-section-head:hover { background: var(--accent-soft); } | |
| 511 | 451 | .ms-img { |
| 512 | 452 | width: 36px; height: 36px; object-fit: cover; border-radius: var(--r-ctl); |
| 513 | 453 | border: 1.5px solid var(--ink); flex: none; |
@@ -531,7 +471,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 531 | 471 | font-style: normal; font-family: var(--font-mono); font-size: 9px; font-weight: 700; |
| 532 | 472 | text-transform: uppercase; letter-spacing: 0.06em; border-radius: 4px; |
| 533 | 473 | padding: 2px 7px; margin-left: 7px; |
| 534 | − background: var(--lime-soft); color: var(--green); border: 1px solid rgba(28, 92, 65, 0.3); | |
| 474 | + background: var(--accent-soft); color: var(--green); border: 1px solid rgba(28, 92, 65, 0.3); | |
| 535 | 475 | vertical-align: 2px; |
| 536 | 476 | } |
| 537 | 477 | .mi-tag-epice { background: var(--amber-soft); color: #9a6414; border-color: rgba(232, 163, 61, 0.5); } |
@@ -553,7 +493,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 553 | 493 | text-transform: uppercase; letter-spacing: 0.05em; border-radius: 4px; padding: 2px 7px; |
| 554 | 494 | background: var(--surface); color: var(--ink-3); border: 1px solid var(--line); |
| 555 | 495 | } |
| 556 | −.og-req.on { background: var(--lime); color: var(--ink); border-color: var(--ink); } | |
| 496 | +.og-req.on { background: var(--accent); color: var(--ink); border-color: var(--ink); } | |
| 557 | 497 | .og-choices { display: flex; flex-wrap: wrap; gap: 6px; } |
| 558 | 498 | .og-choice { |
| 559 | 499 | display: inline-flex; align-items: center; gap: 6px; |
@@ -578,7 +518,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 578 | 518 | } |
| 579 | 519 | .src-table td { padding: 13px 18px; border-bottom: 1px solid var(--line); font-size: 14px; vertical-align: top; } |
| 580 | 520 | .src-table tr:last-child td { border-bottom: none; } |
| 581 | −.src-table tr:hover td { background: var(--lime-soft); } | |
| 521 | +.src-table tr:hover td { background: var(--accent-soft); } | |
| 582 | 522 | .src-table a { color: var(--green); font-weight: 600; border-bottom: 1.5px solid rgba(28, 92, 65, 0.4); } |
| 583 | 523 | .src-integrations { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; max-width: 380px; } |
| 584 | 524 | .src-table .pill, .pill.ok, .pill.todo { |
@@ -587,7 +527,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 587 | 527 | text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid var(--ink); |
| 588 | 528 | box-shadow: none; cursor: default; transform: none; |
| 589 | 529 | } |
| 590 | −.pill.ok { background: var(--lime); color: var(--ink); } | |
| 530 | +.pill.ok { background: var(--accent); color: var(--ink); } | |
| 591 | 531 | .pill.todo { background: var(--amber-soft); color: #9a6414; border-color: rgba(232, 163, 61, 0.6); } |
| 592 | 532 | .count-pill { font-weight: 700; font-family: var(--font-display); font-size: 16px; color: var(--ink); } |
| 593 | 533 | |
@@ -603,7 +543,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 603 | 543 | .tile-v { font-family: var(--font-display); font-weight: 700; font-size: clamp(24px, 3vw, 34px); letter-spacing: -0.03em; line-height: 1.05; color: var(--ink); } |
| 604 | 544 | .tile-k { font-family: var(--font-mono); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-3); margin-top: 6px; } |
| 605 | 545 | .hero-tile { background: var(--ink); color: var(--paper); border-color: var(--ink); box-shadow: 6px 6px 0 rgba(20, 24, 20, 0.25); } |
| 606 | −.hero-tile .tile-v { color: var(--lime); } | |
| 546 | +.hero-tile .tile-v { color: var(--accent); } | |
| 607 | 547 | .hero-tile .tile-k { color: rgba(245, 243, 238, 0.6); } |
| 608 | 548 | |
| 609 | 549 | .viz-card { |
@@ -628,7 +568,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 628 | 568 | display: block; height: 100%; background: var(--green); border-radius: 0; |
| 629 | 569 | min-width: 2px; transition: background 0.12s ease; |
| 630 | 570 | } |
| 631 | −.hbar-row:hover .hbar-fill { background: var(--lime); } | |
| 571 | +.hbar-row:hover .hbar-fill { background: var(--accent); } | |
| 632 | 572 | .hbar-value { font-family: var(--font-mono); font-size: 11px; font-weight: 700; white-space: nowrap; color: var(--ink); } |
| 633 | 573 | @media (max-width: 640px) { |
| 634 | 574 | .hbar-row { grid-template-columns: minmax(80px, 120px) 1fr auto; gap: 8px; } |
@@ -689,7 +629,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 689 | 629 | display: flex; align-items: center; gap: 6px; |
| 690 | 630 | position: fixed; left: 50%; transform: translateX(-50%); |
| 691 | 631 | bottom: calc(74px + env(safe-area-inset-bottom)); z-index: 80; |
| 692 | − background: var(--ink); color: var(--lime); border: 1.5px solid var(--ink); | |
| 632 | + background: var(--ink); color: var(--accent); border: 1.5px solid var(--ink); | |
| 693 | 633 | border-radius: var(--r-ctl); padding: 13px 24px; font-family: var(--font-display); |
| 694 | 634 | font-weight: 700; font-size: 15px; cursor: pointer; |
| 695 | 635 | box-shadow: 5px 5px 0 rgba(20, 24, 20, 0.3); |
@@ -728,7 +668,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 728 | 668 | .tabbar a.active { color: var(--green); } |
| 729 | 669 | .tabbar a svg { display: block; } |
| 730 | 670 | body { padding-bottom: calc(64px + env(safe-area-inset-bottom)); } |
| 731 | − .footer { padding-bottom: calc(72px + env(safe-area-inset-bottom)); } | |
| 671 | + .ka-footer { padding-bottom: calc(72px + env(safe-area-inset-bottom)); } | |
| 732 | 672 | } |
| 733 | 673 | |
| 734 | 674 | /* Anti-zoom iOS : les champs doivent faire >= 16px */ |
@@ -736,23 +676,25 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 736 | 676 | .f-search input, .f-ctl select { font-size: 16px; } |
| 737 | 677 | } |
| 738 | 678 | |
| 739 | −/* ================= Footer ================= */ | |
| 740 | −.footer { | |
| 741 | − background: var(--ink); color: rgba(245, 243, 238, 0.72); margin-top: 20px; | |
| 742 | − padding: 44px 0 max(40px, env(safe-area-inset-bottom)); font-size: 13px; | |
| 743 | − border-top: 2px solid var(--ink); | |
| 679 | +/* ================= Footer (KaFooter — ka-ui) ================= */ | |
| 680 | +/* Le footer commun vient de tokens.css (.ka-footer) + KaFooter.tsx. On garde | |
| 681 | + ici seulement la rangée de nav locale posée au-dessus. */ | |
| 682 | +.prefoot { border-top: 2px solid var(--ink); padding: 22px 0; margin-top: 40px; background: var(--surface-2); } | |
| 683 | +.ka-footer { margin-top: 0; } | |
| 684 | +.prefoot-inner { display: flex; flex-wrap: wrap; gap: 10px 22px; align-items: center; } | |
| 685 | +.prefoot-nav { display: flex; flex-wrap: wrap; gap: 4px 8px; } | |
| 686 | +.prefoot-nav a { | |
| 687 | + display: inline-flex; align-items: center; min-height: var(--touch); | |
| 688 | + padding: 8px 12px; border-radius: var(--r-ctl); | |
| 689 | + font-family: var(--font-display); font-weight: 700; font-size: 13.5px; color: var(--ink); | |
| 744 | 690 | } |
| 745 | −.footer .fbrand { font-family: var(--font-display); font-weight: 700; font-size: 30px; color: var(--paper); letter-spacing: -0.04em; margin-bottom: 6px; display: flex; align-items: center; } | |
| 746 | −.footer .fbrand .logo-ico { margin-right: 13px; } | |
| 747 | −.footer .fbrand .ka { | |
| 748 | − background: var(--lime); color: var(--ink); | |
| 749 | − padding: 1px 7px 3px; border-radius: 5px; margin-left: 4px; | |
| 750 | − display: inline-block; transform: rotate(-2deg); | |
| 691 | +.prefoot-nav a:hover { background: var(--accent); color: var(--on-accent); } | |
| 692 | +.prefoot-osm { | |
| 693 | + margin-left: auto; font-family: var(--font-mono); font-size: 10.5px; | |
| 694 | + letter-spacing: 0.03em; color: var(--ink-3); | |
| 751 | 695 | } |
| 752 | −.footer .fbaseline { color: rgba(245, 243, 238, 0.65); font-size: 14px; margin-bottom: 16px; } | |
| 753 | −.footer b { color: var(--paper); } | |
| 754 | −.footer .frow { display: flex; flex-direction: column; gap: 5px; max-width: 720px; } | |
| 755 | −.footer .fmono { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.04em; color: rgba(245, 243, 238, 0.42); margin-top: 18px; } | |
| 696 | +.prefoot-osm a { color: var(--green); border-bottom: 1px solid rgba(28, 92, 65, 0.4); } | |
| 697 | +@media (max-width: 640px) { .prefoot-osm { margin-left: 0; } } | |
| 756 | 698 | |
| 757 | 699 | /* --- Compte membre (« Se connecter avec KA ») ------------------------------- */ |
| 758 | 700 | .login-btn { |
@@ -764,11 +706,11 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 764 | 706 | box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.12); |
| 765 | 707 | transition: background 0.13s ease, transform 0.1s ease, box-shadow 0.1s ease; |
| 766 | 708 | } |
| 767 | −.login-btn:hover { background: var(--lime-soft); } | |
| 709 | +.login-btn:hover { background: var(--accent-soft); } | |
| 768 | 710 | .login-btn:active { transform: translate(2px, 2px); box-shadow: none; } |
| 769 | 711 | .ka-badge { |
| 770 | 712 | display: inline-flex; align-items: center; justify-content: center; |
| 771 | − background: var(--lime); color: var(--ink); border: 1px solid var(--ink); | |
| 713 | + background: var(--accent); color: var(--ink); border: 1px solid var(--ink); | |
| 772 | 714 | padding: 1px 6px 2px; border-radius: 4px; |
| 773 | 715 | font-family: var(--font-display); font-weight: 700; font-size: 11px; |
| 774 | 716 | transform: rotate(-2deg); |
@@ -776,7 +718,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 776 | 718 | .account { position: relative; } |
| 777 | 719 | .account-btn { |
| 778 | 720 | width: 40px; height: 40px; border-radius: 50%; overflow: hidden; |
| 779 | − border: 1.5px solid var(--ink); background: var(--lime); | |
| 721 | + border: 1.5px solid var(--ink); background: var(--accent); | |
| 780 | 722 | cursor: pointer; padding: 0; |
| 781 | 723 | box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.15); |
| 782 | 724 | } |
@@ -805,7 +747,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 805 | 747 | padding: 10px; border-radius: var(--r-ctl); |
| 806 | 748 | font-family: var(--font-display); font-weight: 700; font-size: 13.5px; color: var(--ink); |
| 807 | 749 | } |
| 808 | −.account-link:hover, .account-menu button:hover { background: var(--lime-soft); } | |
| 750 | +.account-link:hover, .account-menu button:hover { background: var(--accent-soft); } | |
| 809 | 751 | |
| 810 | 752 | /* cœur favori sur les cartes */ |
| 811 | 753 | .fav-btn { |
@@ -817,7 +759,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 817 | 759 | transition: transform 0.12s ease, background 0.12s ease; |
| 818 | 760 | } |
| 819 | 761 | .fav-btn:hover { transform: scale(1.08); } |
| 820 | −.fav-btn.on { background: var(--lime); color: var(--ink); } | |
| 762 | +.fav-btn.on { background: var(--accent); color: var(--ink); } | |
| 821 | 763 | |
| 822 | 764 | /* --- Menu déroulant mobile ------------------------------------------------- */ |
| 823 | 765 | .menu-btn { |
@@ -861,7 +803,7 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 861 | 803 | .mm-link.active .mm-arrow { opacity: 1; color: var(--green); } |
| 862 | 804 | .mm-ico { width: 26px; text-align: center; font-size: 17px; } |
| 863 | 805 | .mm-arrow { margin-left: auto; opacity: 0.25; color: var(--green); transition: opacity 0.15s ease; } |
| 864 | −.mm-link:active { background: var(--lime-soft); border-radius: var(--r-ctl); } | |
| 806 | +.mm-link:active { background: var(--accent-soft); border-radius: var(--r-ctl); } | |
| 865 | 807 | .mm-foot { |
| 866 | 808 | padding: 12px 10px 4px; |
| 867 | 809 | font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); letter-spacing: 0.02em; |
@@ -875,3 +817,47 @@ h1 .hl, h2 .hl, .fbaseline .hl { | ||
| 875 | 817 | .nav { display: none; } |
| 876 | 818 | .mobile-menu { display: block; } |
| 877 | 819 | } |
| 820 | + | |
| 821 | +/* ================= Badge « Un service Groupe KA » (ka-ui) ================= */ | |
| 822 | +.hdr-badge { display: none; } | |
| 823 | +@media (min-width: 900px) { .hdr-badge { display: inline-flex; } } | |
| 824 | +.mm-badge { padding: 14px 10px 2px; } | |
| 825 | + | |
| 826 | +/* ================= Page Contact ================= */ | |
| 827 | +.contact-page { padding: 44px 0 90px; } | |
| 828 | +.contact-page h1 { font-size: clamp(30px, 4.6vw, 48px); margin: 12px 0 6px; color: var(--ink); } | |
| 829 | +.contact-page .sub { color: var(--ink-2); max-width: 640px; margin: 0 0 30px; } | |
| 830 | +.contact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; margin-bottom: 26px; } | |
| 831 | +.contact-card { | |
| 832 | + background: var(--surface); border: 1.5px solid var(--ink); border-radius: var(--r-card); | |
| 833 | + box-shadow: var(--shadow-off-soft); padding: 20px; | |
| 834 | + display: flex; flex-direction: column; gap: 6px; | |
| 835 | +} | |
| 836 | +.contact-card .role { | |
| 837 | + font-family: var(--font-mono); font-size: 10px; font-weight: 700; | |
| 838 | + text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-3); | |
| 839 | +} | |
| 840 | +.contact-card a.mail { | |
| 841 | + display: inline-flex; align-items: center; min-height: var(--touch); | |
| 842 | + font-family: var(--font-display); font-weight: 700; font-size: 16.5px; | |
| 843 | + letter-spacing: -0.01em; color: var(--ink); word-break: break-all; | |
| 844 | +} | |
| 845 | +.contact-card a.mail:hover { color: var(--accent-deep); text-decoration: underline; text-underline-offset: 4px; } | |
| 846 | +.contact-note { | |
| 847 | + border: 1.5px solid var(--ink); border-left: 6px solid var(--accent); | |
| 848 | + border-radius: var(--r-card); background: var(--accent-soft); | |
| 849 | + padding: 16px 18px; max-width: 760px; margin-bottom: 26px; font-size: 14px; | |
| 850 | + box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.1); | |
| 851 | +} | |
| 852 | +.contact-hub { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 38px; } | |
| 853 | +.contact-hub .hint { font-size: 12.5px; color: var(--ink-2); max-width: 460px; } | |
| 854 | +.contact-sites { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; } | |
| 855 | +.contact-sites a { | |
| 856 | + display: inline-flex; align-items: center; gap: 7px; min-height: var(--touch); | |
| 857 | + border: 1.5px solid var(--ink); border-radius: var(--r-ctl); background: var(--surface); | |
| 858 | + padding: 8px 14px; font-family: var(--font-display); font-weight: 700; font-size: 13.5px; | |
| 859 | + color: var(--ink); box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.1); | |
| 860 | + transition: all 0.13s ease; | |
| 861 | +} | |
| 862 | +.contact-sites a:hover { background: var(--accent); color: var(--on-accent); transform: translate(-1px, -1px); box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.2); } | |
| 863 | +.contact-sites .dot { width: 9px; height: 9px; border-radius: 50%; border: 1px solid rgba(20, 24, 20, 0.35); flex: none; } | |
modified
restoka/web.py
+1 −1
@@ -409,7 +409,7 @@ if FRONTEND_DIST.exists(): | ||
| 409 | 409 | resp.headers["Cache-Control"] = "no-cache" |
| 410 | 410 | return resp |
| 411 | 411 | |
| 412 | − _CLIENT_PREFIXES = ("resto/", "sources", "stats", "favoris") | |
| 412 | + _CLIENT_PREFIXES = ("resto/", "sources", "stats", "favoris", "contact") | |
| 413 | 413 | |
| 414 | 414 | @app.get("/{full_path:path}") |
| 415 | 415 | def spa(full_path: str): |
| 416 | 416 | |