SPB Git

spb/ora-ka Public

Ora-Ka — cinq agrégateurs Ka, une barre de recherche hybride (exact + sémantique)

Python 80% TypeScript 12.9% CSS 6.8%
40.1 KB · 668 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Ora-Ka — Landing : LA barre de recherche. Verre dépoli, épurée, moderne.3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# -----------------------------------------------------------------------------56LANDING = """<!doctype html>7<html lang="fr">8<head>9<meta charset="utf-8">10<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">11<title>Ora·Ka — Cherchez tout le Québec</title>12<meta name="description" content="Ora-Ka : une seule barre de recherche pour tout le Québec — propriétés, logements, produits d'ici, véhicules et épicerie. Recherche exacte et sémantique.">13<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%23141518'/%3E%3Ctext x='32' y='42' font-family='Arial Black,sans-serif' font-size='26' font-weight='900' fill='%23ffffff' text-anchor='middle'%3EOK%3C/text%3E%3C/svg%3E">14<link rel="preconnect" href="https://fonts.googleapis.com">15<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>16<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">17<style>18:root{19  --bg:#f5f3ee; --ink:#141814; --ink-2:#4d5551; --ink-3:#8b928c;20  --lime:#d9f26b; --lime-soft:#f0f9d2;21  --glass:rgba(255,255,255,.58); --glass-2:rgba(255,255,255,.44);22  --gline:rgba(255,255,255,.65); --gline-2:rgba(20,24,20,.08);23  --iline:rgba(20,24,20,.85);24  --d-immo:#e23744; --d-lou:#1c5c41; --d-fabri:#c4532e; --d-auto:#ff5a2a; --d-food:#1f7a4d;25  --mono:"JetBrains Mono",ui-monospace,monospace;26  --shadow:0 8px 32px rgba(20,24,20,.08);27  --shadow-lg:0 18px 48px rgba(20,24,20,.12);28  --off:4px 4px 0 var(--ink);29  --off-lg:7px 8px 0 var(--ink);30}31*{box-sizing:border-box;margin:0;padding:0}32html{scroll-behavior:smooth}33body{background:var(--bg);color:var(--ink);font-family:Inter,system-ui,sans-serif;font-size:16px;line-height:1.5;34  -webkit-font-smoothing:antialiased;min-height:100vh;display:flex;flex-direction:column}35::selection{background:var(--lime);color:var(--ink)}36/* halos doux + grain de papier — la texture signature de la famille Ka */37body::before{content:"";position:fixed;inset:-10%;z-index:-1;pointer-events:none;38  background:39    radial-gradient(640px 520px at 12% 12%, rgba(226,55,68,.08), transparent 62%),40    radial-gradient(720px 560px at 88% 8%, rgba(255,90,42,.06), transparent 62%),41    radial-gradient(820px 640px at 78% 92%, rgba(31,122,77,.08), transparent 62%),42    radial-gradient(700px 560px at 8% 88%, rgba(217,242,107,.10), transparent 62%);43  filter:blur(6px)}44body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:2000;opacity:.35;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='.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .04 0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E")}46main{flex:1;width:100%}47.container{max-width:1180px;margin:0 auto;padding:0 22px;width:100%}48.glass{background:var(--glass);backdrop-filter:blur(20px) saturate(1.5);-webkit-backdrop-filter:blur(20px) saturate(1.5);49  border:1px solid var(--gline);box-shadow:var(--shadow)}5051/* ------- en-tête / compte KA ID ------- */52.topbar{position:fixed;top:0;left:0;right:0;z-index:120;display:flex;justify-content:flex-end;53  padding:14px 18px;pointer-events:none}54.topbar>*{pointer-events:auto}55#auth{position:relative;display:flex;align-items:center;min-height:38px}56.ka-btn{display:inline-flex;align-items:center;gap:9px;background:var(--ink);color:#f4f5f7;57  border-radius:999px;padding:8px 16px 8px 9px;font-size:13px;font-weight:600;letter-spacing:.01em;58  text-decoration:none;font-family:Inter,sans-serif;border:1px solid rgba(255,255,255,.08);59  box-shadow:0 6px 20px rgba(20,22,26,.18);transition:transform .18s,box-shadow .18s}60.ka-btn:hover{transform:translateY(-1px);box-shadow:0 10px 26px rgba(20,22,26,.24)}61.ka-badge{display:inline-flex;align-items:center;justify-content:center;background:#d9f26b;color:#16181c;62  border-radius:6px;padding:3px 6px;font-family:"Space Grotesk",sans-serif;font-size:10.5px;font-weight:700;63  letter-spacing:.04em;transform:rotate(-2deg)}64.ka-avatar{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:50%;65  border:1px solid var(--gline);background:var(--ink);color:#d9f26b;font-family:"Space Grotesk",sans-serif;66  font-size:15px;font-weight:700;cursor:pointer;overflow:hidden;padding:0;67  box-shadow:0 6px 20px rgba(20,22,26,.16);transition:transform .18s,box-shadow .18s}68.ka-avatar:hover{transform:translateY(-1px);box-shadow:0 10px 26px rgba(20,22,26,.22)}69.ka-avatar img{width:100%;height:100%;object-fit:cover;display:block}70.ka-menu{position:absolute;top:46px;right:0;width:min(272px,calc(100vw - 36px));border-radius:18px;padding:16px;71  display:none;text-align:left;background:rgba(255,255,255,.86);backdrop-filter:blur(22px) saturate(1.5);72  -webkit-backdrop-filter:blur(22px) saturate(1.5);border:1px solid var(--gline);box-shadow:var(--shadow-lg)}73.ka-menu.open{display:block;animation:menu-in .18s cubic-bezier(.2,.6,.2,1) both}74@keyframes menu-in{from{opacity:0;transform:translateY(-5px) scale(.98)}to{opacity:1;transform:none}}75.ka-menu .m-name{font-size:14px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}76.ka-menu .m-mail{font-size:12px;color:var(--ink-2);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}77.ka-menu .m-id{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--ink-3);78  margin-top:7px;letter-spacing:.02em}79.ka-menu .m-role{display:inline-flex;align-items:center;margin-top:8px;border-radius:999px;padding:3px 10px;80  font-size:10.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--ink);81  background:#d9f26b}82.ka-menu hr{border:0;border-top:1px solid var(--gline-2);margin:12px 0}83.ka-menu .m-link{display:block;font-size:13px;font-weight:500;color:var(--ink-2);text-decoration:none;84  padding:7px 8px;margin:0 -8px;border-radius:10px}85.ka-menu .m-link:hover{color:var(--ink);background:rgba(22,24,28,.05)}86.ka-menu .m-out{display:block;width:calc(100% + 16px);margin:0 -8px;border:0;background:transparent;cursor:pointer;87  text-align:left;font-family:Inter,sans-serif;font-size:13px;font-weight:500;color:#b03540;88  padding:7px 8px;border-radius:10px}89.ka-menu .m-out:hover{background:rgba(221,74,85,.08)}90@media(max-width:560px){.topbar{padding:12px 14px}.ka-btn{padding:7px 13px 7px 8px;font-size:12.5px}}9192/* ------- splash Groupe KA ------- */93#splash{position:fixed;inset:0;z-index:900;background:var(--bg);display:flex;align-items:center;justify-content:center;94  transition:opacity .55s ease,visibility .55s}95#splash::before{content:"";position:absolute;inset:-10%;pointer-events:none;96  background:radial-gradient(640px 520px at 20% 20%, rgba(221,74,85,.07), transparent 62%),97    radial-gradient(720px 560px at 80% 80%, rgba(61,143,102,.07), transparent 62%);filter:blur(6px)}98#splash.done{opacity:0;visibility:hidden}99.sp-inner{position:relative;text-align:center}100.sp-kicker{font-family:var(--mono);font-size:11.5px;font-weight:700;color:var(--ink-2);101  text-transform:uppercase;animation:sp-track 1.1s cubic-bezier(.2,.6,.2,1) both}102@keyframes sp-track{from{letter-spacing:.65em;opacity:0;transform:translateY(6px)}to{letter-spacing:.3em;opacity:1;transform:none}}103.sp-brand{font-family:"Space Grotesk",sans-serif;font-weight:700;letter-spacing:-.045em;font-size:clamp(46px,7vw,72px);104  line-height:1.05;margin-top:10px;animation:sp-brand 1s .12s cubic-bezier(.2,.6,.2,1) both}105@keyframes sp-brand{from{opacity:0;transform:scale(.94) translateY(10px);filter:blur(8px)}to{opacity:1;transform:none;filter:blur(0)}}106.sp-brand .ka{display:inline-block;background:var(--ink);color:var(--lime);border-radius:.14em;107  padding:.02em .16em .06em;margin-left:.06em;animation:sp-ka 1s .3s cubic-bezier(.3,1.5,.5,1) both}108@keyframes sp-ka{from{transform:rotate(-11deg) scale(.85);opacity:0}to{transform:rotate(-2deg) scale(1);opacity:1}}109.sp-dots{display:flex;justify-content:center;gap:10px;margin-top:26px}110.sp-dots i{width:9px;height:9px;border-radius:50%;animation:sp-wave 1.05s ease-in-out infinite}111.sp-dots i:nth-child(1){background:var(--d-immo);animation-delay:0s}112.sp-dots i:nth-child(2){background:var(--d-lou);animation-delay:.09s}113.sp-dots i:nth-child(3){background:var(--d-fabri);animation-delay:.18s}114.sp-dots i:nth-child(4){background:var(--d-auto);animation-delay:.27s}115.sp-dots i:nth-child(5){background:var(--d-food);animation-delay:.36s}116@keyframes sp-wave{0%,60%,100%{transform:translateY(0);opacity:.55}30%{transform:translateY(-9px);opacity:1}}117.sp-line{width:180px;height:2px;border-radius:2px;background:rgba(22,24,28,.09);margin:24px auto 0;overflow:hidden}118.sp-line i{display:block;height:100%;width:100%;border-radius:2px;119  background:linear-gradient(90deg,var(--ink) 55%,var(--lime));120  transform-origin:left;animation:sp-fill 1.25s .1s cubic-bezier(.3,.1,.2,1) both}121@keyframes sp-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}122.sp-tag{margin-top:20px;font-family:var(--mono);font-size:10.5px;color:var(--ink-3);letter-spacing:.08em;text-transform:uppercase;123  animation:sp-tag .8s .5s ease both}124@keyframes sp-tag{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}125@media(prefers-reduced-motion:reduce){#splash{display:none}}126127/* ------- entrée en cascade du héros ------- */128body:not(.ready) .hero>*{opacity:0}129body.ready .hero>*{animation:rise .65s cubic-bezier(.2,.6,.2,1) both}130body.ready .hero>*:nth-child(1){animation-delay:.05s}131body.ready .hero>*:nth-child(2){animation-delay:.14s}132body.ready .hero>*:nth-child(3){animation-delay:.23s}133body.ready .hero>*:nth-child(4){animation-delay:.34s}134body.ready .hero>*:nth-child(5){animation-delay:.45s}135body.ready .hero>*:nth-child(6){animation-delay:.56s}136body.ready .hero>*:nth-child(7){animation-delay:.68s}137@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}138@media(prefers-reduced-motion:reduce){body:not(.ready) .hero>*{opacity:1}body.ready .hero>*{animation:none}}139140/* ------- héro / barre ------- */141.hero{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;142  min-height:64vh;padding:60px 22px 30px;transition:min-height .45s cubic-bezier(.4,0,.2,1),padding .45s cubic-bezier(.4,0,.2,1)}143body.searching .hero{min-height:0;padding:32px 22px 6px}144.kicker{display:inline-flex;align-items:center;gap:10px;font-family:var(--mono);font-size:11.5px;font-weight:700;145  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-2)}146.kicker::before,.kicker::after{content:"";display:inline-block;width:30px;height:2px;background:var(--ink)}147body.searching .kicker{display:none}148.brand{font-family:"Space Grotesk",sans-serif;font-weight:700;letter-spacing:-.045em;font-size:clamp(42px,6.6vw,68px);149  line-height:1;user-select:none;transition:font-size .45s cubic-bezier(.4,0,.2,1);margin-top:14px}150body.searching .brand{font-size:26px;margin-top:0}151.brand .ka{display:inline-block;background:var(--ink);color:var(--lime);border-radius:.14em;152  padding:.02em .16em .06em;margin-left:.06em;transform:rotate(-2deg);transition:transform .3s cubic-bezier(.3,1.4,.5,1)}153.brand:hover .ka{transform:rotate(0)}154body.fetching .brand .ka{animation:ka-tilt .9s ease-in-out infinite}155@keyframes ka-tilt{0%,100%{transform:rotate(-2deg)}50%{transform:rotate(2deg)}}156.sub{margin-top:14px;color:var(--ink-2);font-size:15.5px;font-weight:400;transition:opacity .3s}157.sub b{color:var(--ink);font-weight:600;background:linear-gradient(transparent 62%,var(--lime) 62%)}158body.searching .sub{display:none}159160.barwrap{position:relative;width:min(720px,100%);margin-top:36px;transition:margin .45s}161body.searching .barwrap{margin-top:18px}162.bar{display:flex;align-items:center;border-radius:18px;border:2px solid var(--ink) !important;163  box-shadow:var(--off-lg),var(--shadow) !important;transition:box-shadow .22s,transform .22s}164.bar:focus-within{transform:translate(-2px,-3px);box-shadow:10px 11px 0 var(--ink),var(--shadow-lg) !important}165.bar input{caret-color:var(--d-immo)}166.bar .lens{padding:0 4px 0 22px;font-size:19px;color:var(--ink-3);display:flex;align-items:center;min-width:45px}167.bar .spin{display:none;width:17px;height:17px;border-radius:50%;168  border:2px solid rgba(22,24,28,.14);border-top-color:var(--ink);animation:rot .65s linear infinite}169@keyframes rot{to{transform:rotate(360deg)}}170body.fetching .bar .lg{display:none}171body.fetching .bar .spin{display:block}172body.fetching .bar{box-shadow:var(--shadow-lg)}173.bar input{flex:1;border:0;outline:0;background:transparent;font-family:Inter,sans-serif;174  font-size:17.5px;font-weight:500;color:var(--ink);padding:20px 10px;min-width:0}175.bar input::placeholder{color:var(--ink-3);font-weight:400}176.bar input::-webkit-search-cancel-button{display:none}177.bar .x{display:none;border:0;background:transparent;cursor:pointer;font-size:14px;color:var(--ink-3);padding:10px 20px 10px 6px}178.bar .x:hover{color:var(--ink)}179.barwrap.has-q .x{display:block}180@media(max-width:560px){.bar input{font-size:16px;padding:16px 8px}.bar .lens{padding-left:17px}}181182/* ------- portées ------- */183.scopes{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:20px}184.scope{display:inline-flex;align-items:center;gap:8px;border-radius:999px;padding:8px 15px;185  font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;186  color:var(--ink);cursor:pointer;transition:all .16s;187  background:var(--glass);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1.5px solid var(--ink)}188.scope .dot{width:7px;height:7px;border-radius:50%}189.scope:hover{transform:translate(-1px,-2px);box-shadow:3px 3px 0 var(--ink)}190.scope.on{background:var(--ink);border-color:var(--ink);color:var(--lime);animation:pop .28s cubic-bezier(.3,1.5,.5,1)}191@keyframes pop{0%{transform:scale(.94)}60%{transform:scale(1.045)}100%{transform:scale(1)}}192.suggests{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:26px;transition:opacity .3s}193body.searching .suggests{display:none}194.suggest{border:1px dashed var(--ink-3);background:transparent;padding:5px 12px;font-family:var(--mono);195  font-size:11.5px;color:var(--ink-2);cursor:pointer;border-radius:999px;transition:all .15s}196.suggest:hover{color:var(--ink);border-color:var(--ink);background:var(--glass)}197198/* ------- ticker famille Ka ------- */199.ticker{width:100vw;margin:44px calc(50% - 50vw) 0;background:var(--ink);color:var(--lime);overflow:hidden;200  border-top:2px solid var(--ink);border-bottom:2px solid var(--ink)}201body.searching .ticker{display:none}202.ticker-inner{display:flex;gap:44px;white-space:nowrap;padding:8px 0;font-family:var(--mono);font-size:11.5px;203  font-weight:700;letter-spacing:.1em;text-transform:uppercase;width:max-content;animation:tick 44s linear infinite}204.ticker-inner span::after{content:"◆";margin-left:44px;opacity:.5}205@keyframes tick{from{transform:translateX(0)}to{transform:translateX(-50%)}}206@media(prefers-reduced-motion:reduce){.ticker-inner{animation:none}}207208/* ------- résultats ------- */209#results{padding:6px 0 70px;display:none}210.rhead{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:12px 0 18px}211.rinfo{font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}212.rinfo b{color:var(--ink);font-weight:700;font-size:13px}213.rinfo .ai{display:inline-flex;align-items:center;gap:5px;margin-left:10px;color:var(--ink-3)}214.fchips{display:flex;flex-wrap:wrap;gap:7px}215.fchip{display:inline-flex;align-items:center;gap:7px;border-radius:999px;padding:6px 13px;216  font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;217  color:var(--ink);cursor:pointer;transition:all .15s;text-decoration:none;218  background:var(--glass);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1.5px solid var(--ink)}219.fchip .dot{width:6px;height:6px;border-radius:50%}220.fchip b{color:var(--ink);font-weight:700}221.fchip:hover{transform:translate(-1px,-2px);box-shadow:3px 3px 0 var(--ink)}222.rgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(216px,1fr));gap:16px}223.rcard{display:flex;flex-direction:column;border-radius:14px;overflow:hidden;text-decoration:none;color:var(--ink);224  background:var(--glass);backdrop-filter:blur(18px) saturate(1.4);-webkit-backdrop-filter:blur(18px) saturate(1.4);225  border:1.5px solid var(--ink);box-shadow:var(--off);transition:transform .16s,box-shadow .16s;animation:up .35s both}226.rcard:hover{transform:translate(-2px,-3px);box-shadow:var(--off-lg)}227@keyframes up{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}228.rimg{aspect-ratio:4/3;background:rgba(255,255,255,.35);overflow:hidden;position:relative}229.rimg .ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:30px;opacity:.75}230.rimg img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;transition:transform .45s cubic-bezier(.2,.6,.2,1)}231.rcard:hover .rimg img{transform:scale(1.045)}232.rtag{position:absolute;top:9px;left:9px;display:inline-flex;align-items:center;gap:6px;border-radius:999px;233  padding:4px 10px;font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ink);234  background:rgba(255,255,255,.85);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid var(--iline)}235.rtag .dot{width:6px;height:6px;border-radius:50%}236.rscore{position:absolute;top:9px;right:9px;display:inline-flex;align-items:center;gap:4px;border-radius:999px;237  padding:4px 9px;font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.02em;color:var(--ink);238  background:rgba(255,255,255,.85);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid var(--iline)}239.rscore.exact{background:var(--lime);border-color:var(--ink)}240.rbody{padding:12px 14px 14px;display:flex;flex-direction:column;gap:4px;flex:1}241.rtitle{font-size:14px;font-weight:500;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}242.rtitle mark{background:var(--lime);color:inherit;border-radius:3px;padding:0 2px;font-weight:600}243.rmeter{height:3px;border-radius:2px;background:rgba(20,24,20,.1);margin-top:8px;overflow:hidden}244.rmeter i{display:block;height:100%;border-radius:2px;background:var(--ink)}245.rsub{font-family:var(--mono);font-size:10px;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}246.rprice{font-family:"Space Grotesk",sans-serif;font-size:16px;font-weight:600;margin-top:auto;padding-top:6px;color:var(--ink)}247.notice{margin:36px auto;text-align:center;max-width:430px;color:var(--ink-2);padding:44px 26px;border-radius:14px;248  border:2px dashed rgba(20,24,20,.25);background:transparent !important;box-shadow:none !important}249.notice .big{font-size:36px;margin-bottom:10px}250.loading .rgrid,.loading .rhead{opacity:.45}251.skel{border-radius:14px;overflow:hidden;background:var(--glass-2);border:1.5px solid rgba(20,24,20,.35);position:relative;animation:up .35s both}252.skel::after{content:"";position:absolute;inset:0;transform:translateX(-100%);253  background:linear-gradient(100deg,transparent 30%,rgba(255,255,255,.65) 50%,transparent 70%);254  animation:shimmer 1.4s ease-in-out infinite}255@keyframes shimmer{to{transform:translateX(100%)}}256.skel .si{aspect-ratio:4/3;background:rgba(255,255,255,.45)}257.skel .sb{padding:12px 14px 16px}258.skel .sl{height:11px;border-radius:6px;background:rgba(22,24,28,.07);margin-bottom:8px}259.skel .sl.w60{width:60%}260.ka-loader{display:inline-flex;gap:6px;margin-left:10px;vertical-align:middle}261.ka-loader i{width:6px;height:6px;border-radius:50%;animation:sp-wave 1.05s ease-in-out infinite}262.ka-loader i:nth-child(1){background:var(--d-immo)}263.ka-loader i:nth-child(2){background:var(--d-lou);animation-delay:.09s}264.ka-loader i:nth-child(3){background:var(--d-fabri);animation-delay:.18s}265.ka-loader i:nth-child(4){background:var(--d-auto);animation-delay:.27s}266.ka-loader i:nth-child(5){background:var(--d-food);animation-delay:.36s}267.morewrap{display:flex;justify-content:center;margin-top:28px}268.morebtn{border-radius:999px;padding:11px 26px;font-family:var(--mono);font-size:11.5px;font-weight:700;269  letter-spacing:.06em;text-transform:uppercase;color:var(--ink);cursor:pointer;270  background:var(--glass);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1.5px solid var(--ink);transition:all .15s}271.morebtn:hover{transform:translate(-1px,-2px);box-shadow:var(--off)}272273/* ------- « Mon univers Ka » (favoris du hub, toutes plateformes) ------- */274#kafavs{display:none;padding:4px 0 34px}275#kafavs.show{display:block;animation:up .35s both}276body.searching #kafavs.show{display:none}277.uhead{display:flex;align-items:baseline;justify-content:space-between;gap:8px 18px;flex-wrap:wrap;padding:10px 0 16px}278.utitle{font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}279.utitle .uht{color:var(--d-immo);margin-right:6px}280.ulink{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;281  color:var(--ink-3);text-decoration:none;border-bottom:2px solid var(--lime);padding-bottom:1px;transition:color .15s}282.ulink:hover{color:var(--ink)}283284/* ------- pied ------- */285footer{padding:26px 22px 34px;text-align:center}286.flinks{display:flex;justify-content:center;flex-wrap:wrap;gap:4px 20px;margin-bottom:12px}287.flinks a{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);font-size:11px;font-weight:700;288  letter-spacing:.05em;text-transform:uppercase;color:var(--ink-2);text-decoration:none;padding:4px 2px;transition:color .15s;289  border-bottom:2px solid var(--lime)}290.flinks a .dot{width:6px;height:6px;border-radius:50%;opacity:.7}291.flinks a:hover{color:var(--ink)}292.fgroup{font-size:12px;color:var(--ink-2);margin-bottom:8px}293.fgroup a{color:var(--ink-2);text-decoration:underline;text-underline-offset:2px}294.fgroup a:hover{color:var(--ink)}295.flegal-links{font-size:11.5px;color:var(--ink-3);margin-bottom:8px}296.flegal-links a{color:var(--ink-3);text-decoration:none;margin:0 8px}297.flegal-links a:hover{color:var(--ink)}298.legal{font-size:11px;color:var(--ink-3)}299@media(prefers-reduced-motion:reduce){.rcard{animation:none}}300</style>301</head>302<body>303304<header class="topbar">305  <div id="auth"></div>306</header>307308<div id="splash" aria-hidden="true">309  <div class="sp-inner">310    <div class="sp-kicker">Groupe KA</div>311    <div class="sp-brand">Ora<span class="ka">Ka</span></div>312    <div class="sp-dots"><i></i><i></i><i></i><i></i><i></i></div>313    <div class="sp-line"><i></i></div>314    <div class="sp-tag">Une application du Groupe KA</div>315  </div>316</div>317318<main>319  <section class="hero">320    <div class="kicker">Un moteur · Cinq univers · Tout le Québec</div>321    <h1 class="brand">Ora<span class="ka">Ka</span></h1>322    <p class="sub" id="sub">Une barre. <b>Tout le Québec.</b></p>323324    <div class="barwrap" id="barwrap">325      <div class="bar glass">326        <span class="lens"><span class="lg">⌕</span><span class="spin" aria-hidden="true"></span></span>327        <input id="q" type="search" placeholder="Cherchez n'importe quoi…" autocomplete="off" spellcheck="false" autofocus>328        <button class="x" id="clear" aria-label="Effacer">✕</button>329      </div>330    </div>331332    <div class="scopes" id="scopes">333      <button class="scope on" data-scope="">Tout</button>334      <button class="scope" data-scope="immo"><span class="dot" style="background:var(--d-immo)"></span>Propriétés</button>335      <button class="scope" data-scope="lou"><span class="dot" style="background:var(--d-lou)"></span>Logements</button>336      <button class="scope" data-scope="fabri"><span class="dot" style="background:var(--d-fabri)"></span>Produits QC</button>337      <button class="scope" data-scope="auto"><span class="dot" style="background:var(--d-auto)"></span>Véhicules</button>338      <button class="scope" data-scope="food"><span class="dot" style="background:var(--d-food)"></span>Épicerie</button>339    </div>340341    <div class="suggests">342      <button class="suggest">condo avec vue à Montréal</button>343      <button class="suggest">4½ près de Limoilou</button>344      <button class="suggest">sirop d'érable artisanal</button>345      <button class="suggest">VUS hybride récent</button>346      <button class="suggest">café de spécialité</button>347    </div>348349    <div class="ticker"><div class="ticker-inner" id="ticker">350      <span>ORA·KA — LE MOTEUR DES AGRÉGATEURS</span><span>IMMO·KA — PROPRIÉTÉS À VENDRE</span><span>LOU·KA — LOGEMENTS À LOUER</span><span>FABRI·KA — PRODUITS QUÉBÉCOIS</span><span>AUTO·KA — VÉHICULES D'OCCASION</span><span>FOOD·KA — ÉPICERIE COMPARÉE</span><span>MISE À JOUR AUTOMATIQUE</span><span>ORA·KA — LE MOTEUR DES AGRÉGATEURS</span><span>IMMO·KA — PROPRIÉTÉS À VENDRE</span><span>LOU·KA — LOGEMENTS À LOUER</span><span>FABRI·KA — PRODUITS QUÉBÉCOIS</span><span>AUTO·KA — VÉHICULES D'OCCASION</span><span>FOOD·KA — ÉPICERIE COMPARÉE</span><span>MISE À JOUR AUTOMATIQUE</span>351    </div></div>352  </section>353354  <section id="kafavs" class="container" aria-label="Mon univers Ka">355    <div class="uhead">356      <div class="utitle"><span class="uht">♥</span>Mon univers Ka — tout ce que j'ai sauvegardé</div>357      <a class="ulink" href="https://www.groupe-ka.com/compte" target="_blank" rel="noopener">gérer sur groupe-ka.com/compte ↗</a>358    </div>359    <div class="rgrid" id="kafavs-grid"></div>360  </section>361362  <section id="results" class="container"></section>363</main>364365<footer>366  <div class="flinks">367    <a href="/immo/"><span class="dot" style="background:var(--d-immo)"></span>Immo·Ka</a>368    <a href="/lou/"><span class="dot" style="background:var(--d-lou)"></span>Lou·Ka</a>369    <a href="/fabri/"><span class="dot" style="background:var(--d-fabri)"></span>Fabri·Ka</a>370    <a href="/auto/"><span class="dot" style="background:var(--d-auto)"></span>Auto·Ka</a>371    <a href="/food/"><span class="dot" style="background:var(--d-food)"></span>Food·Ka</a>372  </div>373  <div class="fgroup">Ora-Ka est une application du <a href="https://www.groupe-ka.com" rel="noopener">Groupe KA</a> — <a href="mailto:contact@groupe-ka.com">contact@groupe-ka.com</a></div>374  <div class="flegal-links"><a href="/conditions">Conditions d'utilisation</a>·<a href="/confidentialite">Politique de confidentialité</a></div>375  <div class="legal">© 2026 Groupe KA — Ora-Ka agrège des données publiques ; chaque inscription reste la propriété de sa source.</div>376</footer>377378<script>379(function(){380  /* ---- splash Groupe KA ---- */381  var splash = document.getElementById('splash');382  var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;383  var seen = false;384  try { seen = sessionStorage.getItem('oraka-splash') === '1'; } catch(e){}385  var hasQ = new URLSearchParams(location.search).has('q');386  var dur = (reduced || !splash) ? 0 : (seen || hasQ ? 450 : 1600);387  function boot(){388    document.body.classList.add('ready');389    if(splash){390      splash.classList.add('done');391      setTimeout(function(){ if(splash.parentNode) splash.parentNode.removeChild(splash); }, 650);392    }393    try { sessionStorage.setItem('oraka-splash', '1'); } catch(e){}394  }395  if(dur === 0){ boot(); } else { setTimeout(boot, dur); }396})();397(function(){398  var fmt = new Intl.NumberFormat('fr-CA');399  var PLACE = {immo:'🏡', lou:'🔑', fabri:'📦', auto:'🚗', food:'🛒'};400  var COLOR = {immo:'var(--d-immo)', lou:'var(--d-lou)', fabri:'var(--d-fabri)', auto:'var(--d-auto)', food:'var(--d-food)'};401  var LABEL = {immo:'Propriétés', lou:'Logements', fabri:'Produits QC', auto:'Véhicules', food:'Épicerie'};402403  var input = document.getElementById('q');404  var barwrap = document.getElementById('barwrap');405  var results = document.getElementById('results');406  var scope = '', timer = null, ctrl = null, seq = 0;407  var lastData = null, shown = 0;408  var PAGE = 24;409410  fetch('/api/overview').then(function(r){return r.json()}).then(function(d){411    document.getElementById('sub').innerHTML = 'Une barre. <b>' + fmt.format(d.total) + ' inscriptions.</b> Tout le Québec.';412    if(!input.value) input.placeholder = 'Cherchez parmi ' + fmt.format(d.total) + ' inscriptions…';413    var a = d.apps;414    var parts = [415      'ORA·KA — '+fmt.format(d.total)+' INSCRIPTIONS DANS LE MOTEUR',416      'IMMO·KA — '+fmt.format(a.immo.n)+' PROPRIÉTÉS À VENDRE',417      'LOU·KA — '+fmt.format(a.lou.n)+' LOGEMENTS À LOUER',418      'FABRI·KA — '+fmt.format(a.fabri.n)+' PRODUITS QUÉBÉCOIS',419      'AUTO·KA — '+fmt.format(a.auto.n)+' VÉHICULES D\\'OCCASION',420      'FOOD·KA — '+fmt.format(a.food.n)+' ARTICLES D\\'ÉPICERIE',421      'MISE À JOUR AUTOMATIQUE'422    ];423    var html = parts.map(function(p){return '<span>'+p+'</span>'}).join('');424    var tk = document.getElementById('ticker');425    if(tk) tk.innerHTML = html + html;426  }).catch(function(){});427428  function esc(s){ var d=document.createElement('div'); d.textContent=s==null?'':String(s); return d.innerHTML; }429  function norm(s){ return s.normalize('NFD').replace(/[\\u0300-\\u036f]/g,'').toLowerCase(); }430431  var STOP = {avec:1,pour:1,dans:1,pres:1,sur:1,les:1,des:1,une:1,aux:1,est:1,par:1,chez:1,tout:1,tres:1};432  function hl(title, q){433    var t = esc(title);434    var toks = q.split(/[^0-9A-Za-zÀ-ÖØ-öø-ÿ½'-]+/).filter(function(w){435      return w.length>=3 && !STOP[norm(w)];436    });437    if(!toks.length) return t;438    var nt = norm(t);439    var marks = [];440    toks.forEach(function(tok){441      var n = norm(tok), from = 0, idx;442      while((idx = nt.indexOf(n, from)) !== -1){443        var prev = idx > 0 ? nt[idx-1] : ' ';444        if(!/[a-z0-9]/.test(prev)) marks.push([idx, idx+n.length]);445        from = idx+n.length;446      }447    });448    if(!marks.length) return t;449    marks.sort(function(a,b){return a[0]-b[0]});450    var merged = [marks[0]];451    for(var i=1;i<marks.length;i++){452      var last = merged[merged.length-1];453      if(marks[i][0] <= last[1]) last[1] = Math.max(last[1], marks[i][1]);454      else merged.push(marks[i]);455    }456    var out = '', pos = 0;457    merged.forEach(function(m){458      out += t.slice(pos, m[0]) + '<mark>' + t.slice(m[0], m[1]) + '</mark>';459      pos = m[1];460    });461    return out + t.slice(pos);462  }463464  function pct(it, best){465    if(typeof it.score !== 'number' || !best) return null;466    return Math.max(1, Math.min(100, Math.round(100 * it.score / best)));467  }468469  function card(it, i, best, q){470    var p = pct(it, best);471    var badge = p == null ? '' :472      '<span class="rscore'+(it.match==='exact'?' exact':'')+'" title="'+(it.match==='exact'?'Correspondance exacte':'Correspondance sémantique')+' — pertinence relative">'+473      (it.match==='exact'?'✓':'✦')+' '+p+'&nbsp;%</span>';474    var img = it.image475      ? '<img loading="lazy" src="'+esc(it.image)+'" alt="" onerror="this.remove()">'476      : '';477    return '<a class="rcard" style="animation-delay:'+Math.min(i*20,360)+'ms" href="'+esc(it.href)+'">'+478      '<div class="rimg"><span class="ph">'+PLACE[it.app]+'</span>'+img+479        '<span class="rtag"><span class="dot" style="background:'+COLOR[it.app]+'"></span>'+LABEL[it.app]+'</span>'+480        badge+481      '</div>'+482      '<div class="rbody"><div class="rtitle">'+hl(it.title, q)+'</div>'+483      (it.sub ? '<div class="rsub">'+esc(it.sub)+'</div>' : '')+484      (it.price ? '<div class="rprice">'+esc(it.price)+'</div>' : '')+485      (p != null ? '<div class="rmeter" aria-hidden="true"><i style="width:'+p+'%"></i></div>' : '')+486      '</div></a>';487  }488489  function render(){490    var d = lastData;491    if(!d || !d.q){ hide(); return; }492    var totalExact = 0;493    Object.keys(d.totals||{}).forEach(function(k){ totalExact += d.totals[k]; });494    var chips = Object.keys(d.counts).map(function(a){495      var target = (d.more && d.more[a]) || '#';496      var n = (d.totals && d.totals[a]) ? d.totals[a] : d.counts[a];497      return '<a class="fchip" href="'+esc(target)+'"><span class="dot" style="background:'+COLOR[a]+'"></span>'+498        LABEL[a]+' <b>'+fmt.format(n)+'</b></a>';499    }).join('');500    var best = 0;501    d.hits.forEach(function(h){ if(typeof h.score === 'number' && h.score > best) best = h.score; });502    var html = '<div class="rhead"><div class="rinfo"><b>'+fmt.format(Math.max(totalExact, d.hits.length))+'</b> résultat'+(d.hits.length>1?'s':'')+503      ' pour « '+esc(d.q)+' » — triés par pertinence'+(d.semantic ? '<span class="ai">✦ sémantique active</span>' : '')+'</div>'+504      '<div class="fchips">'+chips+'</div></div>';505    if(d.hits.length){506      html += '<div class="rgrid">'+d.hits.slice(0, shown).map(function(it,i){return card(it,i,best,d.q)}).join('')+'</div>';507      if(shown < d.hits.length){508        html += '<div class="morewrap"><button class="morebtn" id="more">Afficher plus (' + (d.hits.length - shown) + ')</button></div>';509      }510    } else {511      html += '<div class="notice glass"><div class="big">🧭</div>Rien trouvé pour « '+esc(d.q)+' ».<br>Essayez d\\'autres mots — le moteur comprend aussi les descriptions.</div>';512    }513    results.innerHTML = html;514    results.style.display = 'block';515    document.body.classList.add('searching');516    results.classList.remove('loading');517    var more = document.getElementById('more');518    if(more) more.addEventListener('click', function(){ shown += PAGE; render(); });519  }520521  function hide(){522    results.style.display = 'none';523    results.innerHTML = '';524    document.body.classList.remove('searching');525    document.body.classList.remove('fetching');526  }527528  function run(){529    var q = input.value.trim();530    barwrap.classList.toggle('has-q', q.length > 0);531    if(ctrl) ctrl.abort();532    if(!q){ lastData = null; hide(); return; }533    ctrl = new AbortController();534    var my = ++seq;535    document.body.classList.add('fetching');536    if(!lastData){537      var sk = '';538      for(var i=0;i<10;i++) sk += '<div class="skel" style="animation-delay:'+(i*45)+'ms"><div class="si"></div><div class="sb"><div class="sl"></div><div class="sl w60"></div></div></div>';539      results.innerHTML = '<div class="rhead"><div class="rinfo">Recherche dans les cinq univers<span class="ka-loader"><i></i><i></i><i></i><i></i><i></i></span></div></div><div class="rgrid">'+sk+'</div>';540      results.style.display = 'block';541      document.body.classList.add('searching');542    } else {543      results.classList.add('loading');544    }545    fetch('/api/search?q='+encodeURIComponent(q)+(scope?'&scope='+scope:''), {signal: ctrl.signal})546      .then(function(r){return r.json()})547      .then(function(d){ if(my === seq){ lastData = d; shown = PAGE; render(); } })548      .catch(function(){})549      .finally(function(){ if(my === seq) document.body.classList.remove('fetching'); });550  }551552  input.addEventListener('input', function(){553    clearTimeout(timer);554    timer = setTimeout(run, 280);555  });556  input.addEventListener('keydown', function(e){557    if(e.key === 'Enter'){ clearTimeout(timer); run(); }558    if(e.key === 'Escape'){ input.value = ''; run(); }559  });560  document.getElementById('clear').addEventListener('click', function(){ input.value=''; input.focus(); run(); });561562  document.getElementById('scopes').addEventListener('click', function(e){563    var b = e.target.closest('.scope');564    if(!b) return;565    scope = b.dataset.scope || '';566    document.querySelectorAll('.scope').forEach(function(x){ x.classList.toggle('on', x === b); });567    if(input.value.trim()){ clearTimeout(timer); run(); }568    input.focus();569  });570  document.querySelectorAll('.suggest').forEach(function(b){571    b.addEventListener('click', function(){ input.value = b.textContent; run(); input.focus(); });572  });573  document.addEventListener('keydown', function(e){574    if(e.key === '/' && document.activeElement !== input){ e.preventDefault(); input.focus(); }575  });576  var uq = new URLSearchParams(location.search).get('q');577  if(uq){ input.value = uq; run(); }578})();579(function(){580  /* ---- compte KA ID (SSO du Groupe KA) ---- */581  var box = document.getElementById('auth');582  if(!box) return;583  function esc(s){ var d=document.createElement('div'); d.textContent=s==null?'':String(s); return d.innerHTML; }584  function renderBtn(){585    box.innerHTML = '<a class="ka-btn" href="/api/auth/ka/login?next=/"><span class="ka-badge">KA</span>KA ID</a>';586  }587  function renderUser(u){588    var initial = String(u.name || u.email || '?').trim().charAt(0).toUpperCase();589    var av = u.picture ? '<img src="'+esc(u.picture)+'" alt="" onerror="this.remove()">' : esc(initial);590    box.innerHTML =591      '<button class="ka-avatar" id="ka-avatar" aria-label="Compte KA ID" aria-haspopup="true">'+av+'</button>'+592      '<div class="ka-menu" id="ka-menu" role="menu">'+593        '<div class="m-name">'+esc(u.name || 'Compte KA ID')+'</div>'+594        (u.email ? '<div class="m-mail">'+esc(u.email)+'</div>' : '')+595        (u.ka_id ? '<div class="m-id">'+esc(u.ka_id)+'</div>' : '')+596        (u.role ? '<div class="m-role">'+esc(u.role)+'</div>' : '')+597        '<hr>'+598        '<a class="m-link" href="https://www.groupe-ka.com/compte" target="_blank" rel="noopener">Mon profil Groupe KA ↗</a>'+599        '<button class="m-out" id="ka-out">Se déconnecter</button>'+600      '</div>';601    var menu = document.getElementById('ka-menu');602    document.getElementById('ka-avatar').addEventListener('click', function(e){603      e.stopPropagation();604      menu.classList.toggle('open');605    });606    document.addEventListener('click', function(e){607      if(menu.classList.contains('open') && !menu.contains(e.target)) menu.classList.remove('open');608    });609    document.addEventListener('keydown', function(e){610      if(e.key === 'Escape') menu.classList.remove('open');611    });612    document.getElementById('ka-out').addEventListener('click', function(){613      fetch('/api/auth/logout', {method:'POST'})614        .then(function(){ location.reload(); })615        .catch(function(){ location.reload(); });616    });617  }618  fetch('/api/auth/me').then(function(r){return r.json()}).then(function(d){619    if(d && d.user) renderUser(d.user);620    else if(d && d.enabled) renderBtn();621  }).catch(function(){});622})();623(function(){624  /* ---- « Mon univers Ka » : les favoris du membre, toutes plateformes du625     groupe (Lou·Ka, Immo·Ka, Auto·Ka, Fabri·Ka, Food·Ka…), lus du hub.626     Visible seulement si connecté ET s'il y a au moins un favori. ---- */627  var sec = document.getElementById('kafavs');628  var grid = document.getElementById('kafavs-grid');629  if(!sec || !grid) return;630  function esc(s){ var d=document.createElement('div'); d.textContent=s==null?'':String(s); return d.innerHTML; }631  /* pastille d'univers — même principe que celle des résultats de recherche */632  var UNIVERS = {633    'lou-ka':   {label:'Lou·Ka',   color:'#d9f26b'},634    'immo-ka':  {label:'Immo·Ka',  color:'#e23744'},635    'auto-ka':  {label:'Auto·Ka',  color:'#ff5a2a'},636    'fabri-ka': {label:'Fabri·Ka', color:'#C4532E'},637    'food-ka':  {label:'Food·Ka',  color:'#d9f26b'}638  };639  function card(it, i){640    var u = UNIVERS[it.app] || {label: esc(it.app || 'Ka'), color:'var(--ink-3)'};641    var img = it.image_url642      ? '<img loading="lazy" src="'+esc(it.image_url)+'" alt="" onerror="this.remove()">'643      : '';644    return '<a class="rcard" style="animation-delay:'+Math.min(i*20,360)+'ms" href="'+645      (it.url ? esc(it.url) : '#')+'" target="_blank" rel="noopener">'+646      '<div class="rimg"><span class="ph">♥</span>'+img+647        '<span class="rtag"><span class="dot" style="background:'+u.color+'"></span>'+u.label+'</span>'+648      '</div>'+649      '<div class="rbody"><div class="rtitle">'+esc(it.title)+'</div>'+650      (it.subtitle ? '<div class="rsub">'+esc(it.subtitle)+'</div>' : '')+651      (it.price_label ? '<div class="rprice">'+esc(it.price_label)+'</div>' : '')+652      '</div></a>';653  }654  fetch('/api/auth/me').then(function(r){return r.json()}).then(function(d){655    if(!d || !d.user) return;656    fetch('/api/favorites').then(function(r){ return r.ok ? r.json() : null; }).then(function(f){657      var favs = (f && f.favorites) || [];658      if(!favs.length) return;659      grid.innerHTML = favs.map(card).join('');660      sec.classList.add('show');661    }).catch(function(){});662  }).catch(function(){});663})();664</script>665</body>666</html>667"""668