SPB Git

spb/resto-ka

Public

Resto·Ka — tous les restaurants du Québec, menus complets et prix réels (famille ·Ka)

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>
Simon-Pierre Boucher committed (Aug 18, 2026) parent c9f9e09 Browse files

10 changed files +604 −165

modified frontend/index.html +1 −1
@@ -8,7 +8,7 @@
8 <head> 8 <head>
9 <meta charset="UTF-8" /> 9 <meta charset="UTF-8" />
10 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> 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 <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." /> 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 <meta name="theme-color" content="#ffffff" /> 13 <meta name="theme-color" content="#ffffff" />
14 <meta name="mobile-web-app-capable" content="yes" /> 14 <meta name="mobile-web-app-capable" content="yes" />
modified frontend/src/App.tsx +23 −32
@@ -15,6 +15,9 @@ import {
15 IcoChart, IcoCompass, IcoFolder, IcoPlate, IcoSearch, 15 IcoChart, IcoCompass, IcoFolder, IcoPlate, IcoSearch,
16 } from "./components/Icons"; 16 } from "./components/Icons";
17 import { LogoIcon } from "./components/Logo"; 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 import FavorisPage from "./pages/Favoris"; 21 import FavorisPage from "./pages/Favoris";
19 import Home from "./pages/Home"; 22 import Home from "./pages/Home";
20 import RestoPage from "./pages/Resto"; 23 import RestoPage from "./pages/Resto";
@@ -122,6 +125,7 @@ const NAV_LINKS = [
122 { to: "/", label: "Restos", icon: <IcoPlate size={17} />, end: true }, 125 { to: "/", label: "Restos", icon: <IcoPlate size={17} />, end: true },
123 { to: "/stats", label: "Stats", icon: <IcoChart size={17} />, end: false }, 126 { to: "/stats", label: "Stats", icon: <IcoChart size={17} />, end: false },
124 { to: "/sources", label: "Sources", icon: <IcoFolder size={17} />, end: false }, 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 function Header() { 131 function Header() {
@@ -144,6 +148,7 @@ function Header() {
144 Resto-<span className="ka">Ka</span> 148 Resto-<span className="ka">Ka</span>
145 <span className="brand-tag">Chaque resto, chaque plat, chaque prix.</span> 149 <span className="brand-tag">Chaque resto, chaque plat, chaque prix.</span>
146 </NavLink> 150 </NavLink>
151+ <span className="hdr-badge"><GroupeKaBadge /></span>
147 <nav className="nav" aria-label="Navigation principale"> 152 <nav className="nav" aria-label="Navigation principale">
148 {NAV_LINKS.map((l) => ( 153 {NAV_LINKS.map((l) => (
149 <NavLink key={l.to} to={l.to} end={l.end} 154 <NavLink key={l.to} to={l.to} end={l.end}
@@ -179,6 +184,7 @@ function Header() {
179 <span className="mm-arrow" aria-hidden="true">→</span> 184 <span className="mm-arrow" aria-hidden="true">→</span>
180 </NavLink> 185 </NavLink>
181 ))} 186 ))}
187+ <div className="mm-badge"><GroupeKaBadge /></div>
182 <div className="mm-foot"> 188 <div className="mm-foot">
183 Agrégateur indépendant — menus et prix captés aux sources, chaque 189 Agrégateur indépendant — menus et prix captés aux sources, chaque
184 fiche renvoie au resto original et affiche le contexte du prix. 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 function Footer() { 201 function Footer() {
195 return ( 202 return (
196− <footer className="footer"> 203+ <>
197− <div className="container"> 204+ <div className="prefoot">
198− <div className="fbrand"> 205+ <div className="container prefoot-inner">
199− <LogoIcon size={32} dark /> 206+ <nav className="prefoot-nav" aria-label="Navigation du pied de page">
200− Resto-<span className="ka">Ka</span> 207+ {NAV_LINKS.map((l) => (
201− </div> 208+ <NavLink key={l.to} to={l.to} end={l.end}>{l.label}</NavLink>
202− <div className="fbaseline"> 209+ ))}
203− Chaque resto, chaque plat, <span className="hl">chaque prix.</span> 210+ <NavLink to="/favoris">Favoris</NavLink>
204− </div> 211+ </nav>
205− <div className="frow"> 212+ <div className="prefoot-osm">
206− <div> 213+ Données de découverte ©{" "}
207− <b>Agrégateur indépendant</b> des restaurants du Québec — menus 214+ <a href="https://www.openstreetmap.org/copyright" target="_blank"
208− complets et prix réels dans les 17 régions. Les menus proviennent 215+ rel="noopener noreferrer">contributeurs OpenStreetMap</a> (ODbL)
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>
220 </div> 216 </div>
221 </div> 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 </div> 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 <Route path="/favoris" element={<FavorisPage />} /> 252 <Route path="/favoris" element={<FavorisPage />} />
263 <Route path="/stats" element={<StatsPage />} /> 253 <Route path="/stats" element={<StatsPage />} />
264 <Route path="/sources" element={<SourcesPage />} /> 254 <Route path="/sources" element={<SourcesPage />} />
255+ <Route path="/contact" element={<ContactPage />} />
265 <Route 256 <Route
266 path="*" 257 path="*"
267 element={ 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 import ReactDOM from "react-dom/client"; 7 import ReactDOM from "react-dom/client";
8 import { BrowserRouter } from "react-router-dom"; 8 import { BrowserRouter } from "react-router-dom";
9 import App from "./App"; 9 import App from "./App";
10+import "./ka/tokens.css"; // socle ka-ui (importé AVANT styles.css)
10 import "./styles.css"; 11 import "./styles.css";
11 12
12 ReactDOM.createRoot(document.getElementById("root")!).render( 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 Author: Simon-Pierre Boucher <contact@spboucher.ai> 2 Author: Simon-Pierre Boucher <contact@spboucher.ai>
3 File: styles.css 3 File: styles.css
4 Desc: Identité visuelle Resto·Ka — système de design « éditorial sharp » 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, 5+ du GROUPE KA. Le socle (palette fixe, typo, grain, boutons, cartes…)
6− ValoPlex) : 6+ vient de ka-ui (src/ka/tokens.css, importé AVANT ce fichier) ; ici on
7− · Typo display Space Grotesk / texte Inter / micro-étiquettes JetBrains Mono 7+ ne définit que l'ACCENT SAFRAN de Resto-Ka + les classes métier.
8− · Palette : papier #f5f3ee · encre #141814 · vert profond #1c5c41 · 8+ · Accent Resto-Ka : safran #f08c00 (soft #fdeed7 · deep #b96a00)
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
12 · Baseline : « Chaque resto, chaque plat, chaque prix. » 9 · Baseline : « Chaque resto, chaque plat, chaque prix. »
13 · 100 % adaptatif mobile (safe-areas iOS) 10 · 100 % adaptatif mobile (safe-areas iOS)
14 ----------------------------------------------------------------------------- */ 11 ----------------------------------------------------------------------------- */
15 :root { 12 :root {
16− /* palette officielle Groupe KA (éditorial sharp) */ 13+ /* ACCENT du site — seule surcharge de tokens.css (ka-ui) */
17− --paper: #f5f3ee; 14+ --accent: #f08c00;
18− --surface: #ffffff; 15+ --accent-soft: #fdeed7;
19− --surface-2: #faf9f5; 16+ --accent-deep: #b96a00;
20− --ink: #141814; 17+ --on-accent: #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; }
76 18
77−/* surligné lime incliné — la marque des titres du groupe */ 19+ /* alias rétro-compatibles (le code historique utilise --lime) */
78−h1 .hl, h2 .hl, .fbaseline .hl { 20+ --lime: var(--accent);
79− display: inline-block; background: var(--lime); color: var(--ink); 21+ --lime-soft: var(--accent-soft);
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);
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 .container { max-width: 1240px; margin: 0 auto; padding: 0 24px; } 29 .container { max-width: 1240px; margin: 0 auto; padding: 0 24px; }
90 @media (max-width: 640px) { .container { padding: 0 16px; } } 30 @media (max-width: 640px) { .container { padding: 0 16px; } }
@@ -105,7 +45,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
105 .brand .logo-ico { margin-right: 11px; flex: none; } 45 .brand .logo-ico { margin-right: 11px; flex: none; }
106 /* « Ka » en pastille lime inclinée — même geste que la carte membre Groupe KA */ 46 /* « Ka » en pastille lime inclinée — même geste que la carte membre Groupe KA */
107 .brand .ka { 47 .brand .ka {
108− background: var(--lime); color: var(--ink); 48+ background: var(--accent); color: var(--ink);
109 padding: 1px 6px 3px; border-radius: 5px; margin-left: 3px; 49 padding: 1px 6px 3px; border-radius: 5px; margin-left: 3px;
110 display: inline-block; transform: rotate(-2deg); 50 display: inline-block; transform: rotate(-2deg);
111 } 51 }
@@ -122,8 +62,8 @@ h1 .hl, h2 .hl, .fbaseline .hl {
122 color: var(--ink); transition: all 0.13s ease; min-height: 38px; 62 color: var(--ink); transition: all 0.13s ease; min-height: 38px;
123 display: inline-flex; align-items: center; 63 display: inline-flex; align-items: center;
124 } 64 }
125−.nav a:hover { background: var(--lime); border-color: var(--ink); } 65+.nav a:hover { background: var(--accent); border-color: var(--ink); }
126−.nav a.active { background: var(--ink); color: var(--lime); border-color: var(--ink); } 66+.nav a.active { background: var(--ink); color: var(--accent); border-color: var(--ink); }
127 67
128 /* ================= Ticker ================= */ 68 /* ================= Ticker ================= */
129 .ticker { 69 .ticker {
@@ -134,7 +74,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
134 } 74 }
135 .ticker-track { display: inline-flex; gap: 0; animation: ticker 40s linear infinite; will-change: transform; } 75 .ticker-track { display: inline-flex; gap: 0; animation: ticker 40s linear infinite; will-change: transform; }
136 .ticker span { padding: 0 26px; position: relative; } 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 @keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } } 78 @keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
139 @media (prefers-reduced-motion: reduce) { 79 @media (prefers-reduced-motion: reduce) {
140 .ticker-track { animation: none; } 80 .ticker-track { animation: none; }
@@ -159,7 +99,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
159 .stat-chip b { color: var(--ink); font-weight: 700; font-family: var(--font-display); } 99 .stat-chip b { color: var(--ink); font-weight: 700; font-family: var(--font-display); }
160 .stat-chip .pulse { 100 .stat-chip .pulse {
161 width: 8px; height: 8px; border-radius: 50%; background: var(--green); 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 @keyframes pulse { 50% { box-shadow: 0 0 0 7px rgba(28, 92, 65, 0.18); } } 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 .benefit-ico { 112 .benefit-ico {
173 width: 42px; height: 42px; border: 1.5px solid var(--ink); border-radius: var(--r-ctl); 113 width: 42px; height: 42px; border: 1.5px solid var(--ink); border-radius: var(--r-ctl);
174 flex: none; display: flex; align-items: center; justify-content: center; 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 .benefit-ico.blue { background: var(--surface); color: var(--green); } 117 .benefit-ico.blue { background: var(--surface); color: var(--green); }
178 .benefit-label { 118 .benefit-label {
@@ -234,12 +174,12 @@ h1 .hl, h2 .hl, .fbaseline .hl {
234 .pills { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 2px; } 174 .pills { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 2px; }
235 .pill { 175 .pill {
236 display: inline-flex; align-items: center; gap: 7px; 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 border-radius: var(--r-ctl); padding: 5px 12px; font-size: 12.5px; font-weight: 700; 178 border-radius: var(--r-ctl); padding: 5px 12px; font-size: 12.5px; font-weight: 700;
239 font-family: var(--font-display); cursor: pointer; 179 font-family: var(--font-display); cursor: pointer;
240 box-shadow: 2px 2px 0 rgba(20, 24, 20, 0.2); transition: all 0.12s ease; 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 .pill-x { font-size: 10px; opacity: 0.65; } 183 .pill-x { font-size: 10px; opacity: 0.65; }
244 .pill-clear { background: var(--surface); border-color: var(--danger); color: var(--danger); } 184 .pill-clear { background: var(--surface); border-color: var(--danger); color: var(--danger); }
245 .pill-clear:hover { background: var(--danger); color: #fff; } 185 .pill-clear:hover { background: var(--danger); color: #fff; }
@@ -255,10 +195,10 @@ h1 .hl, h2 .hl, .fbaseline .hl {
255 transition: background 0.15s ease, transform 0.1s ease, box-shadow 0.1s ease; 195 transition: background 0.15s ease, transform 0.1s ease, box-shadow 0.1s ease;
256 } 196 }
257 .btn:active { transform: translate(2px, 2px); box-shadow: none !important; } 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 .btn-primary:hover { background: var(--green-deep); } 199 .btn-primary:hover { background: var(--green-deep); }
260 .btn-ghost { background: transparent; color: var(--ink); } 200 .btn-ghost { background: transparent; color: var(--ink); }
261−.btn-ghost:hover { background: var(--lime); } 201+.btn-ghost:hover { background: var(--accent); }
262 .btn:disabled { opacity: 0.4; cursor: default; } 202 .btn:disabled { opacity: 0.4; cursor: default; }
263 203
264 /* ================= Chips ================= */ 204 /* ================= Chips ================= */
@@ -270,8 +210,8 @@ h1 .hl, h2 .hl, .fbaseline .hl {
270 cursor: pointer; font-family: var(--font-display); white-space: nowrap; min-height: 40px; 210 cursor: pointer; font-family: var(--font-display); white-space: nowrap; min-height: 40px;
271 box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.1); transition: all 0.13s ease; 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); } 213+.chip:hover { background: var(--accent-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); } 214+.chip.on { background: var(--ink); color: var(--accent); box-shadow: 4px 4px 0 rgba(20, 24, 20, 0.3); }
275 215
276 /* ================= Results ================= */ 216 /* ================= Results ================= */
277 .results-head { display: flex; align-items: baseline; gap: 14px; margin: 28px 0 18px; } 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 display: flex; align-items: center; justify-content: center; height: 100%; 237 display: flex; align-items: center; justify-content: center; height: 100%;
298 color: var(--ink-3); 238 color: var(--ink-3);
299 background: 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 radial-gradient(circle at 12% 85%, var(--amber-soft) 0, transparent 42%), 241 radial-gradient(circle at 12% 85%, var(--amber-soft) 0, transparent 42%),
302 var(--surface-2); 242 var(--surface-2);
303 } 243 }
@@ -310,10 +250,10 @@ h1 .hl, h2 .hl, .fbaseline .hl {
310 background: rgba(255, 255, 255, 0.95); color: var(--ink); 250 background: rgba(255, 255, 255, 0.95); color: var(--ink);
311 box-shadow: 2px 2px 0 rgba(20, 24, 20, 0.25); 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 .badge.right { left: auto; right: 10px; } 254 .badge.right { left: auto; right: 10px; }
315 /* badge de contexte de prix — TOUJOURS visible (un prix sans contexte est inutilisable) */ 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 .badge.ctx-dine-in, .ctx-pill.ctx-dine-in { background: var(--green); color: #fff; } 257 .badge.ctx-dine-in, .ctx-pill.ctx-dine-in { background: var(--green); color: #fff; }
318 .badge.ctx-delivery, .ctx-pill.ctx-delivery { background: var(--amber); color: var(--ink); } 258 .badge.ctx-delivery, .ctx-pill.ctx-delivery { background: var(--amber); color: var(--ink); }
319 .card-body { padding: 15px 17px; display: flex; flex-direction: column; gap: 6px; flex: 1; } 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 .source-tag { 267 .source-tag {
328 font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; 268 font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
329 text-transform: uppercase; letter-spacing: 0.06em; color: var(--green); 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 border-radius: var(--r-ctl); padding: 3px 9px; 271 border-radius: var(--r-ctl); padding: 3px 9px;
332 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60%; 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 min-height: 46px; 285 min-height: 46px;
346 } 286 }
347 .mode-toggle button + button { border-left: 1.5px solid var(--ink); } 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 /* ================= Résultats par plat ================= */ 290 /* ================= Résultats par plat ================= */
351 .dish-list { 291 .dish-list {
@@ -397,8 +337,8 @@ h1 .hl, h2 .hl, .fbaseline .hl {
397 box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.1); 337 box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.1);
398 transition: all 0.12s ease; 338 transition: all 0.12s ease;
399 } 339 }
400−.pg-btn:hover:not(:disabled) { background: var(--lime-soft); } 340+.pg-btn:hover:not(:disabled) { background: var(--accent-soft); }
401−.pg-btn.on { background: var(--ink); color: var(--lime); } 341+.pg-btn.on { background: var(--ink); color: var(--accent); }
402 .pg-btn:disabled { opacity: 0.35; cursor: default; box-shadow: none; } 342 .pg-btn:disabled { opacity: 0.35; cursor: default; box-shadow: none; }
403 .pg-btn:active:not(:disabled) { transform: translate(2px, 2px); box-shadow: none; } 343 .pg-btn:active:not(:disabled) { transform: translate(2px, 2px); box-shadow: none; }
404 .pg-ellipsis { color: var(--ink-3); font-family: var(--font-mono); padding: 0 4px; } 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 .panel .price { 382 .panel .price {
443 font-family: var(--font-display); font-size: 30px; font-weight: 700; 383 font-family: var(--font-display); font-size: 30px; font-weight: 700;
444 letter-spacing: -0.03em; color: var(--ink); 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 display: inline-block; transform: rotate(-1deg); 386 display: inline-block; transform: rotate(-1deg);
447 } 387 }
448 .panel h1 { font-size: 23px; margin: 10px 0 2px; } 388 .panel h1 { font-size: 23px; margin: 10px 0 2px; }
@@ -455,11 +395,11 @@ h1 .hl, h2 .hl, .fbaseline .hl {
455 .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; } 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 .amenity-row { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 20px; } 396 .amenity-row { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 20px; }
457 .amenity { 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 border: 1px solid rgba(28, 92, 65, 0.35); border-radius: var(--r-ctl); padding: 4px 11px; 399 border: 1px solid rgba(28, 92, 65, 0.35); border-radius: var(--r-ctl); padding: 4px 11px;
460 } 400 }
461 .cta { 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 font-weight: 700; font-family: var(--font-display); border: 1.5px solid var(--ink); 403 font-weight: 700; font-family: var(--font-display); border: 1.5px solid var(--ink);
464 border-radius: var(--r-ctl); padding: 14px; min-height: 48px; 404 border-radius: var(--r-ctl); padding: 14px; min-height: 48px;
465 box-shadow: var(--shadow-off-mid); transition: all 0.14s ease; 405 box-shadow: var(--shadow-off-mid); transition: all 0.14s ease;
@@ -479,7 +419,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
479 font-family: var(--font-display); font-size: 12.5px; font-weight: 700; color: var(--ink-2); 419 font-family: var(--font-display); font-size: 12.5px; font-weight: 700; color: var(--ink-2);
480 } 420 }
481 .view-toggle button + button { border-left: 1.5px solid var(--ink); } 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 .ctx-toggle { margin-bottom: 14px; } 423 .ctx-toggle { margin-bottom: 14px; }
484 424
485 .menu-block { display: flex; flex-direction: column; gap: 16px; } 425 .menu-block { display: flex; flex-direction: column; gap: 16px; }
@@ -490,7 +430,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
490 } 430 }
491 .ctx-banner b { font-family: var(--font-display); font-size: 14px; } 431 .ctx-banner b { font-family: var(--font-display); font-size: 14px; }
492 .ctx-banner span { font-family: var(--font-mono); font-size: 11px; color: var(--ink-2); } 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 .ctx-banner.ctx-takeout b { color: var(--green); } 434 .ctx-banner.ctx-takeout b { color: var(--green); }
495 .ctx-banner.ctx-dine-in { background: var(--surface); } 435 .ctx-banner.ctx-dine-in { background: var(--surface); }
496 .ctx-banner.ctx-dine-in b { color: var(--green-deep); } 436 .ctx-banner.ctx-dine-in b { color: var(--green-deep); }
@@ -507,7 +447,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
507 padding: 14px 18px; text-align: left; 447 padding: 14px 18px; text-align: left;
508 } 448 }
509 .menu-section-head h3 { font-size: 17px; color: var(--ink); letter-spacing: -0.02em; flex: 1; } 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 .ms-img { 451 .ms-img {
512 width: 36px; height: 36px; object-fit: cover; border-radius: var(--r-ctl); 452 width: 36px; height: 36px; object-fit: cover; border-radius: var(--r-ctl);
513 border: 1.5px solid var(--ink); flex: none; 453 border: 1.5px solid var(--ink); flex: none;
@@ -531,7 +471,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
531 font-style: normal; font-family: var(--font-mono); font-size: 9px; font-weight: 700; 471 font-style: normal; font-family: var(--font-mono); font-size: 9px; font-weight: 700;
532 text-transform: uppercase; letter-spacing: 0.06em; border-radius: 4px; 472 text-transform: uppercase; letter-spacing: 0.06em; border-radius: 4px;
533 padding: 2px 7px; margin-left: 7px; 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 vertical-align: 2px; 475 vertical-align: 2px;
536 } 476 }
537 .mi-tag-epice { background: var(--amber-soft); color: #9a6414; border-color: rgba(232, 163, 61, 0.5); } 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 text-transform: uppercase; letter-spacing: 0.05em; border-radius: 4px; padding: 2px 7px; 493 text-transform: uppercase; letter-spacing: 0.05em; border-radius: 4px; padding: 2px 7px;
554 background: var(--surface); color: var(--ink-3); border: 1px solid var(--line); 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 .og-choices { display: flex; flex-wrap: wrap; gap: 6px; } 497 .og-choices { display: flex; flex-wrap: wrap; gap: 6px; }
558 .og-choice { 498 .og-choice {
559 display: inline-flex; align-items: center; gap: 6px; 499 display: inline-flex; align-items: center; gap: 6px;
@@ -578,7 +518,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
578 } 518 }
579 .src-table td { padding: 13px 18px; border-bottom: 1px solid var(--line); font-size: 14px; vertical-align: top; } 519 .src-table td { padding: 13px 18px; border-bottom: 1px solid var(--line); font-size: 14px; vertical-align: top; }
580 .src-table tr:last-child td { border-bottom: none; } 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 .src-table a { color: var(--green); font-weight: 600; border-bottom: 1.5px solid rgba(28, 92, 65, 0.4); } 522 .src-table a { color: var(--green); font-weight: 600; border-bottom: 1.5px solid rgba(28, 92, 65, 0.4); }
583 .src-integrations { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; max-width: 380px; } 523 .src-integrations { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; max-width: 380px; }
584 .src-table .pill, .pill.ok, .pill.todo { 524 .src-table .pill, .pill.ok, .pill.todo {
@@ -587,7 +527,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
587 text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid var(--ink); 527 text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid var(--ink);
588 box-shadow: none; cursor: default; transform: none; 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 .pill.todo { background: var(--amber-soft); color: #9a6414; border-color: rgba(232, 163, 61, 0.6); } 531 .pill.todo { background: var(--amber-soft); color: #9a6414; border-color: rgba(232, 163, 61, 0.6); }
592 .count-pill { font-weight: 700; font-family: var(--font-display); font-size: 16px; color: var(--ink); } 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 .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); } 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 .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; } 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 .hero-tile { background: var(--ink); color: var(--paper); border-color: var(--ink); box-shadow: 6px 6px 0 rgba(20, 24, 20, 0.25); } 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 .hero-tile .tile-k { color: rgba(245, 243, 238, 0.6); } 547 .hero-tile .tile-k { color: rgba(245, 243, 238, 0.6); }
608 548
609 .viz-card { 549 .viz-card {
@@ -628,7 +568,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
628 display: block; height: 100%; background: var(--green); border-radius: 0; 568 display: block; height: 100%; background: var(--green); border-radius: 0;
629 min-width: 2px; transition: background 0.12s ease; 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 .hbar-value { font-family: var(--font-mono); font-size: 11px; font-weight: 700; white-space: nowrap; color: var(--ink); } 572 .hbar-value { font-family: var(--font-mono); font-size: 11px; font-weight: 700; white-space: nowrap; color: var(--ink); }
633 @media (max-width: 640px) { 573 @media (max-width: 640px) {
634 .hbar-row { grid-template-columns: minmax(80px, 120px) 1fr auto; gap: 8px; } 574 .hbar-row { grid-template-columns: minmax(80px, 120px) 1fr auto; gap: 8px; }
@@ -689,7 +629,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
689 display: flex; align-items: center; gap: 6px; 629 display: flex; align-items: center; gap: 6px;
690 position: fixed; left: 50%; transform: translateX(-50%); 630 position: fixed; left: 50%; transform: translateX(-50%);
691 bottom: calc(74px + env(safe-area-inset-bottom)); z-index: 80; 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 border-radius: var(--r-ctl); padding: 13px 24px; font-family: var(--font-display); 633 border-radius: var(--r-ctl); padding: 13px 24px; font-family: var(--font-display);
694 font-weight: 700; font-size: 15px; cursor: pointer; 634 font-weight: 700; font-size: 15px; cursor: pointer;
695 box-shadow: 5px 5px 0 rgba(20, 24, 20, 0.3); 635 box-shadow: 5px 5px 0 rgba(20, 24, 20, 0.3);
@@ -728,7 +668,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
728 .tabbar a.active { color: var(--green); } 668 .tabbar a.active { color: var(--green); }
729 .tabbar a svg { display: block; } 669 .tabbar a svg { display: block; }
730 body { padding-bottom: calc(64px + env(safe-area-inset-bottom)); } 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 /* Anti-zoom iOS : les champs doivent faire >= 16px */ 674 /* Anti-zoom iOS : les champs doivent faire >= 16px */
@@ -736,23 +676,25 @@ h1 .hl, h2 .hl, .fbaseline .hl {
736 .f-search input, .f-ctl select { font-size: 16px; } 676 .f-search input, .f-ctl select { font-size: 16px; }
737 } 677 }
738 678
739−/* ================= Footer ================= */ 679+/* ================= Footer (KaFooter — ka-ui) ================= */
740−.footer { 680+/* Le footer commun vient de tokens.css (.ka-footer) + KaFooter.tsx. On garde
741− background: var(--ink); color: rgba(245, 243, 238, 0.72); margin-top: 20px; 681+ ici seulement la rangée de nav locale posée au-dessus. */
742− padding: 44px 0 max(40px, env(safe-area-inset-bottom)); font-size: 13px; 682+.prefoot { border-top: 2px solid var(--ink); padding: 22px 0; margin-top: 40px; background: var(--surface-2); }
743− border-top: 2px solid var(--ink); 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; } 691+.prefoot-nav a:hover { background: var(--accent); color: var(--on-accent); }
746−.footer .fbrand .logo-ico { margin-right: 13px; } 692+.prefoot-osm {
747−.footer .fbrand .ka { 693+ margin-left: auto; font-family: var(--font-mono); font-size: 10.5px;
748− background: var(--lime); color: var(--ink); 694+ letter-spacing: 0.03em; color: var(--ink-3);
749− padding: 1px 7px 3px; border-radius: 5px; margin-left: 4px;
750− display: inline-block; transform: rotate(-2deg);
751 } 695 }
752−.footer .fbaseline { color: rgba(245, 243, 238, 0.65); font-size: 14px; margin-bottom: 16px; } 696+.prefoot-osm a { color: var(--green); border-bottom: 1px solid rgba(28, 92, 65, 0.4); }
753−.footer b { color: var(--paper); } 697+@media (max-width: 640px) { .prefoot-osm { margin-left: 0; } }
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; }
756 698
757 /* --- Compte membre (« Se connecter avec KA ») ------------------------------- */ 699 /* --- Compte membre (« Se connecter avec KA ») ------------------------------- */
758 .login-btn { 700 .login-btn {
@@ -764,11 +706,11 @@ h1 .hl, h2 .hl, .fbaseline .hl {
764 box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.12); 706 box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.12);
765 transition: background 0.13s ease, transform 0.1s ease, box-shadow 0.1s ease; 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 .login-btn:active { transform: translate(2px, 2px); box-shadow: none; } 710 .login-btn:active { transform: translate(2px, 2px); box-shadow: none; }
769 .ka-badge { 711 .ka-badge {
770 display: inline-flex; align-items: center; justify-content: center; 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 padding: 1px 6px 2px; border-radius: 4px; 714 padding: 1px 6px 2px; border-radius: 4px;
773 font-family: var(--font-display); font-weight: 700; font-size: 11px; 715 font-family: var(--font-display); font-weight: 700; font-size: 11px;
774 transform: rotate(-2deg); 716 transform: rotate(-2deg);
@@ -776,7 +718,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
776 .account { position: relative; } 718 .account { position: relative; }
777 .account-btn { 719 .account-btn {
778 width: 40px; height: 40px; border-radius: 50%; overflow: hidden; 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 cursor: pointer; padding: 0; 722 cursor: pointer; padding: 0;
781 box-shadow: 3px 3px 0 rgba(20, 24, 20, 0.15); 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 padding: 10px; border-radius: var(--r-ctl); 747 padding: 10px; border-radius: var(--r-ctl);
806 font-family: var(--font-display); font-weight: 700; font-size: 13.5px; color: var(--ink); 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 /* cœur favori sur les cartes */ 752 /* cœur favori sur les cartes */
811 .fav-btn { 753 .fav-btn {
@@ -817,7 +759,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
817 transition: transform 0.12s ease, background 0.12s ease; 759 transition: transform 0.12s ease, background 0.12s ease;
818 } 760 }
819 .fav-btn:hover { transform: scale(1.08); } 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 /* --- Menu déroulant mobile ------------------------------------------------- */ 764 /* --- Menu déroulant mobile ------------------------------------------------- */
823 .menu-btn { 765 .menu-btn {
@@ -861,7 +803,7 @@ h1 .hl, h2 .hl, .fbaseline .hl {
861 .mm-link.active .mm-arrow { opacity: 1; color: var(--green); } 803 .mm-link.active .mm-arrow { opacity: 1; color: var(--green); }
862 .mm-ico { width: 26px; text-align: center; font-size: 17px; } 804 .mm-ico { width: 26px; text-align: center; font-size: 17px; }
863 .mm-arrow { margin-left: auto; opacity: 0.25; color: var(--green); transition: opacity 0.15s ease; } 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 .mm-foot { 807 .mm-foot {
866 padding: 12px 10px 4px; 808 padding: 12px 10px 4px;
867 font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); letter-spacing: 0.02em; 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 .nav { display: none; } 817 .nav { display: none; }
876 .mobile-menu { display: block; } 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 resp.headers["Cache-Control"] = "no-cache" 409 resp.headers["Cache-Control"] = "no-cache"
410 return resp 410 return resp
411 411
412− _CLIENT_PREFIXES = ("resto/", "sources", "stats", "favoris") 412+ _CLIENT_PREFIXES = ("resto/", "sources", "stats", "favoris", "contact")
413 413
414 @app.get("/{full_path:path}") 414 @app.get("/{full_path:path}")
415 def spa(full_path: str): 415 def spa(full_path: str):
416 416
Menu

Theme