Identité visuelle officielle Lou-Ka — palette marine #0B1330 / bleu vif #2258FF / orange #FF6A00 / gris #F2F2F2 ; logo « porte entrouverte » (Logo.tsx, favicon + icône PWA sur carré marine) ; wordmark Lou-Ka (Ka orange) ; baseline « La porte d'entrée vers votre prochain chez-vous. » ; héro « Trouvez votre prochain chez-vous. » + rangée des 4 avantages ; Inter partout (fin de Space Grotesk/JetBrains Mono) ; coins très arrondis + pill-shaped + ombres douces (styles.css réécrit, mêmes sélecteurs) ; barre de nav mobile 4 onglets (active orange) ; thème Ka Maps marine/orange + immeuble 3D orange ; PDF (pdfgen.py) et SSR SEO alignés
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
13 changed files +744 −554
modified
frontend/index.html
+3 −3
@@ -9,7 +9,7 @@ | |||||
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> | ||
| 10 | <title>Lou-Ka — Logements à louer au Québec</title> | 10 | <title>Lou-Ka — Logements à louer au Québec</title> | ||
| 11 | <meta name="description" content="Lou-Ka agrège les appartements et logements à louer publiés par les gestionnaires immobiliers partout au Québec — Montréal, Québec, Lévis, Gatineau et plus — toujours à jour." /> | 11 | <meta name="description" content="Lou-Ka agrège les appartements et logements à louer publiés par les gestionnaires immobiliers partout au Québec — Montréal, Québec, Lévis, Gatineau et plus — toujours à jour." /> | ||
| 12 | − <meta name="theme-color" content="#f5f3ee" /> | 12 | + <meta name="theme-color" content="#ffffff" /> | ||
| 13 | <meta name="mobile-web-app-capable" content="yes" /> | 13 | <meta name="mobile-web-app-capable" content="yes" /> | ||
| 14 | <meta name="apple-mobile-web-app-capable" content="yes" /> | 14 | <meta name="apple-mobile-web-app-capable" content="yes" /> | ||
| 15 | <meta name="apple-mobile-web-app-status-bar-style" content="default" /> | 15 | <meta name="apple-mobile-web-app-status-bar-style" content="default" /> | ||
@@ -17,8 +17,8 @@ | |||||
| 17 | <link rel="manifest" href="/manifest.webmanifest" /> | 17 | <link rel="manifest" href="/manifest.webmanifest" /> | ||
| 18 | <link rel="preconnect" href="https://fonts.googleapis.com" /> | 18 | <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| 19 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | 19 | <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | ||
| 20 | − <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" /> | 20 | + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" /> | ||
| 21 | − <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='12' fill='%23121712'/%3E%3Ctext x='32' y='45' font-family='Arial Black,sans-serif' font-size='32' font-weight='900' fill='%23d9f26b' text-anchor='middle'%3ELK%3C/text%3E%3C/svg%3E" /> | 21 | + <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='%230B1330'/%3E%3Crect x='19' y='9' width='26' height='46' rx='8' fill='none' stroke='%23FF6A00' stroke-width='4'/%3E%3Cpolygon points='24.5,16 36,13.5 36,49 24.5,46' fill='%23ffffff'/%3E%3Cpolygon points='24.5,46 36,49 42,52.5 24.5,52.5' fill='%23FF6A00'/%3E%3C/svg%3E" /> | ||
| 22 | </head> | 22 | </head> | ||
| 23 | <body> | 23 | <body> | ||
| 24 | <div id="root"></div> | 24 | <div id="root"></div> | ||
modified
frontend/public/manifest.webmanifest
+4 −4
@@ -1,15 +1,15 @@ | |||||
| 1 | { | 1 | { | ||
| 2 | "name": "Lou-Ka — Logements à louer au Québec", | 2 | "name": "Lou-Ka — Logements à louer au Québec", | ||
| 3 | "short_name": "Lou-Ka", | 3 | "short_name": "Lou-Ka", | ||
| 4 | − "description": "Tous les logements à louer de Québec et Lévis, un seul endroit.", | 4 | + "description": "La porte d'entrée vers votre prochain chez-vous. Tous les logements à louer au Québec, regroupés au même endroit.", | ||
| 5 | "start_url": "/", | 5 | "start_url": "/", | ||
| 6 | "display": "standalone", | 6 | "display": "standalone", | ||
| 7 | − "background_color": "#f5f3ee", | 7 | + "background_color": "#ffffff", | ||
| 8 | − "theme_color": "#f5f3ee", | 8 | + "theme_color": "#0b1330", | ||
| 9 | "lang": "fr", | 9 | "lang": "fr", | ||
| 10 | "icons": [ | 10 | "icons": [ | ||
| 11 | { | 11 | { | ||
| 12 | − "src": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Crect width='512' height='512' rx='96' fill='%23121712'/%3E%3Ctext x='256' y='356' font-family='Arial Black,sans-serif' font-size='256' font-weight='900' fill='%23d9f26b' text-anchor='middle'%3ELK%3C/text%3E%3C/svg%3E", | 12 | + "src": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Crect width='512' height='512' rx='112' fill='%230B1330'/%3E%3Crect x='152' y='72' width='208' height='368' rx='64' fill='none' stroke='%23FF6A00' stroke-width='32'/%3E%3Cpolygon points='196,128 288,108 288,392 196,368' fill='%23ffffff'/%3E%3Cpolygon points='196,368 288,392 336,420 196,420' fill='%23FF6A00'/%3E%3C/svg%3E", | ||
| 13 | "sizes": "512x512", | 13 | "sizes": "512x512", | ||
| 14 | "type": "image/svg+xml", | 14 | "type": "image/svg+xml", | ||
| 15 | "purpose": "any" | 15 | "purpose": "any" | ||
modified
frontend/src/App.tsx
+38 −9
@@ -12,8 +12,10 @@ import { | |||||
| 12 | import { AccountProvider, useAccount } from "./account"; | 12 | import { AccountProvider, useAccount } from "./account"; | ||
| 13 | import CookieConsent from "./components/CookieConsent"; | 13 | import CookieConsent from "./components/CookieConsent"; | ||
| 14 | import { | 14 | import { | ||
| 15 | − IcoChart, IcoCompass, IcoDoc, IcoFolder, IcoHouse, IcoLock, IcoMap, | 15 | + IcoChart, IcoCompass, IcoDoc, IcoFolder, IcoHeart, IcoHouse, IcoLock, | ||
| 16 | + IcoMap, IcoSearch, IcoUser, | ||||
| 16 | } from "./components/Icons"; | 17 | } from "./components/Icons"; | ||
| 18 | +import { LogoIcon } from "./components/Logo"; | ||||
| 17 | import Home from "./pages/Home"; | 19 | import Home from "./pages/Home"; | ||
| 18 | import ListingPage from "./pages/Listing"; | 20 | import ListingPage from "./pages/Listing"; | ||
| 19 | import BotPage from "./pages/Bot"; | 21 | import BotPage from "./pages/Bot"; | ||
@@ -97,12 +99,11 @@ function AccountMenu() { | |||||
| 97 | justifyContent: "center", | 99 | justifyContent: "center", | ||
| 98 | width: 18, | 100 | width: 18, | ||
| 99 | height: 16, | 101 | height: 16, | ||
| 100 | − borderRadius: 4, | 102 | + borderRadius: 5, | ||
| 101 | − background: "#141814", | 103 | + background: "#ff6a00", | ||
| 102 | − color: "#d9f26b", | 104 | + color: "#ffffff", | ||
| 103 | − font: "700 9px/1 'Space Grotesk', system-ui, sans-serif", | 105 | + font: "700 9px/1 'Inter', system-ui, sans-serif", | ||
| 104 | letterSpacing: "-0.02em", | 106 | letterSpacing: "-0.02em", | ||
| 105 | − transform: "rotate(-2deg)", | ||||
| 106 | }} | 107 | }} | ||
| 107 | > | 108 | > | ||
| 108 | KA | 109 | KA | ||
@@ -199,8 +200,9 @@ function Header() { | |||||
| 199 | <header className="header"> | 200 | <header className="header"> | ||
| 200 | <div className="container header-inner"> | 201 | <div className="container header-inner"> | ||
| 201 | <NavLink to="/" className="brand" aria-label="Lou-Ka — accueil"> | 202 | <NavLink to="/" className="brand" aria-label="Lou-Ka — accueil"> | ||
| 202 | − Lou<span className="ka">Ka</span> | 203 | + <LogoIcon size={30} /> | ||
| 203 | − <span className="brand-tag">Québec · Lévis · Montréal — toujours à jour</span> | 204 | + Lou-<span className="ka">Ka</span> | ||
| 205 | + <span className="brand-tag">La porte d'entrée vers votre prochain chez-vous.</span> | ||||
| 204 | </NavLink> | 206 | </NavLink> | ||
| 205 | <nav className="nav" aria-label="Navigation principale"> | 207 | <nav className="nav" aria-label="Navigation principale"> | ||
| 206 | <NavLink to="/" end className={({ isActive }) => (isActive ? "active" : "")}> | 208 | <NavLink to="/" end className={({ isActive }) => (isActive ? "active" : "")}> | ||
@@ -258,7 +260,11 @@ function Footer() { | |||||
| 258 | <footer className="footer"> | 260 | <footer className="footer"> | ||
| 259 | <div className="container"> | 261 | <div className="container"> | ||
| 260 | <div className="fbrand"> | 262 | <div className="fbrand"> | ||
| 261 | − Lou<span className="ka">Ka</span> | 263 | + <LogoIcon size={32} dark /> | ||
| 264 | + Lou-<span className="ka">Ka</span> | ||||
| 265 | + </div> | ||||
| 266 | + <div className="fbaseline"> | ||||
| 267 | + La porte d'entrée vers votre prochain <span className="hl">chez-vous.</span> | ||||
| 262 | </div> | 268 | </div> | ||
| 263 | <div className="frow"> | 269 | <div className="frow"> | ||
| 264 | <div> | 270 | <div> | ||
@@ -294,6 +300,28 @@ function Footer() { | |||||
| 294 | ); | 300 | ); | ||
| 295 | } | 301 | } | ||
| 296 | 302 | ||||
| 303 | +/** Barre de navigation inférieure (mobile) — icône active en orange. */ | ||||
| 304 | +function MobileTabBar() { | ||||
| 305 | + const location = useLocation(); | ||||
| 306 | + const isCarte = new URLSearchParams(location.search).get("view") === "carte"; | ||||
| 307 | + const tabs = [ | ||||
| 308 | + { to: "/", label: "Rechercher", icon: <IcoSearch size={20} />, on: location.pathname === "/" && !isCarte }, | ||||
| 309 | + { to: "/?view=carte", label: "Carte", icon: <IcoMap size={20} />, on: location.pathname === "/" && isCarte }, | ||||
| 310 | + { to: "/favoris", label: "Favoris", icon: <IcoHeart size={20} />, on: location.pathname.startsWith("/favoris") }, | ||||
| 311 | + { to: "/profil", label: "Profil", icon: <IcoUser size={20} />, on: location.pathname.startsWith("/profil") }, | ||||
| 312 | + ]; | ||||
| 313 | + return ( | ||||
| 314 | + <nav className="tabbar" aria-label="Navigation mobile"> | ||||
| 315 | + {tabs.map((t) => ( | ||||
| 316 | + <NavLink key={t.label} to={t.to} className={() => (t.on ? "active" : "")}> | ||||
| 317 | + {t.icon} | ||||
| 318 | + {t.label} | ||||
| 319 | + </NavLink> | ||||
| 320 | + ))} | ||||
| 321 | + </nav> | ||||
| 322 | + ); | ||||
| 323 | +} | ||||
| 324 | + | ||||
| 297 | export default function App() { | 325 | export default function App() { | ||
| 298 | return ( | 326 | return ( | ||
| 299 | <AccountProvider> | 327 | <AccountProvider> | ||
@@ -330,6 +358,7 @@ export default function App() { | |||||
| 330 | </Routes> | 358 | </Routes> | ||
| 331 | </main> | 359 | </main> | ||
| 332 | <Footer /> | 360 | <Footer /> | ||
| 361 | + <MobileTabBar /> | ||||
| 333 | <CookieConsent /> | 362 | <CookieConsent /> | ||
| 334 | </AccountProvider> | 363 | </AccountProvider> | ||
| 335 | ); | 364 | ); | ||
modified
frontend/src/components/Icons.tsx
+12 −5
@@ -1,9 +1,9 @@ | |||||
| 1 | // ----------------------------------------------------------------------------- | 1 | // ----------------------------------------------------------------------------- | ||
| 2 | // Lou-Ka — Agrégateur de logements à louer (province de Québec) | 2 | // Lou-Ka — Agrégateur de logements à louer (province de Québec) | ||
| 3 | // Auteur : Simon-Pierre Boucher — contact@spboucher.ai | 3 | // Auteur : Simon-Pierre Boucher — contact@spboucher.ai | ||
| 4 | −// components/Icons.tsx : icônes SVG maison « éditorial sharp » | 4 | +// components/Icons.tsx : icônes SVG maison — style linéaire (outline), | ||
| 5 | −// Tracés au trait (stroke) 2 px, coins vifs (square/miter), currentColor — | 5 | +// trait moyen 2 px, extrémités arrondies, currentColor — conformes à | ||
| 6 | −// remplacent les emojis génériques pour coller à l'identité de l'app. | 6 | +// l'identité visuelle Lou-Ka (minimaliste et chaleureuse). | ||
| 7 | // ----------------------------------------------------------------------------- | 7 | // ----------------------------------------------------------------------------- | ||
| 8 | import { SVGProps } from "react"; | 8 | import { SVGProps } from "react"; | ||
| 9 | 9 | ||||
@@ -13,8 +13,8 @@ function Base({ size = 16, children, ...rest }: P) { | |||||
| 13 | return ( | 13 | return ( | ||
| 14 | <svg | 14 | <svg | ||
| 15 | width={size} height={size} viewBox="0 0 24 24" fill="none" | 15 | width={size} height={size} viewBox="0 0 24 24" fill="none" | ||
| 16 | − stroke="currentColor" strokeWidth="2" strokeLinecap="square" | 16 | + stroke="currentColor" strokeWidth="2" strokeLinecap="round" | ||
| 17 | − strokeLinejoin="miter" aria-hidden="true" focusable="false" {...rest} | 17 | + strokeLinejoin="round" aria-hidden="true" focusable="false" {...rest} | ||
| 18 | > | 18 | > | ||
| 19 | {children} | 19 | {children} | ||
| 20 | </svg> | 20 | </svg> | ||
@@ -163,6 +163,13 @@ export const IcoHeart = (p: P & { filled?: boolean }) => { | |||||
| 163 | 163 | ||||
| 164 | // -- réseaux sociaux (tracés au trait, même langage que le reste) -------------- | 164 | // -- réseaux sociaux (tracés au trait, même langage que le reste) -------------- | ||
| 165 | 165 | ||||
| 166 | +export const IcoUser = (p: P) => ( | ||||
| 167 | + <Base {...p}> | ||||
| 168 | + <circle cx="12" cy="8" r="3.5" /> | ||||
| 169 | + <path d="M5 20c1.2-3.4 3.8-5 7-5s5.8 1.6 7 5" /> | ||||
| 170 | + </Base> | ||||
| 171 | +); | ||||
| 172 | + | ||||
| 166 | export const IcoInstagram = (p: P) => ( | 173 | export const IcoInstagram = (p: P) => ( | ||
| 167 | <Base {...p}> | 174 | <Base {...p}> | ||
| 168 | <rect x="4" y="4" width="16" height="16" rx="4.5" /> | 175 | <rect x="4" y="4" width="16" height="16" rx="4.5" /> | ||
modified
frontend/src/components/ListingMap3D.tsx
+2 −2
@@ -17,7 +17,7 @@ import { louKaMapTheme } from "../kamaps/theme"; | |||||
| 17 | import { MAPBOX_TOKEN } from "../kamaps/config"; | 17 | import { MAPBOX_TOKEN } from "../kamaps/config"; | ||
| 18 | 18 | ||||
| 19 | /** Rouge signal de la fiche — même langage que les badges d'alerte. */ | 19 | /** Rouge signal de la fiche — même langage que les badges d'alerte. */ | ||
| 20 | −export const BUILDING_RED = "#e03131"; | 20 | +export const BUILDING_RED = "#ff6a00"; // orange officiel Lou-Ka | ||
| 21 | 21 | ||||
| 22 | export default function ListingMap3D({ l }: { l: Listing }) { | 22 | export default function ListingMap3D({ l }: { l: Listing }) { | ||
| 23 | const property = useMemo<MapProperty | null>(() => { | 23 | const property = useMemo<MapProperty | null>(() => { | ||
@@ -41,7 +41,7 @@ export default function ListingMap3D({ l }: { l: Listing }) { | |||||
| 41 | 41 | ||||
| 42 | return ( | 42 | return ( | ||
| 43 | <div className="lmap3d" role="img" | 43 | <div className="lmap3d" role="img" | ||
| 44 | − aria-label={`Carte 3D — ${l.address || l.title}, bâtiment de l'annonce en rouge`}> | 44 | + aria-label={`Carte 3D — ${l.address || l.title}, bâtiment de l'annonce en orange`}> | ||
| 45 | <KaSpotlightMap | 45 | <KaSpotlightMap | ||
| 46 | theme={louKaMapTheme} | 46 | theme={louKaMapTheme} | ||
| 47 | mapboxToken={MAPBOX_TOKEN} | 47 | mapboxToken={MAPBOX_TOKEN} | ||
added
frontend/src/components/Logo.tsx
+62 −0
@@ -0,0 +1,62 @@ | |||||
| 1 | +// ----------------------------------------------------------------------------- | ||||
| 2 | +// Lou-Ka — Agrégateur de logements à louer (province de Québec) | ||||
| 3 | +// Auteur : Simon-Pierre Boucher — contact@spboucher.ai | ||||
| 4 | +// components/Logo.tsx : icône officielle Lou-Ka — la porte d'entrée. | ||||
| 5 | +// Cadre orange #FF6A00 aux coins très arrondis ; porte entrouverte bleu | ||||
| 6 | +// marine #0B1330 en perspective (ouverture vers la droite) ; tapis de | ||||
| 7 | +// lumière orange projeté au sol. Effet « liquid glass » subtil. | ||||
| 8 | +// Baseline : « La porte d'entrée vers votre prochain chez-vous. » | ||||
| 9 | +// ----------------------------------------------------------------------------- | ||||
| 10 | + | ||||
| 11 | +/** Icône seule (fond transparent — pour fonds clairs). */ | ||||
| 12 | +export function LogoIcon({ size = 30, dark = false }: { size?: number; dark?: boolean }) { | ||||
| 13 | + // dark = version pour fond bleu marine : la porte devient blanche | ||||
| 14 | + const door = dark ? "#ffffff" : "#0b1330"; | ||||
| 15 | + const doorTop = dark ? "#ffffff" : "#16204a"; | ||||
| 16 | + const uid = dark ? "lk-dark" : "lk-light-bg"; | ||||
| 17 | + return ( | ||||
| 18 | + <svg | ||||
| 19 | + className="logo-ico" | ||||
| 20 | + width={size} | ||||
| 21 | + height={size} | ||||
| 22 | + viewBox="0 0 64 64" | ||||
| 23 | + fill="none" | ||||
| 24 | + aria-hidden="true" | ||||
| 25 | + > | ||||
| 26 | + <defs> | ||||
| 27 | + <linearGradient id={`${uid}-door`} x1="0" y1="0" x2="0" y2="1"> | ||||
| 28 | + <stop offset="0" stopColor={doorTop} /> | ||||
| 29 | + <stop offset="1" stopColor={door} /> | ||||
| 30 | + </linearGradient> | ||||
| 31 | + <linearGradient id={`${uid}-light`} x1="0" y1="0" x2="1" y2="0"> | ||||
| 32 | + <stop offset="0" stopColor="#ff6a00" /> | ||||
| 33 | + <stop offset="1" stopColor="#ff6a00" stopOpacity="0.72" /> | ||||
| 34 | + </linearGradient> | ||||
| 35 | + <linearGradient id={`${uid}-frame`} x1="0" y1="0" x2="0" y2="1"> | ||||
| 36 | + <stop offset="0" stopColor="#ff7d1f" /> | ||||
| 37 | + <stop offset="1" stopColor="#ff6a00" /> | ||||
| 38 | + </linearGradient> | ||||
| 39 | + </defs> | ||||
| 40 | + {/* cadre : rectangle vertical, coins très arrondis, contour orange épais */} | ||||
| 41 | + <rect x="15" y="5" width="34" height="54" rx="10" stroke={`url(#${uid}-frame)`} strokeWidth="4.5" /> | ||||
| 42 | + {/* porte entrouverte : parallélogramme en perspective, bord droit plus proche */} | ||||
| 43 | + <polygon points="22,13.5 37,10.5 37,52 22,48.5" fill={`url(#${uid}-door)`} /> | ||||
| 44 | + {/* reflet « liquid glass » sur la porte */} | ||||
| 45 | + <polygon points="24,13.2 28,12.4 28,49 24,48.2" fill="#ffffff" opacity="0.08" /> | ||||
| 46 | + {/* lumière projetée au sol — tapis d'accueil */} | ||||
| 47 | + <polygon points="22,48.5 37,52 44.5,55.8 22,55.8" fill={`url(#${uid}-light)`} /> | ||||
| 48 | + </svg> | ||||
| 49 | + ); | ||||
| 50 | +} | ||||
| 51 | + | ||||
| 52 | +/** Wordmark complet : icône + « Lou-Ka » (Lou- marine, Ka orange). */ | ||||
| 53 | +export default function Logo({ size = 30 }: { size?: number }) { | ||||
| 54 | + return ( | ||||
| 55 | + <span style={{ display: "inline-flex", alignItems: "center" }}> | ||||
| 56 | + <LogoIcon size={size} /> | ||||
| 57 | + <span style={{ color: "#0b1330", fontWeight: 700 }}> | ||||
| 58 | + Lou-<span style={{ color: "#ff6a00" }}>Ka</span> | ||||
| 59 | + </span> | ||||
| 60 | + </span> | ||||
| 61 | + ); | ||||
| 62 | +} | ||||
modified
frontend/src/kamaps/theme.ts
+43 −43
@@ -2,75 +2,75 @@ | |||||
| 2 | // Author: Simon-Pierre Boucher | 2 | // Author: Simon-Pierre Boucher | ||
| 3 | // Contact: contact@spboucher.ai | 3 | // Contact: contact@spboucher.ai | ||
| 4 | // Project: Groupe Ka / Ka Maps (Lou-Ka integration) | 4 | // Project: Groupe Ka / Ka Maps (Lou-Ka integration) | ||
| 5 | −// kamaps/theme.ts : thème Lou-Ka Maps — les jetons du design system | 5 | +// kamaps/theme.ts : thème Lou-Ka Maps — identité officielle Lou-Ka | ||
| 6 | −// (encre/lime/vert « éditorial sharp ») appliqués au framework Ka Maps. | 6 | +// (marine #0B1330 / bleu vif #2258FF / orange #FF6A00) appliquée à Ka Maps. | ||
| 7 | // ----------------------------------------------------------------------------- | 7 | // ----------------------------------------------------------------------------- | ||
| 8 | import type { KaMapTheme } from "@groupe-ka/ka-maps"; | 8 | import type { KaMapTheme } from "@groupe-ka/ka-maps"; | ||
| 9 | 9 | ||||
| 10 | −const INK = "#141814"; | 10 | +const NAVY = "#0b1330"; | ||
| 11 | −const PAPER = "#f5f3ee"; | 11 | +const BLUE = "#2258ff"; | ||
| 12 | −const GREEN = "#1c5c41"; | 12 | +const ORANGE = "#ff6a00"; | ||
| 13 | −const LIME = "#d9f26b"; | 13 | +const WHITE = "#ffffff"; | ||
| 14 | 14 | ||||
| 15 | export const louKaMapTheme: KaMapTheme = { | 15 | export const louKaMapTheme: KaMapTheme = { | ||
| 16 | id: "lou-ka", | 16 | id: "lou-ka", | ||
| 17 | productName: "Lou-Ka Maps", | 17 | productName: "Lou-Ka Maps", | ||
| 18 | − accent: GREEN, | 18 | + accent: BLUE, | ||
| 19 | − onAccent: LIME, | 19 | + onAccent: WHITE, | ||
| 20 | fontFamily: | 20 | fontFamily: | ||
| 21 | '"Inter", system-ui, -apple-system, "Segoe UI", sans-serif', | 21 | '"Inter", system-ui, -apple-system, "Segoe UI", sans-serif', | ||
| 22 | markers: { | 22 | markers: { | ||
| 23 | − // Prix : pastille encre, texte BLANC — contraste maximal sur fond clair. | 23 | + // Prix : pastille bleu marine, texte BLANC — contraste maximal sur fond clair. | ||
| 24 | − // Le lime n'apparaît qu'à la sélection (le moment de marque). | 24 | + // L'orange n'apparaît qu'à la sélection (le moment de marque). | ||
| 25 | sale: { | 25 | sale: { | ||
| 26 | − background: INK, | 26 | + background: NAVY, | ||
| 27 | − text: "#ffffff", | 27 | + text: WHITE, | ||
| 28 | halo: "rgba(255,255,255,0.45)", | 28 | halo: "rgba(255,255,255,0.45)", | ||
| 29 | − selectedBackground: LIME, | 29 | + selectedBackground: ORANGE, | ||
| 30 | − selectedText: INK, | 30 | + selectedText: WHITE, | ||
| 31 | }, | 31 | }, | ||
| 32 | rent: { | 32 | rent: { | ||
| 33 | − background: INK, | 33 | + background: NAVY, | ||
| 34 | − text: "#ffffff", | 34 | + text: WHITE, | ||
| 35 | halo: "rgba(255,255,255,0.45)", | 35 | halo: "rgba(255,255,255,0.45)", | ||
| 36 | − selectedBackground: LIME, | 36 | + selectedBackground: ORANGE, | ||
| 37 | − selectedText: INK, | 37 | + selectedText: WHITE, | ||
| 38 | }, | 38 | }, | ||
| 39 | highlight: { | 39 | highlight: { | ||
| 40 | − background: GREEN, | 40 | + background: BLUE, | ||
| 41 | − text: "#ffffff", | 41 | + text: WHITE, | ||
| 42 | halo: "rgba(255,255,255,0.45)", | 42 | halo: "rgba(255,255,255,0.45)", | ||
| 43 | − selectedBackground: LIME, | 43 | + selectedBackground: ORANGE, | ||
| 44 | − selectedText: INK, | 44 | + selectedText: WHITE, | ||
| 45 | }, | 45 | }, | ||
| 46 | }, | 46 | }, | ||
| 47 | cluster: { | 47 | cluster: { | ||
| 48 | − background: INK, | 48 | + background: NAVY, | ||
| 49 | − text: "#ffffff", | 49 | + text: WHITE, | ||
| 50 | − border: LIME, | 50 | + border: ORANGE, | ||
| 51 | }, | 51 | }, | ||
| 52 | − // Cartographie Lou-Ka : fond neutre et raffiné — papier chaud discret, | 52 | + // Cartographie Lou-Ka : fond neutre et raffiné — gris très pâle, | ||
| 53 | − // eau bleu-gris désaturé, parcs verts subtils, bâtiments bien définis. | 53 | + // eau bleu désaturé, parcs verts subtils, bâtiments bien définis. | ||
| 54 | // Les prix restent l'élément dominant de la carte. | 54 | // Les prix restent l'élément dominant de la carte. | ||
| 55 | basemapOverrides: { | 55 | basemapOverrides: { | ||
| 56 | light: { | 56 | light: { | ||
| 57 | − background: "#f6f5f1", | 57 | + background: "#f7f8fa", | ||
| 58 | − residential: "#efeee8", | 58 | + residential: "#f0f1f4", | ||
| 59 | − water: "#c3d6df", | 59 | + water: "#c9d8ef", | ||
| 60 | − waterway: "#b8cdd8", | 60 | + waterway: "#bfd0ea", | ||
| 61 | − park: "#dde8d1", | 61 | + park: "#dce8d8", | ||
| 62 | − wood: "#d2e0c4", | 62 | + wood: "#d0e0ca", | ||
| 63 | − building: "#e7e4db", | 63 | + building: "#e6e8ee", | ||
| 64 | − buildingOutline: "#d2cec1", | 64 | + buildingOutline: "#d0d3dd", | ||
| 65 | road: "#ffffff", | 65 | road: "#ffffff", | ||
| 66 | − roadCasing: "#dcd8cc", | 66 | + roadCasing: "#dcdee6", | ||
| 67 | − motorway: "#f0e9d0", | 67 | + motorway: "#f4ede2", | ||
| 68 | − boundary: "#b0a998", | 68 | + boundary: "#a9adbd", | ||
| 69 | − label: "#474b45", | 69 | + label: "#3e4560", | ||
| 70 | − labelHalo: "rgba(246,245,241,0.92)", | 70 | + labelHalo: "rgba(247,248,250,0.92)", | ||
| 71 | − cityLabel: "#1f231e", | 71 | + cityLabel: "#0b1330", | ||
| 72 | − roadLabel: "#8b8d82", | 72 | + roadLabel: "#8a90a5", | ||
| 73 | − waterLabel: "#5c7c8c", | 73 | + waterLabel: "#5c74a8", | ||
| 74 | }, | 74 | }, | ||
| 75 | }, | 75 | }, | ||
| 76 | supportsDark: false, | 76 | supportsDark: false, | ||
modified
frontend/src/pages/Home.tsx
+25 −8
@@ -15,7 +15,8 @@ import { | |||||
| 15 | import ListingCard from "../components/ListingCard"; | 15 | import ListingCard from "../components/ListingCard"; | ||
| 16 | import Pager from "../components/Pager"; | 16 | import Pager from "../components/Pager"; | ||
| 17 | import { | 17 | import { | ||
| 18 | − IcoAlert, IcoBolt, IcoList, IcoMap, IcoPaw, IcoSearch, IcoSliders, IcoSofa, | 18 | + IcoAlert, IcoBolt, IcoHeart, IcoHouse, IcoList, IcoMap, IcoPaw, IcoPin, | ||
| 19 | + IcoSearch, IcoSliders, IcoSofa, | ||||
| 19 | } from "../components/Icons"; | 20 | } from "../components/Icons"; | ||
| 20 | 21 | ||||
| 21 | // La carte (MapLibre, ~220 ko) n'est chargée que si l'utilisateur l'ouvre | 22 | // La carte (MapLibre, ~220 ko) n'est chargée que si l'utilisateur l'ouvre | ||
@@ -196,16 +197,32 @@ export default function Home() { | |||||
| 196 | return ( | 197 | return ( | ||
| 197 | <div className="container"> | 198 | <div className="container"> | ||
| 198 | <section className="hero"> | 199 | <section className="hero"> | ||
| 199 | − <span className="kicker">Agrégateur — Québec · Lévis · Montréal</span> | 200 | + <span className="kicker">La porte d'entrée vers votre prochain chez-vous.</span> | ||
| 200 | <h1> | 201 | <h1> | ||
| 201 | − Tous les logements <span className="outline">à louer</span>,<br /> | 202 | + Trouvez votre prochain <span className="hl">chez-vous.</span> | ||
| 202 | − <span className="hl">un seul</span> endroit. | ||||
| 203 | </h1> | 203 | </h1> | ||
| 204 | <p className="lede"> | 204 | <p className="lede"> | ||
| 205 | − Lou-Ka rassemble les appartements affichés par les gestionnaires immobiliers du | 205 | + Tous les logements à louer au Québec, regroupés au même endroit — mis à jour | ||
| 206 | − Québec — Québec, Lévis et le Grand Montréal — mis à jour automatiquement, avec | 206 | + automatiquement, avec toutes les photos et un lien direct vers l'annonce originale. | ||
| 207 | − toutes les photos et un lien direct vers l'annonce originale. | ||||
| 208 | </p> | 207 | </p> | ||
| 208 | + <div className="benefits" aria-label="Avantages"> | ||||
| 209 | + <div className="benefit"> | ||||
| 210 | + <span className="benefit-ico"><IcoHouse size={20} /></span> | ||||
| 211 | + <span className="benefit-label">Nouveaux logements</span> | ||||
| 212 | + </div> | ||||
| 213 | + <div className="benefit"> | ||||
| 214 | + <span className="benefit-ico"><IcoSearch size={20} /></span> | ||||
| 215 | + <span className="benefit-label">Recherche facile</span> | ||||
| 216 | + </div> | ||||
| 217 | + <div className="benefit"> | ||||
| 218 | + <span className="benefit-ico"><IcoHeart size={20} /></span> | ||||
| 219 | + <span className="benefit-label">Favoris et alertes</span> | ||||
| 220 | + </div> | ||||
| 221 | + <div className="benefit"> | ||||
| 222 | + <span className="benefit-ico blue"><IcoPin size={20} /></span> | ||||
| 223 | + <span className="benefit-label">Partout au Québec</span> | ||||
| 224 | + </div> | ||||
| 225 | + </div> | ||||
| 209 | <div className="stat-row"> | 226 | <div className="stat-row"> | ||
| 210 | <span className="stat-chip"><span className="pulse" /> Données synchronisées en continu</span> | 227 | <span className="stat-chip"><span className="pulse" /> Données synchronisées en continu</span> | ||
| 211 | {stats && ( | 228 | {stats && ( | ||
@@ -245,7 +262,7 @@ export default function Home() { | |||||
| 245 | <div className="f-search"> | 262 | <div className="f-search"> | ||
| 246 | <IcoSearch size={15} strokeWidth={2.4} /> | 263 | <IcoSearch size={15} strokeWidth={2.4} /> | ||
| 247 | <input | 264 | <input | ||
| 248 | − id="f-q" placeholder="Adresse, quartier, rue…" value={q} | 265 | + id="f-q" placeholder="Ville, quartier ou code postal" value={q} | ||
| 249 | onChange={(e) => setQ(e.target.value)} | 266 | onChange={(e) => setQ(e.target.value)} | ||
| 250 | aria-label="Recherche" | 267 | aria-label="Recherche" | ||
| 251 | /> | 268 | /> | ||
modified
frontend/src/pages/Listing.tsx
+1 −1
@@ -342,7 +342,7 @@ export default function ListingPage() { | |||||
| 342 | <ListingMap3D l={l} /> | 342 | <ListingMap3D l={l} /> | ||
| 343 | </Suspense> | 343 | </Suspense> | ||
| 344 | <p className="fine"> | 344 | <p className="fine"> | ||
| 345 | − Vue 3D du secteur — l'immeuble de l'annonce est surligné en rouge. | 345 | + Vue 3D du secteur — l'immeuble de l'annonce est surligné en orange. | ||
| 346 | Position selon l'adresse géocodée (Adresses Québec). | 346 | Position selon l'adresse géocodée (Adresses Québec). | ||
| 347 | </p> | 347 | </p> | ||
| 348 | </section> | 348 | </section> | ||
modified
frontend/src/styles.css
+537 −462
Large diff (1,790 lines) is not loaded by default to keep the page fast.Load diff
modified
frontend/tsconfig.tsbuildinfo
+1 −1
Large diff (4 lines) is not loaded by default to keep the page fast.Load diff
modified
louka/pdfgen.py
+15 −15
Large diff (47 lines) is not loaded by default to keep the page fast.Load diff
modified
louka/seo.py
+1 −1
Large diff (9 lines) is not loaded by default to keep the page fast.Load diff