Harmonisation ka-ui — accent framboise, badge Groupe KA, KaFooter, page /contact
- tokens.css (ka-ui vendoré, src/ka/) chargé en premier ; le CSS local ne garde que le bloc accent framboise (--accent #d6336c + alias --lime) et le CSS métier — socle dédupliqué, couleurs en dur pointées vers les tokens - Header : badge « Un service Groupe KA » (→ groupe-ka.com) à côté du logo, lien Contact, empilement propre sous 760 px (badge visible sur mobile) - Footer : remplacé par le footer commun KaFooter (siteId sorti-ka) — 3 courriels + rôles, avertissement agrégateur, 15 sites, liens légaux du hub + attribution des données conservée - Page /contact : vue SPA (contenu ecosystem.json) + route SSR seo.py (title/canonical/og + contenu robots) + entrée sitemap-pages.xml - KA ID : lien « Créer un compte » → groupe-ka.com/connexion à côté du bouton « Se connecter avec KA » - SEO : title « Sorti-Ka — Un service Groupe KA » - Responsive/tactile : overflow-x clip (tokens), nav/chips/liens ≥ 44 px, chips 44 px sur pointeur tactile Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 changed files +603 −82
modified
frontend/index.html
+183 −81
@@ -3,9 +3,12 @@ | ||
| 3 | 3 | File: index.html |
| 4 | 4 | Desc: Frontend Sorti-Ka — recherche + fiches événement (SPA sans build, |
| 5 | 5 | routeur History API, consomme /api). Design system Groupe KA |
| 6 | − « éditorial sharp » : papier #f5f3ee · encre #141814 · vert #1c5c41 · | |
| 7 | − lime #d9f26b · Space Grotesk / Inter / JetBrains Mono · bordures | |
| 8 | − encre + ombres décalées + grain de film. SSR : sortika/seo.py | |
| 6 | + « éditorial sharp » via ka-ui (src/ka/tokens.css, chargé en premier) : | |
| 7 | + papier #f5f3ee · encre #141814 · vert #1c5c41 · ACCENT FRAMBOISE | |
| 8 | + #d6336c · Space Grotesk / Inter / JetBrains Mono · bordures encre + | |
| 9 | + ombres décalées + grain de film. Footer commun KaFooter et page | |
| 10 | + /contact : contenu issu de src/ka/ecosystem.json (source de vérité — | |
| 11 | + resynchroniser le HTML si le JSON change). SSR : sortika/seo.py | |
| 9 | 12 | injecte <head> et contenu dans les placeholders SSR_HEAD/SSR_BODY. |
| 10 | 13 | --> |
| 11 | 14 | <!DOCTYPE html> |
@@ -13,54 +16,26 @@ | ||
| 13 | 16 | <head> |
| 14 | 17 | <meta charset="utf-8"> |
| 15 | 18 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 16 | −<title>Sorti·Ka — Toutes les sorties et tous les événements du Québec</title> | |
| 19 | +<title>Sorti-Ka — Un service Groupe KA</title> | |
| 17 | 20 | <meta name="description" content="Agrégateur indépendant de sorties et d'événements — concerts, festivals, spectacles, expositions, famille — dans les 17 régions du Québec, avec dates, lieux et gratuité."> |
| 18 | 21 | <!--SSR_HEAD--> |
| 19 | 22 | <link rel="preconnect" href="https://fonts.googleapis.com"> |
| 20 | 23 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| 21 | 24 | <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet"> |
| 25 | +<link rel="stylesheet" href="/src/ka/tokens.css"> | |
| 22 | 26 | <style> |
| 27 | +/* ka-ui/tokens.css (chargé ci-dessus) fournit palette, socle et composants | |
| 28 | + communs ; ici : SEULEMENT l'accent framboise de Sorti·Ka + le CSS métier. */ | |
| 23 | 29 | :root{ |
| 24 | − /* système de design Groupe KA « éditorial sharp » */ | |
| 25 | − --paper:#f5f3ee; --surface:#fff; --surface-2:#faf9f5; | |
| 26 | − --ink:#141814; --ink-2:#4d5551; --ink-3:#8b928c; | |
| 27 | − --line:rgba(20,24,20,.14); --line-strong:rgba(20,24,20,.85); | |
| 28 | − --green:#1c5c41; --green-deep:#123f2e; | |
| 29 | − --lime:#d9f26b; --lime-soft:#f0f9d2; | |
| 30 | − --amber:#e8a33d; --amber-soft:#fdf3e2; | |
| 31 | − --danger:#b3423a; --danger-soft:#fbe9e7; | |
| 32 | − --r-card:10px; --r-ctl:6px; | |
| 33 | − --shadow-flat:0 1px 2px rgba(20,24,20,.05); | |
| 34 | − --shadow-off:6px 6px 0 var(--ink); | |
| 35 | − --shadow-off-soft:8px 8px 0 rgba(20,24,20,.08); | |
| 36 | − --font-display:"Space Grotesk",system-ui,sans-serif; | |
| 37 | − --font-body:"Inter",system-ui,sans-serif; | |
| 38 | − --font-mono:"JetBrains Mono",ui-monospace,monospace; | |
| 30 | + --accent:#d6336c; --accent-soft:#fbe0eb; --accent-deep:#a12551; --on-accent:#ffffff; | |
| 31 | + /* alias rétro-compatibles — tout le CSS local en --lime suit l'accent */ | |
| 32 | + --lime:var(--accent); --lime-soft:var(--accent-soft); | |
| 39 | 33 | } |
| 40 | −*{box-sizing:border-box} | |
| 41 | −html{scroll-behavior:smooth} | |
| 42 | −body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--font-body); | |
| 43 | − font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased} | |
| 44 | −body::before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.35;z-index:9999; | |
| 45 | − 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")} | |
| 46 | −body>*{position:relative} | |
| 47 | −h1,h2,h3,h4{font-family:var(--font-display);letter-spacing:-.03em;margin:0} | |
| 48 | −a{color:inherit;text-decoration:none} | |
| 34 | +h1,h2,h3,h4{margin:0} | |
| 35 | +a{text-decoration:none} | |
| 49 | 36 | button{font-family:inherit} |
| 50 | −img{display:block} | |
| 51 | −::selection{background:var(--lime);color:var(--ink)} | |
| 52 | −:focus-visible{outline:2px solid var(--green);outline-offset:2px} | |
| 53 | 37 | .container{max-width:1240px;margin:0 auto;padding:0 24px} |
| 54 | 38 | @media(max-width:640px){.container{padding:0 16px}} |
| 55 | − | |
| 56 | −/* ---------- micro-typo signature ---------- */ | |
| 57 | −.kicker{display:inline-flex;align-items:center;gap:10px;font-family:var(--font-mono); | |
| 58 | − font-size:11.5px;font-weight:500;text-transform:uppercase;letter-spacing:.12em;color:var(--green)} | |
| 59 | −.kicker::before{content:"";width:22px;height:2px;background:var(--green)} | |
| 60 | −.klabel{font-family:var(--font-mono);font-size:10px;font-weight:700; | |
| 61 | − text-transform:uppercase;letter-spacing:.1em;color:var(--ink-3)} | |
| 62 | −.hl{display:inline-block;background:var(--lime);border-radius:8px;padding:0 10px 2px;transform:rotate(-1deg)} | |
| 63 | − | |
| 64 | 39 | /* ---------- cartes & boutons signature ---------- */ |
| 65 | 40 | .gk-card{background:var(--surface);border:1.5px solid var(--ink); |
| 66 | 41 | border-radius:var(--r-card);box-shadow:var(--shadow-off-soft)} |
@@ -71,10 +46,10 @@ img{display:block} | ||
| 71 | 46 | font-family:var(--font-display);font-size:14px;font-weight:700;cursor:pointer; |
| 72 | 47 | transition:background .15s ease,transform .1s ease,box-shadow .1s ease} |
| 73 | 48 | .btn:active{transform:translate(2px,2px);box-shadow:none!important} |
| 74 | −.btn-primary{background:var(--ink);color:var(--lime);box-shadow:4px 4px 0 rgba(20,24,20,.25)} | |
| 75 | −.btn-primary:hover{background:var(--green-deep)} | |
| 49 | +.btn-primary{background:var(--ink);color:var(--accent);box-shadow:4px 4px 0 rgba(20,24,20,.25)} | |
| 50 | +.btn-primary:hover{background:var(--accent-deep)} | |
| 76 | 51 | .btn-ghost{background:var(--surface);color:var(--ink);box-shadow:4px 4px 0 rgba(20,24,20,.12)} |
| 77 | −.btn-ghost:hover{background:var(--lime-soft)} | |
| 52 | +.btn-ghost:hover{background:var(--accent-soft)} | |
| 78 | 53 | |
| 79 | 54 | /* ---------- header / ticker ---------- */ |
| 80 | 55 | .ticker{background:var(--ink);color:rgba(245,243,238,.85);overflow:hidden; |
@@ -88,22 +63,33 @@ img{display:block} | ||
| 88 | 63 | .header{position:sticky;top:0;z-index:50;background:rgba(245,243,238,.9); |
| 89 | 64 | backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1.5px solid var(--ink)} |
| 90 | 65 | .header-inner{display:flex;align-items:center;gap:18px;height:64px} |
| 66 | +.header-inner .gk-badge{flex:none} | |
| 67 | +@media(max-width:760px){ | |
| 68 | + /* le header s'empile (badge Groupe KA + nav visibles) — pas de débordement */ | |
| 69 | + .header-inner{flex-wrap:wrap;height:auto;min-height:56px;padding:8px 0;gap:6px 12px} | |
| 70 | + .nav{margin-left:0;flex-wrap:wrap} | |
| 71 | +} | |
| 91 | 72 | .brand{font-family:var(--font-display);font-weight:700;font-size:24px;letter-spacing:-.03em; |
| 92 | 73 | display:flex;align-items:baseline;line-height:1;cursor:pointer} |
| 93 | −.brand .ka{background:var(--lime);border-radius:6px;padding:0 6px 2px;margin-left:2px;transform:rotate(-1deg)} | |
| 74 | +.brand .ka{background:var(--accent);color:var(--on-accent);border-radius:6px;padding:0 6px 2px;margin-left:2px;transform:rotate(-1deg)} | |
| 94 | 75 | .brand-tag{font-family:var(--font-mono);font-size:10px;text-transform:uppercase; |
| 95 | 76 | letter-spacing:.08em;color:var(--ink-3);margin-left:8px} |
| 96 | 77 | @media(max-width:900px){.brand-tag{display:none}} |
| 97 | 78 | .nav{margin-left:auto;display:flex;gap:6px} |
| 98 | −.nav a{padding:8px 14px;border:1.5px solid transparent;border-radius:var(--r-ctl); | |
| 79 | +.nav a{display:inline-flex;align-items:center;min-height:44px;padding:8px 14px; | |
| 80 | + border:1.5px solid transparent;border-radius:var(--r-ctl); | |
| 99 | 81 | font-family:var(--font-display);font-weight:700;font-size:13.5px;transition:all .15s ease} |
| 100 | −.nav a:hover{border-color:var(--ink);background:var(--lime-soft)} | |
| 82 | +.nav a:hover{border-color:var(--ink);background:var(--accent-soft)} | |
| 101 | 83 | .btn-ka{display:inline-flex;align-items:center;gap:8px;padding:8px 15px;min-height:38px; |
| 102 | 84 | border:1.5px solid var(--ink);border-radius:var(--r-ctl);background:var(--ink); |
| 103 | 85 | color:var(--lime);font-family:var(--font-display);font-weight:700;font-size:13px; |
| 104 | 86 | cursor:pointer;box-shadow:3px 3px 0 rgba(20,24,20,.25);transition:background .15s ease} |
| 105 | −.btn-ka:hover{background:var(--green-deep)} | |
| 106 | −.btn-ka .kdot{background:var(--lime);color:var(--ink);border-radius:4px; | |
| 87 | +.btn-ka:hover{background:var(--accent-deep)} | |
| 88 | +#auth{display:inline-flex;align-items:center;gap:10px;flex:none} | |
| 89 | +.ka-signup{font-family:var(--font-mono);font-size:10px;font-weight:700;text-transform:uppercase; | |
| 90 | + letter-spacing:.06em;color:var(--ink-2);min-height:44px;display:inline-flex;align-items:center} | |
| 91 | +.ka-signup:hover{color:var(--accent-deep);text-decoration:underline} | |
| 92 | +.btn-ka .kdot{background:var(--accent);color:var(--on-accent);border-radius:4px; | |
| 107 | 93 | padding:0 5px;font-weight:800;transform:rotate(-1deg)} |
| 108 | 94 | .user-chip{display:inline-flex;align-items:center;gap:9px;border:1.5px solid var(--ink); |
| 109 | 95 | border-radius:var(--r-pill,999px);background:var(--surface);padding:4px 12px 4px 5px; |
@@ -149,9 +135,10 @@ img{display:block} | ||
| 149 | 135 | .chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px} |
| 150 | 136 | .chip{border:1.5px solid var(--ink);background:var(--surface);border-radius:var(--r-ctl); |
| 151 | 137 | padding:6px 12px;font-family:var(--font-display);font-size:12.5px;font-weight:700; |
| 138 | + text-transform:none;letter-spacing:0;min-height:34px; | |
| 152 | 139 | cursor:pointer;color:var(--ink);line-height:1;transition:all .12s ease} |
| 153 | −.chip:hover{background:var(--lime-soft)} | |
| 154 | −.chip.on{background:var(--ink);color:var(--lime);box-shadow:3px 3px 0 rgba(20,24,20,.25)} | |
| 140 | +.chip:hover{background:var(--accent-soft)} | |
| 141 | +.chip.on{background:var(--ink);color:var(--accent);box-shadow:3px 3px 0 rgba(20,24,20,.25)} | |
| 155 | 142 | .chip.free.on{background:var(--green);border-color:var(--green);color:#fff} |
| 156 | 143 | |
| 157 | 144 | /* ---------- résultats ---------- */ |
@@ -166,7 +153,7 @@ img{display:block} | ||
| 166 | 153 | .card-img img{width:100%;height:100%;object-fit:cover} |
| 167 | 154 | .badge-date{position:absolute;left:10px;bottom:10px;font-family:var(--font-mono); |
| 168 | 155 | font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em; |
| 169 | − color:var(--ink);background:var(--lime);border:1.5px solid var(--ink); | |
| 156 | + color:var(--on-accent);background:var(--accent);border:1.5px solid var(--ink); | |
| 170 | 157 | border-radius:var(--r-ctl);padding:4px 9px;white-space:nowrap;box-shadow:2px 2px 0 var(--ink)} |
| 171 | 158 | .card-body{padding:15px 16px 14px;display:flex;flex-direction:column;gap:8px;flex:1} |
| 172 | 159 | .card-top{display:flex;justify-content:space-between;gap:8px;align-items:flex-start} |
@@ -194,9 +181,10 @@ img{display:block} | ||
| 194 | 181 | |
| 195 | 182 | /* ---------- fiche événement ---------- */ |
| 196 | 183 | .detail{padding:34px 0 50px} |
| 197 | −.back{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-mono); | |
| 184 | +.back{display:inline-flex;align-items:center;gap:8px;min-height:44px;font-family:var(--font-mono); | |
| 198 | 185 | font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em; |
| 199 | − color:var(--green);cursor:pointer;margin-bottom:20px} | |
| 186 | + color:var(--green);cursor:pointer;margin-bottom:8px} | |
| 187 | +@media(pointer:coarse){.chip{min-height:44px}.user-chip .out{min-height:32px;padding:2px 8px}} | |
| 200 | 188 | .back:hover{text-decoration:underline} |
| 201 | 189 | .detail-grid{display:grid;grid-template-columns:1.5fr 1fr;gap:26px;align-items:start} |
| 202 | 190 | @media(max-width:860px){.detail-grid{grid-template-columns:1fr}} |
@@ -231,13 +219,32 @@ img{display:block} | ||
| 231 | 219 | color:var(--green);text-transform:uppercase;letter-spacing:.06em} |
| 232 | 220 | .src-card .n{font-size:12.5px;color:var(--ink-3);margin-top:6px} |
| 233 | 221 | .src-card .n b{color:var(--green-deep)} |
| 234 | −footer{background:var(--ink);color:rgba(245,243,238,.72);padding:36px 0 46px;font-size:13px} | |
| 235 | −footer .fam{display:flex;flex-wrap:wrap;gap:4px 14px;margin-bottom:14px} | |
| 236 | −footer .fam a{font-family:var(--font-display);font-weight:700;color:#f5f3ee;padding:4px 0} | |
| 237 | −footer .fam a:hover{color:var(--lime)} | |
| 238 | −footer .fam .lbl{font-family:var(--font-mono);font-size:10px;text-transform:uppercase; | |
| 239 | − letter-spacing:.08em;color:rgba(245,243,238,.45);align-self:center} | |
| 240 | −footer a.mail{color:var(--lime)} | |
| 222 | +.ka-footer{margin-top:0} | |
| 223 | + | |
| 224 | +/* ---------- page /contact ---------- */ | |
| 225 | +.contact-page{padding:44px 0 64px} | |
| 226 | +.contact-page h1{font-size:clamp(30px,4.2vw,48px);line-height:1.06;margin-top:16px;max-width:820px} | |
| 227 | +.contact-page .lede{color:var(--ink-2);font-size:16px;max-width:660px;margin:16px 0 0} | |
| 228 | +.contact-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(260px,100%),1fr)); | |
| 229 | + gap:18px;margin-top:28px} | |
| 230 | +.contact-card{padding:20px 22px;display:flex;flex-direction:column;gap:6px} | |
| 231 | +.contact-card a.mail{display:inline-flex;align-items:center;min-height:44px; | |
| 232 | + font-family:var(--font-mono);font-weight:700;font-size:14px;color:var(--accent-deep); | |
| 233 | + word-break:break-all} | |
| 234 | +.contact-card a.mail:hover{text-decoration:underline;text-underline-offset:4px} | |
| 235 | +.contact-note{margin-top:26px;padding:18px 20px;border-left:3px solid var(--accent); | |
| 236 | + background:var(--surface);border-radius:0 var(--r-card) var(--r-card) 0; | |
| 237 | + box-shadow:var(--shadow-flat);max-width:760px} | |
| 238 | +.contact-note b{color:var(--ink)} | |
| 239 | +.contact-sites{margin-top:36px} | |
| 240 | +.contact-sites ul{list-style:none;display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 0;padding:0} | |
| 241 | +.contact-sites a{display:inline-flex;align-items:center;min-height:44px;padding:8px 14px; | |
| 242 | + border:1.5px solid var(--ink);border-radius:var(--r-ctl);background:var(--surface); | |
| 243 | + font-family:var(--font-display);font-weight:700;font-size:13px; | |
| 244 | + box-shadow:3px 3px 0 rgba(20,24,20,.12);transition:background .15s ease} | |
| 245 | +.contact-sites a:hover{background:var(--accent-soft)} | |
| 246 | +.contact-sites a small{font-family:var(--font-mono);font-weight:500;font-size:10px; | |
| 247 | + color:var(--ink-3);margin-left:8px} | |
| 241 | 248 | #ssr{display:none} |
| 242 | 249 | .hidden{display:none!important} |
| 243 | 250 | </style> |
@@ -258,9 +265,13 @@ footer a.mail{color:var(--lime)} | ||
| 258 | 265 | <header class="header"> |
| 259 | 266 | <div class="container header-inner"> |
| 260 | 267 | <a class="brand" data-nav href="/">Sorti<span class="ka">·Ka</span></a> |
| 268 | + <a class="gk-badge" href="https://www.groupe-ka.com" target="_blank" rel="noopener noreferrer" | |
| 269 | + aria-label="Un service Groupe KA — visiter le portail de l'écosystème"> | |
| 270 | + Un service <b>Groupe<span class="ka">KA</span></b></a> | |
| 261 | 271 | <span class="brand-tag">Sorties & événements — Québec</span> |
| 262 | 272 | <nav class="nav"> |
| 263 | 273 | <a href="/#sources">Sources</a> |
| 274 | + <a data-nav href="/contact">Contact</a> | |
| 264 | 275 | <a href="/docs" target="_blank" rel="noopener">API</a> |
| 265 | 276 | </nav> |
| 266 | 277 | <span id="auth"></span> |
@@ -343,23 +354,101 @@ footer a.mail{color:var(--lime)} | ||
| 343 | 354 | </div> |
| 344 | 355 | </main> |
| 345 | 356 | |
| 346 | −<footer> | |
| 357 | +<!-- ======== VUE CONTACT (/contact) — contenu : src/ka/ecosystem.json ======== --> | |
| 358 | +<main id="view-contact" class="hidden"> | |
| 359 | + <div class="container contact-page"> | |
| 360 | + <span class="kicker">Contact · Groupe KA</span> | |
| 361 | + <h1>Écrire au <span class="hl">Groupe KA</span></h1> | |
| 362 | + <p class="lede">Sorti·Ka est un service du Groupe KA, holding québécois d'agrégateurs de | |
| 363 | + produits et services entièrement automatisés. Trois adresses, selon votre besoin :</p> | |
| 364 | + | |
| 365 | + <div class="contact-grid"> | |
| 366 | + <div class="contact-card gk-card"> | |
| 367 | + <span class="klabel">Projets, partenariats & données</span> | |
| 368 | + <a class="mail" href="mailto:contact@groupe-ka.com">contact@groupe-ka.com</a> | |
| 369 | + </div> | |
| 370 | + <div class="contact-card gk-card"> | |
| 371 | + <span class="klabel">Médias & questions générales</span> | |
| 372 | + <a class="mail" href="mailto:info@groupe-ka.com">info@groupe-ka.com</a> | |
| 373 | + </div> | |
| 374 | + <div class="contact-card gk-card"> | |
| 375 | + <span class="klabel">Légal, vie privée & Loi 25</span> | |
| 376 | + <a class="mail" href="mailto:admin@groupe-ka.com">admin@groupe-ka.com</a> | |
| 377 | + </div> | |
| 378 | + </div> | |
| 379 | + | |
| 380 | + <p class="contact-note"><b>Groupe KA est un agrégateur de contenu :</b> nous ne vendons rien, | |
| 381 | + ne louons rien et ne sommes partie à aucune transaction. Pour les billets, contactez la | |
| 382 | + billetterie ou l'organisateur indiqué sur chaque fiche.</p> | |
| 383 | + <p class="contact-note">La création de compte <b>KA ID</b> se fait sur le hub | |
| 384 | + <a href="https://www.groupe-ka.com/connexion" target="_blank" rel="noopener noreferrer" | |
| 385 | + style="color:var(--accent-deep);font-weight:700">groupe-ka.com/connexion</a> ; | |
| 386 | + chaque site de l'écosystème délègue sa connexion au hub.</p> | |
| 387 | + | |
| 388 | + <div class="contact-sites"> | |
| 389 | + <span class="kicker">L'écosystème ·Ka</span> | |
| 390 | + <ul> | |
| 391 | + <li><a href="https://www.groupe-ka.com" target="_blank" rel="noopener noreferrer">Groupe KA<small>le portail</small></a></li> | |
| 392 | + <li><a href="https://www.trouve-ka.com" target="_blank" rel="noopener noreferrer">Trouve·Ka<small>recherche</small></a></li> | |
| 393 | + <li><a href="https://www.lou-ka.com" target="_blank" rel="noopener noreferrer">Lou·Ka<small>logements</small></a></li> | |
| 394 | + <li><a href="https://www.immo-ka.com" target="_blank" rel="noopener noreferrer">Immo·Ka<small>propriétés</small></a></li> | |
| 395 | + <li><a href="https://www.vrai-prix.com" target="_blank" rel="noopener noreferrer">Vrai-Prix<small>valeur immo</small></a></li> | |
| 396 | + <li><a href="https://www.auto-ka.com" target="_blank" rel="noopener noreferrer">Auto·Ka<small>autos usagées</small></a></li> | |
| 397 | + <li><a href="https://www.fabri-ka.com" target="_blank" rel="noopener noreferrer">Fabri·Ka<small>fait au Québec</small></a></li> | |
| 398 | + <li><a href="https://www.food-ka.com" target="_blank" rel="noopener noreferrer">Food·Ka<small>épicerie</small></a></li> | |
| 399 | + <li><a href="https://www.resto-ka.com" target="_blank" rel="noopener noreferrer">Resto·Ka<small>restos</small></a></li> | |
| 400 | + <li><a href="https://www.crea-ka.com" target="_blank" rel="noopener noreferrer">Créa·Ka<small>créateurs</small></a></li> | |
| 401 | + <li><a href="https://www.api-ka.com" target="_blank" rel="noopener noreferrer">API·Ka<small>données API</small></a></li> | |
| 402 | + </ul> | |
| 403 | + </div> | |
| 404 | + </div> | |
| 405 | +</main> | |
| 406 | + | |
| 407 | +<!-- ======== FOOTER COMMUN GROUPE KA (ka-ui/KaFooter, siteId="sorti-ka") ======== --> | |
| 408 | +<footer class="ka-footer" id="contact"> | |
| 347 | 409 | <div class="container"> |
| 348 | − <div class="fam"> | |
| 349 | − <span class="lbl">Famille ·Ka</span> | |
| 350 | − <a href="https://www.lou-ka.com" target="_blank" rel="noopener">Lou·Ka</a> | |
| 351 | − <a href="https://www.immo-ka.com" target="_blank" rel="noopener">Immo·Ka</a> | |
| 352 | − <a href="https://www.auto-ka.com" target="_blank" rel="noopener">Auto·Ka</a> | |
| 353 | − <a href="https://www.food-ka.com" target="_blank" rel="noopener">Food·Ka</a> | |
| 354 | − <a href="https://www.fabri-ka.com" target="_blank" rel="noopener">Fabri·Ka</a> | |
| 355 | − <a href="https://www.toit-ka.com" target="_blank" rel="noopener">Toit·Ka</a> | |
| 356 | − <a href="https://www.forma-ka.com" target="_blank" rel="noopener">Forma·Ka</a> | |
| 357 | − <a href="https://www.groupe-ka.com" target="_blank" rel="noopener">Groupe KA</a> | |
| 410 | + <a class="wordmark" href="https://www.groupe-ka.com" target="_blank" rel="noopener noreferrer" | |
| 411 | + aria-label="Groupe KA — le portail de l'écosystème">Groupe <span class="ka">KA</span></a> | |
| 412 | + <p class="desc">Holding québécois d'agrégateurs de produits et services entièrement automatisés. | |
| 413 | + Des centaines de connecteurs lisent les sites à la source, normalisent la donnée et se | |
| 414 | + resynchronisent seuls. <b style="color:var(--paper)">Sorti·Ka</b> — Toutes les sorties, | |
| 415 | + dans les 17 régions — est un service Groupe KA.</p> | |
| 416 | + <p class="notice"><b>Groupe KA est un agrégateur de contenu : nous ne vendons rien, | |
| 417 | + ne louons rien et ne sommes partie à aucune transaction.</b></p> | |
| 418 | + <ul class="sites"> | |
| 419 | + <li><a href="https://www.groupe-ka.com" target="_blank" rel="noopener noreferrer">Groupe KA</a></li> | |
| 420 | + <li><a href="https://www.trouve-ka.com" target="_blank" rel="noopener noreferrer">Trouve·Ka</a></li> | |
| 421 | + <li><a href="https://www.lou-ka.com" target="_blank" rel="noopener noreferrer">Lou·Ka</a></li> | |
| 422 | + <li><a href="https://www.immo-ka.com" target="_blank" rel="noopener noreferrer">Immo·Ka</a></li> | |
| 423 | + <li><a href="https://www.vrai-prix.com" target="_blank" rel="noopener noreferrer">Vrai-Prix</a></li> | |
| 424 | + <li><a href="https://www.auto-ka.com" target="_blank" rel="noopener noreferrer">Auto·Ka</a></li> | |
| 425 | + <li><a href="https://www.fabri-ka.com" target="_blank" rel="noopener noreferrer">Fabri·Ka</a></li> | |
| 426 | + <li><a href="https://www.food-ka.com" target="_blank" rel="noopener noreferrer">Food·Ka</a></li> | |
| 427 | + <li><a href="https://www.resto-ka.com" target="_blank" rel="noopener noreferrer">Resto·Ka</a></li> | |
| 428 | + <li><a href="https://www.crea-ka.com" target="_blank" rel="noopener noreferrer">Créa·Ka</a></li> | |
| 429 | + <li><a href="https://www.api-ka.com" target="_blank" rel="noopener noreferrer">API·Ka</a></li> | |
| 430 | + <li><a href="https://www.valoplex.com" target="_blank" rel="noopener noreferrer">ValoPlex</a></li> | |
| 431 | + <li><a href="https://www.ka2.bot" target="_blank" rel="noopener noreferrer">Ka2</a></li> | |
| 432 | + <li><a href="https://www.ka4.bot" target="_blank" rel="noopener noreferrer">Ka4</a></li> | |
| 433 | + <li><a href="https://www.ka6.bot" target="_blank" rel="noopener noreferrer">Ka6</a></li> | |
| 434 | + </ul> | |
| 435 | + <div class="contacts"> | |
| 436 | + <p style="margin:0"><a href="mailto:contact@groupe-ka.com">contact@groupe-ka.com</a> | |
| 437 | + <span>Projets, partenariats & données</span></p> | |
| 438 | + <p style="margin:0"><a href="mailto:info@groupe-ka.com">info@groupe-ka.com</a> | |
| 439 | + <span>Médias & questions générales</span></p> | |
| 440 | + <p style="margin:0"><a href="mailto:admin@groupe-ka.com">admin@groupe-ka.com</a> | |
| 441 | + <span>Légal, vie privée & Loi 25</span></p> | |
| 358 | 442 | </div> |
| 359 | − <div>Données : SIT Québec (MTO, CC 4.0) · Ville de Montréal (CC BY 4.0) · Ville de Laval (CC 4.0) · | |
| 360 | − Le point de vente · evenko · Atuvu.ca · La Vitrine · Répertoire des municipalités (MAMH).</div> | |
| 361 | − <div style="margin-top:8px">© 2026 Sorti·Ka — Simon-Pierre Boucher · | |
| 362 | − <a class="mail" href="mailto:contact@spboucher.ai">contact@spboucher.ai</a></div> | |
| 443 | + <p class="legal">Données : SIT Québec (MTO, CC 4.0) · Ville de Montréal (CC BY 4.0) · | |
| 444 | + Ville de Laval (CC 4.0) · Le point de vente · evenko · Atuvu.ca · La Vitrine · | |
| 445 | + Répertoire des municipalités (MAMH).</p> | |
| 446 | + <p class="legal">© 2026 Groupe KA — Simon-Pierre Boucher | |
| 447 | + · <a data-nav href="/contact">Contact</a> | |
| 448 | + · <a href="https://www.groupe-ka.com/conditions" target="_blank" rel="noopener noreferrer">Conditions d'utilisation</a> | |
| 449 | + · <a href="https://www.groupe-ka.com/confidentialite" target="_blank" rel="noopener noreferrer">Politique de confidentialité</a> | |
| 450 | + · <a href="https://www.groupe-ka.com/loi-25" target="_blank" rel="noopener noreferrer">Protection des renseignements personnels (Loi 25)</a> | |
| 451 | + · <a href="https://www.groupe-ka.com/bots" target="_blank" rel="noopener noreferrer">Transparence des robots d'indexation</a></p> | |
| 363 | 452 | </div> |
| 364 | 453 | </footer> |
| 365 | 454 | |
@@ -519,21 +608,32 @@ async function showDetail(uid){ | ||
| 519 | 608 | bindNav($("d-similar")); |
| 520 | 609 | }catch(e){ $("d-similar").innerHTML = ""; } |
| 521 | 610 | |
| 522 | − $("view-list").classList.add("hidden"); | |
| 523 | − $("view-detail").classList.remove("hidden"); | |
| 611 | + show("view-detail"); | |
| 524 | 612 | window.scrollTo({top:0}); |
| 525 | 613 | } |
| 526 | 614 | |
| 615 | +/* ---------- vues ---------- */ | |
| 616 | +const VIEWS = ["view-list","view-detail","view-contact"]; | |
| 617 | +function show(id){ | |
| 618 | + VIEWS.forEach(v => $(v).classList.toggle("hidden", v !== id)); | |
| 619 | +} | |
| 620 | + | |
| 527 | 621 | function showList(){ |
| 528 | − document.title = "Sorti·Ka — Toutes les sorties et tous les événements du Québec"; | |
| 529 | − $("view-detail").classList.add("hidden"); | |
| 530 | − $("view-list").classList.remove("hidden"); | |
| 622 | + document.title = "Sorti-Ka — Un service Groupe KA"; | |
| 623 | + show("view-list"); | |
| 624 | +} | |
| 625 | + | |
| 626 | +function showContact(){ | |
| 627 | + document.title = "Contact — Sorti-Ka | Un service Groupe KA"; | |
| 628 | + show("view-contact"); | |
| 629 | + window.scrollTo({top:0}); | |
| 531 | 630 | } |
| 532 | 631 | |
| 533 | 632 | /* ---------- routeur ---------- */ |
| 534 | 633 | function route(){ |
| 535 | 634 | const m = location.pathname.match(/^\/evenement\/(.+)$/); |
| 536 | 635 | if(m) showDetail(decodeURIComponent(m[1])); |
| 636 | + else if(location.pathname === "/contact") showContact(); | |
| 537 | 637 | else showList(); |
| 538 | 638 | } |
| 539 | 639 | function go(path){ |
@@ -606,7 +706,9 @@ async function init(){ | ||
| 606 | 706 | }; |
| 607 | 707 | } else { |
| 608 | 708 | $("auth").innerHTML = `<a class="btn-ka" href="/api/auth/ka/login"> |
| 609 | − <span class="kdot">·Ka</span> Se connecter avec KA</a>`; | |
| 709 | + <span class="kdot">·Ka</span> Se connecter avec KA</a> | |
| 710 | + <a class="ka-signup" href="https://www.groupe-ka.com/connexion" | |
| 711 | + target="_blank" rel="noopener">Créer un compte</a>`; | |
| 610 | 712 | } |
| 611 | 713 | } |
| 612 | 714 | }catch(e){} |
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
sortika/seo.py
+34 −1
@@ -143,6 +143,39 @@ def event_page(uid: str) -> HTMLResponse: | ||
| 143 | 143 | return _render(head, ssr) |
| 144 | 144 | |
| 145 | 145 | |
| 146 | +@router.get("/contact", include_in_schema=False) | |
| 147 | +def contact_page() -> HTMLResponse: | |
| 148 | + """Page /contact (SPA) — head SEO + contenu SSR pour les robots. | |
| 149 | + Contenu aligné sur ka-ui/ecosystem.json (contacts Groupe KA).""" | |
| 150 | + title = f"Contact — {SITE_NAME} | Un service Groupe KA" | |
| 151 | + desc = ("Écrire au Groupe KA : contact@groupe-ka.com (projets, partenariats " | |
| 152 | + "et données), info@groupe-ka.com (médias et questions générales), " | |
| 153 | + "admin@groupe-ka.com (légal, vie privée et Loi 25).") | |
| 154 | + canonical = f"{BASE_URL}/contact" | |
| 155 | + head = ( | |
| 156 | + f"<title>{_e(title)}</title>\n" | |
| 157 | + f'<meta name="description" content="{_e(desc)}">\n' | |
| 158 | + f'<link rel="canonical" href="{canonical}">\n' | |
| 159 | + f'<meta property="og:title" content="{_e(title)}">\n' | |
| 160 | + f'<meta property="og:description" content="{_e(desc)}">\n' | |
| 161 | + f'<meta property="og:type" content="website">\n' | |
| 162 | + f'<meta property="og:url" content="{canonical}">' | |
| 163 | + ) | |
| 164 | + ssr = ( | |
| 165 | + "<article><h1>Contact — Groupe KA</h1>" | |
| 166 | + "<p>Sorti-Ka est un service du Groupe KA.</p>" | |
| 167 | + '<p><a href="mailto:contact@groupe-ka.com">contact@groupe-ka.com</a> — ' | |
| 168 | + "projets, partenariats et données.</p>" | |
| 169 | + '<p><a href="mailto:info@groupe-ka.com">info@groupe-ka.com</a> — ' | |
| 170 | + "médias et questions générales.</p>" | |
| 171 | + '<p><a href="mailto:admin@groupe-ka.com">admin@groupe-ka.com</a> — ' | |
| 172 | + "légal, vie privée et Loi 25.</p>" | |
| 173 | + '<p><a href="https://www.groupe-ka.com" rel="noopener">Groupe KA — ' | |
| 174 | + "le portail de l'écosystème</a></p></article>" | |
| 175 | + ) | |
| 176 | + return _render(head, ssr) | |
| 177 | + | |
| 178 | + | |
| 146 | 179 | @router.get("/robots.txt", include_in_schema=False) |
| 147 | 180 | def robots() -> PlainTextResponse: |
| 148 | 181 | return PlainTextResponse( |
@@ -179,7 +212,7 @@ def sitemap_index() -> Response: | ||
| 179 | 212 | |
| 180 | 213 | @router.get("/sitemap-pages.xml", include_in_schema=False) |
| 181 | 214 | def sitemap_pages() -> Response: |
| 182 | − return _urlset([(f"{BASE_URL}/", None)]) | |
| 215 | + return _urlset([(f"{BASE_URL}/", None), (f"{BASE_URL}/contact", None)]) | |
| 183 | 216 | |
| 184 | 217 | |
| 185 | 218 | @router.get("/sitemap-evenements-{num}.xml", include_in_schema=False) |
| 186 | 219 | |