Files
boc/quixzoom-landing-fixed/zoomer-start.html
T
Bernt 6989a98d75 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
2026-07-07 07:11:50 +00:00

584 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Become a Zoomer — quiXzoom</title>
<link rel="stylesheet" >
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font-text, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif);
background: var(--bg-primary, #000);
color: var(--label-primary, #fff);
min-height: 100dvh;
padding-top: env(safe-area-inset-top, 44px);
padding-bottom: env(safe-area-inset-bottom, 34px);
-webkit-font-smoothing: antialiased;
}
/* Nav Bar */
.nav-bar {
position: sticky;
top: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 16px;
background: rgba(242,242,247,0.82);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
border-bottom: 0.5px solid rgba(255,255,255,0.12);
}
.nav-bar .nav-logo {
font-family: var(--font-display, -apple-system, BlinkMacSystemFont, "SF Pro Display", sans-serif);
font-size: 17px;
font-weight: 600;
letter-spacing: -0.41px;
background: linear-gradient(135deg, #007AFF 0%, #5856D6 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Hero */
.hero {
padding: 48px 20px 32px;
text-align: center;
}
.hero-badge {
display: inline-block;
background: rgba(0,122,255,0.15);
border: 0.5px solid rgba(0,122,255,0.4);
color: #007AFF;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 5px 14px;
border-radius: var(--radius-xl);
margin-bottom: 20px;
}
.hero h1 {
font-family: var(--font-display, -apple-system, BlinkMacSystemFont, "SF Pro Display", sans-serif);
font-size: 34px;
font-weight: 700;
line-height: 1.12;
letter-spacing: -0.8px;
margin-bottom: 16px;
}
.hero h1 .accent {
background: linear-gradient(135deg, #007AFF 0%, #5856D6 50%, #AF52DE 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero p {
font-size: 17px;
line-height: 1.5;
color: rgba(60,60,67,0.7);
max-width: 340px;
margin: 0 auto 28px;
}
/* Earnings Banner */
.earnings-card {
margin: 0 20px 32px;
background: linear-gradient(135deg, rgba(52,199,89,0.15) 0%, rgba(0,122,255,0.10) 100%);
border: 0.5px solid rgba(52,199,89,0.3);
border-radius: var(--radius-lg);
padding: 20px;
text-align: center;
}
.earnings-card .amount {
font-family: var(--font-display, -apple-system, "SF Pro Display", sans-serif);
font-size: 42px;
font-weight: 700;
letter-spacing: -1px;
color: #34C759;
}
.earnings-card .amount-label {
font-size: 13px;
color: rgba(255,255,255,0.75);
margin-top: 4px;
}
/* 3-Step How It Works */
.section-title {
font-family: var(--font-display, -apple-system, "SF Pro Display", sans-serif);
font-size: 22px;
font-weight: 700;
letter-spacing: -0.4px;
padding: 0 20px;
margin-bottom: 16px;
}
.steps {
padding: 0 20px;
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 36px;
}
.step-card {
display: flex;
align-items: flex-start;
gap: 16px;
background: rgba(255,255,255,0.06);
border: 0.5px solid rgba(255,255,255,0.1);
border-radius: var(--radius-lg);
padding: 18px 16px;
transition: background 0.2s;
}
.step-card:active {
background: rgba(255,255,255,0.1);
}
.step-icon {
width: 44px;
height: 44px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
flex-shrink: 0;
}
.step-icon.blue { background: rgba(0,122,255,0.18); }
.step-icon.green { background: rgba(52,199,89,0.18); }
.step-icon.purple { background: rgba(88,86,214,0.18); }
.step-content .step-num {
font-size: 12px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: rgba(255,255,255,0.75);
margin-bottom: 4px;
}
.step-content .step-title {
font-size: 17px;
font-weight: 600;
letter-spacing: -0.3px;
margin-bottom: 4px;
}
.step-content .step-desc {
font-size: 15px;
color: rgba(255,255,255,0.75);
line-height: 1.4;
}
/* Live Missions */
.missions-section {
margin-bottom: 36px;
}
.missions-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
margin-bottom: 12px;
}
.live-dot {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: rgba(60,60,67,0.5);
}
.live-dot::before {
content: '';
width: 8px;
height: 8px;
border-radius: var(--radius-full);
background: #34C759;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.85); }
}
.missions-scroll {
display: flex;
gap: 12px;
overflow-x: auto;
padding: 0 20px 4px;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.missions-scroll::-webkit-scrollbar { display: none; }
.mission-card {
flex: 0 0 260px;
background: rgba(255,255,255,0.07);
border: 0.5px solid rgba(255,255,255,0.1);
border-radius: var(--radius-lg);
padding: 16px;
}
.mission-type-badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
font-weight: 600;
padding: 3px 10px;
border-radius: var(--radius-md);
margin-bottom: 10px;
}
.mission-type-badge.facade { background: rgba(88,86,214,0.2); color: #5856D6; }
.mission-type-badge.roof { background: rgba(255,149,0,0.2); color: #FF9500; }
.mission-type-badge.road { background: rgba(90,200,250,0.2); color: #5AC8FA; }
.mission-type-badge.water { background: rgba(255,45,85,0.2); color: #FF2D55; }
.mission-card h3 {
font-size: 15px;
font-weight: 600;
letter-spacing: -0.2px;
margin-bottom: 6px;
line-height: 1.3;
}
.mission-card .mission-meta {
font-size: 13px;
color: rgba(60,60,67,0.5);
margin-bottom: 12px;
}
.mission-reward {
font-size: 22px;
font-weight: 700;
color: #34C759;
letter-spacing: -0.4px;
}
.mission-reward span {
font-size: 14px;
font-weight: 500;
color: rgba(52,199,89,0.7);
}
/* CTA Buttons */
.cta-section {
padding: 0 20px;
margin-bottom: 40px;
}
.btn-primary {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 56px;
background: #007AFF;
color: #fff;
border: none;
border-radius: var(--radius-lg);
font-family: var(--font-text, -apple-system, sans-serif);
font-size: 17px;
font-weight: 600;
letter-spacing: -0.2px;
cursor: pointer;
text-decoration: none;
transition: opacity 0.15s;
margin-bottom: 12px;
}
.btn-primary:active { opacity: 0.8; }
.btn-secondary {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 50px;
background: rgba(255,255,255,0.08);
color: rgba(255,255,255,0.85);
border: 0.5px solid rgba(255,255,255,0.15);
border-radius: var(--radius-lg);
font-size: 16px;
font-weight: 500;
cursor: pointer;
text-decoration: none;
transition: background 0.15s;
}
.btn-secondary:active { background: rgba(255,255,255,0.14); }
/* Stats Strip */
.stats-strip {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1px;
background: rgba(255,255,255,0.08);
border-radius: var(--radius-lg);
margin: 0 20px 36px;
overflow: hidden;
border: 0.5px solid rgba(255,255,255,0.1);
}
.stat {
background: rgba(255,255,255,0.04);
padding: 16px 12px;
text-align: center;
}
.stat-value {
font-family: var(--font-display, -apple-system, "SF Pro Display", sans-serif);
font-size: 22px;
font-weight: 700;
letter-spacing: -0.4px;
margin-bottom: 4px;
}
.stat-label {
font-size: 12px;
color: rgba(255,255,255,0.75);
}
/* FAQ / Trust */
.trust-section {
padding: 0 20px;
margin-bottom: 48px;
}
.trust-item {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 0;
border-bottom: 0.5px solid rgba(255,255,255,0.08);
font-size: 15px;
color: rgba(255,255,255,0.75);
}
.trust-item:last-child { border-bottom: none; }
.trust-icon {
width: 32px;
height: 32px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
background: rgba(52,199,89,0.15);
}
/* Loading state for missions */
.missions-loading {
padding: 0 20px;
color: rgba(255,255,255,0.75);
font-size: 15px;
}
@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>
<style id="wcag-contrast-enforcement">
/* AAMOS GLOBAL CONTRAST ENFORCEMENT — WCAG AA (4.5:1 minimum) */
/* Regel: Ingen text under 70% opacity. Aldrig. */
body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
/* Fix common low-contrast patterns */
[style*="rgba(60,60,67,0.4)"] { color: rgba(60,60,67,0.65) !important; }
[style*="rgba(60,60,67,0.3)"] { color: rgba(60,60,67,0.65) !important; }
[style*="rgba(255,255,255,0.4)"] { color: rgba(255,255,255,0.72) !important; }
[style*="rgba(255,255,255,0.3)"] { color: rgba(255,255,255,0.72) !important; }
[style*="opacity: 0.4"] { opacity: 0.72 !important; }
[style*="opacity: 0.3"] { opacity: 0.72 !important; }
@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 src="i18n.js"></script>
<meta property="og:type" content="website">
<meta property="og:title" content="Become a Zoomer — quiXzoom">
<meta property="og:description" content="quiXzoom — Earn per Mission. No Platform Fee.">
<meta property="og:url" content="https://www.quixzoom.com/zoomer-start.html">
<meta property="og:image" content="https://www.quixzoom.com/images/app-screenshot.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="quiXzoom — start earning as a Zoomer today">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://www.quixzoom.com/images/app-screenshot.jpg">
<meta name="twitter:title" content="Become a Zoomer — quiXzoom">
</head>
<body>
<!-- Nav Bar -->
<div class="nav-bar">
<span class="nav-logo">quiXzoom</span>
</div>
<!-- Hero -->
<section class="hero">
<div class="hero-badge"> Launching August 2026</div>
<h1>Earn money on<br><span class="accent">what you see around you</span></h1>
<p>Take photo missions near you. Get paid fast. No boss, no invoicing.</p>
</section>
<!-- Earnings Banner -->
<div class="earnings-card">
<div class="amount">900 4 500 kr</div>
<div class="amount-label">per mission · paid out within 48h</div>
</div>
<!-- Stats -->
<div class="stats-strip">
<div class="stat">
<div class="stat-value" id="stat-missions">4</div>
<div class="stat-label">Open missions</div>
</div>
<div class="stat">
<div class="stat-value">48h</div>
<div class="stat-label">Payout time</div>
</div>
<div class="stat">
<div class="stat-value">2 100</div>
<div class="stat-label">Avg per mission</div>
</div>
</div>
<!-- How It Works -->
<p class="section-title">How it works</p>
<div class="steps">
<div class="step-card">
<div class="step-icon blue"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg></div>
<div class="step-content">
<div class="step-num">Step 1</div>
<div class="step-title">Find missions near you</div>
<div class="step-desc">Open the map and find missions in your area — facade scans, roof photos, road inspections.</div>
</div>
</div>
<div class="step-card">
<div class="step-icon green"></div>
<div class="step-content">
<div class="step-num">Step 2</div>
<div class="step-title">Complete the mission</div>
<div class="step-desc">Head over, take the photos as briefed. Upload directly from the app. Usually 1545 min.</div>
</div>
</div>
<div class="step-card">
<div class="step-icon purple"></div>
<div class="step-content">
<div class="step-num">Step 3</div>
<div class="step-title">Get paid</div>
<div class="step-desc">When the mission is approved, your payment is released immediately. No middlemen.</div>
</div>
</div>
</div>
<!-- Live Missions -->
<div class="missions-section">
<div class="missions-header">
<p class="section-title" style="padding:0;margin:0">Live missions</p>
<div class="live-dot">Live</div>
</div>
<div class="missions-scroll" id="missions-container">
<div class="mission-card" style="opacity:0.5;text-align:center;padding:24px">
<div style="font-size:24px;margin-bottom:8px"></div>
<div style="font-size:14px;color:#aaa">Loading missions near you...</div>
</div>
</div>
</div>
<!-- Trust signals -->
<div class="trust-section">
<p class="section-title" style="padding:0;margin-bottom:12px">Always included</p>
<div class="trust-item">
<div class="trust-icon"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
Clear brief — you know exactly what to do
</div>
<div class="trust-item">
<div class="trust-icon"></div>
Secure payout via Stripe Connect
</div>
<div class="trust-item">
<div class="trust-icon"></div>
No special gear — a smartphone is enough
</div>
<div class="trust-item">
<div class="trust-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>
Approval and payment within 48 hours
</div>
<div class="trust-item">
<div class="trust-icon"></div>
Choose which missions you take
</div>
</div>
<!-- CTA -->
<div class="cta-section">
<a href="/join" class="btn-primary">
Register as a Zoomer →
</a>
<a href="/map" class="btn-secondary">
View missions on map
</a>
</div>
<script>
const API_BASE = 'https://api.quixzoom.com/api/qz';
function missionEmoji(t){
if(/elledn|kabel|transfor/i.test(t)) return '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg>';
if(/vag|potthal|trottoar/i.test(t)) return '';
if(/kanal|bro/i.test(t)) return '';
if(/gatuliv|neon/i.test(t)) return '';
return '';
}
function renderMissions(missions){
const c=document.getElementById('missions-container');
if(!missions.length){c.innerHTML='<div style="text-align:center;padding:24px;color:#aaa;font-size:14px"> No missions near you right now</div>';return;}
c.innerHTML=missions.slice(0,6).map(m=>{
const d=m.distance_meters<1000?Math.round(m.distance_meters)+'m':(m.distance_meters/1000).toFixed(1)+'km';
return '<div class="mission-card" onclick="location.href=\''/zoomer-onboarding-flow\'" style="cursor:pointer">'
+'<div class="mission-type-badge road">'+missionEmoji(m.title)+' '+(m.area_name||'Mission')+'</div>'
+'<h3>'+m.title+'</h3>'
+'<div class="mission-meta"> '+d+' away · '+(m.required_photos||1)+' photo</div>'
+'<div class="mission-reward">'+m.reward_credits+' cr<span> on approval</span></div>'
+'</div>';
}).join('');
const el=document.getElementById('stat-missions');
if(el&&missions.length>0) el.textContent=missions.length+'+';
}
function load(lat,lng){
const url=lat?`${API_BASE}/missions?lat=${lat}&lng=${lng}&radius=15&limit=20`:`${API_BASE}/missions?lat=59.33&lng=18.07&radius=15&limit=20`;
fetch(url).then(r=>r.json()).then(d=>renderMissions(d.missions||[])).catch(()=>{});
}
if(navigator.geolocation){navigator.geolocation.getCurrentPosition(p=>load(p.coords.latitude,p.coords.longitude),()=>load(null,null),{timeout:5000});}
else load(null,null);
</script>
</body>
</html>