feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
+430
View File
@@ -0,0 +1,430 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="theme-color" content="#f2f2f7">
<link rel="apple-touch-icon" href="https://landvex.com/apple-touch-icon.png">
<title>City Intelligence — Landvex</title>
<link rel="stylesheet" href="/assets/css/landvex-complete.css">
<style>
*{box-sizing:border-box;margin:0;padding:0;-webkit-font-smoothing:antialiased}
:root{
--bg:#f2f2f7;--s1:#fff;--s2:#f2f2f7;--border:rgba(0,0,0,0.08);
--text:#1c1c1e;--t2:rgba(28,28,30,0.7);--t3:rgba(28,28,30,0.45);
--accent:#007aff;--accent3:#0066cc;
--green:#16a34a;--red:#dc2626;--yellow:#ca8a04;
}
html,body{min-height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Text','Helvetica Neue',sans-serif;background:var(--bg);color:var(--text)}
/* NAV */
nav{background:rgba(255,255,255,0.92);backdrop-filter:blur(20px) saturate(180%);border-bottom:1px solid var(--border);padding:0 24px;height:52px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100}
.nav-logo{font-size:15px;font-weight:800;letter-spacing:-0.4px;color:var(--text);text-decoration:none}
.nav-logo em{color:var(--accent3);font-style:normal}
.nav-links{display:flex;gap: 16px;align-items:center}
.nav-links a{font-size:13px;font-weight:500;color:var(--t2);text-decoration:none;transition:color 0.12s}
.nav-links a:hover{color:var(--text)}
.nav-links a.active{color:var(--accent3);font-weight:700}
/* HERO */
.hero{padding: 24px 48px;text-align:center;max-width:680px;margin:0 auto}
.hero-eyebrow{font-size:11px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--accent3);margin-bottom:14px}
.hero-title{font-size:clamp(32px,6vw,52px);font-weight:900;letter-spacing:-1.5px;line-height:1.08;color:var(--text);margin-bottom:18px}
.hero-title span{color:var(--accent3)}
.hero-sub{font-size:17px;color:var(--t2);line-height:1.6;max-width:520px;margin:0 auto 24px}
.hero-badge{display:inline-flex;align-items:center;gap: 16px;background:rgba(0,122,255,0.08);border:1px solid rgba(0,122,255,0.18);border-radius: var(--radius-xl);padding: 24px;font-size:11px;font-weight:600;color:var(--accent3)}
.hero-badge::before{content:'';width:6px;height:6px;border-radius: var(--radius-full);background:#22c55e;animation:pulse 1.8s ease-in-out infinite}
@keyframes pulse{0%{opacity:1}50%{opacity:0.4}100%{opacity:1}}
/* CITY GRID */
.cities-section{max-width:1100px;margin:0 auto;padding:0 24px 80px}
.section-header{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:24px;padding-bottom:14px;border-bottom:1px solid var(--border)}
.section-title{font-size:13px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--t3)}
.section-count{font-size:12px;color:var(--t3)}
.cities-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap: 16px}
.city-card{background:var(--s1);border:1px solid var(--border);border-radius: var(--radius-xl);padding:24px;text-decoration:none;color:var(--text);display:block;transition:all 0.15s;position:relative;overflow:hidden}
.city-card:hover{border-color:rgba(0,122,255,0.25);box-shadow:0 8px 32px rgba(0,0,0,0.08);transform:translateY(-2px)}
.city-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;border-radius: var(--radius-xl) 20px 0 0}
.city-card.sweden::before{background:linear-gradient(90deg,#006aa7,#fecc02)}
.city-card-flag{font-size:32px;margin-bottom:14px;line-height:1}
.city-card-name{font-size:22px;font-weight:900;letter-spacing:-0.6px;color:var(--text);margin-bottom:4px}
.city-card-region{font-size:11px;color:var(--t3);font-weight:500;margin-bottom: 24px}
.city-card-stats{display:grid;grid-template-columns:1fr 1fr;gap: 16px;margin-bottom:20px}
.city-stat{background:var(--s2);border-radius: var(--radius-md);padding: 24px}
.city-stat-val{font-size:18px;font-weight:800;letter-spacing:-0.3px;line-height:1;margin-bottom:2px}
.city-stat-lbl{font-size:9px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;color:var(--t3)}
.city-card-tags{display:flex;flex-wrap:wrap;gap: 16px;margin-bottom:20px}
.tag{font-size:10px;font-weight:600;padding: 24px;border-radius: var(--radius-sm);letter-spacing:0.2px}
.tag-green{background:rgba(22,163,74,0.1);color:#15803d}
.tag-yellow{background:rgba(202,138,4,0.1);color:#92400e}
.tag-red{background:rgba(220,38,38,0.1);color:#991b1b}
.tag-blue{background:rgba(0,122,255,0.1);color:var(--accent3)}
.city-card-cta{display:flex;align-items:center;justify-content:space-between;padding-top:16px;border-top:1px solid var(--border)}
.city-card-cta-label{font-size:13px;font-weight:700;color:var(--accent3)}
.city-card-cta-arrow{font-size:16px;color:var(--accent3);transition:transform 0.12s}
.city-card:hover .city-card-cta-arrow{transform:translateX(4px)}
/* COMING SOON card */
.city-card.coming-soon{opacity:0.55;pointer-events:none}
.city-card.coming-soon .city-card-cta-label{color:var(--t3)}
/* METHODOLOGY */
.method-bar{max-width:1100px;margin:0 auto 48px;padding:0 24px}
.method-inner{background:var(--s1);border:1px solid var(--border);border-radius: var(--radius-lg);padding: 24px;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap: 16px}
.method-item{display:flex;align-items:flex-start;gap: 16px}
.method-icon{width:32px;height:32px;border-radius: var(--radius-md);background:rgba(0,122,255,0.08);display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0}
.method-title{font-size:12px;font-weight:700;color:var(--text);margin-bottom:2px}
.method-sub{font-size:11px;color:var(--t2);line-height:1.4}
footer{text-align:center;padding: 24px;font-size:11px;color:var(--t3);border-top:1px solid var(--border)}
footer a{color:var(--accent3);text-decoration:none}
@media(max-width:600px){
.hero{padding: 24px 32px}
.cities-section{padding:0 16px 60px}
.method-bar{padding:0 16px}
.cities-grid{grid-template-columns:1fr}
nav{padding:0 16px}
}
</style>
<style>
@media (max-width: 640px) {
.hero h1 { font-size: 2rem !important; }
.hero p { font-size: 1rem !important; }
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": [
"Organization",
"Corporation"
],
"@id": "https://landvex.com/#organization",
"name": "Landvex",
"alternateName": [
"LandveX",
"Landvex AB",
"LandveX AB"
],
"url": "https://landvex.com",
"logo": {
"@type": "ImageObject",
"url": "https://landvex.com/apple-touch-icon.png",
"width": 180,
"height": 180
},
"foundingDate": "2024",
"foundingLocation": {
"@type": "Place",
"addressLocality": "Gothenburg",
"addressRegion": "Gothenburg",
"addressCountry": "SE"
},
"legalName": "Landvex Inc",
"address": {
"@type": "PostalAddress",
"addressLocality": "Houston",
"addressRegion": "TX",
"addressCountry": "US"
},
"location": [
{
"@type": "Place",
"name": "Houston, Texas, USA (US HQ)",
"address": {
"@type": "PostalAddress",
"addressLocality": "Houston",
"addressRegion": "TX",
"addressCountry": "US"
}
},
{
"@type": "Place",
"name": "Tyresö, Sweden (EU HQ)",
"address": {
"@type": "PostalAddress",
"addressLocality": "Gothenburg",
"addressRegion": "Gothenburg",
"addressCountry": "SE"
}
}
],
"taxID": "pending-EIN",
"description": "Landvex is a decision intelligence company that identifies contradictions between official narratives and observed physical reality. Using the quiXzoom field observation network and the AMOS AI analysis engine, Landvex delivers infrastructure risk indexes, urban intelligence scores, and contradiction reports to infrastructure owners, municipalities, enterprises, and investors globally.",
"disambiguatingDescription": "Landvex (LandveX) is a decision intelligence company with US headquarters in Houston, Texas (Landvex Inc.) and European headquarters in Tyresö, Sweden (Landvex AB, org.nr 559141-7042). Landvex delivers control intelligence — infrastructure risk scores, urban intelligence, and contradiction analysis — to municipalities, enterprises, infrastructure operators and investors. Landvex is not a UK meat wholesaler, food distributor, logistics company, package tracker, or land investment platform.",
"knowsAbout": [
"control intelligence",
"decision intelligence",
"infrastructure risk assessment",
"urban intelligence",
"field data collection",
"contradiction detection",
"physical world analytics",
"geospatial analytics"
],
"brand": {
"@type": "Brand",
"name": "Landvex",
"slogan": "Control intelligence for the physical world."
},
"sameAs": [
"https://landvex.com",
"https://www.linkedin.com/company/landvex",
"https://x.com/landvex",
"https://twitter.com/landvex",
"https://www.crunchbase.com/organization/landvex",
"https://github.com/landvex",
"https://www.instagram.com/landvex"
],
"contactPoint": {
"@type": "ContactPoint",
"email": "contact@landvex.com",
"contactType": "sales"
},
"slogan": "Where reported reality conflicts with observed reality."
}
</script>
</head>
<body>
<nav aria-label="Main navigation">
<a href="https://landvex.com" class="nav-logo">Land<em>vex</em></a>
<div class="nav-links">
<a href="/intelligence" class="active">Intelligence</a>
<a href="/stockholm-intelligence">Stockholm</a>
<a href="https://landvex.com/methodology/">Methodology</a>
<a href="https://landvex.com">landvex.com</a>
</div>
</nav>
<div class="lv-hero">
<div class="lv-hero-eyebrow">Landvex City Intelligence</div>
<h1 class="hero-title">Sweden — <span>Live City Data</span></h1>
<p class="lv-hero-subtitle">Real-time intelligence dashboards for Sweden's major cities. District-level scores, contradiction detection, live police events, and urban momentum tracking.</p>
<div class="hero-badge">Live data · Jun 2026</div>
</div>
<div class="cities-section">
<div class="section-header">
<div class="section-title">Swedish Cities</div>
<div class="section-count">4 cities · 60+ districts monitored</div>
</div>
<div class="cities-grid">
<!-- STOCKHOLM -->
<a href="/stockholm-intelligence" class="city-card sweden">
<div class="city-card-flag"><span class="flag-se">SE</span></div>
<div class="city-card-name">Stockholm</div>
<div class="city-card-region">Capital · Stockholm County</div>
<div class="city-card-stats">
<div class="city-stat">
<div class="city-stat-val">94%</div>
<div class="city-stat-lbl">Confidence</div>
</div>
<div class="city-stat">
<div class="city-stat-val">60+</div>
<div class="city-stat-lbl">Districts</div>
</div>
<div class="city-stat">
<div class="city-stat-val">2</div>
<div class="city-stat-lbl">High Contra</div>
</div>
<div class="city-stat">
<div class="city-stat-val">Live</div>
<div class="city-stat-lbl">Data Feed</div>
</div>
</div>
<div class="city-card-tags">
<span class="tag tag-green">Global coverage</span>
<span class="tag tag-red">RinkebyTensta </span>
<span class="tag tag-blue">Hagastaden +91 UMI</span>
<span class="tag tag-green">Östermalm premium</span>
</div>
<div class="city-card-cta">
<span class="city-card-cta-label">Explore Stockholm →</span>
<span class="city-card-cta-arrow"></span>
</div>
</a>
<!-- GÖTEBORG -->
<a href="/goteborg-intelligence" class="city-card sweden">
<div class="city-card-flag"></div>
<div class="city-card-name">Göteborg</div>
<div class="city-card-region">Västra Götaland · Sweden's 2nd City</div>
<div class="city-card-stats">
<div class="city-stat">
<div class="city-stat-val">89%</div>
<div class="city-stat-lbl">Confidence</div>
</div>
<div class="city-stat">
<div class="city-stat-val">8</div>
<div class="city-stat-lbl">Districts</div>
</div>
<div class="city-stat">
<div class="city-stat-val">1</div>
<div class="city-stat-lbl">High Contra</div>
</div>
<div class="city-stat">
<div class="city-stat-val">Live</div>
<div class="city-stat-lbl">Data Feed</div>
</div>
</div>
<div class="city-card-tags">
<span class="tag tag-blue">Mölndal pharma hub</span>
<span class="tag tag-red">Angered HIGH</span>
<span class="tag tag-green">Linnéstaden +54 UMI</span>
<span class="tag tag-yellow">Avenyn tourism</span>
</div>
<div class="city-card-cta">
<span class="city-card-cta-label">Explore Göteborg →</span>
<span class="city-card-cta-arrow"></span>
</div>
</a>
<!-- MALMÖ -->
<a href="/malmo-intelligence" class="city-card sweden">
<div class="city-card-flag"></div>
<div class="city-card-name">Malmö</div>
<div class="city-card-region">Skåne · Öresund Region</div>
<div class="city-card-stats">
<div class="city-stat">
<div class="city-stat-val">86%</div>
<div class="city-stat-lbl">Confidence</div>
</div>
<div class="city-stat">
<div class="city-stat-val">7</div>
<div class="city-stat-lbl">Districts</div>
</div>
<div class="city-stat">
<div class="city-stat-val">1</div>
<div class="city-stat-lbl">High Contra</div>
</div>
<div class="city-stat">
<div class="city-stat-val">Live</div>
<div class="city-stat-lbl">Data Feed</div>
</div>
</div>
<div class="city-card-tags">
<span class="tag tag-blue">Hyllie → CPH 20 min</span>
<span class="tag tag-red">Rosengård HIGH</span>
<span class="tag tag-green">Västra Hamnen +56</span>
<span class="tag tag-yellow">Turning Torso icon</span>
</div>
<div class="city-card-cta">
<span class="city-card-cta-label">Explore Malmö →</span>
<span class="city-card-cta-arrow"></span>
</div>
</a>
<!-- UPPSALA -->
<a href="/uppsala-intelligence" class="city-card sweden">
<div class="city-card-flag"></div>
<div class="city-card-name">Uppsala</div>
<div class="city-card-region">Uppsala County · University City</div>
<div class="city-card-stats">
<div class="city-stat">
<div class="city-stat-val">84%</div>
<div class="city-stat-lbl">Confidence</div>
</div>
<div class="city-stat">
<div class="city-stat-val">5</div>
<div class="city-stat-lbl">Districts</div>
</div>
<div class="city-stat">
<div class="city-stat-val">1</div>
<div class="city-stat-lbl">Medium Contra</div>
</div>
<div class="city-stat">
<div class="city-stat-val">Live</div>
<div class="city-stat-lbl">Data Feed</div>
</div>
</div>
<div class="city-card-tags">
<span class="tag tag-blue">Uppsala Universitet</span>
<span class="tag tag-green">Safe city profile</span>
<span class="tag tag-yellow">Gottsunda medium</span>
<span class="tag tag-green">Fålhagen +44 UMI</span>
</div>
<div class="city-card-cta">
<span class="city-card-cta-label">Explore Uppsala →</span>
<span class="city-card-cta-arrow"></span>
</div>
</a>
</div>
</div>
<!-- METHODOLOGY BAR -->
<div class="method-bar">
<div>Intelligence Sources</div>
<div class="method-inner">
<div class="method-item">
<div class="method-icon"></div>
<div>
<div class="method-title">District Scoring</div>
<div class="method-sub">6 profile dimensions per district. Business, Tourist, Safety, Investment, Nightlife, Infrastructure.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon"></div>
<div>
<div class="method-title">Contradiction Engine</div>
<div class="method-sub">Detects gaps between official data and observed reality. High/Medium/Low classification.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon"></div>
<div>
<div class="method-title">Live Police Events</div>
<div class="method-sub">Polisen.se API — real-time incident data mapped to districts.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon"></div>
<div>
<div class="method-title">Local News</div>
<div class="method-sub">SVT local feeds for each city — Stockholm, Väst, Skåne, Uppland.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 12L6 8L9 11L14 5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 5H14V9" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div>
<div class="method-title">Urban Momentum Index</div>
<div class="method-sub">UMI tracks trajectory. High positive = fast appreciation or development signal.</div>
</div>
</div>
<div class="method-item">
<div class="method-icon"></div>
<div>
<div class="method-title">Source First</div>
<div class="method-sub">Every data point is sourced and weighted. Confidence score shown per district.</div>
</div>
</div>
</div>
</div>
<footer class="lv-footer" role="contentinfo">
<a href="https://landvex.com">landvex.com</a> ·
<a href="https://landvex.com/methodology/">Methodology</a> ·
<a href="https://landvex.com/privacy/">Privacy</a><br><br>
© 2026 Landvex. City Intelligence. Source First.
</footer>
</body>
</html>