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
+13 -13
View File
@@ -57,7 +57,7 @@
.stat-label { font-size: 14px; color: var(--text-dim); line-height: 1.5; }
.districts-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 40px; }
.district-chip { background: var(--bg-dark); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px 20px; display: flex; align-items: center; gap: 10px; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
.district-chip .chip-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--blue); flex-shrink: 0; }
.district-chip span { font-size: 14px; font-weight: 600; color: var(--text-light); }
.intel-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 40px; }
.intel-card { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 32px 28px; transition: border-color 0.25s, transform 0.2s; }
@@ -79,13 +79,13 @@
.district-name { font-weight: 600; color: #1d1d1f; }
.table-note { margin-top: 12px; font-size: 12px; color: var(--text-muted); }
.contradiction-bg { background: #e8f0fe; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: 16px; padding: 36px 40px; margin-bottom: 32px; }
.contradiction-box { background: rgba(0,0,0,.05); border: 1px solid rgba(0,0,0,.1); border-radius: var(--radius-lg); padding: 36px 40px; margin-bottom: 32px; }
.conflict-header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px; }
.conflict-flag-svg { flex-shrink: 0; margin-top: 2px; }
.conflict-title { color: #f87171; font-weight: 800; font-size: .9375rem; margin-bottom: 4px; }
.conflict-desc { color: rgba(0,0,0,.6); font-size: .875rem; line-height: 1.6; }
.conflict-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.conflict-side { border-radius: 10px; padding: 16px; }
.conflict-side { border-radius: var(--radius-md); padding: 16px; }
.conflict-official { background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.2); }
.conflict-observed { background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.2); }
.conflict-side-label { font-size: .75rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 10px; }
@@ -101,7 +101,7 @@
.ce-feature-icon { width: 28px; height: 28px; margin: 0 auto 10px; color: rgba(0,0,0,0.75); }
.ce-feature h4 { color: #1d1d1f; font-weight: 700; margin-bottom: 6px; }
.ce-feature p { color: rgba(0,0,0,.75); font-size: .8125rem; }
.cta-banner { background: #e8f0fe; border-radius: 16px; padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner { background: #e8f0fe; border-radius: var(--radius-lg); padding: 40px 48px; display: flex; align-items: center; gap: 48px; flex-wrap: wrap; margin-top: 48px; }
.cta-banner h3 { color: #1d1d1f; font-size: 1.25rem; font-weight: 800; margin-bottom: 8px; }
.cta-banner p { color: rgba(0,0,0,.7); line-height: 1.65; font-size: .9375rem; }
.cta-banner-text { flex: 1; min-width: 260px; }
@@ -127,7 +127,7 @@
.footer-links { display: flex; gap: 24px; align-items: center; flex-wrap: wrap; }
.footer-links a { font-size: 14px; color: var(--text-muted); transition: color 0.2s; }
.footer-links a:hover { color: #1d1d1f; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 8px; display: flex; align-items: center; justify-content: center; transition: border-color 0.2s, background 0.2s; }
.footer-social { width: 32px; height: 32px; border: 1px solid var(--border); 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); }
@media (max-width: 900px) { nav { padding: 0 20px; } .stats-row { grid-template-columns: 1fr 1fr; } .districts-grid { grid-template-columns: repeat(2, 1fr); } .intel-grid { grid-template-columns: 1fr; } .conflict-grid { grid-template-columns: 1fr; } .ce-features { grid-template-columns: 1fr; } }
@media (max-width: 600px) {
@@ -151,7 +151,7 @@
.scores-grid { grid-template-columns: 1fr !important; }
}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius:8px;line-height:1.6;max-width:800px}
.lv-disclaimer{font-size:.75rem;color:rgba(0,0,0,.75);margin-top:24px;padding:12px 16px;background:rgba(0,0,0,.04);border-radius: var(--radius-md);line-height:1.6;max-width:800px}
.lv-disclaimer a{color:rgba(0,0,0,.65);text-decoration:underline}
.lv-disclaimer--light{color:#888;background:#f5f5f5}
.lv-disclaimer--light a{color:#555}
@@ -361,18 +361,18 @@
<div class="conflict-side conflict-official">
<div class="conflict-side-label">Official narrative</div>
<ul>
<li>&#10003; City prioritises residential over commercial development</li>
<li>&#10003; Zuidas: controlled mixed-use densification 2024&ndash;2026</li>
<li>&#10003; Noord: transformation to residential and creative hub</li>
<li>&#100003 City prioritises residential over commercial development</li>
<li>&#100003 Zuidas: controlled mixed-use densification 2024&ndash;2026</li>
<li>&#100003 Noord: transformation to residential and creative hub</li>
</ul>
</div>
<div class="conflict-side conflict-observed">
<div class="conflict-side-label">Observed reality</div>
<ul>
<li>&#10007; Office-to-residential conversions behind schedule across Centrum</li>
<li>&#10007; Nieuw-West: commercial vacancy rising despite residential demand signals</li>
<li>&#10007; Noord: commercial development outpacing residential completions</li>
<li>&#10007; Zuidas: institutional demand exceeding permitted supply</li>
<li>&#100007 Office-to-residential conversions behind schedule across Centrum</li>
<li>&#100007 Nieuw-West: commercial vacancy rising despite residential demand signals</li>
<li>&#100007 Noord: commercial development outpacing residential completions</li>
<li>&#100007 Zuidas: institutional demand exceeding permitted supply</li>
</ul>
</div>
</div>