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
+21 -21
View File
@@ -62,7 +62,7 @@
a { color: inherit; text-decoration: none; }
/* ── NAV ── */
/* NAV */
nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
@@ -112,7 +112,7 @@
}
.btn-outline:hover { background: rgba(0,0,0,0.04); transform: translateY(-1px); }
/* ── HERO ── */
/* HERO */
.hero {
background: var(--bg-dark);
padding: 120px 24px 72px;
@@ -161,13 +161,13 @@
margin: 0 auto 40px;
}
/* ── OVERALL STATUS BANNER ── */
/* OVERALL STATUS BANNER */
.status-banner {
max-width: 860px;
margin: 0 auto 0;
background: var(--green-bg);
border: 1.5px solid var(--green-border);
border-radius: 16px;
border-radius: var(--radius-lg);
padding: 20px 28px;
display: flex;
align-items: center;
@@ -176,7 +176,7 @@
.status-banner-dot {
width: 14px; height: 14px;
border-radius: 50%;
border-radius: var(--radius-full);
background: var(--green-dot);
flex-shrink: 0;
box-shadow: 0 0 0 4px rgba(34,197,94,0.18);
@@ -215,14 +215,14 @@
white-space: nowrap;
}
/* ── MAIN CONTENT ── */
/* MAIN CONTENT */
.main {
max-width: 860px;
margin: 0 auto;
padding: 0 24px 80px;
}
/* ── UPTIME STATS ── */
/* UPTIME STATS */
.uptime-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -251,7 +251,7 @@
color: var(--text-muted);
}
/* ── SECTION HEADERS ── */
/* SECTION HEADERS */
.section-heading {
font-size: 18px;
font-weight: 800;
@@ -262,7 +262,7 @@
border-bottom: 1px solid var(--border-light);
}
/* ── COMPONENT LIST ── */
/* COMPONENT LIST */
.component-list {
display: flex;
flex-direction: column;
@@ -346,7 +346,7 @@
.status-dot {
width: 7px; height: 7px;
border-radius: 50%;
border-radius: var(--radius-full);
flex-shrink: 0;
}
@@ -354,7 +354,7 @@
.status-dot.degraded { background: #f59e0b; }
.status-dot.outage { background: #ef4444; }
/* ── INCIDENT HISTORY ── */
/* INCIDENT HISTORY */
.incident-box {
background: #f8f9fc;
border: 1px solid var(--border-light);
@@ -384,7 +384,7 @@
margin: 0 auto;
}
/* ── SUBSCRIBE SECTION ── */
/* SUBSCRIBE SECTION */
.subscribe-box {
background: var(--bg-dark);
border-radius: var(--radius-lg);
@@ -429,7 +429,7 @@
transform: translateY(-1px);
}
/* ── FOOTER ── */
/* FOOTER */
footer {
background: var(--bg-dark);
border-top: 1px solid var(--border);
@@ -452,14 +452,14 @@
.footer-social {
width: 32px; height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
transition: border-color 0.2s, background 0.2s;
}
.footer-social:hover { border-color: rgba(0,0,0,0.25); background: rgba(0,0,0,0.04); }
.footer-tagline { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
/* ── RESPONSIVE ── */
/* RESPONSIVE */
@media (max-width: 700px) {
nav { padding: 0 20px; }
.nav-links { display: none; }
@@ -530,7 +530,7 @@
<!-- HERO -->
<section class="hero">
<div class="hero-eyebrow">
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,0.25);"></span>
<span style="display:inline-block;width:8px;height:8px;border-radius: var(--radius-full);background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,0.25);"></span>
All systems ready for August 2026 launch
</div>
<h1>System Status</h1>
@@ -574,7 +574,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon"></div>
<div class="component-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg></div>
<div>
<div class="component-name">Intelligence API</div>
<div class="component-desc">Scores, reports, and raw data endpoints</div>
@@ -588,7 +588,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🌐</div>
<div class="component-icon"></div>
<div>
<div class="component-name">Field Data Collection (quiXzoom)</div>
<div class="component-desc">Contributor network ingestion and review pipeline</div>
@@ -602,7 +602,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🔄</div>
<div class="component-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 8C2 11.3 4.7 14 8 14C10.5 14 12.7 12.5 13.7 10.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M14 8C14 4.7 11.3 2 8 2C5.5 2 3.3 3.5 2.3 5.7" stroke="#666" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 7.5L14 10L11.5 9.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2.5 8.5L2 6L4.5 6.5" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<div>
<div class="component-name">Data Processing Pipeline</div>
<div class="component-desc">Normalization, scoring, and contradiction detection</div>
@@ -616,7 +616,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🖥️</div>
<div class="component-icon"></div>
<div>
<div class="component-name">Web Platform</div>
<div class="component-desc">landvex.com and client portal</div>
@@ -630,7 +630,7 @@
<div class="component-item">
<div class="component-left">
<div class="component-icon">🔐</div>
<div class="component-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="7" width="10" height="7" rx="1" stroke="#666" stroke-width="1.5"/><path d="M5 7V5C5 3.3 6.3 2 8 2C9.7 2 11 3.3 11 5V7" stroke="#666" stroke-width="1.5"/><circle cx="8" cy="10.5" r="1" fill="#666"/></svg></div>
<div>
<div class="component-name">Authentication</div>
<div class="component-desc">API authentication, contributor login, and session management</div>