spb/groupe-ka Public
Groupe KA — site du holding + KA ID (compte unique & SSO des 7 plateformes). Next.js 16, SQLite, Google & Apple login.
TypeScript 93.9%
CSS 6%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2// Figures & visualisations du Groupe KA — SVG/HTML dessinés sur mesure,3// style « éditorial sharp ». Une mesure = une teinte ; identité portée par4// les étiquettes et pastilles de marque, jamais par la couleur seule.56/* ------------------------------------------------------------------ */7/* Données partagées des figures */8/* ------------------------------------------------------------------ */910export const FIG_PLATFORMS: {11 name: string;12 accent: string;13 domain: string;14}[] = [15 { name: "Lou·Ka", accent: "#d9f26b", domain: "lou-ka.com" },16 { name: "Immo·Ka", accent: "#e23744", domain: "immo-ka.com" },17 { name: "Vrai-Prix", accent: "#ff5148", domain: "vrai-prix.com" },18 { name: "ValoPlex", accent: "#ff9f45", domain: "valoplex.com" },19 { name: "Auto·Ka", accent: "#ff5a2a", domain: "auto-ka.com" },20 { name: "Fabri·Ka", accent: "#C4532E", domain: "fabri-ka.com" },21 { name: "Food·Ka", accent: "#d9f26b", domain: "food-ka.com" },22];2324const ACCENT: Record<string, string> = Object.fromEntries(25 FIG_PLATFORMS.map((p) => [p.name, p.accent]),26);2728function BrandChip({ name }: { name: string }) {29 return (30 <span31 aria-hidden="true"32 className="inline-flex h-[16px] w-[16px] flex-none items-center justify-center rounded-[4px] bg-ink"33 >34 <span35 className="h-[10px] w-[10px] rounded-[2.5px]"36 style={{ background: ACCENT[name] ?? "#d9f26b" }}37 />38 </span>39 );40}4142function FigCaption({ lead, children }: { lead: string; children: React.ReactNode }) {43 return (44 <figcaption className="border-t-[1.5px] border-ink bg-surface-2 px-5 py-4 text-[13px] text-ink-2 sm:px-7">45 <span className="gk-mono font-bold text-green">{lead} — </span>46 {children}47 </figcaption>48 );49}5051/* ------------------------------------------------------------------ */52/* 1. Le sablier ·Ka : plein de sites → connecteurs → KA → 7 flèches */53/* ------------------------------------------------------------------ */5455const FIG_SOURCES = [56 "Gestionnaires immobiliers",57 "Agences de courtage",58 "Rôles fonciers (MAMH)",59 "Registres de ventes",60 "Concessionnaires autos",61 "Marchands d'occasion",62 "Boutiques québécoises",63 "Sites de fabricants",64 "Bannières d'épicerie",65 "Circulaires & soldes",66];6768export function AggregationFigure() {69 const W = 1060;70 const H = 620;71 const srcX = 196; // bord droit des chips sources72 const kaL = 462; // bord gauche de la boîte KA73 const kaR = 598; // bord droit de la boîte KA74 const kaY = H / 2;75 const dstX = 836; // bord gauche des chips plateformes7677 const srcYs = FIG_SOURCES.map(78 (_, i) => 52 + (i * (H - 104)) / (FIG_SOURCES.length - 1),79 );80 const dstYs = FIG_PLATFORMS.map(81 (_, i) => 70 + (i * (H - 140)) / (FIG_PLATFORMS.length - 1),82 );8384 return (85 <figure className="gk-card mt-7 overflow-hidden p-0">86 <div className="overflow-x-auto">87 <svg88 viewBox={`0 0 ${W} ${H}`}89 role="img"90 aria-label="Schéma de l'agrégation KA : des centaines de sites sources sont lus par des connecteurs, la donnée converge vers KA qui la redistribue vers les sept plateformes."91 className="block min-w-[760px]"92 >93 <defs>94 <marker95 id="ka-arrow"96 viewBox="0 0 10 10"97 refX="8"98 refY="5"99 markerWidth="7"100 markerHeight="7"101 orient="auto-start-reverse"102 >103 <path d="M0,0 L10,5 L0,10 z" fill="#141814" />104 </marker>105 </defs>106107 {/* ---- flèches sources → KA (la moitié « plein de sites ») ---- */}108 {srcYs.map((y, i) => (109 <path110 key={`in-${i}`}111 d={`M ${srcX} ${y} C ${srcX + 130} ${y}, ${kaL - 120} ${kaY}, ${kaL - 8} ${kaY + (i - (FIG_SOURCES.length - 1) / 2) * 7}`}112 fill="none"113 stroke="#141814"114 strokeWidth="1.5"115 strokeOpacity="0.5"116 className="fig-flow"117 style={{ animationDelay: `${i * 0.13}s` }}118 />119 ))}120121 {/* ---- flèches KA → plateformes (le split) ---- */}122 {dstYs.map((y, i) => (123 <path124 key={`out-${i}`}125 d={`M ${kaR + 4} ${kaY + (i - (FIG_PLATFORMS.length - 1) / 2) * 9} C ${kaR + 120} ${kaY}, ${dstX - 130} ${y}, ${dstX - 12} ${y}`}126 fill="none"127 stroke="#141814"128 strokeWidth="2"129 strokeOpacity="0.75"130 markerEnd="url(#ka-arrow)"131 className="fig-flow"132 style={{ animationDelay: `${0.4 + i * 0.11}s` }}133 />134 ))}135136 {/* ---- chips sources (plein de sites) ---- */}137 {FIG_SOURCES.map((label, i) => (138 <g key={label}>139 <rect140 x="16"141 y={srcYs[i] - 15}142 width={srcX - 16}143 height="30"144 rx="6"145 fill="#ffffff"146 stroke="#141814"147 strokeWidth="1.5"148 />149 <circle cx="32" cy={srcYs[i]} r="3.5" fill="#1c5c41" />150 <text151 x="44"152 y={srcYs[i] + 3.5}153 fontFamily="var(--font-jetbrains), monospace"154 fontSize="10.5"155 fontWeight="700"156 fill="#141814"157 >158 {label}159 </text>160 </g>161 ))}162 <text163 x="16"164 y="24"165 fontFamily="var(--font-jetbrains), monospace"166 fontSize="11"167 fontWeight="700"168 letterSpacing="1.5"169 fill="#8b928c"170 >171 DES CENTAINES DE SITES172 </text>173174 {/* ---- étiquette connecteurs sur le faisceau ---- */}175 <g transform={`translate(${(srcX + kaL) / 2 - 8} ${kaY - 116}) rotate(-4)`}>176 <rect177 x="-98"178 y="-24"179 width="196"180 height="48"181 rx="12"182 fill="#d9f26b"183 stroke="#141814"184 strokeWidth="1.5"185 />186 <text187 x="0"188 y="-4"189 textAnchor="middle"190 fontFamily="var(--font-jetbrains), monospace"191 fontSize="11"192 fontWeight="700"193 letterSpacing="1"194 fill="#141814"195 >196 450+ CONNECTEURS197 </text>198 <text199 x="0"200 y="13"201 textAnchor="middle"202 fontFamily="var(--font-jetbrains), monospace"203 fontSize="8.5"204 fill="#2c3a2c"205 >206 lecture à la source · normalisation207 </text>208 </g>209210 {/* ---- la boîte KA ---- */}211 <g transform={`translate(${(kaL + kaR) / 2} ${kaY}) rotate(-2)`}>212 <rect213 x={-(kaR - kaL) / 2 + 6}214 y="-64"215 width={kaR - kaL}216 height="140"217 rx="12"218 fill="rgba(20,24,20,0.18)"219 />220 <rect221 x={-(kaR - kaL) / 2}222 y="-70"223 width={kaR - kaL}224 height="140"225 rx="12"226 fill="#141814"227 />228 <text229 x="0"230 y="6"231 textAnchor="middle"232 fontFamily="var(--font-space-grotesk), sans-serif"233 fontSize="58"234 fontWeight="700"235 letterSpacing="-2"236 fill="#d9f26b"237 >238 KA239 </text>240 <text241 x="0"242 y="34"243 textAnchor="middle"244 fontFamily="var(--font-jetbrains), monospace"245 fontSize="10.5"246 fontWeight="700"247 letterSpacing="2"248 fill="rgba(245,243,238,0.75)"249 >250 = AGRÉGATION251 </text>252 <text253 x="0"254 y="51"255 textAnchor="middle"256 fontFamily="var(--font-jetbrains), monospace"257 fontSize="8"258 fill="rgba(245,243,238,0.55)"259 >260 dédoublonner · géocoder261 </text>262 </g>263264 {/* ---- chips plateformes (le split ·Ka) ---- */}265 {FIG_PLATFORMS.map((p, i) => (266 <g key={p.name}>267 <rect268 x={dstX}269 y={dstYs[i] - 20}270 width={W - dstX - 16}271 height="40"272 rx="8"273 fill="#ffffff"274 stroke="#141814"275 strokeWidth="1.5"276 />277 <rect278 x={dstX + 12}279 y={dstYs[i] - 8}280 width="16"281 height="16"282 rx="4"283 fill="#141814"284 />285 <rect286 x={dstX + 15}287 y={dstYs[i] - 5}288 width="10"289 height="10"290 rx="2.5"291 fill={p.accent}292 />293 <text294 x={dstX + 38}295 y={dstYs[i] - 1}296 fontFamily="var(--font-space-grotesk), sans-serif"297 fontSize="14.5"298 fontWeight="700"299 fill="#141814"300 >301 {p.name}302 </text>303 <text304 x={dstX + 38}305 y={dstYs[i] + 13}306 fontFamily="var(--font-jetbrains), monospace"307 fontSize="9"308 fill="#8b928c"309 >310 {p.domain}311 </text>312 </g>313 ))}314 <text315 x={dstX}316 y="34"317 fontFamily="var(--font-jetbrains), monospace"318 fontSize="11"319 fontWeight="700"320 letterSpacing="1.5"321 fill="#8b928c"322 >323 SEPT PLATEFORMES324 </text>325 </svg>326 </div>327 <FigCaption lead="LE SABLIER ·KA">328 des centaines de sites éparpillés, lus à la source par des connecteurs329 dédiés ; tout converge vers KA, qui normalise, dédoublonne et330 redistribue vers sept plateformes. Une flèche entre, sept flèches331 sortent : c'est ça, agréger.332 </FigCaption>333 </figure>334 );335}336337/* ------------------------------------------------------------------ */338/* 2. Le pipeline : de la page web à la donnée propre (vertical) */339/* ------------------------------------------------------------------ */340341const PIPELINE_STEPS: { num: string; title: string; desc: string }[] = [342 {343 num: "01",344 title: "Site source",345 desc: "La page publique, telle que tout le monde la voit.",346 },347 {348 num: "02",349 title: "Connecteur",350 desc: "Un lecteur dédié par site — il connaît sa structure par cœur.",351 },352 {353 num: "03",354 title: "Normalisation",355 desc: "Mêmes champs, mêmes unités : un 3½ est un 3½ partout.",356 },357 {358 num: "04",359 title: "Dédoublonnage & géocodage",360 desc: "Une seule fiche par annonce réelle, posée sur la carte.",361 },362 {363 num: "05",364 title: "Publication",365 desc: "En ligne, datée, traçable jusqu'à la source originale.",366 },367];368369export function PipelineFigure() {370 const W = 480;371 const rowH = 96;372 const boxW = 356;373 const H = PIPELINE_STEPS.length * rowH + 24;374375 return (376 <figure className="gk-card overflow-hidden p-0">377 <div className="overflow-x-auto">378 <svg379 viewBox={`0 0 ${W} ${H}`}380 role="img"381 aria-label="Le pipeline de la donnée KA : site source, connecteur, normalisation, dédoublonnage et géocodage, publication — puis resynchronisation en boucle."382 className="block min-w-[420px]"383 >384 <defs>385 <marker386 id="pipe-arrow"387 viewBox="0 0 10 10"388 refX="8"389 refY="5"390 markerWidth="7"391 markerHeight="7"392 orient="auto-start-reverse"393 >394 <path d="M0,0 L10,5 L0,10 z" fill="#141814" />395 </marker>396 </defs>397398 {/* connecteurs verticaux entre stations */}399 {PIPELINE_STEPS.slice(0, -1).map((_, i) => (400 <line401 key={`v-${i}`}402 x1={24 + boxW / 2}403 y1={20 + i * rowH + 64}404 x2={24 + boxW / 2}405 y2={20 + (i + 1) * rowH - 6}406 stroke="#141814"407 strokeWidth="2"408 markerEnd="url(#pipe-arrow)"409 className="fig-flow"410 style={{ animationDelay: `${i * 0.2}s` }}411 />412 ))}413414 {/* boucle de resynchronisation : bas → haut, à droite */}415 <path416 d={`M ${24 + boxW} ${20 + (PIPELINE_STEPS.length - 1) * rowH + 32}417 C ${W - 26} ${20 + (PIPELINE_STEPS.length - 1) * rowH + 32},418 ${W - 26} ${20 + 32},419 ${24 + boxW + 10} ${20 + 32}`}420 fill="none"421 stroke="#1c5c41"422 strokeWidth="2"423 markerEnd="url(#pipe-arrow)"424 className="fig-flow"425 style={{ animationDelay: "0.5s" }}426 />427 <g428 transform={`translate(${W - 27} ${H / 2}) rotate(90)`}429 >430 <rect431 x="-78"432 y="-13"433 width="156"434 height="26"435 rx="13"436 fill="#d9f26b"437 stroke="#141814"438 strokeWidth="1.5"439 />440 <text441 x="0"442 y="4"443 textAnchor="middle"444 fontFamily="var(--font-jetbrains), monospace"445 fontSize="9.5"446 fontWeight="700"447 letterSpacing="1"448 fill="#141814"449 >450 RESYNC EN CONTINU451 </text>452 </g>453454 {/* stations */}455 {PIPELINE_STEPS.map((s, i) => (456 <g key={s.num}>457 <rect458 x="24"459 y={20 + i * rowH}460 width={boxW}461 height="64"462 rx="10"463 fill={i === PIPELINE_STEPS.length - 1 ? "#141814" : "#ffffff"}464 stroke="#141814"465 strokeWidth="1.5"466 />467 <text468 x="42"469 y={20 + i * rowH + 27}470 fontFamily="var(--font-jetbrains), monospace"471 fontSize="11"472 fontWeight="700"473 fill={i === PIPELINE_STEPS.length - 1 ? "#d9f26b" : "#1c5c41"}474 >475 {s.num}476 </text>477 <text478 x="70"479 y={20 + i * rowH + 27}480 fontFamily="var(--font-space-grotesk), sans-serif"481 fontSize="14.5"482 fontWeight="700"483 fill={i === PIPELINE_STEPS.length - 1 ? "#f5f3ee" : "#141814"}484 >485 {s.title}486 </text>487 <text488 x="42"489 y={20 + i * rowH + 48}490 fontFamily="var(--font-inter), sans-serif"491 fontSize="11"492 fill={493 i === PIPELINE_STEPS.length - 1494 ? "rgba(245,243,238,0.7)"495 : "#4d5551"496 }497 >498 {s.desc}499 </text>500 </g>501 ))}502 </svg>503 </div>504 <FigCaption lead="LE PIPELINE">505 cinq stations, aucune main humaine. La boucle verte à droite est le506 secret : tout le pipeline se rejoue en continu, donc une annonce507 retirée à la source disparaît d'elle-même.508 </FigCaption>509 </figure>510 );511}512513/* ------------------------------------------------------------------ */514/* 3. Le cycle ·Ka : la boucle jour et nuit */515/* ------------------------------------------------------------------ */516517const CYCLE_STEPS = [518 { num: "01", label: "Lire les sources", angle: -90 },519 { num: "02", label: "Comparer à hier", angle: 0 },520 { num: "03", label: "Mettre à jour", angle: 90 },521 { num: "04", label: "Publier & tracer", angle: 180 },522];523524export function CycleFigure() {525 const S = 480;526 const cx = S / 2;527 const cy = S / 2 + 6;528 const R = 150;529530 return (531 <figure className="gk-card overflow-hidden p-0">532 <div className="flex justify-center">533 <svg534 viewBox={`0 0 ${S} ${S}`}535 role="img"536 aria-label="Le cycle KA : lire les sources, comparer à hier, mettre à jour, publier et tracer — en boucle, jour et nuit."537 className="block w-full max-w-[480px]"538 >539 <defs>540 <marker541 id="cyc-arrow"542 viewBox="0 0 10 10"543 refX="7"544 refY="5"545 markerWidth="6.5"546 markerHeight="6.5"547 orient="auto"548 >549 <path d="M0,0 L10,5 L0,10 z" fill="#141814" />550 </marker>551 </defs>552553 {/* anneau pointillé qui tourne (le flux) */}554 <circle555 cx={cx}556 cy={cy}557 r={R}558 fill="none"559 stroke="#141814"560 strokeWidth="2"561 strokeOpacity="0.65"562 className="fig-flow"563 />564 {/* flèches de sens, aux diagonales */}565 {[-45, 45, 135, 225].map((a) => {566 const rad = (a * Math.PI) / 180;567 const x = cx + R * Math.cos(rad);568 const y = cy + R * Math.sin(rad);569 // tangente (sens horaire)570 const tx = -Math.sin(rad);571 const ty = Math.cos(rad);572 return (573 <line574 key={a}575 x1={x - tx}576 y1={y - ty}577 x2={x + tx * 2}578 y2={y + ty * 2}579 stroke="#141814"580 strokeWidth="2"581 markerEnd="url(#cyc-arrow)"582 />583 );584 })}585586 {/* KA au centre */}587 <g transform={`translate(${cx} ${cy}) rotate(-2)`}>588 <rect x="-48" y="-42" width="96" height="88" rx="10" fill="rgba(20,24,20,0.18)" transform="translate(5 5)" />589 <rect x="-48" y="-42" width="96" height="88" rx="10" fill="#141814" />590 <text591 x="0"592 y="8"593 textAnchor="middle"594 fontFamily="var(--font-space-grotesk), sans-serif"595 fontSize="40"596 fontWeight="700"597 letterSpacing="-1.5"598 fill="#d9f26b"599 >600 KA601 </text>602 <text603 x="0"604 y="30"605 textAnchor="middle"606 fontFamily="var(--font-jetbrains), monospace"607 fontSize="8"608 letterSpacing="1.5"609 fill="rgba(245,243,238,0.65)"610 >611 JOUR ET NUIT612 </text>613 </g>614615 {/* étapes sur l'anneau */}616 {CYCLE_STEPS.map((s) => {617 const rad = (s.angle * Math.PI) / 180;618 const x = cx + R * Math.cos(rad);619 const y = cy + R * Math.sin(rad);620 const w = 150;621 return (622 <g key={s.num} transform={`translate(${x} ${y})`}>623 <rect624 x={-w / 2}625 y="-19"626 width={w}627 height="38"628 rx="8"629 fill="#ffffff"630 stroke="#141814"631 strokeWidth="1.5"632 />633 <text634 x={-w / 2 + 12}635 y="4.5"636 fontFamily="var(--font-jetbrains), monospace"637 fontSize="10"638 fontWeight="700"639 fill="#1c5c41"640 >641 {s.num}642 </text>643 <text644 x={-w / 2 + 34}645 y="4.5"646 fontFamily="var(--font-space-grotesk), sans-serif"647 fontSize="12.5"648 fontWeight="700"649 fill="#141814"650 >651 {s.label}652 </text>653 </g>654 );655 })}656 </svg>657 </div>658 <FigCaption lead="LE CYCLE ·KA">659 quatre gestes, en boucle, sans intervention humaine. C'est la660 définition maison de l'agrégation : pas un instantané, un661 battement.662 </FigCaption>663 </figure>664 );665}666667/* ------------------------------------------------------------------ */668/* 4. Barres : ce qui est en ligne en ce moment (une mesure, une */669/* teinte ; identité par étiquette + pastille de marque) */670/* ------------------------------------------------------------------ */671672const INVENTORY: { name: string; value: number; unit: string }[] = [673 { name: "Fabri·Ka", value: 281000, unit: "produits québécois" },674 { name: "Immo·Ka", value: 54000, unit: "propriétés à vendre" },675 { name: "Food·Ka", value: 23700, unit: "prix d'épicerie" },676 { name: "Auto·Ka", value: 15900, unit: "véhicules d'occasion" },677 { name: "Lou·Ka", value: 7000, unit: "logements à louer" },678];679680const fmt = (n: number) => n.toLocaleString("fr-CA").replace(/ /g, " ");681682export function InventoryBars() {683 const max = Math.max(...INVENTORY.map((d) => d.value));684 return (685 <figure className="gk-card overflow-hidden p-0">686 <div className="p-5 sm:p-7">687 <p className="klabel">Inventaire vivant · aujourd'hui</p>688 <h3 className="gk-display mt-2 text-[17px] font-bold uppercase">689 Ce qui est en ligne en ce moment690 </h3>691 <div className="mt-6 space-y-5">692 {INVENTORY.map((d) => (693 <div key={d.name} title={`${d.name} — ${fmt(d.value)}+ ${d.unit}`}>694 <div className="flex items-baseline justify-between gap-3">695 <span className="flex items-center gap-2 text-[13px] font-bold">696 <BrandChip name={d.name} />697 <span className="gk-display">{d.name}</span>698 <span className="gk-mono text-[10px] font-medium text-ink-3">699 {d.unit}700 </span>701 </span>702 <span className="gk-mono text-[12.5px] font-bold text-ink">703 {fmt(d.value)}+704 </span>705 </div>706 <div className="mt-[6px] h-[14px] w-full rounded-r-[4px] bg-[rgba(20,24,20,0.06)]">707 <div708 className="bar-grow h-full rounded-r-[4px] bg-green"709 style={{ width: `${Math.max((d.value / max) * 100, 1.5)}%` }}710 />711 </div>712 </div>713 ))}714 </div>715 </div>716 <FigCaption lead="L'INVENTAIRE">717 chaque barre est vivante : elle grossit quand les connecteurs trouvent718 du neuf, elle maigrit quand une annonce disparaît à la source. Compté719 aujourd'hui, pas promis.720 </FigCaption>721 </figure>722 );723}724725/* ------------------------------------------------------------------ */726/* 5. Matrice de connecteurs : un point = un connecteur */727/* ------------------------------------------------------------------ */728729const CONNECTORS: { name: string; count: number; kind: string }[] = [730 { name: "Auto·Ka", count: 124, kind: "concessionnaires" },731 { name: "Lou·Ka", count: 108, kind: "gestionnaires" },732 { name: "Immo·Ka", count: 65, kind: "agences" },733 { name: "Food·Ka", count: 20, kind: "bannières" },734];735736export function ConnectorMatrix() {737 return (738 <figure className="gk-card overflow-hidden p-0">739 <div className="p-5 sm:p-7">740 <p className="klabel">La flotte · un point = un connecteur</p>741 <h3 className="gk-display mt-2 text-[17px] font-bold uppercase">742 317 connecteurs, dessinés un par un743 </h3>744 <div className="mt-6 space-y-6">745 {CONNECTORS.map((c) => (746 <div key={c.name} title={`${c.name} — ${c.count} connecteurs (${c.kind})`}>747 <div className="flex items-baseline justify-between gap-3">748 <span className="flex items-center gap-2 text-[13px] font-bold">749 <BrandChip name={c.name} />750 <span className="gk-display">{c.name}</span>751 <span className="gk-mono text-[10px] font-medium text-ink-3">752 {c.kind}753 </span>754 </span>755 <span className="gk-mono text-[12.5px] font-bold">756 {c.count}757 </span>758 </div>759 <div className="mt-[8px] flex max-w-[460px] flex-wrap gap-[3px]">760 {Array.from({ length: c.count }, (_, i) => (761 <span762 key={i}763 className="h-[7px] w-[7px] rounded-[2px] bg-green"764 />765 ))}766 </div>767 </div>768 ))}769 </div>770 </div>771 <FigCaption lead="LA FLOTTE">772 chaque point est un petit programme qui connaît un site par cœur et le773 relit sans arrêt. S'y ajoutent les flux des 1 204 boutiques de774 Fabri·Ka et les données publiques qui alimentent Vrai-Prix et ValoPlex.775 </FigCaption>776 </figure>777 );778}779780/* ------------------------------------------------------------------ */781/* 6. Tuiles de couverture : les deux moteurs d'évaluation */782/* ------------------------------------------------------------------ */783784const COVERAGE: {785 name: string;786 value: string;787 label: string;788 sub: string;789}[] = [790 {791 name: "Vrai-Prix",792 value: "3 747 008",793 label: "propriétés couvertes",794 sub: "toutes les unités du rôle foncier du Québec, estimées",795 },796 {797 name: "ValoPlex",798 value: "393 867",799 label: "plex évalués",800 sub: "1 733 744 portes — l'unité d'analyse du plex",801 },802];803804export function CoverageTiles() {805 return (806 <div className="flex h-full flex-col gap-5">807 {COVERAGE.map((c) => (808 <article key={c.name} className="gk-card gk-card-hover flex-1 p-5 sm:p-7">809 <p className="klabel flex items-center gap-2">810 <BrandChip name={c.name} />811 {c.name} · couverture812 </p>813 <p className="gk-display mt-3 text-[clamp(34px,4.5vw,48px)] leading-none font-bold tracking-[-0.03em]">814 {c.value}815 </p>816 <p className="gk-display mt-1 text-[14px] font-bold uppercase text-green">817 {c.label}818 </p>819 <p className="mt-2 text-[12.5px] text-ink-2">{c.sub}</p>820 </article>821 ))}822 <p className="gk-mono text-[11px] leading-relaxed text-ink-3">823 Couvrir n'est pas agréger au rabais : chaque estimation garde ses824 comparables, ses ajustements en dollars et sa fourchette.825 </p>826 </div>827 );828}829