6989a98d75
- 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
915 lines
32 KiB
HTML
915 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="sv" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<link rel="canonical" href="https://www.quixzoom.com/launch.html">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<title>quiXzoom Launch — Sverige 2026</title>
|
||
<meta name="description" content="quiXzoom launches in Sweden 2026. Join the revolution — earn money taking photos, flexible missions, instant payouts.">
|
||
<link rel="stylesheet" >
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--ios-bg-primary: #000000;
|
||
--ios-bg-secondary: #1C1C1E;
|
||
--ios-bg-tertiary: #2C2C2E;
|
||
--ios-bg-elevated: #3A3A3C;
|
||
--ios-separator: rgba(84,84,88,0.65);
|
||
--ios-label: #FFFFFF;
|
||
--ios-label-secondary: rgba(235,235,245,0.6);
|
||
--ios-label-tertiary: rgba(235,235,245,0.3);
|
||
--ios-blue: #0A84FF;
|
||
--ios-green: #30D158;
|
||
--ios-orange: #FF9F0A;
|
||
--ios-red: #FF453A;
|
||
--ios-purple: #BF5AF2;
|
||
--ios-teal: #64D2FF;
|
||
--ios-yellow: #FFD60A;
|
||
--ios-indigo: #5E5CE6;
|
||
--ios-pink: #FF375F;
|
||
--ios-mint: #63E6E2;
|
||
--radius-card: 16px;
|
||
--radius-sm: 10px;
|
||
--font-display: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
|
||
--font-text: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
|
||
--spring: cubic-bezier(0.34,1.56,0.64,1);
|
||
}
|
||
|
||
body {
|
||
background: var(--ios-bg-primary);
|
||
color: var(--ios-label);
|
||
font-family: var(--font-text);
|
||
-webkit-font-smoothing: antialiased;
|
||
min-height: 100dvh;
|
||
padding-bottom: calc(40px + env(safe-area-inset-bottom, 0));
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* Nav Bar */
|
||
.nav-bar {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
background: rgba(242,242,247,0.85);
|
||
backdrop-filter: saturate(180%) blur(24px);
|
||
-webkit-backdrop-filter: saturate(180%) blur(24px);
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: calc(env(safe-area-inset-top, 0px) + 10px) 20px 10px;
|
||
min-height: 52px;
|
||
}
|
||
.nav-back {
|
||
color: var(--ios-blue);
|
||
font-size: 17px;
|
||
text-decoration: none;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.nav-title {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
font-family: var(--font-display);
|
||
letter-spacing: -0.3px;
|
||
}
|
||
.nav-pill {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
padding: 4px 10px;
|
||
border-radius: var(--radius-xl);
|
||
background: var(--ios-bg-tertiary);
|
||
color: var(--ios-label-secondary);
|
||
}
|
||
.nav-pill.critical { background: rgba(255,69,58,0.2); color: var(--ios-red); }
|
||
.nav-pill.warning { background: rgba(255,159,10,0.2); color: var(--ios-orange); }
|
||
.nav-pill.ok { background: rgba(48,209,88,0.2); color: var(--ios-green); }
|
||
|
||
/* Hero Countdown */
|
||
.hero {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 32px 20px 20px;
|
||
text-align: center;
|
||
background: linear-gradient(180deg, rgba(10,132,255,0.08) 0%, transparent 100%);
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
}
|
||
.hero-eyebrow {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.5px;
|
||
text-transform: uppercase;
|
||
color: var(--ios-blue);
|
||
margin-bottom: 8px;
|
||
}
|
||
.hero-title {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
font-family: var(--font-display);
|
||
letter-spacing: -0.5px;
|
||
margin-bottom: 24px;
|
||
}
|
||
.countdown-ring {
|
||
position: relative;
|
||
width: 160px;
|
||
height: 160px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.countdown-ring svg {
|
||
width: 160px;
|
||
height: 160px;
|
||
transform: rotate(-90deg);
|
||
}
|
||
.countdown-ring circle {
|
||
fill: none;
|
||
stroke-width: 6;
|
||
stroke-linecap: round;
|
||
}
|
||
.countdown-ring .track { stroke: var(--ios-bg-tertiary); }
|
||
.countdown-ring .progress { stroke: var(--ios-blue); transition: stroke-dashoffset 1s ease; }
|
||
.countdown-inner {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.countdown-days {
|
||
font-size: 52px;
|
||
font-weight: 800;
|
||
font-family: var(--font-display);
|
||
line-height: 1;
|
||
letter-spacing: -2px;
|
||
}
|
||
.countdown-label {
|
||
font-size: 13px;
|
||
color: var(--ios-label-secondary);
|
||
font-weight: 500;
|
||
margin-top: 2px;
|
||
}
|
||
.hero-date {
|
||
font-size: 15px;
|
||
color: var(--ios-label-secondary);
|
||
margin-bottom: 4px;
|
||
}
|
||
.hero-date strong { color: var(--ios-label); font-weight: 600; }
|
||
|
||
/* Stat Row */
|
||
.stat-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1px;
|
||
background: var(--ios-separator);
|
||
border-top: 1px solid var(--ios-separator);
|
||
border-bottom: 1px solid var(--ios-separator);
|
||
margin-top: 0;
|
||
}
|
||
.stat-cell {
|
||
background: var(--ios-bg-primary);
|
||
padding: 14px 12px;
|
||
text-align: center;
|
||
}
|
||
.stat-num {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
font-family: var(--font-display);
|
||
letter-spacing: -0.5px;
|
||
}
|
||
.stat-desc {
|
||
font-size: 11px;
|
||
color: var(--ios-label-secondary);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* Content */
|
||
.content { padding: 0 0 20px; }
|
||
|
||
/* Section header */
|
||
.section-header {
|
||
padding: 24px 20px 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.section-title {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
font-family: var(--font-display);
|
||
letter-spacing: -0.3px;
|
||
}
|
||
.section-badge {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--ios-label-secondary);
|
||
}
|
||
.section-badge.ok { color: var(--ios-green); }
|
||
.section-badge.warn { color: var(--ios-orange); }
|
||
.section-badge.crit { color: var(--ios-red); }
|
||
|
||
/* Card list */
|
||
.card-list {
|
||
margin: 0 16px;
|
||
background: var(--ios-bg-secondary);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Milestone row */
|
||
.milestone-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 14px 16px;
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.milestone-row:last-child { border-bottom: none; }
|
||
.milestone-row:active { background: var(--ios-bg-tertiary); }
|
||
|
||
.checkbox {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: var(--radius-full);
|
||
border: 2px solid var(--ios-bg-elevated);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
transition: all 0.2s var(--spring);
|
||
background: var(--ios-bg-secondary);
|
||
}
|
||
.checkbox.checked {
|
||
background: var(--ios-green);
|
||
border-color: var(--ios-green);
|
||
}
|
||
.checkbox.checked::after {
|
||
content: '';
|
||
width: 6px;
|
||
height: 10px;
|
||
border: 2px solid white;
|
||
border-top: none;
|
||
border-left: none;
|
||
transform: rotate(45deg) translate(-1px, -1px);
|
||
display: block;
|
||
}
|
||
.checkbox.critical { border-color: var(--ios-red); }
|
||
|
||
.milestone-text { flex: 1; min-width: 0; }
|
||
.milestone-label {
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
color: var(--ios-label);
|
||
line-height: 1.3;
|
||
}
|
||
.milestone-meta {
|
||
font-size: 12px;
|
||
color: var(--ios-label-secondary);
|
||
margin-top: 2px;
|
||
}
|
||
.milestone-row.done .milestone-label {
|
||
color: var(--ios-label-secondary);
|
||
text-decoration: line-through;
|
||
}
|
||
.blocker-tag {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 3px 8px;
|
||
border-radius: var(--radius-sm);
|
||
background: rgba(255,69,58,0.15);
|
||
color: var(--ios-red);
|
||
flex-shrink: 0;
|
||
}
|
||
.warn-tag {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 3px 8px;
|
||
border-radius: var(--radius-sm);
|
||
background: rgba(255,159,10,0.15);
|
||
color: var(--ios-orange);
|
||
flex-shrink: 0;
|
||
}
|
||
.ok-tag {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 3px 8px;
|
||
border-radius: var(--radius-sm);
|
||
background: rgba(48,209,88,0.15);
|
||
color: var(--ios-green);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Revenue / Tech status rows */
|
||
.status-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 14px 16px;
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
}
|
||
.status-row:last-child { border-bottom: none; }
|
||
.status-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: var(--radius-md);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
.status-icon.blue { background: rgba(10,132,255,0.15); }
|
||
.status-icon.green { background: rgba(48,209,88,0.15); }
|
||
.status-icon.orange { background: rgba(255,159,10,0.15); }
|
||
.status-icon.red { background: rgba(255,69,58,0.15); }
|
||
.status-icon.purple { background: rgba(191,90,242,0.15); }
|
||
|
||
.status-text { flex: 1; min-width: 0; }
|
||
.status-label {
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
}
|
||
.status-desc {
|
||
font-size: 12px;
|
||
color: var(--ios-label-secondary);
|
||
margin-top: 2px;
|
||
}
|
||
.status-dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: var(--radius-full);
|
||
flex-shrink: 0;
|
||
}
|
||
.status-dot.red { background: var(--ios-red); }
|
||
.status-dot.orange { background: var(--ios-orange); }
|
||
.status-dot.green { background: var(--ios-green); }
|
||
.status-dot.grey { background: var(--ios-label-secondary); }
|
||
|
||
/* Team action rows */
|
||
.action-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
padding: 14px 16px;
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
}
|
||
.action-row:last-child { border-bottom: none; }
|
||
.action-avatar {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: var(--radius-full);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
flex-shrink: 0;
|
||
}
|
||
.action-text { flex: 1; min-width: 0; }
|
||
.action-who {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
}
|
||
.action-task {
|
||
font-size: 14px;
|
||
color: var(--ios-label-secondary);
|
||
margin-top: 2px;
|
||
line-height: 1.4;
|
||
}
|
||
.action-time {
|
||
font-size: 12px;
|
||
color: var(--ios-label-tertiary);
|
||
flex-shrink: 0;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* Progress bar */
|
||
.progress-bar-wrap {
|
||
padding: 0 16px 16px;
|
||
margin: 0 16px;
|
||
background: var(--ios-bg-secondary);
|
||
border-radius: 0 0 var(--radius-card) var(--radius-card);
|
||
}
|
||
.progress-bar-label {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 13px;
|
||
color: var(--ios-label-secondary);
|
||
margin-bottom: 8px;
|
||
padding-top: 4px;
|
||
}
|
||
.progress-bar-track {
|
||
height: 6px;
|
||
background: var(--ios-bg-elevated);
|
||
border-radius: var(--radius-sm);
|
||
overflow: hidden;
|
||
}
|
||
.progress-bar-fill {
|
||
height: 100%;
|
||
border-radius: var(--radius-sm);
|
||
background: var(--ios-green);
|
||
transition: width 0.8s var(--spring);
|
||
}
|
||
|
||
/* Info card */
|
||
.info-card {
|
||
margin: 16px 16px 0;
|
||
background: rgba(10,132,255,0.08);
|
||
border: 1px solid rgba(10,132,255,0.2);
|
||
border-radius: var(--radius-card);
|
||
padding: 14px 16px;
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: flex-start;
|
||
}
|
||
.info-card-icon { font-size: 20px; flex-shrink: 0; }
|
||
.info-card-text { font-size: 14px; color: var(--ios-label-secondary); line-height: 1.5; }
|
||
.info-card-text strong { color: var(--ios-blue); }
|
||
|
||
/* Last updated */
|
||
.last-updated {
|
||
text-align: center;
|
||
font-size: 12px;
|
||
color: var(--ios-label-tertiary);
|
||
padding: 20px 20px 4px;
|
||
}
|
||
|
||
/* Spinner */
|
||
.spin {
|
||
display: inline-block;
|
||
width: 14px;
|
||
height: 14px;
|
||
border: 2px solid var(--ios-label-tertiary);
|
||
border-top-color: var(--ios-blue);
|
||
border-radius: var(--radius-full);
|
||
animation: spin 0.8s linear infinite;
|
||
vertical-align: middle;
|
||
}
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
|
||
/* Responsive */
|
||
@media (min-width: 600px) {
|
||
.hero, .stat-row, .content { max-width: 480px; margin-left: auto; margin-right: auto; }
|
||
.nav-bar { max-width: none; }
|
||
}
|
||
@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>
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:title" content="quiXzoom Launch — Sverige 2026">
|
||
<meta property="og:description" content="quiXzoom — Earn per Mission. No Platform Fee.">
|
||
<meta property="og:url" content="https://www.quixzoom.com/launch.html">
|
||
<meta property="og:image" content="https://www.quixzoom.com/images/sweden-launch.jpg">
|
||
<meta property="og:image:width" content="1200">
|
||
<meta property="og:image:height" content="630">
|
||
<meta property="og:image:alt" content="quiXzoom launch August 2026 — earn per mission">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="twitter:image" content="https://www.quixzoom.com/images/sweden-launch.jpg">
|
||
<meta name="twitter:title" content="quiXzoom Launch — Sverige 2026">
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Nav Bar -->
|
||
<nav class="nav-bar">
|
||
<a class="nav-back" href="aamos-hub/index.html">
|
||
<svg width="10" height="17" viewBox="0 0 10 17" fill="none"><path d="M9 1L1 8.5L9 16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
Hub
|
||
</a>
|
||
<span class="nav-title">quiXzoom Launch</span>
|
||
<span class="nav-pill critical" id="nav-pill"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> <span id="nav-days">—</span>d</span>
|
||
</nav>
|
||
|
||
<!-- Hero -->
|
||
<section class="hero">
|
||
<div class="hero-eyebrow"><span class="flag-se">SE</span> Sverige-lansering</div>
|
||
<h1 class="hero-title">quiXzoom Launch Tracker</h1>
|
||
|
||
<div class="countdown-ring">
|
||
<svg viewBox="0 0 160 160">
|
||
<circle class="track" cx="80" cy="80" r="70" />
|
||
<circle class="progress" cx="80" cy="80" r="70"
|
||
stroke-dasharray="439.8"
|
||
id="progress-arc"
|
||
stroke-dashoffset="0" />
|
||
</svg>
|
||
<div class="countdown-inner">
|
||
<span class="countdown-days" id="countdown-days">—</span>
|
||
<span class="countdown-label">dagar kvar</span>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="hero-date">Lanseringsdatum: <strong>1 augusti 2026</strong></p>
|
||
</section>
|
||
|
||
<!-- Stats -->
|
||
<div class="stat-row">
|
||
<div class="stat-cell">
|
||
<div class="stat-num" id="stat-done" style="color:var(--ios-green)">0</div>
|
||
<div class="stat-desc">Klara</div>
|
||
</div>
|
||
<div class="stat-cell">
|
||
<div class="stat-num" id="stat-remaining" style="color:var(--ios-orange)">9</div>
|
||
<div class="stat-desc">Återstår</div>
|
||
</div>
|
||
<div class="stat-cell">
|
||
<div class="stat-num" id="stat-blockers" style="color:var(--ios-red)">2</div>
|
||
<div class="stat-desc">Blockers</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Content -->
|
||
<div class="content">
|
||
|
||
<!-- Section 1: Kritiska Milstolpar -->
|
||
<div class="section-header">
|
||
<h2 class="section-title">Kritiska Milstolpar</h2>
|
||
<span class="section-badge crit" id="milestone-badge">0 / 9</span>
|
||
</div>
|
||
|
||
<div class="card-list" id="milestone-list">
|
||
<!-- Stripe KYC -->
|
||
<div class="milestone-row" data-id="stripe-kyc" data-blocker="true" onclick="toggleMilestone(this)">
|
||
<div class="checkbox critical" id="chk-stripe-kyc"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">Stripe KYC aktiverat</div>
|
||
<div class="milestone-meta">Blockar alla betalningar · Prioritet #1</div>
|
||
</div>
|
||
<span class="blocker-tag">BLOCKER</span>
|
||
</div>
|
||
|
||
<!-- TED EU -->
|
||
<div class="milestone-row" data-id="ted-api" data-blocker="true" onclick="toggleMilestone(this)">
|
||
<div class="checkbox critical" id="chk-ted-api"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">TED EU API-nyckel konfigurerad</div>
|
||
<div class="milestone-meta">5 min · gratis · Erik fixar</div>
|
||
</div>
|
||
<span class="blocker-tag">BLOCKER</span>
|
||
</div>
|
||
|
||
<!-- EIN -->
|
||
<div class="milestone-row" data-id="ein" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-ein"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">EIN QuiXzoom Inc (Delaware) mottaget</div>
|
||
<div class="milestone-meta">Winston follow-up pågår</div>
|
||
</div>
|
||
<span class="warn-tag">VÄNTAR</span>
|
||
</div>
|
||
|
||
<!-- Zoomer rekrytering -->
|
||
<div class="milestone-row" data-id="zoomer-rekrytering" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-zoomer-rekrytering"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">Zoomer rekryteringskampanj aktiv</div>
|
||
<div class="milestone-meta">Mål: 20+ zoomers inför soft launch</div>
|
||
</div>
|
||
<span class="warn-tag">EJ STARTAD</span>
|
||
</div>
|
||
|
||
<!-- App Store -->
|
||
<div class="milestone-row" data-id="app-store" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-app-store"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">App Store-ansökan (iOS + Android)</div>
|
||
<div class="milestone-meta">Johan ansvarig · Review tar 1–3 dagar</div>
|
||
</div>
|
||
<span class="warn-tag">EJ STARTAT</span>
|
||
</div>
|
||
|
||
<!-- Stripe Connect -->
|
||
<div class="milestone-row" data-id="stripe-connect" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-stripe-connect"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">Stripe Connect för zoomer-utbetalningar</div>
|
||
<div class="milestone-meta">Beror på Stripe KYC</div>
|
||
</div>
|
||
<span class="warn-tag">BLOCKERAS</span>
|
||
</div>
|
||
|
||
<!-- PR -->
|
||
<div class="milestone-row" data-id="pr" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-pr"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">PR / pressrelease klar</div>
|
||
<div class="milestone-meta">Deadline: 25 maj 2026</div>
|
||
</div>
|
||
<span class="warn-tag">EJ KLAR</span>
|
||
</div>
|
||
|
||
<!-- Beta-test -->
|
||
<div class="milestone-row" data-id="beta" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-beta"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">Beta-test genomfört (min. 10 zoomers)</div>
|
||
<div class="milestone-meta">Soft launch: 1 augusti 2026</div>
|
||
</div>
|
||
<span class="warn-tag">EJ STARTAD</span>
|
||
</div>
|
||
|
||
<!-- Legal -->
|
||
<div class="milestone-row" data-id="legal" onclick="toggleMilestone(this)">
|
||
<div class="checkbox" id="chk-legal"></div>
|
||
<div class="milestone-text">
|
||
<div class="milestone-label">Legal (terms, privacy policy) godkänt</div>
|
||
<div class="milestone-meta">GDPR Art. 13, Art. 30 + zoomer-avtal</div>
|
||
</div>
|
||
<span class="warn-tag">EJ KLART</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Progress bar (continuation of card) -->
|
||
<div class="progress-bar-wrap" style="margin: 0 16px; border-radius: 0 0 16px 16px; padding: 12px 16px 16px;">
|
||
<div class="progress-bar-label">
|
||
<span>Framsteg</span>
|
||
<span id="pct-label">0%</span>
|
||
</div>
|
||
<div class="progress-bar-track">
|
||
<div class="progress-bar-fill" id="progress-fill" style="width:0%"></div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Section 2: Revenue Readiness -->
|
||
<div class="section-header">
|
||
<h2 class="section-title">Revenue Readiness</h2>
|
||
<span class="section-badge crit" id="revenue-badge"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> Ej redo</span>
|
||
</div>
|
||
|
||
<div class="card-list">
|
||
<div class="status-row">
|
||
<div class="status-icon red"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">Stripe KYC</div>
|
||
<div class="status-desc">Blockar alla live-betalningar — aktivera omedelbart</div>
|
||
</div>
|
||
<div class="status-dot red"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon orange"><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 class="status-text">
|
||
<div class="status-label">Payment flow</div>
|
||
<div class="status-desc">Ej testbar utan Stripe KYC · Implementerat i kod</div>
|
||
</div>
|
||
<div class="status-dot orange"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon green"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">Pricing konfigurerat</div>
|
||
<div class="status-desc">quiXzoom-priser satt i systemet</div>
|
||
</div>
|
||
<div class="status-dot green"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon red"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">Stripe Connect (zoomer-utbetalningar)</div>
|
||
<div class="status-desc">Väntar på KYC-godkännande</div>
|
||
</div>
|
||
<div class="status-dot red"></div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Section 3: Tech Readiness -->
|
||
<div class="section-header">
|
||
<h2 class="section-title">Tech Readiness</h2>
|
||
<span class="section-badge warn" id="tech-badge">Delvis redo</span>
|
||
</div>
|
||
|
||
<div class="card-list">
|
||
<div class="status-row">
|
||
<div class="status-icon green"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3" stroke="#666" stroke-width="1.5"/><path d="M8 2V4M8 12V14M2 8H4M12 8H14M4.3 4.3L5.7 5.7M10.3 10.3L11.7 11.7M4.3 11.7L5.7 10.3M10.3 5.7L11.7 4.3" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
|
||
<div class="status-text">
|
||
<div class="status-label">AAMOS backend</div>
|
||
<div class="status-desc">Live · amos.wavult.com operationell</div>
|
||
</div>
|
||
<div class="status-dot green"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon green"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">quixzoom.com</div>
|
||
<div class="status-desc">Landing page live</div>
|
||
</div>
|
||
<div class="status-dot green"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon orange"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">iOS / Android app</div>
|
||
<div class="status-desc">Under utveckling · App Store-submission ej genomförd</div>
|
||
</div>
|
||
<div class="status-dot orange"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon orange"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">TED EU API</div>
|
||
<div class="status-desc">API-nyckel saknas · 5 min att fixa</div>
|
||
</div>
|
||
<div class="status-dot orange"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon red"></div>
|
||
<div class="status-text">
|
||
<div class="status-label">Load-test + rollback-plan</div>
|
||
<div class="status-desc">Ej genomfört · Kritisk inför launch</div>
|
||
</div>
|
||
<div class="status-dot red"></div>
|
||
</div>
|
||
<div class="status-row">
|
||
<div class="status-icon red"><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 class="status-text">
|
||
<div class="status-label">Pentest / säkerhetsgranskning</div>
|
||
<div class="status-desc">Ej genomförd · Måste göras inför launch</div>
|
||
</div>
|
||
<div class="status-dot red"></div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Section 4: Team Actions Needed -->
|
||
<div class="section-header">
|
||
<h2 class="section-title">Team Actions Needed</h2>
|
||
<span class="section-badge crit">Åtgärd krävs</span>
|
||
</div>
|
||
|
||
<div class="card-list">
|
||
<div class="action-row">
|
||
<div class="action-avatar" style="background:rgba(10,132,255,0.15);color:var(--ios-blue)">ES</div>
|
||
<div class="action-text">
|
||
<div class="action-who">Erik</div>
|
||
<div class="action-task">Stripe KYC — logga in på Stripe Dashboard, slutför verifiering<br><strong style="color:var(--ios-red)">BLOCKER</strong> · Tar 30–60 min</div>
|
||
</div>
|
||
<div class="action-time">Idag</div>
|
||
</div>
|
||
<div class="action-row">
|
||
<div class="action-avatar" style="background:rgba(10,132,255,0.15);color:var(--ios-blue)">ES</div>
|
||
<div class="action-text">
|
||
<div class="action-who">Erik</div>
|
||
<div class="action-task">TED EU API-nyckel — skaffa och konfigurera<br><em style="color:var(--ios-label-secondary)">Gratis · 5 min</em></div>
|
||
</div>
|
||
<div class="action-time">Idag</div>
|
||
</div>
|
||
<div class="action-row">
|
||
<div class="action-avatar" style="background:rgba(48,209,88,0.15);color:var(--ios-green)">JN</div>
|
||
<div class="action-text">
|
||
<div class="action-who">Johan</div>
|
||
<div class="action-task">App Store submission — iOS + Android<br><em style="color:var(--ios-label-secondary)">Review tar 1–3 dagar efter submission</em></div>
|
||
</div>
|
||
<div class="action-time">Denna vecka</div>
|
||
</div>
|
||
<div class="action-row">
|
||
<div class="action-avatar" style="background:rgba(191,90,242,0.15);color:var(--ios-purple)">WK</div>
|
||
<div class="action-text">
|
||
<div class="action-who">Winston</div>
|
||
<div class="action-task">EIN follow-up — QuiXzoom Inc Delaware<br><em style="color:var(--ios-label-secondary)">Kontakta IRS / formation-agent</em></div>
|
||
</div>
|
||
<div class="action-time">Denna vecka</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-card">
|
||
<div class="info-card-icon"></div>
|
||
<div class="info-card-text">
|
||
<strong>Nästa kritiska deadline:</strong> PR/pressrelease måste vara klar senast <strong>25 maj</strong> (3 veckor innan launch). Zoomer-rekrytering bör starta omedelbart — mål: 20+ zoomers till soft launch <strong>1 augusti</strong>.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="last-updated" id="last-updated">Uppdaterad <span id="updated-time">—</span></div>
|
||
|
||
</div><!-- /content -->
|
||
|
||
<script>
|
||
// Launch date
|
||
const LAUNCH_DATE = new Date('2026-06-15T00:00:00+02:00');
|
||
// We assume campaign started ~90 days before launch for the ring
|
||
const CAMPAIGN_START = new Date('2026-03-17T00:00:00+02:00');
|
||
|
||
function daysUntilLaunch() {
|
||
const now = new Date();
|
||
return Math.max(0, Math.ceil((LAUNCH_DATE - now) / 86400000));
|
||
}
|
||
|
||
function updateHero() {
|
||
const days = daysUntilLaunch();
|
||
document.getElementById('countdown-days').textContent = days;
|
||
document.getElementById('nav-days').textContent = days;
|
||
|
||
// Ring: 0 = no time left, full = 100% remaining
|
||
const total = (LAUNCH_DATE - CAMPAIGN_START) / 86400000;
|
||
const remaining = (LAUNCH_DATE - new Date()) / 86400000;
|
||
const pct = Math.max(0, Math.min(1, remaining / total));
|
||
const circumference = 2 * Math.PI * 70; // r=70
|
||
const offset = circumference * (1 - pct);
|
||
document.getElementById('progress-arc').style.strokeDasharray = circumference;
|
||
document.getElementById('progress-arc').style.strokeDashoffset = offset;
|
||
|
||
// Color
|
||
const arc = document.getElementById('progress-arc');
|
||
arc.style.stroke = days <= 7 ? 'var(--ios-red)' : days <= 21 ? 'var(--ios-orange)' : 'var(--ios-blue)';
|
||
|
||
const pill = document.getElementById('nav-pill');
|
||
pill.className = 'nav-pill ' + (days <= 7 ? 'critical' : days <= 21 ? 'warning' : 'critical');
|
||
|
||
document.getElementById('updated-time').textContent = new Date().toLocaleString('sv-SE', {
|
||
day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit'
|
||
});
|
||
}
|
||
|
||
// Milestone state (persisted in localStorage)
|
||
const STORAGE_KEY = 'qx_launch_milestones_v2';
|
||
|
||
function loadState() {
|
||
try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); } catch { return {}; }
|
||
}
|
||
|
||
function saveState(state) {
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
||
}
|
||
|
||
function applyState() {
|
||
const state = loadState();
|
||
let done = 0, blockers = 0;
|
||
const rows = document.querySelectorAll('.milestone-row[data-id]');
|
||
rows.forEach(row => {
|
||
const id = row.dataset.id;
|
||
const chk = document.getElementById('chk-' + id);
|
||
if (state[id]) {
|
||
chk.classList.add('checked');
|
||
row.classList.add('done');
|
||
done++;
|
||
// Remove tags when done
|
||
const tag = row.querySelector('.blocker-tag, .warn-tag, .ok-tag');
|
||
if (tag) tag.style.display = 'none';
|
||
} else {
|
||
chk.classList.remove('checked');
|
||
row.classList.remove('done');
|
||
if (row.dataset.blocker === 'true' && !state[id]) blockers++;
|
||
}
|
||
});
|
||
const remaining = rows.length - done;
|
||
document.getElementById('stat-done').textContent = done;
|
||
document.getElementById('stat-remaining').textContent = remaining;
|
||
document.getElementById('stat-blockers').textContent = blockers;
|
||
document.getElementById('milestone-badge').textContent = done + ' / ' + rows.length;
|
||
|
||
const pct = Math.round((done / rows.length) * 100);
|
||
document.getElementById('progress-fill').style.width = pct + '%';
|
||
document.getElementById('pct-label').textContent = pct + '%';
|
||
|
||
const badge = document.getElementById('milestone-badge');
|
||
badge.className = 'section-badge ' + (done === rows.length ? 'ok' : done > rows.length / 2 ? 'warn' : 'crit');
|
||
}
|
||
|
||
function toggleMilestone(row) {
|
||
const id = row.dataset.id;
|
||
const state = loadState();
|
||
state[id] = !state[id];
|
||
saveState(state);
|
||
applyState();
|
||
}
|
||
|
||
// Init
|
||
updateHero();
|
||
applyState();
|
||
setInterval(updateHero, 60000);
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|