feat: PDF « L'agrégation, expliquée » — 12 pages de schémas, téléchargeable sur le site
- Document A4 paysage aux couleurs du groupe : couverture, définition ·Ka, sablier à 10 plateformes, pipeline, cycle, éclaireurs Ka2/Ka4/Ka6, Trouve·Ka, inventaire vivant, flotte de 340 connecteurs, moteurs d'évaluation, socle, manifeste - Source HTML autonome (figures générées en JS, rendu chromium --print-to-pdf) - Servi sur /documents/groupe-ka-agregation.pdf + bouton dans la section #flux Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 changed files +1,020 −0
added
assets/poster/groupe-ka-agregation.html
+1007 −0
@@ -0,0 +1,1007 @@ | ||
| 1 | +<!DOCTYPE html> | |
| 2 | +<!-- Auteur : Simon-Pierre Boucher — contact@spboucher.ai --> | |
| 3 | +<!-- Document « L'agrégation, expliquée » — 12 pages A4 paysage. | |
| 4 | + Rendu PDF : chromium --headless --print-to-pdf (les figures sont générées en JS). --> | |
| 5 | +<html lang="fr"> | |
| 6 | +<head> | |
| 7 | +<meta charset="UTF-8" /> | |
| 8 | +<title>Groupe KA — L'agrégation, expliquée</title> | |
| 9 | +<link rel="preconnect" href="https://fonts.googleapis.com" /> | |
| 10 | +<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | |
| 11 | +<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;700&family=Inter:wght@400;600&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet" /> | |
| 12 | +<style> | |
| 13 | + :root { | |
| 14 | + --paper: #f5f3ee; | |
| 15 | + --surface: #ffffff; | |
| 16 | + --ink: #141814; | |
| 17 | + --ink-2: #4d5551; | |
| 18 | + --ink-3: #8b928c; | |
| 19 | + --green: #1c5c41; | |
| 20 | + --green-deep: #123f2e; | |
| 21 | + --lime: #d9f26b; | |
| 22 | + --lime-soft: #f0f9d2; | |
| 23 | + --display: "Space Grotesk", system-ui, sans-serif; | |
| 24 | + --body: "Inter", system-ui, sans-serif; | |
| 25 | + --mono: "JetBrains Mono", ui-monospace, monospace; | |
| 26 | + } | |
| 27 | + * { margin: 0; padding: 0; box-sizing: border-box; } | |
| 28 | + @page { size: 297mm 210mm; margin: 0; } | |
| 29 | + html, body { background: #1a1e1a; } | |
| 30 | + body { font-family: var(--body); color: var(--ink); } | |
| 31 | + | |
| 32 | + .page { | |
| 33 | + width: 297mm; | |
| 34 | + height: 210mm; | |
| 35 | + background: var(--paper); | |
| 36 | + position: relative; | |
| 37 | + overflow: hidden; | |
| 38 | + page-break-after: always; | |
| 39 | + padding: 12mm 14mm 11mm; | |
| 40 | + display: flex; | |
| 41 | + flex-direction: column; | |
| 42 | + } | |
| 43 | + .page::after { | |
| 44 | + content: ""; | |
| 45 | + position: absolute; inset: 0; | |
| 46 | + pointer-events: none; | |
| 47 | + opacity: 0.05; | |
| 48 | + background-image: radial-gradient(var(--ink) 0.6px, transparent 0.6px); | |
| 49 | + background-size: 5px 5px; | |
| 50 | + } | |
| 51 | + .page.dark { background: var(--ink); color: var(--paper); } | |
| 52 | + .page.dark::after { background-image: radial-gradient(var(--lime) 0.6px, transparent 0.6px); opacity: 0.04; } | |
| 53 | + | |
| 54 | + .mono { font-family: var(--mono); } | |
| 55 | + .display { font-family: var(--display); } | |
| 56 | + | |
| 57 | + /* ---------- En-tête / pied de page communs ---------- */ | |
| 58 | + .phead { | |
| 59 | + display: flex; | |
| 60 | + align-items: baseline; | |
| 61 | + justify-content: space-between; | |
| 62 | + border-bottom: 2px solid var(--ink); | |
| 63 | + padding-bottom: 4mm; | |
| 64 | + } | |
| 65 | + .dark .phead { border-bottom-color: rgba(245,243,238,0.25); } | |
| 66 | + .wordmark { | |
| 67 | + font-family: var(--display); | |
| 68 | + font-weight: 700; | |
| 69 | + font-size: 17px; | |
| 70 | + letter-spacing: -0.04em; | |
| 71 | + display: inline-flex; | |
| 72 | + align-items: baseline; | |
| 73 | + gap: 5px; | |
| 74 | + } | |
| 75 | + .kabox { | |
| 76 | + display: inline-block; | |
| 77 | + background: var(--ink); | |
| 78 | + color: var(--lime); | |
| 79 | + border-radius: 5px; | |
| 80 | + padding: 0 6px 2px; | |
| 81 | + transform: rotate(-2deg); | |
| 82 | + } | |
| 83 | + .dark .kabox { background: var(--lime); color: var(--ink); } | |
| 84 | + .phead .doc { | |
| 85 | + font-family: var(--mono); | |
| 86 | + font-weight: 700; | |
| 87 | + font-size: 9px; | |
| 88 | + letter-spacing: 0.16em; | |
| 89 | + text-transform: uppercase; | |
| 90 | + color: var(--ink-3); | |
| 91 | + } | |
| 92 | + .dark .phead .doc { color: rgba(245,243,238,0.55); } | |
| 93 | + .pnum { font-family: var(--mono); font-weight: 700; font-size: 10px; color: var(--green); } | |
| 94 | + .dark .pnum { color: var(--lime); } | |
| 95 | + | |
| 96 | + .pfoot { | |
| 97 | + margin-top: auto; | |
| 98 | + border-top: 1.5px dashed rgba(20,24,20,0.3); | |
| 99 | + padding-top: 3mm; | |
| 100 | + display: flex; | |
| 101 | + justify-content: space-between; | |
| 102 | + font-family: var(--mono); | |
| 103 | + font-size: 8.5px; | |
| 104 | + letter-spacing: 0.1em; | |
| 105 | + text-transform: uppercase; | |
| 106 | + color: var(--ink-3); | |
| 107 | + } | |
| 108 | + .dark .pfoot { border-top-color: rgba(245,243,238,0.2); color: rgba(245,243,238,0.5); } | |
| 109 | + | |
| 110 | + /* ---------- Typo de section ---------- */ | |
| 111 | + .kicker { | |
| 112 | + font-family: var(--mono); | |
| 113 | + font-weight: 700; | |
| 114 | + font-size: 10px; | |
| 115 | + letter-spacing: 0.18em; | |
| 116 | + text-transform: uppercase; | |
| 117 | + color: var(--green); | |
| 118 | + display: inline-flex; | |
| 119 | + align-items: center; | |
| 120 | + gap: 8px; | |
| 121 | + margin-top: 6mm; | |
| 122 | + } | |
| 123 | + .kicker::before { content: ""; width: 22px; height: 2px; background: var(--green); } | |
| 124 | + .dark .kicker { color: var(--lime); } | |
| 125 | + .dark .kicker::before { background: var(--lime); } | |
| 126 | + h2 { | |
| 127 | + font-family: var(--display); | |
| 128 | + font-weight: 700; | |
| 129 | + font-size: 34px; | |
| 130 | + line-height: 1; | |
| 131 | + letter-spacing: -0.03em; | |
| 132 | + text-transform: uppercase; | |
| 133 | + margin-top: 3.5mm; | |
| 134 | + } | |
| 135 | + .hl { | |
| 136 | + display: inline-block; | |
| 137 | + background: var(--lime); | |
| 138 | + color: var(--ink); | |
| 139 | + border-radius: 6px; | |
| 140 | + padding: 0 8px 2px; | |
| 141 | + transform: rotate(-1deg); | |
| 142 | + } | |
| 143 | + .outline { color: transparent; -webkit-text-stroke: 2px var(--ink); } | |
| 144 | + .dark .outline { -webkit-text-stroke: 2px var(--paper); } | |
| 145 | + .lede { margin-top: 3.5mm; font-size: 12px; line-height: 1.55; color: var(--ink-2); max-width: 150mm; } | |
| 146 | + .dark .lede { color: rgba(245,243,238,0.75); } | |
| 147 | + | |
| 148 | + /* ---------- Cartes & pilules ---------- */ | |
| 149 | + .card { | |
| 150 | + background: var(--surface); | |
| 151 | + border: 1.5px solid var(--ink); | |
| 152 | + border-radius: 9px; | |
| 153 | + box-shadow: 6px 6px 0 rgba(20,24,20,0.12); | |
| 154 | + } | |
| 155 | + .chip { | |
| 156 | + display: inline-flex; | |
| 157 | + align-items: center; | |
| 158 | + gap: 6px; | |
| 159 | + padding: 5px 11px; | |
| 160 | + background: var(--surface); | |
| 161 | + border: 1.5px solid var(--ink); | |
| 162 | + border-radius: 999px; | |
| 163 | + font-family: var(--mono); | |
| 164 | + font-size: 9.5px; | |
| 165 | + font-weight: 700; | |
| 166 | + box-shadow: 3px 3px 0 rgba(20,24,20,0.12); | |
| 167 | + white-space: nowrap; | |
| 168 | + } | |
| 169 | + .caption { | |
| 170 | + border-top: 1.5px solid var(--ink); | |
| 171 | + background: #faf9f5; | |
| 172 | + padding: 3mm 5mm; | |
| 173 | + font-size: 10.5px; | |
| 174 | + color: var(--ink-2); | |
| 175 | + border-radius: 0 0 8px 8px; | |
| 176 | + } | |
| 177 | + .caption b { font-family: var(--mono); color: var(--green); } | |
| 178 | + | |
| 179 | + /* ---------- Page 1 : couverture ---------- */ | |
| 180 | + .cover h1 { | |
| 181 | + font-family: var(--display); | |
| 182 | + font-weight: 700; | |
| 183 | + font-size: 96px; | |
| 184 | + line-height: 0.95; | |
| 185 | + letter-spacing: -0.04em; | |
| 186 | + text-transform: uppercase; | |
| 187 | + margin-top: 8mm; | |
| 188 | + } | |
| 189 | + .cover .sub { margin-top: 7mm; font-size: 14.5px; color: var(--ink-2); max-width: 165mm; line-height: 1.6; } | |
| 190 | + .cover-chips { display: flex; gap: 8px; margin-top: 8mm; flex-wrap: wrap; } | |
| 191 | + .cover-marks { display: flex; gap: 6px; margin-top: auto; flex-wrap: wrap; padding-bottom: 2mm; } | |
| 192 | + .markpill { | |
| 193 | + font-family: var(--display); font-weight: 700; font-size: 12.5px; | |
| 194 | + border: 1.5px solid var(--ink); border-radius: 999px; padding: 4px 12px 5px; | |
| 195 | + background: var(--surface); box-shadow: 3px 3px 0 rgba(20,24,20,0.15); | |
| 196 | + } | |
| 197 | + .markpill i { font-style: normal; border-radius: 4px; padding: 0 4px 1px; margin-left: 2px; display: inline-block; transform: rotate(-2deg); } | |
| 198 | + | |
| 199 | + /* ---------- Page 2 : définition ---------- */ | |
| 200 | + .def-grid { display: grid; grid-template-columns: 1.15fr 1fr; gap: 8mm; margin-top: 7mm; align-items: stretch; } | |
| 201 | + .defcard { padding: 8mm 9mm; } | |
| 202 | + .defcard .word { font-family: var(--display); font-weight: 700; font-size: 52px; letter-spacing: -0.03em; } | |
| 203 | + .defcard .pos { font-family: var(--mono); font-size: 12px; color: var(--ink-3); margin-left: 8px; } | |
| 204 | + .defcard ol { margin-top: 6mm; list-style: none; } | |
| 205 | + .defcard li { display: flex; gap: 10px; font-size: 12.5px; line-height: 1.55; color: var(--ink-2); margin-top: 4mm; } | |
| 206 | + .defcard li b.n { font-family: var(--mono); color: var(--green); } | |
| 207 | + .defcard .quote { margin-top: 6mm; border-top: 1.5px dashed rgba(20,24,20,0.3); padding-top: 5mm; font-style: italic; font-size: 12.5px; color: var(--ink-2); } | |
| 208 | + .eqcard { background: var(--ink); color: var(--paper); border-radius: 9px; border: 1.5px solid var(--ink); padding: 8mm 9mm; box-shadow: 6px 6px 0 rgba(20,24,20,0.25); display: flex; flex-direction: column; } | |
| 209 | + .eqcard .k { font-family: var(--mono); font-weight: 700; font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--lime); } | |
| 210 | + .eqcard .eq { font-family: var(--display); font-weight: 700; font-size: 33px; line-height: 1.12; letter-spacing: -0.03em; text-transform: uppercase; margin-top: 5mm; } | |
| 211 | + .eqcard .eq .lime { color: var(--lime); } | |
| 212 | + .eqcard p { margin-top: 5mm; font-size: 11.5px; line-height: 1.6; color: rgba(245,243,238,0.75); } | |
| 213 | + | |
| 214 | + /* ---------- Figures pleine page ---------- */ | |
| 215 | + .figwrap { margin-top: 5mm; border: 1.5px solid var(--ink); border-radius: 9px; background: var(--surface); box-shadow: 6px 6px 0 rgba(20,24,20,0.12); overflow: hidden; flex: 1; display: flex; flex-direction: column; } | |
| 216 | + .figwrap svg { display: block; width: 100%; flex: 1; } | |
| 217 | + .figbody { flex: 1; display: flex; align-items: center; justify-content: center; padding: 4mm 6mm; } | |
| 218 | + | |
| 219 | + /* ---------- Pages barres & matrice ---------- */ | |
| 220 | + .bars { padding: 5.5mm 9mm 4mm; } | |
| 221 | + .barrow { margin-top: 3.8mm; } | |
| 222 | + .barrow:first-child { margin-top: 0; } | |
| 223 | + .barhead { display: flex; justify-content: space-between; align-items: baseline; } | |
| 224 | + .barhead .nm { font-family: var(--display); font-weight: 700; font-size: 13px; display: inline-flex; align-items: center; gap: 7px; } | |
| 225 | + .brand { width: 13px; height: 13px; border-radius: 3.5px; background: var(--ink); display: inline-flex; align-items: center; justify-content: center; } | |
| 226 | + .brand i { width: 8px; height: 8px; border-radius: 2px; display: block; } | |
| 227 | + .barhead .u { font-family: var(--mono); font-size: 9px; color: var(--ink-3); font-weight: 400; margin-left: 4px; } | |
| 228 | + .barhead .v { font-family: var(--mono); font-weight: 700; font-size: 11.5px; } | |
| 229 | + .bartrack { margin-top: 1.5mm; height: 4.4mm; background: rgba(20,24,20,0.06); border-radius: 0 3px 3px 0; } | |
| 230 | + .barfill { height: 100%; background: var(--green); border-radius: 0 3px 3px 0; } | |
| 231 | + | |
| 232 | + .dots-block { margin-top: 4.2mm; } | |
| 233 | + .dots-block:first-child { margin-top: 0; } | |
| 234 | + .dots { display: flex; flex-wrap: wrap; gap: 2.1px; margin-top: 1.6mm; max-width: 235mm; } | |
| 235 | + .dots i { width: 6.5px; height: 6.5px; border-radius: 2px; background: var(--green); display: block; } | |
| 236 | + | |
| 237 | + /* ---------- Page moteurs d'évaluation ---------- */ | |
| 238 | + .eval-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8mm; margin-top: 6mm; flex: 1; } | |
| 239 | + .evalcard { padding: 8mm 9mm; display: flex; flex-direction: column; } | |
| 240 | + .evalcard .k { font-family: var(--mono); font-weight: 700; font-size: 9.5px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); display: flex; align-items: center; gap: 7px; } | |
| 241 | + .evalcard .big { font-family: var(--display); font-weight: 700; font-size: 46px; letter-spacing: -0.03em; margin-top: 4mm; } | |
| 242 | + .evalcard .lbl { font-family: var(--display); font-weight: 700; font-size: 13px; text-transform: uppercase; color: var(--green); margin-top: 1mm; } | |
| 243 | + .evalcard p { font-size: 11px; color: var(--ink-2); margin-top: 3mm; line-height: 1.55; } | |
| 244 | + .gauge { margin-top: auto; padding-top: 6mm; } | |
| 245 | + .gauge .glab { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 8.5px; color: var(--ink-3); margin-bottom: 1.4mm; } | |
| 246 | + .gtrack { position: relative; height: 7mm; border: 1.5px solid var(--ink); border-radius: 5px; background: var(--surface); overflow: hidden; } | |
| 247 | + .gband { position: absolute; top: 0; bottom: 0; background: var(--lime); opacity: 0.85; } | |
| 248 | + .gmid { position: absolute; top: -1mm; bottom: -1mm; width: 2.2px; background: var(--ink); } | |
| 249 | + | |
| 250 | + /* ---------- Page socle : tableau ---------- */ | |
| 251 | + .tbl { margin-top: 6mm; border: 1.5px solid var(--ink); border-radius: 9px; overflow: hidden; background: var(--surface); box-shadow: 6px 6px 0 rgba(20,24,20,0.12); } | |
| 252 | + table { width: 100%; border-collapse: collapse; font-size: 11px; } | |
| 253 | + th { font-family: var(--mono); font-size: 8.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; text-align: left; color: var(--ink-3); background: #faf9f5; padding: 2.6mm 4mm; border-bottom: 1.5px solid var(--ink); } | |
| 254 | + td { padding: 2.45mm 4mm; border-bottom: 1px solid rgba(20,24,20,0.14); vertical-align: middle; } | |
| 255 | + tr:last-child td { border-bottom: none; } | |
| 256 | + td.nm { font-family: var(--display); font-weight: 700; white-space: nowrap; } | |
| 257 | + td.url { font-family: var(--mono); font-size: 9.5px; white-space: nowrap; color: var(--green); } | |
| 258 | + td .brand { vertical-align: -2px; margin-right: 6px; } | |
| 259 | + | |
| 260 | + /* ---------- Page manifeste (fin, sombre) ---------- */ | |
| 261 | + .mani-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7mm; margin-top: 8mm; } | |
| 262 | + .mani { border: 1.5px solid rgba(245,243,238,0.3); border-radius: 9px; padding: 7mm; } | |
| 263 | + .mani .n { font-family: var(--mono); font-weight: 700; font-size: 11px; color: var(--lime); } | |
| 264 | + .mani h3 { font-family: var(--display); font-weight: 700; font-size: 16.5px; text-transform: uppercase; margin-top: 3mm; } | |
| 265 | + .mani p { font-size: 11px; line-height: 1.6; color: rgba(245,243,238,0.75); margin-top: 3mm; } | |
| 266 | + .contact { margin-top: 10mm; display: flex; gap: 6mm; align-items: baseline; flex-wrap: wrap; } | |
| 267 | + .contact .mail { font-family: var(--mono); font-weight: 700; font-size: 13px; color: var(--lime); } | |
| 268 | + .contact .role { font-family: var(--mono); font-size: 9px; color: rgba(245,243,238,0.5); } | |
| 269 | + | |
| 270 | + /* ---------- Légendes SVG en HTML ---------- */ | |
| 271 | + svg text { font-family: var(--mono); } | |
| 272 | + svg .t-display { font-family: var(--display); } | |
| 273 | + svg .t-body { font-family: var(--body); } | |
| 274 | +</style> | |
| 275 | +</head> | |
| 276 | +<body> | |
| 277 | + | |
| 278 | +<!-- ============================ PAGE 1 — COUVERTURE ============================ --> | |
| 279 | +<section class="page cover"> | |
| 280 | + <header class="phead"> | |
| 281 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 282 | + <span class="doc">Document de référence · 2026</span> | |
| 283 | + <span class="pnum">01 / 12</span> | |
| 284 | + </header> | |
| 285 | + <p class="kicker">Holding d'agrégateurs automatisés · Québec</p> | |
| 286 | + <h1>L'agrégation,<br /><span class="outline">expliquée</span> <span class="hl">·Ka</span></h1> | |
| 287 | + <p class="sub"> | |
| 288 | + Comment mille sites deviennent un seul endroit : les connecteurs, le pipeline, le cycle, | |
| 289 | + les éclaireurs, le moteur de recherche et les dix plateformes du Groupe KA — chaque | |
| 290 | + mécanisme dessiné, chaque chiffre compté. Zéro saisie manuelle, zéro boîte noire. | |
| 291 | + </p> | |
| 292 | + <div class="cover-chips"> | |
| 293 | + <span class="chip">10 agrégateurs en ligne</span> | |
| 294 | + <span class="chip">1 moteur de recherche québécois</span> | |
| 295 | + <span class="chip">450+ connecteurs sources</span> | |
| 296 | + <span class="chip">22 M+ d'observations immobilières</span> | |
| 297 | + <span class="chip">100 % données à la source</span> | |
| 298 | + </div> | |
| 299 | + <div class="cover-marks" id="cover-marks"></div> | |
| 300 | + <footer class="pfoot"><span>groupe-ka.com</span><span>© 2026 Groupe KA — Simon-Pierre Boucher</span></footer> | |
| 301 | +</section> | |
| 302 | + | |
| 303 | +<!-- ============================ PAGE 2 — LE NOM ============================ --> | |
| 304 | +<section class="page"> | |
| 305 | + <header class="phead"> | |
| 306 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 307 | + <span class="doc">L'agrégation, expliquée</span> | |
| 308 | + <span class="pnum">02 / 12</span> | |
| 309 | + </header> | |
| 310 | + <p class="kicker">Chapitre 01 · Le nom</p> | |
| 311 | + <h2>·Ka, le suffixe qui veut dire <span class="hl">agréger</span></h2> | |
| 312 | + <div class="def-grid"> | |
| 313 | + <div class="card defcard"> | |
| 314 | + <span class="word">·Ka</span><span class="pos">/ka/ suffixe</span> | |
| 315 | + <ol> | |
| 316 | + <li><b class="n">1.</b><span><b>Agréger</b> — rassembler en un seul endroit ce qui était éparpillé | |
| 317 | + sur des centaines de sites : annonces, prix, menus, événements, profils.</span></li> | |
| 318 | + <li><b class="n">2.</b><span>Marque de fabrique du groupe : Lou·Ka loge, Immo·Ka vend, Auto·Ka roule, | |
| 319 | + Fabri·Ka fabrique, Food·Ka nourrit, Resto·Ka régale, Sorti·Ka divertit, Créa·Ka rassemble.</span></li> | |
| 320 | + <li><b class="n">3.</b><span>Par extension : toute machine qui lit les sources en continu, normalise la | |
| 321 | + donnée et publie sans jamais rien inventer.</span></li> | |
| 322 | + </ol> | |
| 323 | + <p class="quote">« Tout ce qui finit en <b class="display">·Ka</b> agrège. »</p> | |
| 324 | + </div> | |
| 325 | + <div class="eqcard"> | |
| 326 | + <span class="k">L'équation ·Ka</span> | |
| 327 | + <p class="eq">1 000+ sites lus<br /><span class="lime">→ 450+ connecteurs</span><br />→ 10 plateformes</p> | |
| 328 | + <p>La donnée entre éparpillée, elle ressort rangée. Entre les deux, aucune main humaine — | |
| 329 | + c'est le suffixe ·Ka qui travaille : lecture à la source, normalisation, dédoublonnage, | |
| 330 | + géocodage, publication traçable. Les pages qui suivent montrent la machine, à découvert.</p> | |
| 331 | + </div> | |
| 332 | + </div> | |
| 333 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 01 · Le nom</span></footer> | |
| 334 | +</section> | |
| 335 | + | |
| 336 | +<!-- ============================ PAGE 3 — LE SABLIER ============================ --> | |
| 337 | +<section class="page"> | |
| 338 | + <header class="phead"> | |
| 339 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 340 | + <span class="doc">L'agrégation, expliquée</span> | |
| 341 | + <span class="pnum">03 / 12</span> | |
| 342 | + </header> | |
| 343 | + <p class="kicker">Chapitre 02 · Le geste fondateur</p> | |
| 344 | + <h2>Plein de sites. <span class="hl">Une seule flèche.</span></h2> | |
| 345 | + <div class="figwrap"> | |
| 346 | + <div class="figbody" id="fig-sablier"></div> | |
| 347 | + <div class="caption"><b>LE SABLIER ·KA —</b> des centaines de sites éparpillés, lus à la source par des | |
| 348 | + connecteurs dédiés ; tout converge vers KA, qui normalise, dédoublonne et redistribue vers dix | |
| 349 | + plateformes. Une flèche entre, dix flèches sortent : c'est ça, agréger.</div> | |
| 350 | + </div> | |
| 351 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 02 · Le sablier</span></footer> | |
| 352 | +</section> | |
| 353 | + | |
| 354 | +<!-- ============================ PAGE 4 — LE PIPELINE ============================ --> | |
| 355 | +<section class="page"> | |
| 356 | + <header class="phead"> | |
| 357 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 358 | + <span class="doc">L'agrégation, expliquée</span> | |
| 359 | + <span class="pnum">04 / 12</span> | |
| 360 | + </header> | |
| 361 | + <p class="kicker">Chapitre 03 · De la page web à la donnée propre</p> | |
| 362 | + <h2>Le pipeline, <span class="hl">station par station</span></h2> | |
| 363 | + <div class="figwrap"> | |
| 364 | + <div class="figbody" id="fig-pipeline"></div> | |
| 365 | + <div class="caption"><b>LE PIPELINE —</b> cinq stations, aucune main humaine. La boucle verte est le secret : | |
| 366 | + tout le pipeline se rejoue en continu, donc une annonce retirée à la source disparaît d'elle-même.</div> | |
| 367 | + </div> | |
| 368 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 03 · Le pipeline</span></footer> | |
| 369 | +</section> | |
| 370 | + | |
| 371 | +<!-- ============================ PAGE 5 — LE CYCLE ============================ --> | |
| 372 | +<section class="page"> | |
| 373 | + <header class="phead"> | |
| 374 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 375 | + <span class="doc">L'agrégation, expliquée</span> | |
| 376 | + <span class="pnum">05 / 12</span> | |
| 377 | + </header> | |
| 378 | + <p class="kicker">Chapitre 04 · La boucle jour et nuit</p> | |
| 379 | + <h2>Pas un instantané, <span class="hl">un battement</span></h2> | |
| 380 | + <div class="figwrap"> | |
| 381 | + <div class="figbody" id="fig-cycle"></div> | |
| 382 | + <div class="caption"><b>LE CYCLE ·KA —</b> quatre gestes, en boucle, sans intervention humaine : lire les | |
| 383 | + sources, comparer à hier, mettre à jour, publier et tracer. C'est la définition maison de l'agrégation.</div> | |
| 384 | + </div> | |
| 385 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 04 · Le cycle</span></footer> | |
| 386 | +</section> | |
| 387 | + | |
| 388 | +<!-- ============================ PAGE 6 — LES ÉCLAIREURS ============================ --> | |
| 389 | +<section class="page"> | |
| 390 | + <header class="phead"> | |
| 391 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 392 | + <span class="doc">L'agrégation, expliquée</span> | |
| 393 | + <span class="pnum">06 / 12</span> | |
| 394 | + </header> | |
| 395 | + <p class="kicker">Chapitre 05 · Avant d'agréger, découvrir</p> | |
| 396 | + <h2>Les éclaireurs : <span class="hl">Ka2 · Ka4 · Ka6</span></h2> | |
| 397 | + <div class="figwrap"> | |
| 398 | + <div class="figbody" id="fig-bots"></div> | |
| 399 | + <div class="caption"><b>LA DÉCOUVERTE —</b> les connecteurs lisent des sources connues ; les bots IA les | |
| 400 | + trouvent. Ka2, Ka4 et Ka6 cartographient le web québécois entité par entité (robots.txt respecté, | |
| 401 | + données publiques seulement) et alimentent tout l'écosystème en nouvelles sources.</div> | |
| 402 | + </div> | |
| 403 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 05 · Les éclaireurs</span></footer> | |
| 404 | +</section> | |
| 405 | + | |
| 406 | +<!-- ============================ PAGE 7 — TROUVE·KA ============================ --> | |
| 407 | +<section class="page"> | |
| 408 | + <header class="phead"> | |
| 409 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 410 | + <span class="doc">L'agrégation, expliquée</span> | |
| 411 | + <span class="pnum">07 / 12</span> | |
| 412 | + </header> | |
| 413 | + <p class="kicker">Chapitre 06 · Le produit phare</p> | |
| 414 | + <h2>Trouve·Ka <span class="hl">cherche le Québec</span></h2> | |
| 415 | + <div class="figwrap"> | |
| 416 | + <div class="figbody" id="fig-trouveka"></div> | |
| 417 | + <div class="caption"><b>LE MOTEUR —</b> les grands moteurs cherchent le monde ; Trouve·Ka ne contient que | |
| 418 | + le web d'ici. Un commerce de Rimouski ou un blogue de Limoilou, noyés dans un index mondial, sont en | |
| 419 | + première page dans un index 100 % Québec. C'est l'agrégation poussée à sa conclusion logique.</div> | |
| 420 | + </div> | |
| 421 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 06 · Trouve·Ka</span></footer> | |
| 422 | +</section> | |
| 423 | + | |
| 424 | +<!-- ============================ PAGE 8 — L'INVENTAIRE ============================ --> | |
| 425 | +<section class="page"> | |
| 426 | + <header class="phead"> | |
| 427 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 428 | + <span class="doc">L'agrégation, expliquée</span> | |
| 429 | + <span class="pnum">08 / 12</span> | |
| 430 | + </header> | |
| 431 | + <p class="kicker">Chapitre 07 · La preuve par les nombres</p> | |
| 432 | + <h2>L'inventaire <span class="hl">vivant</span></h2> | |
| 433 | + <div class="figwrap" style="flex:1"> | |
| 434 | + <div class="bars" id="fig-bars" style="flex:1"></div> | |
| 435 | + <div class="caption"><b>L'INVENTAIRE —</b> chaque barre est vivante : elle grossit quand les connecteurs | |
| 436 | + trouvent du neuf, elle maigrit quand une annonce disparaît à la source. Compté aujourd'hui, pas promis.</div> | |
| 437 | + </div> | |
| 438 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 07 · L'inventaire</span></footer> | |
| 439 | +</section> | |
| 440 | + | |
| 441 | +<!-- ============================ PAGE 9 — LA FLOTTE ============================ --> | |
| 442 | +<section class="page"> | |
| 443 | + <header class="phead"> | |
| 444 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 445 | + <span class="doc">L'agrégation, expliquée</span> | |
| 446 | + <span class="pnum">09 / 12</span> | |
| 447 | + </header> | |
| 448 | + <p class="kicker">Chapitre 08 · Un point = un connecteur</p> | |
| 449 | + <h2>340 connecteurs, <span class="hl">dessinés un par un</span></h2> | |
| 450 | + <div class="figwrap" style="flex:1"> | |
| 451 | + <div class="bars" id="fig-dots" style="flex:1"></div> | |
| 452 | + <div class="caption"><b>LA FLOTTE —</b> chaque point est un petit programme qui connaît un site par cœur et | |
| 453 | + le relit sans arrêt. S'y ajoutent les flux des 1 204 boutiques de Fabri·Ka, les données publiques de | |
| 454 | + Vrai-Prix et ValoPlex, et l'annuaire multi-plateformes de Créa·Ka.</div> | |
| 455 | + </div> | |
| 456 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 08 · La flotte</span></footer> | |
| 457 | +</section> | |
| 458 | + | |
| 459 | +<!-- ============================ PAGE 10 — LES MOTEURS D'ÉVALUATION ============================ --> | |
| 460 | +<section class="page"> | |
| 461 | + <header class="phead"> | |
| 462 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 463 | + <span class="doc">L'agrégation, expliquée</span> | |
| 464 | + <span class="pnum">10 / 12</span> | |
| 465 | + </header> | |
| 466 | + <p class="kicker">Chapitre 09 · Agréger pour évaluer</p> | |
| 467 | + <h2>Deux moteurs, <span class="hl">tout le Québec</span></h2> | |
| 468 | + <div class="eval-grid"> | |
| 469 | + <div class="card evalcard"> | |
| 470 | + <span class="k"><span class="brand"><i style="background:#ff5148"></i></span>Vrai-Prix · résidentiel</span> | |
| 471 | + <span class="big">3 747 008</span> | |
| 472 | + <span class="lbl">propriétés couvertes</span> | |
| 473 | + <p>Toutes les unités du rôle foncier du Québec, estimées par un moteur hybride — modèle hédonique + | |
| 474 | + comparables réels ajustés (745 119 ventes publiées 2021-2026, MdAPE 11 %). Chaque ajustement est | |
| 475 | + affiché en dollars.</p> | |
| 476 | + <div class="gauge"> | |
| 477 | + <div class="glab"><span>P10</span><span>fourchette honnête — jamais un faux prix unique</span><span>P90</span></div> | |
| 478 | + <div class="gtrack"><div class="gband" style="left:18%;width:64%"></div><div class="gmid" style="left:50%"></div></div> | |
| 479 | + </div> | |
| 480 | + </div> | |
| 481 | + <div class="card evalcard"> | |
| 482 | + <span class="k"><span class="brand"><i style="background:#ff9f45"></i></span>ValoPlex · immeubles à revenus</span> | |
| 483 | + <span class="big">393 867</span> | |
| 484 | + <span class="lbl">plex évalués · 1 733 744 portes</span> | |
| 485 | + <p>Le moteur spécialisé des plex, entraîné exclusivement sur ~91 060 ventes réelles d'immeubles à | |
| 486 | + revenus. L'unité d'analyse : la porte. Fourchette calibrée — 8 fois sur 10, le vrai prix tombe dedans.</p> | |
| 487 | + <div class="gauge"> | |
| 488 | + <div class="glab"><span>P10</span><span>valeur totale et valeur par porte, calcul montré en entier</span><span>P90</span></div> | |
| 489 | + <div class="gtrack"><div class="gband" style="left:22%;width:56%"></div><div class="gmid" style="left:50%"></div></div> | |
| 490 | + </div> | |
| 491 | + </div> | |
| 492 | + </div> | |
| 493 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 09 · Les moteurs d'évaluation</span></footer> | |
| 494 | +</section> | |
| 495 | + | |
| 496 | +<!-- ============================ PAGE 11 — LE SOCLE ============================ --> | |
| 497 | +<section class="page"> | |
| 498 | + <header class="phead"> | |
| 499 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 500 | + <span class="doc">L'agrégation, expliquée</span> | |
| 501 | + <span class="pnum">11 / 12</span> | |
| 502 | + </header> | |
| 503 | + <p class="kicker">Chapitre 10 · Une seule fondation</p> | |
| 504 | + <h2>Le socle : <span class="hl">onze plateformes</span></h2> | |
| 505 | + <div class="tbl"> | |
| 506 | + <table id="socle-table"> | |
| 507 | + <thead><tr><th>Plateforme</th><th>Marché</th><th>Moteur</th><th>Adresse</th></tr></thead> | |
| 508 | + <tbody></tbody> | |
| 509 | + </table> | |
| 510 | + </div> | |
| 511 | + <footer class="pfoot"><span>groupe-ka.com</span><span>Chapitre 10 · Le socle</span></footer> | |
| 512 | +</section> | |
| 513 | + | |
| 514 | +<!-- ============================ PAGE 12 — MANIFESTE ============================ --> | |
| 515 | +<section class="page dark"> | |
| 516 | + <header class="phead"> | |
| 517 | + <span class="wordmark">Groupe <span class="kabox">KA</span></span> | |
| 518 | + <span class="doc">L'agrégation, expliquée</span> | |
| 519 | + <span class="pnum">12 / 12</span> | |
| 520 | + </header> | |
| 521 | + <p class="kicker">Notre manifeste</p> | |
| 522 | + <h2>Trois principes, <span class="hl">aucune exception</span></h2> | |
| 523 | + <div class="mani-grid"> | |
| 524 | + <div class="mani"><span class="n">01</span><h3>Zéro boîte noire</h3> | |
| 525 | + <p>Un score magique n'est pas une réponse. Chaque estimation montre ses comparables, ses ajustements | |
| 526 | + en dollars et sa fourchette d'incertitude. Si le calcul ne peut pas être montré, il ne sort pas.</p></div> | |
| 527 | + <div class="mani"><span class="n">02</span><h3>Données réelles</h3> | |
| 528 | + <p>Rôles d'évaluation foncière, ventes publiées, annonces vérifiées à la source. Aucun prix inventé : | |
| 529 | + quand la donnée n'existe pas, on affiche qu'elle n'existe pas. Validé sur des ventes jamais vues.</p></div> | |
| 530 | + <div class="mani"><span class="n">03</span><h3>Indépendance</h3> | |
| 531 | + <p>Aucun courtier ni annonceur ne paie pour influencer un résultat. Pas de traceur publicitaire, pas de | |
| 532 | + revente de données. Le français d'abord, le Québec comme terrain, la Loi 25 comme plancher.</p></div> | |
| 533 | + </div> | |
| 534 | + <div class="contact"> | |
| 535 | + <span><span class="mail">contact@groupe-ka.com</span><br /><span class="role">Projets, partenariats & données</span></span> | |
| 536 | + <span><span class="mail">info@groupe-ka.com</span><br /><span class="role">Médias & questions générales</span></span> | |
| 537 | + <span><span class="mail">admin@groupe-ka.com</span><br /><span class="role">Légal, vie privée & Loi 25</span></span> | |
| 538 | + </div> | |
| 539 | + <footer class="pfoot"><span>groupe-ka.com</span><span>© 2026 Groupe KA — Simon-Pierre Boucher · contact@spboucher.ai</span></footer> | |
| 540 | +</section> | |
| 541 | + | |
| 542 | +<script> | |
| 543 | +"use strict"; | |
| 544 | +/* Figures générées : mêmes données et même langage graphique que le site groupe-ka.com. */ | |
| 545 | + | |
| 546 | +const INK = "#141814", GREEN = "#1c5c41", LIME = "#d9f26b", PAPER = "#f5f3ee", | |
| 547 | + INK3 = "#8b928c", WHITE = "#ffffff"; | |
| 548 | + | |
| 549 | +const PLATFORMS = [ | |
| 550 | + { name: "Lou·Ka", accent: "#d9f26b", domain: "lou-ka.com" }, | |
| 551 | + { name: "Immo·Ka", accent: "#e23744", domain: "immo-ka.com" }, | |
| 552 | + { name: "Vrai-Prix",accent: "#ff5148", domain: "vrai-prix.com" }, | |
| 553 | + { name: "ValoPlex", accent: "#ff9f45", domain: "valoplex.com" }, | |
| 554 | + { name: "Auto·Ka", accent: "#ff5a2a", domain: "auto-ka.com" }, | |
| 555 | + { name: "Fabri·Ka", accent: "#C4532E", domain: "fabri-ka.com" }, | |
| 556 | + { name: "Food·Ka", accent: "#d9f26b", domain: "food-ka.com" }, | |
| 557 | + { name: "Resto·Ka", accent: "#ffc53d", domain: "resto-ka.com" }, | |
| 558 | + { name: "Sorti·Ka", accent: "#ff6a00", domain: "sorti-ka.com" }, | |
| 559 | + { name: "Créa·Ka", accent: "#7da2ff", domain: "crea-ka.com" }, | |
| 560 | +]; | |
| 561 | +const ACCENT = Object.fromEntries(PLATFORMS.map(p => [p.name, p.accent])); | |
| 562 | + | |
| 563 | +const SOURCES = [ | |
| 564 | + "Gestionnaires immobiliers", "Agences de courtage", "Rôles fonciers (MAMH)", | |
| 565 | + "Registres de ventes", "Concessionnaires autos", "Marchands d'occasion", | |
| 566 | + "Boutiques québécoises", "Sites de fabricants", "Bannières d'épicerie", | |
| 567 | + "Circulaires & soldes", "Menus de restaurants", "Billetteries d'événements", | |
| 568 | + "Profils de créateurs", | |
| 569 | +]; | |
| 570 | + | |
| 571 | +/* ---------- Couverture : pilules des marques ---------- */ | |
| 572 | +(function () { | |
| 573 | + const wrap = document.getElementById("cover-marks"); | |
| 574 | + const marks = [ | |
| 575 | + ["Trouve","Ka","#b9cfee","#141814"], ["Lou","Ka","#d9f26b","#141814"], | |
| 576 | + ["Immo","Ka","#e23744","#1a1214"], ["Vrai","Prix","#ff5148","#141814"], | |
| 577 | + ["Valo","Plex","#ff9f45","#141814"], ["Auto","Ka","#ff5a2a","#17181c"], | |
| 578 | + ["Fabri","Ka","#C4532E","#1E1B16"], ["Food","Ka","#d9f26b","#14231a"], | |
| 579 | + ["Resto","Ka","#ffc53d","#1a1410"], ["Sorti","Ka","#ff6a00","#0b1330"], | |
| 580 | + ["Créa","Ka","#7da2ff","#10162b"], | |
| 581 | + ]; | |
| 582 | + for (const [l, r, accent, box] of marks) { | |
| 583 | + const s = document.createElement("span"); | |
| 584 | + s.className = "markpill"; | |
| 585 | + s.innerHTML = `${l}<i style="background:${box};color:${accent}">${r}</i>`; | |
| 586 | + wrap.appendChild(s); | |
| 587 | + } | |
| 588 | +})(); | |
| 589 | + | |
| 590 | +/* ---------- Utilitaire SVG ---------- */ | |
| 591 | +function svgEl(tag, attrs, text) { | |
| 592 | + const el = document.createElementNS("http://www.w3.org/2000/svg", tag); | |
| 593 | + for (const k in attrs) el.setAttribute(k, attrs[k]); | |
| 594 | + if (text != null) el.textContent = text; | |
| 595 | + return el; | |
| 596 | +} | |
| 597 | +function makeSvg(w, h, label) { | |
| 598 | + const svg = svgEl("svg", { viewBox: `0 0 ${w} ${h}`, role: "img", "aria-label": label }); | |
| 599 | + const marker = svgEl("marker", { id: "arr" + Math.floor(w), viewBox: "0 0 10 10", refX: 8, refY: 5, markerWidth: 6.5, markerHeight: 6.5, orient: "auto-start-reverse" }); | |
| 600 | + marker.appendChild(svgEl("path", { d: "M0,0 L10,5 L0,10 z", fill: INK })); | |
| 601 | + const defs = svgEl("defs", {}); | |
| 602 | + defs.appendChild(marker); | |
| 603 | + svg.appendChild(defs); | |
| 604 | + svg.dataset.marker = "arr" + Math.floor(w); | |
| 605 | + return svg; | |
| 606 | +} | |
| 607 | +function brandChip(svg, x, y, accent, s = 16) { | |
| 608 | + svg.appendChild(svgEl("rect", { x, y, width: s, height: s, rx: 4, fill: INK })); | |
| 609 | + svg.appendChild(svgEl("rect", { x: x + 3, y: y + 3, width: s - 6, height: s - 6, rx: 2.5, fill: accent })); | |
| 610 | +} | |
| 611 | + | |
| 612 | +/* ---------- 1. Le sablier (page 3) ---------- */ | |
| 613 | +(function () { | |
| 614 | + const W = 1150, H = 640; | |
| 615 | + const svg = makeSvg(W, H, "Le sablier de l'agrégation KA"); | |
| 616 | + const M = svg.dataset.marker; | |
| 617 | + const srcX = 218, kaL = 500, kaR = 648, kaY = H / 2, dstX = 900; | |
| 618 | + | |
| 619 | + const srcYs = SOURCES.map((_, i) => 50 + i * (H - 100) / (SOURCES.length - 1)); | |
| 620 | + const dstYs = PLATFORMS.map((_, i) => 62 + i * (H - 124) / (PLATFORMS.length - 1)); | |
| 621 | + | |
| 622 | + srcYs.forEach((y, i) => svg.appendChild(svgEl("path", { | |
| 623 | + d: `M ${srcX} ${y} C ${srcX + 140} ${y}, ${kaL - 120} ${kaY}, ${kaL - 8} ${kaY + (i - (SOURCES.length - 1) / 2) * 6}`, | |
| 624 | + fill: "none", stroke: INK, "stroke-width": 1.5, "stroke-opacity": 0.5, "stroke-dasharray": "7 7", | |
| 625 | + }))); | |
| 626 | + dstYs.forEach((y, i) => svg.appendChild(svgEl("path", { | |
| 627 | + d: `M ${kaR + 4} ${kaY + (i - (PLATFORMS.length - 1) / 2) * 8} C ${kaR + 120} ${kaY}, ${dstX - 130} ${y}, ${dstX - 12} ${y}`, | |
| 628 | + fill: "none", stroke: INK, "stroke-width": 2, "stroke-opacity": 0.75, "marker-end": `url(#${M})`, "stroke-dasharray": "7 7", | |
| 629 | + }))); | |
| 630 | + | |
| 631 | + SOURCES.forEach((label, i) => { | |
| 632 | + svg.appendChild(svgEl("rect", { x: 16, y: srcYs[i] - 15, width: srcX - 16, height: 30, rx: 6, fill: WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 633 | + svg.appendChild(svgEl("circle", { cx: 33, cy: srcYs[i], r: 3.5, fill: GREEN })); | |
| 634 | + svg.appendChild(svgEl("text", { x: 46, y: srcYs[i] + 3.5, "font-size": 11, "font-weight": 700, fill: INK }, label)); | |
| 635 | + }); | |
| 636 | + svg.appendChild(svgEl("text", { x: 16, y: 24, "font-size": 11.5, "font-weight": 700, "letter-spacing": 1.5, fill: INK3 }, "DES CENTAINES DE SITES")); | |
| 637 | + | |
| 638 | + const gLbl = svgEl("g", { transform: `translate(${(srcX + kaL) / 2 - 6} ${kaY - 128}) rotate(-4)` }); | |
| 639 | + gLbl.appendChild(svgEl("rect", { x: -102, y: -25, width: 204, height: 50, rx: 13, fill: LIME, stroke: INK, "stroke-width": 1.5 })); | |
| 640 | + gLbl.appendChild(svgEl("text", { x: 0, y: -4, "text-anchor": "middle", "font-size": 12, "font-weight": 700, "letter-spacing": 1, fill: INK }, "450+ CONNECTEURS")); | |
| 641 | + gLbl.appendChild(svgEl("text", { x: 0, y: 14, "text-anchor": "middle", "font-size": 9, fill: "#2c3a2c" }, "lecture à la source · normalisation")); | |
| 642 | + svg.appendChild(gLbl); | |
| 643 | + | |
| 644 | + const gKa = svgEl("g", { transform: `translate(${(kaL + kaR) / 2} ${kaY}) rotate(-2)` }); | |
| 645 | + gKa.appendChild(svgEl("rect", { x: -(kaR - kaL) / 2 + 6, y: -66, width: kaR - kaL, height: 146, rx: 12, fill: "rgba(20,24,20,0.18)" })); | |
| 646 | + gKa.appendChild(svgEl("rect", { x: -(kaR - kaL) / 2, y: -72, width: kaR - kaL, height: 146, rx: 12, fill: INK })); | |
| 647 | + const kaTxt = svgEl("text", { x: 0, y: 8, "text-anchor": "middle", "font-size": 62, "font-weight": 700, "letter-spacing": -2, fill: LIME }, "KA"); | |
| 648 | + kaTxt.setAttribute("class", "t-display"); | |
| 649 | + gKa.appendChild(kaTxt); | |
| 650 | + gKa.appendChild(svgEl("text", { x: 0, y: 37, "text-anchor": "middle", "font-size": 11, "font-weight": 700, "letter-spacing": 2, fill: "rgba(245,243,238,0.75)" }, "= AGRÉGATION")); | |
| 651 | + gKa.appendChild(svgEl("text", { x: 0, y: 55, "text-anchor": "middle", "font-size": 8.5, fill: "rgba(245,243,238,0.55)" }, "dédoublonner · géocoder")); | |
| 652 | + svg.appendChild(gKa); | |
| 653 | + | |
| 654 | + PLATFORMS.forEach((p, i) => { | |
| 655 | + const y = dstYs[i]; | |
| 656 | + svg.appendChild(svgEl("rect", { x: dstX, y: y - 21, width: W - dstX - 14, height: 42, rx: 8, fill: WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 657 | + brandChip(svg, dstX + 13, y - 8, p.accent); | |
| 658 | + const nm = svgEl("text", { x: dstX + 40, y: y - 1, "font-size": 15.5, "font-weight": 700, fill: INK }, p.name); | |
| 659 | + nm.setAttribute("class", "t-display"); | |
| 660 | + svg.appendChild(nm); | |
| 661 | + svg.appendChild(svgEl("text", { x: dstX + 40, y: y + 14, "font-size": 9, fill: INK3 }, p.domain)); | |
| 662 | + }); | |
| 663 | + svg.appendChild(svgEl("text", { x: dstX, y: 30, "font-size": 11.5, "font-weight": 700, "letter-spacing": 1.5, fill: INK3 }, "DIX PLATEFORMES")); | |
| 664 | + | |
| 665 | + document.getElementById("fig-sablier").appendChild(svg); | |
| 666 | +})(); | |
| 667 | + | |
| 668 | +/* ---------- 2. Le pipeline horizontal (page 4) ---------- */ | |
| 669 | +(function () { | |
| 670 | + const STEPS = [ | |
| 671 | + ["01", "Site source", "La page publique, telle que tout le monde la voit."], | |
| 672 | + ["02", "Connecteur", "Un lecteur dédié par site — il connaît sa structure par cœur."], | |
| 673 | + ["03", "Normalisation", "Mêmes champs, mêmes unités : un 3½ est un 3½ partout."], | |
| 674 | + ["04", "Dédoublonnage & géocodage", "Une seule fiche par annonce réelle, posée sur la carte."], | |
| 675 | + ["05", "Publication", "En ligne, datée, traçable jusqu'à la source originale."], | |
| 676 | + ]; | |
| 677 | + const W = 1150, H = 430, boxW = 205, boxH = 210, gap = (W - 40 - 5 * boxW) / 4, top = 90; | |
| 678 | + const svg = makeSvg(W, H, "Le pipeline de la donnée KA"); | |
| 679 | + const M = svg.dataset.marker; | |
| 680 | + | |
| 681 | + STEPS.forEach((s, i) => { | |
| 682 | + const x = 20 + i * (boxW + gap); | |
| 683 | + const last = i === STEPS.length - 1; | |
| 684 | + svg.appendChild(svgEl("rect", { x: x + 5, y: top + 5, width: boxW, height: boxH, rx: 12, fill: "rgba(20,24,20,0.14)" })); | |
| 685 | + svg.appendChild(svgEl("rect", { x, y: top, width: boxW, height: boxH, rx: 12, fill: last ? INK : WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 686 | + svg.appendChild(svgEl("text", { x: x + 18, y: top + 36, "font-size": 13, "font-weight": 700, fill: last ? LIME : GREEN }, s[0])); | |
| 687 | + const t = svgEl("text", { x: x + 18, y: top + 68, "font-size": 16.5, "font-weight": 700, fill: last ? PAPER : INK }); | |
| 688 | + t.setAttribute("class", "t-display"); | |
| 689 | + // titre sur 1-2 lignes | |
| 690 | + const words = s[1].split(" "); | |
| 691 | + let line = "", lines = []; | |
| 692 | + for (const w of words) { | |
| 693 | + if ((line + " " + w).trim().length > 15) { lines.push(line.trim()); line = w; } else line += " " + w; | |
| 694 | + } | |
| 695 | + lines.push(line.trim()); | |
| 696 | + lines.forEach((ln, li) => t.appendChild(svgEl("tspan", { x: x + 18, dy: li === 0 ? 0 : 21 }, ln))); | |
| 697 | + svg.appendChild(t); | |
| 698 | + // description | |
| 699 | + const d = svgEl("text", { x: x + 18, y: top + 68 + lines.length * 21 + 12, "font-size": 10.5, fill: last ? "rgba(245,243,238,0.7)" : "#4d5551" }); | |
| 700 | + d.setAttribute("class", "t-body"); | |
| 701 | + let dl = "", dls = []; | |
| 702 | + for (const w of s[2].split(" ")) { | |
| 703 | + if ((dl + " " + w).trim().length > 28) { dls.push(dl.trim()); dl = w; } else dl += " " + w; | |
| 704 | + } | |
| 705 | + dls.push(dl.trim()); | |
| 706 | + dls.forEach((ln, li) => d.appendChild(svgEl("tspan", { x: x + 18, dy: li === 0 ? 0 : 15 }, ln))); | |
| 707 | + svg.appendChild(d); | |
| 708 | + if (!last) svg.appendChild(svgEl("line", { | |
| 709 | + x1: x + boxW + 6, y1: top + boxH / 2, x2: x + boxW + gap - 8, y2: top + boxH / 2, | |
| 710 | + stroke: INK, "stroke-width": 2, "marker-end": `url(#${M})`, "stroke-dasharray": "7 7", | |
| 711 | + })); | |
| 712 | + }); | |
| 713 | + | |
| 714 | + // boucle de resynchronisation : dernière station → première | |
| 715 | + const y0 = top + boxH + 44; | |
| 716 | + svg.appendChild(svgEl("path", { | |
| 717 | + d: `M ${20 + 4 * (boxW + gap) + boxW / 2} ${top + boxH + 6} L ${20 + 4 * (boxW + gap) + boxW / 2} ${y0} | |
| 718 | + L ${20 + boxW / 2} ${y0} L ${20 + boxW / 2} ${top + boxH + 14}`, | |
| 719 | + fill: "none", stroke: GREEN, "stroke-width": 2.5, "marker-end": `url(#${M})`, "stroke-dasharray": "7 7", | |
| 720 | + })); | |
| 721 | + const gR = svgEl("g", { transform: `translate(${W / 2} ${y0}) rotate(-2)` }); | |
| 722 | + gR.appendChild(svgEl("rect", { x: -110, y: -17, width: 220, height: 34, rx: 17, fill: LIME, stroke: INK, "stroke-width": 1.5 })); | |
| 723 | + gR.appendChild(svgEl("text", { x: 0, y: 4.5, "text-anchor": "middle", "font-size": 11.5, "font-weight": 700, "letter-spacing": 1, fill: INK }, "RESYNC EN CONTINU")); | |
| 724 | + svg.appendChild(gR); | |
| 725 | + svg.appendChild(svgEl("text", { x: 20, y: 34, "font-size": 11.5, "font-weight": 700, "letter-spacing": 1.5, fill: INK3 }, "CINQ STATIONS · AUCUNE MAIN HUMAINE")); | |
| 726 | + | |
| 727 | + document.getElementById("fig-pipeline").appendChild(svg); | |
| 728 | +})(); | |
| 729 | + | |
| 730 | +/* ---------- 3. Le cycle (page 5) ---------- */ | |
| 731 | +(function () { | |
| 732 | + const STEPS = [ | |
| 733 | + ["01", "Lire les sources", -90], ["02", "Comparer à hier", 0], | |
| 734 | + ["03", "Mettre à jour", 90], ["04", "Publier & tracer", 180], | |
| 735 | + ]; | |
| 736 | + const W = 1150, S = 440, cx = W / 2, cy = S / 2 + 4, R = 150; | |
| 737 | + const svg = makeSvg(W, S, "Le cycle KA, jour et nuit"); | |
| 738 | + const M = svg.dataset.marker; | |
| 739 | + | |
| 740 | + svg.appendChild(svgEl("circle", { cx, cy, r: R, fill: "none", stroke: INK, "stroke-width": 2, "stroke-opacity": 0.65, "stroke-dasharray": "7 7" })); | |
| 741 | + [-45, 45, 135, 225].forEach((a) => { | |
| 742 | + const rad = a * Math.PI / 180, x = cx + R * Math.cos(rad), y = cy + R * Math.sin(rad); | |
| 743 | + const tx = -Math.sin(rad), ty = Math.cos(rad); | |
| 744 | + svg.appendChild(svgEl("line", { x1: x - tx, y1: y - ty, x2: x + tx * 2, y2: y + ty * 2, stroke: INK, "stroke-width": 2, "marker-end": `url(#${M})` })); | |
| 745 | + }); | |
| 746 | + | |
| 747 | + const gKa = svgEl("g", { transform: `translate(${cx} ${cy}) rotate(-2)` }); | |
| 748 | + gKa.appendChild(svgEl("rect", { x: -43, y: -37, width: 96, height: 88, rx: 10, fill: "rgba(20,24,20,0.18)" })); | |
| 749 | + gKa.appendChild(svgEl("rect", { x: -48, y: -42, width: 96, height: 88, rx: 10, fill: INK })); | |
| 750 | + const t = svgEl("text", { x: 0, y: 8, "text-anchor": "middle", "font-size": 40, "font-weight": 700, "letter-spacing": -1.5, fill: LIME }, "KA"); | |
| 751 | + t.setAttribute("class", "t-display"); | |
| 752 | + gKa.appendChild(t); | |
| 753 | + gKa.appendChild(svgEl("text", { x: 0, y: 30, "text-anchor": "middle", "font-size": 8, "letter-spacing": 1.5, fill: "rgba(245,243,238,0.65)" }, "JOUR ET NUIT")); | |
| 754 | + svg.appendChild(gKa); | |
| 755 | + | |
| 756 | + STEPS.forEach(([num, label, angle]) => { | |
| 757 | + const rad = angle * Math.PI / 180, x = cx + R * Math.cos(rad), y = cy + R * Math.sin(rad), w = 178; | |
| 758 | + const g = svgEl("g", { transform: `translate(${x} ${y})` }); | |
| 759 | + g.appendChild(svgEl("rect", { x: -w / 2, y: -21, width: w, height: 42, rx: 9, fill: WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 760 | + g.appendChild(svgEl("text", { x: -w / 2 + 13, y: 5, "font-size": 11, "font-weight": 700, fill: GREEN }, num)); | |
| 761 | + const l = svgEl("text", { x: -w / 2 + 38, y: 5, "font-size": 14, "font-weight": 700, fill: INK }, label); | |
| 762 | + l.setAttribute("class", "t-display"); | |
| 763 | + g.appendChild(l); | |
| 764 | + svg.appendChild(g); | |
| 765 | + }); | |
| 766 | + | |
| 767 | + // légendes latérales | |
| 768 | + const notes = [ | |
| 769 | + [30, "Une annonce retirée à la source", "disparaît d'elle-même du site."], | |
| 770 | + [W - 260, "Une baisse de prix est détectée", "et historisée automatiquement."], | |
| 771 | + ]; | |
| 772 | + notes.forEach(([x, l1, l2]) => { | |
| 773 | + svg.appendChild(svgEl("rect", { x, y: cy - 34, width: 230, height: 68, rx: 9, fill: "#faf9f5", stroke: INK, "stroke-width": 1.5, "stroke-dasharray": "5 5" })); | |
| 774 | + const n = svgEl("text", { x: x + 16, y: cy - 8, "font-size": 11, fill: "#4d5551" }); | |
| 775 | + n.setAttribute("class", "t-body"); | |
| 776 | + n.appendChild(svgEl("tspan", { x: x + 16, dy: 0 }, l1)); | |
| 777 | + n.appendChild(svgEl("tspan", { x: x + 16, dy: 17 }, l2)); | |
| 778 | + svg.appendChild(n); | |
| 779 | + }); | |
| 780 | + | |
| 781 | + document.getElementById("fig-cycle").appendChild(svg); | |
| 782 | +})(); | |
| 783 | + | |
| 784 | +/* ---------- 4. Les éclaireurs (page 6) ---------- */ | |
| 785 | +(function () { | |
| 786 | + const W = 1300, H = 470; | |
| 787 | + const svg = makeSvg(W, H, "Les bots de découverte Ka2, Ka4 et Ka6"); | |
| 788 | + const M = svg.dataset.marker; | |
| 789 | + | |
| 790 | + // nuage du web québécois (points) | |
| 791 | + const cloud = { x: 30, y: 60, w: 260, h: 340 }; | |
| 792 | + svg.appendChild(svgEl("rect", { x: cloud.x, y: cloud.y, width: cloud.w, height: cloud.h, rx: 14, fill: WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 793 | + let seed = 7; | |
| 794 | + const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647; | |
| 795 | + for (let i = 0; i < 130; i++) { | |
| 796 | + svg.appendChild(svgEl("circle", { | |
| 797 | + cx: cloud.x + 18 + rnd() * (cloud.w - 36), cy: cloud.y + 34 + rnd() * (cloud.h - 56), | |
| 798 | + r: 2 + rnd() * 2.4, fill: rnd() > 0.82 ? GREEN : "rgba(20,24,20,0.35)", | |
| 799 | + })); | |
| 800 | + } | |
| 801 | + svg.appendChild(svgEl("text", { x: cloud.x + 16, y: cloud.y + 22, "font-size": 10.5, "font-weight": 700, "letter-spacing": 1.2, fill: INK3 }, "LE WEB QUÉBÉCOIS")); | |
| 802 | + | |
| 803 | + // bots | |
| 804 | + const bots = [ | |
| 805 | + ["Ka", "2", "#d9f26b", "cartographie les entités", 96], | |
| 806 | + ["Ka", "4", "#ff9f45", "activité à découvert", 232], | |
| 807 | + ["Ka", "6", "#b9cfee", "nourrit Trouve·Ka", 368], | |
| 808 | + ]; | |
| 809 | + const botX = 430, botW = 250; | |
| 810 | + bots.forEach(([l, d, accent, sub, y]) => { | |
| 811 | + svg.appendChild(svgEl("path", { d: `M ${cloud.x + cloud.w + 6} ${cloud.y + cloud.h / 2} C ${botX - 90} ${cloud.y + cloud.h / 2}, ${botX - 90} ${y}, ${botX - 8} ${y}`, fill: "none", stroke: INK, "stroke-width": 1.7, "stroke-opacity": 0.6, "stroke-dasharray": "7 7", "marker-end": `url(#${M})` })); | |
| 812 | + svg.appendChild(svgEl("rect", { x: botX, y: y - 36, width: botW, height: 72, rx: 11, fill: WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 813 | + const t = svgEl("text", { x: botX + 18, y: y + 2, "font-size": 25, "font-weight": 700, "letter-spacing": -1, fill: INK }); | |
| 814 | + t.setAttribute("class", "t-display"); | |
| 815 | + t.appendChild(svgEl("tspan", {}, l)); | |
| 816 | + svg.appendChild(t); | |
| 817 | + svg.appendChild(svgEl("rect", { x: botX + 55, y: y - 20, width: 28, height: 28, rx: 6, fill: INK, transform: `rotate(-4 ${botX + 69} ${y - 6})` })); | |
| 818 | + const dt = svgEl("text", { x: botX + 69, y: y + 2, "text-anchor": "middle", "font-size": 19, "font-weight": 700, fill: accent }); | |
| 819 | + dt.setAttribute("class", "t-display"); | |
| 820 | + dt.textContent = d; | |
| 821 | + svg.appendChild(dt); | |
| 822 | + svg.appendChild(svgEl("text", { x: botX + 100, y: y - 2, "font-size": 10.5, "font-weight": 700, fill: GREEN }, "BOT IA")); | |
| 823 | + const s = svgEl("text", { x: botX + 100, y: y + 15, "font-size": 10.5, fill: "#4d5551" }, sub); | |
| 824 | + s.setAttribute("class", "t-body"); | |
| 825 | + svg.appendChild(s); | |
| 826 | + svg.appendChild(svgEl("path", { d: `M ${botX + botW + 4} ${y} C ${botX + botW + 80} ${y}, ${botX + botW + 80} ${H / 2}, ${botX + botW + 130} ${H / 2}`, fill: "none", stroke: INK, "stroke-width": 1.7, "stroke-opacity": 0.6, "stroke-dasharray": "7 7", "marker-end": `url(#${M})` })); | |
| 827 | + }); | |
| 828 | + | |
| 829 | + // registre d'entités | |
| 830 | + const regX = botX + botW + 140, regW = 180; | |
| 831 | + svg.appendChild(svgEl("rect", { x: regX + 5, y: H / 2 - 55 + 5, width: regW, height: 110, rx: 12, fill: "rgba(20,24,20,0.14)" })); | |
| 832 | + svg.appendChild(svgEl("rect", { x: regX, y: H / 2 - 55, width: regW, height: 110, rx: 12, fill: INK })); | |
| 833 | + const rt = svgEl("text", { x: regX + regW / 2, y: H / 2 - 12, "text-anchor": "middle", "font-size": 17, "font-weight": 700, fill: LIME }); | |
| 834 | + rt.setAttribute("class", "t-display"); | |
| 835 | + rt.textContent = "Registre"; | |
| 836 | + svg.appendChild(rt); | |
| 837 | + const rt2 = svgEl("text", { x: regX + regW / 2, y: H / 2 + 10, "text-anchor": "middle", "font-size": 17, "font-weight": 700, fill: LIME }); | |
| 838 | + rt2.setAttribute("class", "t-display"); | |
| 839 | + rt2.textContent = "d'entités"; | |
| 840 | + svg.appendChild(rt2); | |
| 841 | + svg.appendChild(svgEl("text", { x: regX + regW / 2, y: H / 2 + 34, "text-anchor": "middle", "font-size": 8.5, "letter-spacing": 1, fill: "rgba(245,243,238,0.6)" }, "entreprises · personnes · sites")); | |
| 842 | + | |
| 843 | + // vers les connecteurs | |
| 844 | + svg.appendChild(svgEl("line", { x1: regX + regW + 6, y1: H / 2, x2: regX + regW + 78, y2: H / 2, stroke: INK, "stroke-width": 2.2, "marker-end": `url(#${M})`, "stroke-dasharray": "7 7" })); | |
| 845 | + const outX = regX + regW + 90; | |
| 846 | + svg.appendChild(svgEl("rect", { x: outX, y: H / 2 - 62, width: W - outX - 16, height: 124, rx: 12, fill: LIME, stroke: INK, "stroke-width": 1.5 })); | |
| 847 | + const ot = svgEl("text", { x: outX + 18, y: H / 2 - 26, "font-size": 16, "font-weight": 700, fill: INK }); | |
| 848 | + ot.setAttribute("class", "t-display"); | |
| 849 | + ot.textContent = "Nouvelles sources"; | |
| 850 | + svg.appendChild(ot); | |
| 851 | + const ol = svgEl("text", { x: outX + 18, y: H / 2 - 2, "font-size": 10.5, fill: "#2c3a2c" }); | |
| 852 | + ol.setAttribute("class", "t-body"); | |
| 853 | + ["Boutique qui ouvre, gestionnaire,", "concessionnaire : repérés par les", "bots, branchés par les ·Ka."].forEach((ln, i) => | |
| 854 | + ol.appendChild(svgEl("tspan", { x: outX + 18, dy: i === 0 ? 0 : 16 }, ln))); | |
| 855 | + svg.appendChild(ol); | |
| 856 | + | |
| 857 | + document.getElementById("fig-bots").appendChild(svg); | |
| 858 | +})(); | |
| 859 | + | |
| 860 | +/* ---------- 5. Trouve·Ka (page 7) ---------- */ | |
| 861 | +(function () { | |
| 862 | + const W = 1150, H = 470; | |
| 863 | + const svg = makeSvg(W, H, "Trouve·Ka, le moteur de recherche du web québécois"); | |
| 864 | + const M = svg.dataset.marker; | |
| 865 | + | |
| 866 | + // robot → index → barre de recherche → résultats | |
| 867 | + const steps = [ | |
| 868 | + { x: 30, w: 250, title: "Le robot", sub: ["Parcourt le web québécois", "jour et nuit — et lui seul.", "robots.txt respecté."], tag: "CRAWLER DÉDIÉ" }, | |
| 869 | + { x: 330, w: 250, title: "L'index", sub: ["Des milliers de domaines d'ici,", "recherche plein texte,", "croissance heure par heure."], tag: "100 % QUÉBEC" }, | |
| 870 | + ]; | |
| 871 | + steps.forEach((s) => { | |
| 872 | + svg.appendChild(svgEl("rect", { x: s.x, y: 80, width: s.w, height: 170, rx: 12, fill: WHITE, stroke: INK, "stroke-width": 1.5 })); | |
| 873 | + svg.appendChild(svgEl("text", { x: s.x + 18, y: 112, "font-size": 10, "font-weight": 700, "letter-spacing": 1.2, fill: GREEN }, s.tag)); | |
| 874 | + const t = svgEl("text", { x: s.x + 18, y: 144, "font-size": 21, "font-weight": 700, fill: INK }); | |
| 875 | + t.setAttribute("class", "t-display"); | |
| 876 | + t.textContent = s.title; | |
| 877 | + svg.appendChild(t); | |
| 878 | + const d = svgEl("text", { x: s.x + 18, y: 170, "font-size": 11, fill: "#4d5551" }); | |
| 879 | + d.setAttribute("class", "t-body"); | |
| 880 | + s.sub.forEach((ln, i) => d.appendChild(svgEl("tspan", { x: s.x + 18, dy: i === 0 ? 0 : 17 }, ln))); | |
| 881 | + svg.appendChild(d); | |
| 882 | + }); | |
| 883 | + svg.appendChild(svgEl("line", { x1: 284, y1: 165, x2: 322, y2: 165, stroke: INK, "stroke-width": 2, "marker-end": `url(#${M})`, "stroke-dasharray": "7 7" })); | |
| 884 | + svg.appendChild(svgEl("line", { x1: 584, y1: 165, x2: 622, y2: 165, stroke: INK, "stroke-width": 2, "marker-end": `url(#${M})`, "stroke-dasharray": "7 7" })); | |
| 885 | + | |
| 886 | + // barre de recherche | |
| 887 | + const sbX = 634, sbW = 480; | |
| 888 | + svg.appendChild(svgEl("rect", { x: sbX + 4, y: 134 + 4, width: sbW, height: 62, rx: 31, fill: "rgba(20,24,20,0.5)" })); | |
| 889 | + svg.appendChild(svgEl("rect", { x: sbX, y: 134, width: sbW, height: 62, rx: 31, fill: WHITE, stroke: INK, "stroke-width": 2 })); | |
| 890 | + svg.appendChild(svgEl("text", { x: sbX + 26, y: 172, "font-size": 18, "font-weight": 700, fill: INK }, "⌕")); | |
| 891 | + svg.appendChild(svgEl("text", { x: sbX + 54, y: 171, "font-size": 12.5, fill: INK3 }, "poutine · plex à vendre · Gaspésie…")); | |
| 892 | + svg.appendChild(svgEl("rect", { x: sbX + sbW - 116, y: 145, width: 102, height: 40, rx: 20, fill: INK })); | |
| 893 | + const st = svgEl("text", { x: sbX + sbW - 65, y: 170, "text-anchor": "middle", "font-size": 13.5, "font-weight": 700, fill: "#b9cfee" }); | |
| 894 | + st.setAttribute("class", "t-display"); | |
| 895 | + st.textContent = "Chercher"; | |
| 896 | + svg.appendChild(st); | |
| 897 | + const wm = svgEl("text", { x: sbX, y: 118, "font-size": 21, "font-weight": 700, "letter-spacing": -0.7, fill: INK }); | |
| 898 | + wm.setAttribute("class", "t-display"); | |
| 899 | + wm.textContent = "Trouve"; | |
| 900 | + svg.appendChild(wm); | |
| 901 | + svg.appendChild(svgEl("rect", { x: sbX + 78, y: 98, width: 34, height: 26, rx: 6, fill: INK, transform: `rotate(-3 ${sbX + 95} 111)` })); | |
| 902 | + const wk = svgEl("text", { x: sbX + 95, y: 117, "text-anchor": "middle", "font-size": 16, "font-weight": 700, fill: "#b9cfee" }); | |
| 903 | + wk.setAttribute("class", "t-display"); | |
| 904 | + wk.textContent = "Ka"; | |
| 905 | + svg.appendChild(wk); | |
| 906 | + svg.appendChild(svgEl("text", { x: sbX + 124, y: 114, "font-size": 9.5, fill: INK3 }, "www.trouve-ka.com")); | |
| 907 | + | |
| 908 | + // résultats (3 lignes) | |
| 909 | + ["Microbrasserie — Rimouski", "Érablière — Lanaudière", "Blogue de quartier — Limoilou"].forEach((r, i) => { | |
| 910 | + const y = 232 + i * 46; | |
| 911 | + svg.appendChild(svgEl("rect", { x: sbX, y, width: sbW, height: 36, rx: 8, fill: "#faf9f5", stroke: INK, "stroke-width": 1.2 })); | |
| 912 | + svg.appendChild(svgEl("circle", { cx: sbX + 20, cy: y + 18, r: 3.5, fill: GREEN })); | |
| 913 | + const rt = svgEl("text", { x: sbX + 34, y: y + 23, "font-size": 12.5, "font-weight": 600, fill: INK }); | |
| 914 | + rt.setAttribute("class", "t-body"); | |
| 915 | + rt.textContent = r; | |
| 916 | + svg.appendChild(rt); | |
| 917 | + svg.appendChild(svgEl("text", { x: sbX + sbW - 20, y: y + 23, "text-anchor": "end", "font-size": 9, fill: GREEN }, "1re page")); | |
| 918 | + }); | |
| 919 | + | |
| 920 | + // note comparaison | |
| 921 | + svg.appendChild(svgEl("rect", { x: 30, y: 290, width: 554, height: 130, rx: 12, fill: INK })); | |
| 922 | + const nt = svgEl("text", { x: 56, y: 330, "font-size": 20, "font-weight": 700, "letter-spacing": -0.5, fill: PAPER }); | |
| 923 | + nt.setAttribute("class", "t-display"); | |
| 924 | + nt.appendChild(svgEl("tspan", { x: 56, dy: 0 }, "LES GRANDS MOTEURS CHERCHENT LE MONDE.")); | |
| 925 | + const sp = svgEl("tspan", { x: 56, dy: 28, fill: LIME }, "TROUVE·KA CHERCHE LE QUÉBEC."); | |
| 926 | + nt.appendChild(sp); | |
| 927 | + svg.appendChild(nt); | |
| 928 | + svg.appendChild(svgEl("text", { x: 56, y: 396, "font-size": 10, "letter-spacing": 1, fill: "rgba(245,243,238,0.6)" }, "NOURRI PAR LES ÉCLAIREURS KA2 · KA4 · KA6 — ZÉRO PUBLICITÉ, ZÉRO PROFILAGE")); | |
| 929 | + | |
| 930 | + document.getElementById("fig-trouveka").appendChild(svg); | |
| 931 | +})(); | |
| 932 | + | |
| 933 | +/* ---------- 6. Barres d'inventaire (page 8) ---------- */ | |
| 934 | +(function () { | |
| 935 | + const INVENTORY = [ | |
| 936 | + ["Fabri·Ka", 281000, "produits québécois"], | |
| 937 | + ["Resto·Ka", 61000, "plats & prix réels"], | |
| 938 | + ["Immo·Ka", 54000, "propriétés à vendre"], | |
| 939 | + ["Food·Ka", 23700, "prix d'épicerie"], | |
| 940 | + ["Auto·Ka", 15900, "véhicules d'occasion"], | |
| 941 | + ["Sorti·Ka", 11100, "événements & sorties"], | |
| 942 | + ["Lou·Ka", 7000, "logements à louer"], | |
| 943 | + ["Créa·Ka", 2200, "créateurs d'ici"], | |
| 944 | + ]; | |
| 945 | + const max = Math.max(...INVENTORY.map((d) => d[1])); | |
| 946 | + const fmt = (n) => n.toLocaleString("fr-CA"); | |
| 947 | + const wrap = document.getElementById("fig-bars"); | |
| 948 | + for (const [name, value, unit] of INVENTORY) { | |
| 949 | + const row = document.createElement("div"); | |
| 950 | + row.className = "barrow"; | |
| 951 | + row.innerHTML = | |
| 952 | + `<div class="barhead"><span class="nm"><span class="brand"><i style="background:${ACCENT[name]}"></i></span>${name}<span class="u">${unit}</span></span>` + | |
| 953 | + `<span class="v">${fmt(value)}+</span></div>` + | |
| 954 | + `<div class="bartrack"><div class="barfill" style="width:${Math.max((value / max) * 100, 1.6)}%"></div></div>`; | |
| 955 | + wrap.appendChild(row); | |
| 956 | + } | |
| 957 | +})(); | |
| 958 | + | |
| 959 | +/* ---------- 7. Matrice de connecteurs (page 9) ---------- */ | |
| 960 | +(function () { | |
| 961 | + const CONNECTORS = [ | |
| 962 | + ["Auto·Ka", 124, "concessionnaires"], | |
| 963 | + ["Lou·Ka", 108, "gestionnaires"], | |
| 964 | + ["Immo·Ka", 65, "agences"], | |
| 965 | + ["Food·Ka", 20, "bannières"], | |
| 966 | + ["Resto·Ka", 19, "chaînes de restos"], | |
| 967 | + ["Sorti·Ka", 4, "billetteries & données ouvertes"], | |
| 968 | + ]; | |
| 969 | + const wrap = document.getElementById("fig-dots"); | |
| 970 | + for (const [name, count, kind] of CONNECTORS) { | |
| 971 | + const b = document.createElement("div"); | |
| 972 | + b.className = "dots-block"; | |
| 973 | + b.innerHTML = | |
| 974 | + `<div class="barhead"><span class="nm"><span class="brand"><i style="background:${ACCENT[name]}"></i></span>${name}<span class="u">${kind}</span></span>` + | |
| 975 | + `<span class="v">${count}</span></div>` + | |
| 976 | + `<div class="dots">${"<i></i>".repeat(count)}</div>`; | |
| 977 | + wrap.appendChild(b); | |
| 978 | + } | |
| 979 | +})(); | |
| 980 | + | |
| 981 | +/* ---------- 8. Tableau du socle (page 11) ---------- */ | |
| 982 | +(function () { | |
| 983 | + const ROWS = [ | |
| 984 | + ["Trouve·Ka", "#b9cfee", "Recherche — tout le web québécois", "Crawler dédié + index de recherche plein texte", "www.trouve-ka.com"], | |
| 985 | + ["Lou·Ka", "#d9f26b", "Location résidentielle", "108 connecteurs sources + synchronisation horaire", "www.lou-ka.com"], | |
| 986 | + ["Immo·Ka", "#e23744", "Propriétés à vendre", "65 connecteurs d'agences + détection des ventes", "www.immo-ka.com"], | |
| 987 | + ["Vrai-Prix", "#ff5148", "Résidentiel — tout le Québec", "Hédonique LightGBM + comparables réels ajustés", "www.vrai-prix.com"], | |
| 988 | + ["ValoPlex", "#ff9f45", "Immeubles à revenus (plex)", "Hédonique monotone + ventes de plex comparables", "www.valoplex.com"], | |
| 989 | + ["Auto·Ka", "#ff5a2a", "Voitures usagées", "125 connecteurs concessionnaires + suivi des prix", "www.auto-ka.com"], | |
| 990 | + ["Fabri·Ka", "#C4532E", "Produits québécois", "1 204 boutiques en ligne indexées par région", "www.fabri-ka.com"], | |
| 991 | + ["Food·Ka", "#d9f26b", "Épicerie", "20 bannières — prix, soldes et prix unitaires", "www.food-ka.com"], | |
| 992 | + ["Resto·Ka", "#ffc53d", "Restaurants — menus & prix", "19 chaînes intégrées — 61 000+ plats suivis", "www.resto-ka.com"], | |
| 993 | + ["Sorti·Ka", "#ff6a00", "Sorties & événements", "Billetteries + données ouvertes — MàJ horaire", "www.sorti-ka.com"], | |
| 994 | + ["Créa·Ka", "#7da2ff", "Créateurs de contenu", "Annuaire multi-plateformes + résolution d'identité", "www.crea-ka.com"], | |
| 995 | + ]; | |
| 996 | + const tbody = document.querySelector("#socle-table tbody"); | |
| 997 | + for (const [name, accent, market, engine, url] of ROWS) { | |
| 998 | + const tr = document.createElement("tr"); | |
| 999 | + tr.innerHTML = | |
| 1000 | + `<td class="nm"><span class="brand"><i style="background:${accent}"></i></span>${name}</td>` + | |
| 1001 | + `<td>${market}</td><td>${engine}</td><td class="url">${url}</td>`; | |
| 1002 | + tbody.appendChild(tr); | |
| 1003 | + } | |
| 1004 | +})(); | |
| 1005 | +</script> | |
| 1006 | +</body> | |
| 1007 | +</html> | |
added
assets/poster/pdf/groupe-ka-agregation.pdf
+0 −0
Binary file not shown.
added
public/documents/groupe-ka-agregation.pdf
+0 −0
Binary file not shown.
modified
src/app/page.tsx
+13 −0
@@ -715,6 +715,19 @@ export default function Home() { | ||
| 715 | 715 | ramène la donnée brute, et KA la transforme en un seul endroit |
| 716 | 716 | propre. Voici la machine, à découvert. |
| 717 | 717 | </p> |
| 718 | + <div className="mt-5 flex flex-wrap items-center gap-3"> | |
| 719 | + <a | |
| 720 | + href="/documents/groupe-ka-agregation.pdf" | |
| 721 | + target="_blank" | |
| 722 | + rel="noopener noreferrer" | |
| 723 | + className="btn btn-ghost" | |
| 724 | + > | |
| 725 | + ⤓ Télécharger « L'agrégation, expliquée » — PDF · 12 pages | |
| 726 | + </a> | |
| 727 | + <span className="gk-mono text-[11px] text-ink-3"> | |
| 728 | + Tous les schémas du groupe, en un document | |
| 729 | + </span> | |
| 730 | + </div> | |
| 718 | 731 | <AggregationFigure /> |
| 719 | 732 | <div className="mt-6 grid items-start gap-6 lg:grid-cols-2"> |
| 720 | 733 | <PipelineFigure /> |
| 721 | 734 | |