docs: add quixzoom-auth-core product to AAMOS
- Product documentation in docs/products/ - Updated MEMORY.md with product info - quiXzoom Auth Core as AAMOS Identity product
This commit is contained in:
@@ -214,6 +214,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/infrastructure-scan.jpg">
|
||||
<meta name="twitter:title" content="quiXzoom — 3D Atlas">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -436,5 +437,234 @@ renderScanPoints();
|
||||
refresh();
|
||||
setInterval(refresh, 30000);
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
@@ -181,5 +182,234 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -50,6 +50,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head><body>
|
||||
<div class="header">
|
||||
<h1> Zoomer Admin</h1>
|
||||
@@ -104,4 +105,233 @@ async function verify(id,btn){
|
||||
await load();
|
||||
}
|
||||
load();
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</script></body></html>
|
||||
|
||||
@@ -153,6 +153,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
@@ -819,5 +820,234 @@ window.addEventListener('load', ()=>{
|
||||
setInterval(loadAll, 30000);
|
||||
});
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -429,6 +429,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1041,5 +1042,234 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
setTimeout(runAll, 600);
|
||||
});
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -236,6 +236,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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="App Store Guide — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -666,5 +667,234 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
// Auto-open step 1 on load
|
||||
toggle(1);
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Careers at quiXzoom</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<meta name="description" content="Join the quiXzoom team. We're building the world's largest network of verified field photographers.">
|
||||
<style>
|
||||
:root{--bg-white:#FFFFFF;--bg-off:#F7F7F5;--blue:#0066FF;--text-dark:#1a1a1a;--text-body:#3a3a3a;--text-muted:#6B6B6B;--radius-card:16px;}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Inter',sans-serif;background:var(--bg-white);color:var(--text-body);line-height:1.65}
|
||||
.container{max-width:800px;margin:0 auto;padding:48px 24px}
|
||||
h1{font-size:2.5rem;font-weight:900;color:var(--text-dark);margin-bottom:16px}
|
||||
h2{font-size:1.5rem;font-weight:800;color:var(--text-dark);margin:32px 0 16px}
|
||||
p{margin-bottom:16px;color:var(--text-body)}
|
||||
.back{margin-bottom:24px;display:inline-block;color:var(--blue);text-decoration:none;font-weight:600}
|
||||
.back:hover{text-decoration:underline}
|
||||
.job-card{background:var(--bg-off);border-radius:var(--radius-card);padding:24px;margin-bottom:16px}
|
||||
.job-title{font-size:1.1rem;font-weight:700;color:var(--text-dark);margin-bottom:8px}
|
||||
.job-meta{font-size:.85rem;color:var(--text-muted);margin-bottom:12px}
|
||||
.job-desc{font-size:.9rem;color:var(--text-body);margin-bottom:12px}
|
||||
.btn{background:var(--blue);color:#fff;border:none;border-radius:100px;padding:10px 24px;font-size:.9rem;font-weight:700;cursor:pointer;text-decoration:none;display:inline-block}
|
||||
.btn:hover{background:#0052CC}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/" class="back">← Back to quiXzoom</a>
|
||||
<h1>Careers at quiXzoom</h1>
|
||||
<p>We're building the world's largest network of verified field photographers. Join us.</p>
|
||||
|
||||
<h2>Open Positions</h2>
|
||||
|
||||
<div class="job-card">
|
||||
<div class="job-title">Senior Backend Engineer</div>
|
||||
<div class="job-meta">Stockholm · Full-time · Remote OK</div>
|
||||
<div class="job-desc">Build the infrastructure that powers millions of photo missions. Go, PostgreSQL, AWS.</div>
|
||||
<a href="mailto:careers@quixzoom.com?subject=Senior Backend Engineer" class="btn">Apply</a>
|
||||
</div>
|
||||
|
||||
<div class="job-card">
|
||||
<div class="job-title">AI/ML Engineer</div>
|
||||
<div class="job-meta">Stockholm · Full-time</div>
|
||||
<div class="job-desc">Develop computer vision models for automated photo review and anomaly detection.</div>
|
||||
<a href="mailto:careers@quixzoom.com?subject=AI/ML Engineer" class="btn">Apply</a>
|
||||
</div>
|
||||
|
||||
<div class="job-card">
|
||||
<div class="job-title">Community Manager</div>
|
||||
<div class="job-meta">Stockholm · Full-time</div>
|
||||
<div class="job-desc">Grow and nurture the Zoomer community across multiple markets.</div>
|
||||
<a href="mailto:careers@quixzoom.com?subject=Community Manager" class="btn">Apply</a>
|
||||
</div>
|
||||
|
||||
<h2>Don't see your role?</h2>
|
||||
<p>We're always looking for exceptional people. Send your CV and a note to <a href="mailto:careers@quixzoom.com" style="color:var(--blue)">careers@quixzoom.com</a>.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Careers at quiXzoom</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<meta name="description" content="Join the quiXzoom team. We're building the world's largest network of verified field photographers.">
|
||||
<style>
|
||||
:root{--bg-white:#FFFFFF;--bg-off:#F7F7F5;--blue:#0066FF;--text-dark:#1a1a1a;--text-body:#3a3a3a;--text-muted:#6B6B6B;--radius-card:16px;}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Inter',sans-serif;background:var(--bg-white);color:var(--text-body);line-height:1.65}
|
||||
.container{max-width:800px;margin:0 auto;padding:48px 24px}
|
||||
h1{font-size:2.5rem;font-weight:900;color:var(--text-dark);margin-bottom:16px}
|
||||
h2{font-size:1.5rem;font-weight:800;color:var(--text-dark);margin:32px 0 16px}
|
||||
p{margin-bottom:16px;color:var(--text-body)}
|
||||
.back{margin-bottom:24px;display:inline-block;color:var(--blue);text-decoration:none;font-weight:600}
|
||||
.back:hover{text-decoration:underline}
|
||||
.job-card{background:var(--bg-off);border-radius:var(--radius-card);padding:24px;margin-bottom:16px}
|
||||
.job-title{font-size:1.1rem;font-weight:700;color:var(--text-dark);margin-bottom:8px}
|
||||
.job-meta{font-size:.85rem;color:var(--text-muted);margin-bottom:12px}
|
||||
.job-desc{font-size:.9rem;color:var(--text-body);margin-bottom:12px}
|
||||
.btn{background:var(--blue);color:#fff;border:none;border-radius:100px;padding:10px 24px;font-size:.9rem;font-weight:700;cursor:pointer;text-decoration:none;display:inline-block}
|
||||
.btn:hover{background:#0052CC}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/" class="back">← Back to quiXzoom</a>
|
||||
<h1>Careers at quiXzoom</h1>
|
||||
<p>We're building the world's largest network of verified field photographers. Join us.</p>
|
||||
|
||||
<h2>Open Positions</h2>
|
||||
|
||||
<div class="job-card">
|
||||
<div class="job-title">Senior Backend Engineer</div>
|
||||
<div class="job-meta">Stockholm · Full-time · Remote OK</div>
|
||||
<div class="job-desc">Build the infrastructure that powers millions of photo missions. Go, PostgreSQL, AWS.</div>
|
||||
<a href="mailto:careers@quixzoom.com?subject=Senior Backend Engineer" class="btn">Apply</a>
|
||||
</div>
|
||||
|
||||
<div class="job-card">
|
||||
<div class="job-title">AI/ML Engineer</div>
|
||||
<div class="job-meta">Stockholm · Full-time</div>
|
||||
<div class="job-desc">Develop computer vision models for automated photo review and anomaly detection.</div>
|
||||
<a href="mailto:careers@quixzoom.com?subject=AI/ML Engineer" class="btn">Apply</a>
|
||||
</div>
|
||||
|
||||
<div class="job-card">
|
||||
<div class="job-title">Community Manager</div>
|
||||
<div class="job-meta">Stockholm · Full-time</div>
|
||||
<div class="job-desc">Grow and nurture the Zoomer community across multiple markets.</div>
|
||||
<a href="mailto:careers@quixzoom.com?subject=Community Manager" class="btn">Apply</a>
|
||||
</div>
|
||||
|
||||
<h2>Don't see your role?</h2>
|
||||
<p>We're always looking for exceptional people. Send your CV and a note to <a href="mailto:careers@quixzoom.com" style="color:var(--blue)">careers@quixzoom.com</a>.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Changelog — quiXzoom</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<meta name="description" content="quiXzoom changelog. Latest updates, features, and improvements.">
|
||||
<style>
|
||||
:root{--bg-white:#FFFFFF;--bg-off:#F7F7F5;--blue:#0066FF;--text-dark:#1a1a1a;--text-body:#3a3a3a;--text-muted:#6B6B6B;--radius-card:16px;}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Inter',sans-serif;background:var(--bg-white);color:var(--text-body);line-height:1.65}
|
||||
.container{max-width:800px;margin:0 auto;padding:48px 24px}
|
||||
h1{font-size:2.5rem;font-weight:900;color:var(--text-dark);margin-bottom:16px}
|
||||
h2{font-size:1.3rem;font-weight:800;color:var(--text-dark);margin:32px 0 12px}
|
||||
p{margin-bottom:16px;color:var(--text-body)}
|
||||
.back{margin-bottom:24px;display:inline-block;color:var(--blue);text-decoration:none;font-weight:600}
|
||||
.back:hover{text-decoration:underline}
|
||||
.entry{background:var(--bg-off);border-radius:var(--radius-card);padding:24px;margin-bottom:16px}
|
||||
.date{font-size:.8rem;color:var(--text-muted);margin-bottom:8px;font-weight:600}
|
||||
.version{font-size:1.1rem;font-weight:700;color:var(--text-dark);margin-bottom:8px}
|
||||
.changes{list-style:none}
|
||||
.changes li{padding:4px 0;font-size:.9rem}
|
||||
.changes li::before{content:"✓ ";color:var(--blue);font-weight:700}
|
||||
.badge{display:inline-block;background:var(--blue);color:#fff;font-size:.7rem;padding:2px 8px;border-radius:100px;margin-left:8px}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="/" class="back">← Back to quiXzoom</a>
|
||||
<h1>Changelog</h1>
|
||||
<p>Latest updates and improvements to the quiXzoom platform.</p>
|
||||
|
||||
<div class="entry">
|
||||
<div class="date">July 2026</div>
|
||||
<div class="version">Pre-launch Updates <span class="badge">LATEST</span></div>
|
||||
<ul class="changes">
|
||||
<li>Sweden launch preparation — 200+ missions loaded</li>
|
||||
<li>Stripe Connect integration for Zoomer payouts</li>
|
||||
<li>AI photo review pipeline with llava:7b</li>
|
||||
<li>Multi-language support (11 languages)</li>
|
||||
<li>Lead packages — pre-collected intelligence available</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="entry">
|
||||
<div class="date">June 2026</div>
|
||||
<div class="version">Beta Platform</div>
|
||||
<ul class="changes">
|
||||
<li>Zoomer registration and onboarding flow</li>
|
||||
<li>Mission claim and submission system</li>
|
||||
<li>Geolocation verification and GPS fraud detection</li>
|
||||
<li>QX Credits system with weekly payouts</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="entry">
|
||||
<div class="date">May 2026</div>
|
||||
<div class="version">Alpha Release</div>
|
||||
<ul class="changes">
|
||||
<li>Initial platform architecture</li>
|
||||
<li>Mobile app prototype</li>
|
||||
<li>First Zoomer recruitment campaign</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -148,6 +148,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -484,5 +485,234 @@ async function submitFav(){
|
||||
// Init
|
||||
loadAll();
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -525,6 +525,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -568,7 +569,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<!-- Hero summary -->
|
||||
<div class="hero-card animate-in" id="heroCard">
|
||||
<div class="hero-tag">EU Direktiv 2021/514 · DAC7</div>
|
||||
<div class="hero-title" id="heroRevenue">€0.00</div>
|
||||
<div class="hero-title" id="heroRevenue">$0.00</div>
|
||||
<div class="hero-subtitle">Total intäkt att rapportera <span id="heroYear">2026</span></div>
|
||||
<div class="hero-stats">
|
||||
<div class="hero-stat">
|
||||
@@ -641,7 +642,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
|
||||
<!-- Footer note -->
|
||||
<div class="footer-note animate-in">
|
||||
Tröskelvärden: >30 uppdrag ELLER >2 000 EUR per kalenderår (OR-logik).<br>
|
||||
Tröskelvärden: >30 uppdrag ELLER >2 000 USD per kalenderår (OR-logik).<br>
|
||||
Källa: EU-direktiv 2021/514 (DAC7) · Art. 8ac DAC.
|
||||
</div>
|
||||
|
||||
@@ -658,7 +659,7 @@ let statusData = null;
|
||||
// Format helpers
|
||||
|
||||
function formatEur(v) {
|
||||
return new Intl.NumberFormat('sv-SE', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(v);
|
||||
return new Intl.NumberFormat('sv-SE', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(v);
|
||||
}
|
||||
|
||||
function formatDate(isoStr) {
|
||||
@@ -684,9 +685,9 @@ function statusClass(status) {
|
||||
|
||||
function reasonLabel(reason) {
|
||||
const map = {
|
||||
exceeds_both_thresholds: '> 30 uppdrag & > €2 000',
|
||||
exceeds_both_thresholds: '> 30 uppdrag & > $2 000',
|
||||
exceeds_transaction_threshold: '> 30 uppdrag',
|
||||
exceeds_consideration_threshold: '> €2 000',
|
||||
exceeds_consideration_threshold: '> $2 000',
|
||||
below_threshold: 'Under tröskel',
|
||||
};
|
||||
return map[reason] || reason;
|
||||
@@ -904,5 +905,234 @@ function escHtml(str) {
|
||||
|
||||
loadData();
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -480,6 +480,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -513,7 +514,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<strong>EU-direktiv 2021/514 (DAC7)</strong> kräver att digitala plattformar rapporterar säljares (zoomers) intäkter till skattemyndigheterna. quiXzoom är en <strong>rapporteringsskyldig plattformsoperatör</strong> och måste varje år lämna uppgifter om zoomers som:
|
||||
<br><br>
|
||||
• Genomfört <strong>≥ 30 transaktioner</strong> under kalenderåret, <em>eller</em><br>
|
||||
• Tjänat in <strong>≥ 2 000 EUR</strong> under kalenderåret
|
||||
• Tjänat in <strong>≥ 2 000 USD</strong> under kalenderåret
|
||||
<br><br>
|
||||
Rapporteringen sker i <strong>XML-format</strong> enligt Skatteverkets tekniska specifikation. Första rapportering avser kalenderår 2023.
|
||||
</div>
|
||||
@@ -664,7 +665,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
</div>
|
||||
|
||||
<div class="divider-text">
|
||||
Alla zoomers med ≥30 transaktioner eller ≥2 000 EUR intäkt under kalenderåret inkluderas i rapporten.
|
||||
Alla zoomers med ≥30 transaktioner eller ≥2 000 USD intäkt under kalenderåret inkluderas i rapporten.
|
||||
</div>
|
||||
|
||||
<!-- Export Section -->
|
||||
@@ -822,5 +823,234 @@ function generateDAC7XML() {
|
||||
</DPI:FATCA_OECD>`;
|
||||
}
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -109,6 +109,7 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
.footer-tagline{font-size:.8rem;color:rgba(10,10,10,.55);margin-top:8px;font-style:italic}
|
||||
@media(max-width:768px){.footer-inner{flex-direction:column;align-items:flex-start}}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -160,7 +161,7 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
<section class="stats-bar">
|
||||
<div class="container">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item"><span class="stat-value">€3–30</span><span class="stat-label">Per mission</span></div>
|
||||
<div class="stat-item"><span class="stat-value">$3–30</span><span class="stat-label">Per mission</span></div>
|
||||
<div class="stat-item"><span class="stat-value">0%</span><span class="stat-label">Platform fee</span></div>
|
||||
<div class="stat-item"><span class="stat-value">Weekly</span><span class="stat-label">Payouts</span></div>
|
||||
<div class="stat-item"><span class="stat-value">Free</span><span class="stat-label">To join</span></div>
|
||||
@@ -189,7 +190,7 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
<div class="step-card">
|
||||
<div class="step-number">3</div>
|
||||
<h3>Get Paid</h3>
|
||||
<p>Submit your photos. AI reviews instantly. On approval, QX Credits are added to your balance. Cash out weekly — minimum €250.</p>
|
||||
<p>Submit your photos. AI reviews instantly. On approval, QX Credits are added to your balance. Cash out weekly — minimum $250.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -206,7 +207,7 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
<div class="tier-card">
|
||||
<h3>Casual</h3>
|
||||
<p class="tier-missions">2–4 missions/week</p>
|
||||
<p class="tier-earnings">€60–480<span style="font-size:.875rem;color:var(--text-muted)"> / month</span></p>
|
||||
<p class="tier-earnings">$60–480<span style="font-size:.875rem;color:var(--text-muted)"> / month</span></p>
|
||||
<ul class="tier-features">
|
||||
<li>Supplementary income</li>
|
||||
<li>No minimum hours</li>
|
||||
@@ -216,7 +217,7 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
<div class="tier-card featured">
|
||||
<h3>Active</h3>
|
||||
<p class="tier-missions">8–12 missions/week</p>
|
||||
<p class="tier-earnings">€240–1,440<span style="font-size:.875rem;color:var(--text-muted)"> / month</span></p>
|
||||
<p class="tier-earnings">$240–1,440<span style="font-size:.875rem;color:var(--text-muted)"> / month</span></p>
|
||||
<ul class="tier-features">
|
||||
<li>Consistent volume</li>
|
||||
<li>Priority mission access</li>
|
||||
@@ -226,7 +227,7 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
<div class="tier-card">
|
||||
<h3>Pro</h3>
|
||||
<p class="tier-missions">15+ missions/week</p>
|
||||
<p class="tier-earnings">€450–2,400+<span style="font-size:.875rem;color:var(--text-muted)"> / month</span></p>
|
||||
<p class="tier-earnings">$450–2,400+<span style="font-size:.875rem;color:var(--text-muted)"> / month</span></p>
|
||||
<ul class="tier-features">
|
||||
<li>Maximum volume</li>
|
||||
<li>Premium missions</li>
|
||||
@@ -268,5 +269,234 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
@@ -294,5 +295,234 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>quiXzoom for Organisations — Field Data on Demand</title>
|
||||
<title>quiXzoom for Organisations — Order Raw Field Data</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<meta name="description" content="quiXzoom for organisations: deploy photo missions at scale. Infrastructure inspection, property documentation, retail audits, and more. AI-reviewed, geoverified, fast.">
|
||||
<meta name="description" content="Order raw geotagged photos from verified field contributors. Infrastructure, property, retail audits, environmental surveys. Delivered in hours.">
|
||||
<link rel="canonical" href="https://www.quixzoom.com/for-organisations/">
|
||||
<style>
|
||||
:root{
|
||||
@@ -40,13 +40,13 @@
|
||||
.nav-links{display:flex;align-items:center;gap:32px}
|
||||
.nav-links a{color:var(--text-muted);font-size:var(--text-base);font-weight:500;transition:color .15s}
|
||||
.nav-links a:hover{color:var(--text-dark)}
|
||||
.btn-nav{background:var(--blue);color:#fff;border:none;border-radius: var(--radius-lg);padding:10px 24px;font-size:var(--text-base);font-weight:700;transition:background .15s;white-space:nowrap}
|
||||
.btn-nav{background:var(--blue);color:#fff;border:none;border-radius:var(--radius-lg);padding:10px 24px;font-size:var(--text-base);font-weight:700;transition:background .15s;white-space:nowrap}
|
||||
.btn-nav:hover{background:var(--blue-dark)}
|
||||
.mobile-menu{display:none}
|
||||
@media(max-width:768px){
|
||||
.nav-links{display:none}
|
||||
.hamburger{display:flex;flex-direction:column;gap:5px;cursor:pointer;padding:8px;background:none;border:none}
|
||||
.hamburger span{display:block;width:22px;height:2px;background:var(--text-dark);border-radius: var(--radius-sm)}
|
||||
.hamburger span{display:block;width:22px;height:2px;background:var(--text-dark);border-radius:var(--radius-sm)}
|
||||
.mobile-menu{display:none;position:fixed;inset:0;top:68px;background:var(--bg-white);z-index:299;padding:24px;flex-direction:column;gap:0;border-top:1px solid rgba(0,0,0,.08)}
|
||||
.mobile-menu.open{display:flex}
|
||||
.mobile-menu a{color:var(--text-body);font-size:1.1rem;font-weight:500;padding:18px 0;border-bottom:1px solid rgba(0,0,0,.08);display:block}
|
||||
@@ -57,60 +57,51 @@
|
||||
.hero-org-inner{max-width:720px}
|
||||
.hero-org h1{color:var(--text-dark);margin-bottom:24px}
|
||||
.hero-org p{font-size:clamp(1.0625rem,2vw,1.25rem);color:var(--text-body);line-height:1.6;margin-bottom:32px}
|
||||
.hero-badge{display:inline-flex;align-items:center;gap:8px;background:var(--blue-pale);border:1px solid rgba(0,102,255,.15);border-radius: 100px;padding:8px 18px;font-size:var(--text-sm);font-weight:600;color:var(--blue);margin-bottom:28px}
|
||||
.hero-badge{display:inline-flex;align-items:center;gap:8px;background:var(--blue-pale);border:1px solid rgba(0,102,255,.15);border-radius:100px;padding:8px 18px;font-size:var(--text-sm);font-weight:600;color:var(--blue);margin-bottom:28px}
|
||||
.hero-cta{display:flex;gap:12px;flex-wrap:wrap}
|
||||
.btn-primary{background:var(--blue);color:#fff;border:none;border-radius: var(--radius-lg);padding:14px 32px;font-size:1rem;font-weight:700;transition:background .15s}
|
||||
.btn-primary{background:var(--blue);color:#fff;border:none;border-radius:var(--radius-lg);padding:14px 32px;font-size:1rem;font-weight:700;transition:background .15s}
|
||||
.btn-primary:hover{background:var(--blue-dark)}
|
||||
.btn-ghost{background:transparent;border:2px solid rgba(0,0,0,.15);color:var(--text-dark);border-radius: var(--radius-lg);padding:14px 32px;font-size:1rem;font-weight:700;transition:border-color .15s,background .15s}
|
||||
.btn-ghost{background:transparent;border:2px solid rgba(0,0,0,.15);color:var(--text-dark);border-radius:var(--radius-lg);padding:14px 32px;font-size:1rem;font-weight:700;transition:border-color .15s,background .15s}
|
||||
.btn-ghost:hover{border-color:var(--blue);background:var(--blue-pale)}
|
||||
|
||||
/* USE CASES */
|
||||
.usecase-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
|
||||
.usecase-card{background:var(--bg-off);border-radius: var(--radius-card);padding:32px 28px;transition:transform var(--transition),box-shadow var(--transition)}
|
||||
.usecase-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-hover)}
|
||||
.usecase-icon{font-size:2rem;margin-bottom:16px}
|
||||
.usecase-card h3{font-size:1.125rem;font-weight:800;color:var(--text-dark);margin-bottom:8px}
|
||||
.usecase-card p{font-size:var(--text-sm);color:var(--text-body);line-height:1.6}
|
||||
@media(max-width:900px){.usecase-grid{grid-template-columns:1fr 1fr}}
|
||||
@media(max-width:600px){.usecase-grid{grid-template-columns:1fr}}
|
||||
/* PRODUCTS */
|
||||
.product-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
|
||||
.product-card{background:var(--bg-off);border-radius:var(--radius-card);padding:32px 28px;transition:transform var(--transition),box-shadow var(--transition)}
|
||||
.product-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-hover)}
|
||||
.product-icon{font-size:2rem;margin-bottom:16px}
|
||||
.product-card h3{font-size:1.125rem;font-weight:800;color:var(--text-dark);margin-bottom:8px}
|
||||
.product-card p{font-size:var(--text-sm);color:var(--text-body);line-height:1.6}
|
||||
.product-price{font-size:1.5rem;font-weight:900;color:var(--blue);margin-top:12px}
|
||||
.product-price span{font-size:var(--text-sm);color:var(--text-muted);font-weight:500}
|
||||
@media(max-width:900px){.product-grid{grid-template-columns:1fr 1fr}}
|
||||
@media(max-width:600px){.product-grid{grid-template-columns:1fr}}
|
||||
|
||||
/* FEATURES */
|
||||
/* HOW IT WORKS */
|
||||
.feature-row{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;margin-bottom:64px}
|
||||
.feature-row.reverse{direction:rtl}
|
||||
.feature-row.reverse>*{direction:ltr}
|
||||
.feature-visual{background:var(--bg-off);border-radius: var(--radius-card);padding:40px;text-align:center}
|
||||
.feature-visual{background:var(--bg-off);border-radius:var(--radius-card);padding:40px;text-align:center}
|
||||
.feature-visual .big-icon{font-size:4rem}
|
||||
.feature-text h3{font-size:var(--text-2xl);color:var(--text-dark);margin-bottom:16px}
|
||||
.feature-text p{color:var(--text-body);margin-bottom:16px}
|
||||
.feature-list li{display:flex;align-items:flex-start;gap:10px;font-size:var(--text-base);padding:6px 0}
|
||||
.feature-list li::before{content:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';color:var(--green);font-weight:700}
|
||||
.feature-list li::before{content:'✓';color:var(--green);font-weight:700}
|
||||
@media(max-width:768px){.feature-row{grid-template-columns:1fr;gap:32px}}
|
||||
|
||||
/* STATS */
|
||||
.stats-bar{background:var(--blue);padding:48px 0}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;text-align:center}
|
||||
.stat-item{padding:20px 16px;position:relative}
|
||||
.stat-item:not(:last-child)::after{content:'';position:absolute;right:0;top:20%;height:60%;width:1px;background:rgba(255,255,255,.3)}
|
||||
.stat-value{font-size:clamp(1.75rem,3vw,2.5rem);font-weight:900;color:#fff;letter-spacing:-.04em;line-height:1;display:block;margin-bottom:8px}
|
||||
.stat-label{font-size:.9rem;color:rgba(255,255,255,.9);font-weight:500}
|
||||
@media(max-width:600px){.stats-grid{grid-template-columns:repeat(2,1fr);gap:1px;background:rgba(255,255,255,.2)}.stat-item{background:var(--blue)}.stat-item::after{display:none}}
|
||||
|
||||
/* PRICING */
|
||||
.pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px}
|
||||
.pricing-card{background:var(--bg-off);border:2px solid #E8E8E8;border-radius: var(--radius-card);padding:36px 28px;text-align:center}
|
||||
.pricing-card.featured{border-color:var(--blue);transform:scale(1.02)}
|
||||
.pricing-card h3{font-size:1.5rem;font-weight:900;margin-bottom:4px;color:var(--text-dark)}
|
||||
.pricing-price{font-size:2rem;font-weight:900;color:var(--blue);margin:16px 0}
|
||||
.pricing-price span{font-size:var(--text-sm);color:var(--text-muted);font-weight:500}
|
||||
.pricing-features{text-align:left;margin:24px 0}
|
||||
.pricing-features li{display:flex;align-items:flex-start;gap:10px;font-size:var(--text-base);padding:6px 0}
|
||||
.pricing-features li::before{content:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';color:var(--green);font-weight:700}
|
||||
@media(max-width:900px){.pricing-grid{grid-template-columns:1fr;max-width:480px;margin:0 auto}}
|
||||
|
||||
/* CTA */
|
||||
.cta-section{background:var(--bg-off);padding:80px 0;text-align:center}
|
||||
.cta-section h2{margin-bottom:16px}
|
||||
.cta-section p{font-size:var(--text-lg);color:var(--text-muted);margin-bottom:32px;max-width:560px;margin-left:auto;margin-right:auto}
|
||||
/* ECOSYSTEM */
|
||||
.eco-box{background:var(--bg-white);border:2px solid rgba(0,102,255,.15);border-radius:var(--radius-card);padding:32px;margin-top:40px}
|
||||
.eco-title{font-size:.78rem;font-weight:700;color:var(--blue);letter-spacing:.06em;text-transform:uppercase;margin-bottom:20px}
|
||||
.eco-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
|
||||
.eco-item{padding:20px;background:rgba(0,102,255,.04);border-radius:12px;border-left:4px solid var(--blue)}
|
||||
.eco-item.amos{border-left-color:#7B61FF}
|
||||
.eco-item.landvex{border-left-color:#00C853}
|
||||
.eco-item-title{font-size:1.1rem;font-weight:800;color:var(--text-dark);margin-bottom:6px}
|
||||
.eco-item-desc{font-size:.85rem;color:var(--text-muted);line-height:1.6;margin-bottom:8px}
|
||||
.eco-item-for{font-size:.75rem;font-weight:600}
|
||||
.eco-item-for.qx{color:var(--blue)}
|
||||
.eco-item-for.amos{color:#7B61FF}
|
||||
.eco-item-for.landvex{color:#00C853}
|
||||
@media(max-width:768px){.eco-grid{grid-template-columns:1fr}}
|
||||
|
||||
/* FOOTER */
|
||||
footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:64px 0 40px}
|
||||
@@ -126,6 +117,7 @@
|
||||
@media(max-width:900px){.footer-inner{grid-template-columns:1fr 1fr;gap:36px}.footer-brand{grid-column:1/-1}}
|
||||
@media(max-width:560px){.footer-inner{grid-template-columns:1fr}.footer-brand{grid-column:auto}.footer-bottom{flex-direction:column;align-items:flex-start}}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
@@ -138,7 +130,7 @@
|
||||
<a href="/about">About</a>
|
||||
<a href="/support">Support</a>
|
||||
</div>
|
||||
<a href="mailto:enterprise@quixzoom.com" class="btn-nav">Contact Sales</a>
|
||||
<a href="/order/" class="btn-nav">Start Order →</a>
|
||||
<button class="hamburger" onclick="document.querySelector('.mobile-menu').classList.toggle('open')" aria-label="Menu">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
@@ -152,83 +144,89 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="hero-org">
|
||||
<div class="container">
|
||||
<div class="hero-org-inner">
|
||||
<div class="hero-badge"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Enterprise Field Intelligence</div>
|
||||
<h1>Verified reality,<br>on demand.</h1>
|
||||
<p>Deploy photo missions at scale. Infrastructure inspection, property documentation, retail audits, environmental surveys — captured by verified Zoomers, reviewed by AI, delivered in hours.</p>
|
||||
<div class="hero-badge">📸 Raw Field Data on Demand</div>
|
||||
<h1>Order verified photos<br>from the field.</h1>
|
||||
<p>Get raw geotagged photos from verified contributors. Infrastructure, property, retail, environmental — any location, delivered in hours. Pay per mission with Stripe.</p>
|
||||
<div class="hero-cta">
|
||||
<a href="mailto:enterprise@quixzoom.com" class="btn-primary">Request a demo</a>
|
||||
<a href="#use-cases" class="btn-ghost">Explore use cases</a>
|
||||
<a href="/order/" class="btn-primary">Start Order →</a>
|
||||
<a href="#products" class="btn-ghost">Explore Products</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="stats-bar">
|
||||
<div class="container">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item"><span class="stat-value">~30 min</span><span class="stat-label">Average mission time</span></div>
|
||||
<div class="stat-item"><span class="stat-value">AI-reviewed</span><span class="stat-label">Every submission</span></div>
|
||||
<div class="stat-item"><span class="stat-value">100%</span><span class="stat-label">Geoverified</span></div>
|
||||
<div class="stat-item"><span class="stat-value">5 markets</span><span class="stat-label">Phased rollout from Aug 2026</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="use-cases" class="section-pad">
|
||||
<!-- PRODUCTS -->
|
||||
<section id="products" class="section-pad">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>Use cases</h2>
|
||||
<p>How organisations use quiXzoom to get eyes on the ground.</p>
|
||||
<h2>What you can order</h2>
|
||||
<p>Three ways to get field data. Choose what fits your needs.</p>
|
||||
</div>
|
||||
<div class="usecase-grid">
|
||||
<div class="usecase-card">
|
||||
<div class="usecase-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg></div>
|
||||
<h3>Infrastructure Inspection</h3>
|
||||
<p>Bridges, roads, facades, and utilities. Document condition without sending an inspector. Get geotagged, timestamped imagery within hours.</p>
|
||||
<div class="product-grid">
|
||||
<div class="product-card">
|
||||
<div class="product-icon">📸</div>
|
||||
<h3>Raw Field Photography</h3>
|
||||
<p>Order specific shots from any location. Geotagged, timestamped, verified. You define the brief — angles, coverage, metadata.</p>
|
||||
<div class="product-price">From $0.50<span> / mission</span></div>
|
||||
<a href="/order/" style="display:inline-block;margin-top:16px;color:var(--blue);font-weight:600;font-size:var(--text-sm)">Order now →</a>
|
||||
</div>
|
||||
<div class="usecase-card">
|
||||
<div class="usecase-icon"></div>
|
||||
<h3>Property Documentation</h3>
|
||||
<p>Pre-purchase inspections, rental condition reports, insurance claims. Standardised photo sets from any address.</p>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">📋</div>
|
||||
<h3>Lead Packages</h3>
|
||||
<p>Buy pre-collected intelligence. Example: "Dirty skylights on Östermalm" — get company names, raw photos, and flagged issues.</p>
|
||||
<div class="product-price">From $5<span> / lead</span></div>
|
||||
<a href="/order/#leads" style="display:inline-block;margin-top:16px;color:var(--blue);font-weight:600;font-size:var(--text-sm)">Browse leads →</a>
|
||||
</div>
|
||||
<div class="usecase-card">
|
||||
<div class="usecase-icon"></div>
|
||||
<h3>Retail & OOH Audit</h3>
|
||||
<p>Verify signage placement, shelf compliance, promotional displays. Competitive intelligence from real stores, not syndicated data.</p>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">🤖</div>
|
||||
<h3>AMOS AI Analysis</h3>
|
||||
<p>Add AI analysis to your raw data. Object detection, anomaly alerts, change detection, risk scoring. Automated reports.</p>
|
||||
<div class="product-price">From $10<span> / analysis</span></div>
|
||||
<a href="/order/#ai" style="display:inline-block;margin-top:16px;color:var(--blue);font-weight:600;font-size:var(--text-sm)">Learn more →</a>
|
||||
</div>
|
||||
<div class="usecase-card">
|
||||
<div class="usecase-icon"></div>
|
||||
<h3>Environmental Survey</h3>
|
||||
<p>Track vegetation, erosion, flood damage, coastal change. Repeatable observation points with GPS accuracy.</p>
|
||||
</div>
|
||||
<div class="usecase-card">
|
||||
<div class="usecase-icon"></div>
|
||||
<h3>Road Condition</h3>
|
||||
<p>Potholes, surface degradation, signage visibility. Municipalities prioritise repairs with verified visual evidence.</p>
|
||||
</div>
|
||||
<div class="usecase-card">
|
||||
<div class="usecase-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>
|
||||
<h3>Event & Emergency</h3>
|
||||
<p>Storm damage, accident documentation, construction progress. Rapid deployment when speed matters.</p>
|
||||
</div>
|
||||
|
||||
<!-- ECOSYSTEM -->
|
||||
<div class="eco-box">
|
||||
<div class="eco-title">The quiXzoom Ecosystem — Three Layers</div>
|
||||
<div class="eco-grid">
|
||||
<div class="eco-item">
|
||||
<div class="eco-item-title">quiXzoom</div>
|
||||
<div class="eco-item-desc">Raw field data collection. Verified Zoomers. Geotagged photos delivered in hours. Pay per mission.</div>
|
||||
<div class="eco-item-for qx">For: Everyone</div>
|
||||
</div>
|
||||
<div class="eco-item amos">
|
||||
<div class="eco-item-title">AMOS AI</div>
|
||||
<div class="eco-item-desc">AI analysis engine. Object detection, anomaly alerts, change detection, risk scoring on your data.</div>
|
||||
<div class="eco-item-for amos">For: Data owners & developers</div>
|
||||
</div>
|
||||
<div class="eco-item landvex">
|
||||
<div class="eco-item-title">Landvex</div>
|
||||
<div class="eco-item-desc">Business intelligence. Decision support, control intelligence, strategic insights for enterprise & government.</div>
|
||||
<div class="eco-item-for landvex">For: Enterprise, Government, Infrastructure</div>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin-top:16px;font-size:.8rem;color:var(--text-muted);text-align:center">Start with quiXzoom raw data → Add AMOS AI analysis → Upgrade to Landvex intelligence</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- HOW IT WORKS -->
|
||||
<section class="section-pad" style="background:var(--bg-off)">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>How it works</h2>
|
||||
<p>From mission definition to verified data delivery.</p>
|
||||
<p>From order to delivery in three steps.</p>
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
<div class="feature-visual"><div class="big-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 13V10L10 3L13 6L6 13H3Z" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M9 4L12 7" stroke="#666" stroke-width="1.5"/></svg></div></div>
|
||||
<div class="feature-visual"><div class="big-icon">📝</div></div>
|
||||
<div class="feature-text">
|
||||
<h3>1. Define your mission</h3>
|
||||
<p>Set location, requirements, and compensation. Use templates or build custom briefs. Define exactly what angles, coverage, and metadata you need.</p>
|
||||
<h3>1. Place your order</h3>
|
||||
<p>Define location, requirements, and budget. Use templates or build custom briefs. Set exactly what angles, coverage, and metadata you need.</p>
|
||||
<ul class="feature-list">
|
||||
<li>Geofenced capture zones</li>
|
||||
<li>Example images for Zoomers</li>
|
||||
@@ -238,7 +236,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-row reverse">
|
||||
<div class="feature-visual"><div class="big-icon"></div></div>
|
||||
<div class="feature-visual"><div class="big-icon">📱</div></div>
|
||||
<div class="feature-text">
|
||||
<h3>2. Zoomers capture</h3>
|
||||
<p>Verified Zoomers in the area claim and complete missions. Every photo carries GPS, timestamp, and integrity hash at capture.</p>
|
||||
@@ -251,115 +249,109 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
<div class="feature-visual"><div class="big-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></div>
|
||||
<div class="feature-visual"><div class="big-icon">✅</div></div>
|
||||
<div class="feature-text">
|
||||
<h3>3. AI review & delivery</h3>
|
||||
<p>Submissions are reviewed for completeness, quality, and specification compliance within minutes. Approved data is delivered to your dashboard or API.</p>
|
||||
<h3>3. Receive your data</h3>
|
||||
<p>Submissions are reviewed for completeness and quality. Approved data is delivered to your dashboard or API within hours.</p>
|
||||
<ul class="feature-list">
|
||||
<li>Automated completeness check</li>
|
||||
<li>Image quality scoring</li>
|
||||
<li>Geolocation verification</li>
|
||||
<li>API or dashboard export</li>
|
||||
<li>Image quality verification</li>
|
||||
<li>Geolocation confirmation</li>
|
||||
<li>Dashboard or API export</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- USE CASES -->
|
||||
<section class="section-pad">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>Pricing</h2>
|
||||
<p>Transparent, per-mission pricing. No hidden fees.</p>
|
||||
<h2>Use cases</h2>
|
||||
<p>How organisations use quiXzoom raw data.</p>
|
||||
</div>
|
||||
<div class="pricing-grid">
|
||||
<div class="pricing-card">
|
||||
<h3>Standard</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm)">For occasional missions</p>
|
||||
<div class="pricing-price">€30<span>/mission + platform fee</span></div>
|
||||
<ul class="pricing-features">
|
||||
<li>Self-serve mission creation</li>
|
||||
<li>Standard turnaround (24–48h)</li>
|
||||
<li>Dashboard access</li>
|
||||
<li>Email support</li>
|
||||
</ul>
|
||||
<a href="mailto:enterprise@quixzoom.com" class="btn-ghost" style="display:block;width:100%">Get started</a>
|
||||
<div class="product-grid">
|
||||
<div class="product-card">
|
||||
<div class="product-icon">🌉</div>
|
||||
<h3>Infrastructure Inspection</h3>
|
||||
<p>Bridges, roads, facades, utilities. Document condition without sending an inspector. Get geotagged imagery within hours.</p>
|
||||
</div>
|
||||
<div class="pricing-card featured">
|
||||
<h3>Professional</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm)">For regular operations</p>
|
||||
<div class="pricing-price">€75<span>/mission + platform fee</span></div>
|
||||
<ul class="pricing-features">
|
||||
<li>Priority mission deployment</li>
|
||||
<li>Faster turnaround (4–12h)</li>
|
||||
<li>API access</li>
|
||||
<li>Dedicated account manager</li>
|
||||
<li>Custom mission templates</li>
|
||||
</ul>
|
||||
<a href="mailto:enterprise@quixzoom.com" class="btn-primary" style="display:block;width:100%">Contact sales</a>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">🏢</div>
|
||||
<h3>Property Documentation</h3>
|
||||
<p>Pre-purchase inspections, rental condition reports, insurance claims. Standardised photo sets from any address.</p>
|
||||
</div>
|
||||
<div class="pricing-card">
|
||||
<h3>Enterprise</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm)">For large-scale programs</p>
|
||||
<div class="pricing-price">Custom</div>
|
||||
<ul class="pricing-features">
|
||||
<li>Volume pricing</li>
|
||||
<li>SLA guarantees</li>
|
||||
<li>Custom integrations</li>
|
||||
<li>White-label options</li>
|
||||
<li>Dedicated Zoomer network</li>
|
||||
</ul>
|
||||
<a href="mailto:enterprise@quixzoom.com" class="btn-ghost" style="display:block;width:100%">Contact sales</a>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">🛒</div>
|
||||
<h3>Retail & OOH Audit</h3>
|
||||
<p>Verify signage placement, shelf compliance, promotional displays. Competitive intelligence from real stores.</p>
|
||||
</div>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">🌿</div>
|
||||
<h3>Environmental Survey</h3>
|
||||
<p>Track vegetation, erosion, flood damage, coastal change. Repeatable observation points with GPS accuracy.</p>
|
||||
</div>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">🛣️</div>
|
||||
<h3>Road Condition</h3>
|
||||
<p>Potholes, surface degradation, signage visibility. Municipalities prioritise repairs with verified visual evidence.</p>
|
||||
</div>
|
||||
<div class="product-card">
|
||||
<div class="product-icon">⚡</div>
|
||||
<h3>Event & Emergency</h3>
|
||||
<p>Storm damage, accident documentation, construction progress. Rapid deployment when speed matters.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="enterprise" class="cta-section">
|
||||
<!-- CTA -->
|
||||
<section style="background:var(--bg-off);padding:80px 0;text-align:center">
|
||||
<div class="container">
|
||||
<h2>Ready to deploy?</h2>
|
||||
<p>Talk to our team about your field data needs. We'll design a mission program that fits your timeline and budget.</p>
|
||||
<a href="mailto:enterprise@quixzoom.com" class="btn-primary">Request a demo →</a>
|
||||
<p style="margin-top:16px;font-size:var(--text-sm);color:var(--text-muted)">Or email us directly at <a href="mailto:enterprise@quixzoom.com" style="color:var(--blue);text-decoration:underline">enterprise@quixzoom.com</a></p>
|
||||
<h2 style="margin-bottom:16px">Ready to order?</h2>
|
||||
<p style="font-size:var(--text-lg);color:var(--text-muted);margin-bottom:32px;max-width:560px;margin-left:auto;margin-right:auto">Start with a single mission or browse lead packages. Pay with Stripe — no setup fees.</p>
|
||||
<a href="/order/" class="btn-primary">Start Order →</a>
|
||||
<p style="margin-top:24px;font-size:var(--text-sm);color:var(--text-muted)">Need enterprise invoicing or volume pricing? → <a href="https://www.landvex.com" style="color:var(--blue)">Contact Landvex</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer>
|
||||
<div class="container footer-inner">
|
||||
<div class="footer-brand">
|
||||
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
|
||||
<p class="footer-tagline">Verified reality, on demand. Field intelligence for organisations that need to see what's happening on the ground.</p>
|
||||
<p class="footer-tagline">Raw field data on demand. Verified photos from verified contributors, delivered in hours.</p>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>Product</h4>
|
||||
<ul>
|
||||
<li><a href="/">For Zoomers</a></li>
|
||||
<li><a href="/for-organisations/">For organisations</a></li>
|
||||
<li><a href="/for-organisations/">For Organisations</a></li>
|
||||
<li><a href="/pricing">Pricing</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
<li><a href="/order/">Start Order</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>Resources</h4>
|
||||
<ul>
|
||||
<li><a href="/academy/">Zoomer Academy</a></li>
|
||||
<li><a href="/glossary">Glossary</a></li>
|
||||
<li><a href="/status">System status</a></li>
|
||||
<li><a href="/support">Support</a></li>
|
||||
<li><a href="/status">System Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>Legal</h4>
|
||||
<h4>Company</h4>
|
||||
<ul>
|
||||
<li><a href="/privacy/">Privacy policy</a></li>
|
||||
<li><a href="/terms/">Terms of service</a></li>
|
||||
<li><a href="/security">Security</a></li>
|
||||
<li><a href="/support">Support</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
<li><a href="/careers/">Careers</a></li>
|
||||
<li><a href="https://www.landvex.com">Landvex</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container footer-bottom">
|
||||
<p class="footer-copy">© 2026 quiXzoom · Operated by Landvex AB</p>
|
||||
<p class="footer-copy">© 2026 quiXzoom Inc · A US company · Operated by Landvex AB in Sweden</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -390,6 +390,7 @@ main {
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -554,7 +555,7 @@ main {
|
||||
<p class="term-long">When AI review determines that a submission satisfies all specification, quality, and location requirements for a mission, the submission is marked as Approved. This event is the trigger for payout to the Zoomer and for delivery of the submission package to the orderer. Approval also counts positively toward the Zoomer's approval rate and rating.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Zoomer submits a marina documentation mission. AI review approves the submission within 2 minutes; the Zoomer sees a confirmation in-app and their €12 earnings are queued for the next payout cycle. The orderer receives the submission package via webhook.</p>
|
||||
<p>A Zoomer submits a marina documentation mission. AI review approves the submission within 2 minutes; the Zoomer sees a confirmation in-app and their $12 earnings are queued for the next payout cycle. The orderer receives the submission package via webhook.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -574,7 +575,7 @@ main {
|
||||
<p class="term-long">Approval rate is the primary quality signal on the quiXzoom platform. It is calculated as approved submissions divided by total submissions over the preceding 30 days. A Zoomer's approval rate directly determines their tier eligibility and access to premium and surge missions. Professional tier requires maintaining above 92% approval; Active tier requires above 85%. Zoomers below 70% receive coaching and may be temporarily restricted from claiming new missions.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Zoomer with 38 approvals and 2 rejections out of 40 submissions in the last 30 days has a 95% approval rate. This qualifies them for Professional tier and unlocks access to premium infrastructure missions paying up to €30 per submission.</p>
|
||||
<p>A Zoomer with 38 approvals and 2 rejections out of 40 submissions in the last 30 days has a 95% approval rate. This qualifies them for Professional tier and unlocks access to premium infrastructure missions paying up to $30 per submission.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -699,7 +700,7 @@ main {
|
||||
<p class="term-long">A Contradiction is flagged when an observation deviates significantly from the expected state: a building that was intact now shows damage, a road that was clear now has a sinkhole, or an asset that was present is now missing. Contradictions are not errors — they are valuable signals. They trigger alerts, escalate priority, and often indicate the most urgent decisions a customer must make. The Contradiction engine cross-references observations against historical data, design specifications, and regulatory baselines.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A quarterly observation of a commercial roof shows new water staining not present in the previous observation. AMOS flags this as a Contradiction, alerting the property manager to schedule repairs before the rainy season — preventing a €50,000 claim.</p>
|
||||
<p>A quarterly observation of a commercial roof shows new water staining not present in the previous observation. AMOS flags this as a Contradiction, alerting the property manager to schedule repairs before the rainy season — preventing a $50,000 claim.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1131,7 +1132,7 @@ main {
|
||||
<p class="term-long">An Index is the primary output of the AMOS analysis engine — a single number that summarises complex, multi-dimensional observation data into an intuitive, comparable metric. Different indices serve different purposes: the City Health Index measures overall infrastructure condition; the Infrastructure Risk Index quantifies probability of failure; the Commercial Opportunity Index identifies high-potential investment areas. Indices are computed from aggregated observations, weighted by confidence scores, and updated continuously as new observations flow in.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In Landvex</span>
|
||||
<p>A city's Road Condition Index drops from 72 to 58 over six months. AMOS traces the decline to increased pothole observations in the northern district. The municipality uses this Index to justify a €2M resurfacing budget — with the Index projected to return to 75 post-works.</p>
|
||||
<p>A city's Road Condition Index drops from 72 to 58 over six months. AMOS traces the decline to increased pothole observations in the northern district. The municipality uses this Index to justify a $2M resurfacing budget — with the Index projected to return to 75 post-works.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1234,7 +1235,7 @@ main {
|
||||
<p class="term-long">A mission is the fundamental unit of work on the quiXzoom platform. Each mission has a geographic pin or geofence, a compensation amount, a mission brief (describing what to photograph and how), a time window within which the work must be completed, and a submission specification defining how many photos are required and from which vantage points. Missions may be standard, premium, or surge, and may be single-submission or multi-submission.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>An insurance company creates a mission to document storm damage at a specific address. The mission has a 200-metre geofence, requires 8 photos from specified angles, pays €14, and has a 6-hour time window from claim. A nearby Zoomer sees it in the app, claims it, and completes the documentation.</p>
|
||||
<p>An insurance company creates a mission to document storm damage at a specific address. The mission has a 200-metre geofence, requires 8 photos from specified angles, pays $14, and has a 6-hour time window from claim. A nearby Zoomer sees it in the app, claims it, and completes the documentation.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1293,7 +1294,7 @@ main {
|
||||
<p class="term-long">Multi-submission missions are used by orderers who want several independent documentation sets of the same location — typically for comparison, validation, or higher confidence. The first Zoomer to submit receives the highest compensation; subsequent approved submissions receive progressively lower amounts. The orderer sets the maximum number of submissions they will accept and the compensation schedule. Multi-submission missions remain claimable by additional Zoomers until the orderer's submission limit is reached.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>An orderer creates a multi-submission mission for a flood-affected road segment, accepting 3 submissions at €18 / €12 / €8. Three different Zoomers claim and submit independently — the orderer receives three distinct photographic records of the same location, useful for triangulating damage assessment.</p>
|
||||
<p>An orderer creates a multi-submission mission for a flood-affected road segment, accepting 3 submissions at $18 / $12 / $8. Three different Zoomers claim and submit independently — the orderer receives three distinct photographic records of the same location, useful for triangulating damage assessment.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1424,7 +1425,7 @@ main {
|
||||
<p class="term-long">Payouts are processed via Stripe Connect and deposited to the Zoomer's linked IBAN. Earnings from approved submissions accumulate in the Zoomer's Stripe Connect balance and are disbursed on a rolling schedule. Payout frequency and minimum threshold are configurable within the app. Payouts require completed identity verification (KYC) and a linked bank account. Zoomers receive an in-app notification and email confirmation for each payout.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Zoomer completes 5 missions in a week, earning €72 total. After each approval, earnings add to their Stripe Connect balance. On Friday, their accumulated balance is disbursed to their Dutch IBAN via SEPA — arriving as a single bank transfer with a quiXzoom reference.</p>
|
||||
<p>A Zoomer completes 5 missions in a week, earning $72 total. After each approval, earnings add to their Stripe Connect balance. On Friday, their accumulated balance is disbursed to their Dutch IBAN via SEPA — arriving as a single bank transfer with a quiXzoom reference.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1464,7 +1465,7 @@ main {
|
||||
<p class="term-long">The platform fee is charged to the orderer and covers quiXzoom's infrastructure, AI review system, Zoomer management, customer support, and margin. Zoomers receive their full stated mission compensation — the platform fee is paid by the orderer separately and does not come from Zoomer earnings. Platform fees are calculated as a percentage of mission compensation, with enterprise volume pricing available for high-volume orderers.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>An orderer creates a mission paying the Zoomer €12. quiXzoom's platform fee adds an additional amount on top — the orderer pays the total; the Zoomer receives exactly €12 upon approval. The platform fee structure is transparent and set out in the orderer's service agreement.</p>
|
||||
<p>An orderer creates a mission paying the Zoomer $12. quiXzoom's platform fee adds an additional amount on top — the orderer pays the total; the Zoomer receives exactly $12 upon approval. The platform fee structure is transparent and set out in the orderer's service agreement.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1481,10 +1482,10 @@ main {
|
||||
<span class="term-category cat-platform">Platform</span>
|
||||
</div>
|
||||
<p class="term-short">A high-threshold mission type requiring specific Zoomer qualifications and a high approval rate, offering significantly elevated compensation.</p>
|
||||
<p class="term-long">Premium missions are reserved for experienced, high-performing Zoomers. Qualification requires Professional tier (or Active tier with relevant specialisation) and an approval rate above 92%. These missions typically involve technical infrastructure documentation — bridges, power facilities, maritime assets — with precise specification requirements. Compensation for premium missions reflects both the technical difficulty and the high-value use cases they serve, typically ranging from €20 to €40+ per submission.</p>
|
||||
<p class="term-long">Premium missions are reserved for experienced, high-performing Zoomers. Qualification requires Professional tier (or Active tier with relevant specialisation) and an approval rate above 92%. These missions typically involve technical infrastructure documentation — bridges, power facilities, maritime assets — with precise specification requirements. Compensation for premium missions reflects both the technical difficulty and the high-value use cases they serve, typically ranging from $20 to $40+ per submission.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A bridge inspection premium mission paying €28 appears for a Zoomer with a Maritime specialisation and 96% approval rate. Zoomers without Professional tier or the relevant specialisation do not see this mission in their feed — it is filtered to qualified Zoomers only.</p>
|
||||
<p>A bridge inspection premium mission paying $28 appears for a Zoomer with a Maritime specialisation and 96% approval rate. Zoomers without Professional tier or the relevant specialisation do not see this mission in their feed — it is filtered to qualified Zoomers only.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1504,7 +1505,7 @@ main {
|
||||
<p class="term-long">Professional tier represents quiXzoom's most engaged and highest-quality Zoomers. Qualification requires sustaining 15+ completed missions per week over the qualifying window, maintaining an approval rate above 92%, and having completed identity verification. Professional tier Zoomers receive priority access to new mission types, higher compensation caps, dedicated support, and early access to specialisation programmes. They form the backbone of supply for high-value, time-critical orderer requests.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A full-time quiXzoom contributor averaging 20 missions/week with a 97% approval rate holds Professional tier. They access premium infrastructure missions paying €25–40, receive surge alerts first, and have the option to take on specialisation certifications for maritime and bridge inspection mission types.</p>
|
||||
<p>A full-time quiXzoom contributor averaging 20 missions/week with a 97% approval rate holds Professional tier. They access premium infrastructure missions paying $25–40, receive surge alerts first, and have the option to take on specialisation certifications for maritime and bridge inspection mission types.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1674,11 +1675,11 @@ main {
|
||||
<h3 class="term-name">SEPA</h3>
|
||||
<span class="term-category cat-payment">Payment</span>
|
||||
</div>
|
||||
<p class="term-short">Single Euro Payments Area — the European bank transfer network used for EUR payout disbursements to Zoomers.</p>
|
||||
<p class="term-long">SEPA covers 36 European countries and enables fast, low-cost euro bank transfers using IBAN identifiers. SEPA Credit Transfers (SCT) typically settle within 1 business day; SEPA Instant Credit Transfers (SCT Inst) settle in under 10 seconds where supported. quiXzoom uses Stripe Connect's SEPA infrastructure for EUR disbursements to Zoomers in SEPA-member countries. For Zoomers in countries outside SEPA, SWIFT transfers apply, which may have longer settlement windows and additional fees.</p>
|
||||
<p class="term-short">Single Euro Payments Area — the European bank transfer network used for USD payout disbursements to Zoomers.</p>
|
||||
<p class="term-long">SEPA covers 36 European countries and enables fast, low-cost euro bank transfers using IBAN identifiers. SEPA Credit Transfers (SCT) typically settle within 1 business day; SEPA Instant Credit Transfers (SCT Inst) settle in under 10 seconds where supported. quiXzoom uses Stripe Connect's SEPA infrastructure for USD disbursements to Zoomers in SEPA-member countries. For Zoomers in countries outside SEPA, SWIFT transfers apply, which may have longer settlement windows and additional fees.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Zoomer in Finland has earned €85 across 7 missions. Stripe Connect initiates a SEPA Credit Transfer to their Finnish IBAN — the funds arrive in their bank account within 1 business day at no charge to the Zoomer.</p>
|
||||
<p>A Zoomer in Finland has earned $85 across 7 missions. Stripe Connect initiates a SEPA Credit Transfer to their Finnish IBAN — the funds arrive in their bank account within 1 business day at no charge to the Zoomer.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1718,7 +1719,7 @@ main {
|
||||
<p class="term-long">Specialisations are earned through a training and assessment programme administered within the quiXzoom app. Each specialisation covers the technical requirements, safety considerations, and documentation standards for a specific mission domain. Available specialisations include Maritime (marina and harbour documentation), Bridge & Civil (infrastructure inspection), Energy Infrastructure (substations, wind turbines), and Retail Audit. Completing a specialisation assessment unlocks the corresponding premium mission category in the Zoomer's feed.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Zoomer living near a large marina completes the Maritime specialisation — a 45-minute in-app training covering mooring documentation, vessel identification standards, and marina facility photography requirements. They now see maritime premium missions paying €20–35 in their area, which are invisible to non-specialised Zoomers.</p>
|
||||
<p>A Zoomer living near a large marina completes the Maritime specialisation — a 45-minute in-app training covering mooring documentation, vessel identification standards, and marina facility photography requirements. They now see maritime premium missions paying $20–35 in their area, which are invisible to non-specialised Zoomers.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1778,7 +1779,7 @@ main {
|
||||
<p class="term-long">A submission is the output of a claimed and completed mission. It consists of the required number of geo-tagged images, their EXIF metadata, a GPS trace (if required), and any optional notes from the Zoomer. Submissions are uploaded through the quiXzoom app and enter the AI review pipeline immediately. The submission event locks the mission as completed — a Zoomer cannot resubmit after an initial submission without contacting support. Submission quality directly impacts approval rate and rating.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Zoomer completes a 6-photo road condition survey and taps Submit. The submission package (images, EXIF, GPS trace) is uploaded to S3 and enters AI review. Within 90 seconds, the Zoomer receives an in-app approval notification and their €10 earnings are queued for payout.</p>
|
||||
<p>A Zoomer completes a 6-photo road condition survey and taps Submit. The submission package (images, EXIF, GPS trace) is uploaded to S3 and enters AI review. Within 90 seconds, the Zoomer receives an in-app approval notification and their $10 earnings are queued for payout.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1798,7 +1799,7 @@ main {
|
||||
<p class="term-long">Supplementary tier is the starting tier for all new Zoomers. It provides access to standard missions in the Zoomer's zone, with no minimum weekly commitment. Supplementary Zoomers earn the standard mission rates and can access surge missions in their area. To progress to Active tier, a Zoomer must demonstrate sustained volume (8+ missions/week) and maintain an approval rate above 85% over a qualifying period.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A gig worker uses quiXzoom alongside their other activities — completing 3 missions per week in their neighbourhood while walking their dog or commuting. At Supplementary tier, they access standard missions with no pressure to increase volume; their €30–40/week in earnings is a useful addition to their income mix.</p>
|
||||
<p>A gig worker uses quiXzoom alongside their other activities — completing 3 missions per week in their neighbourhood while walking their dog or commuting. At Supplementary tier, they access standard missions with no pressure to increase volume; their $30–40/week in earnings is a useful addition to their income mix.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1817,7 +1818,7 @@ main {
|
||||
<p class="term-long">Surge missions are created by orderers (typically insurers or infrastructure managers) in response to time-critical events requiring rapid field documentation. They carry elevated compensation to incentivise Zoomers to prioritise them and accept shorter time windows. Surge alerts are pushed as priority notifications to all Zoomers in the affected zone, with Professional tier Zoomers notified first. Surge missions typically have tighter specification requirements and stricter AI review thresholds given their legal and claims significance.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A severe storm hits coastal Sweden. An insurer activates 40 surge missions across 12 postcodes within 20 minutes of the event. Zoomers in those zones receive priority push notifications — the missions pay €35 each (vs. €12 standard) with a 3-hour time window. 38 of 40 missions are claimed within 45 minutes.</p>
|
||||
<p>A severe storm hits coastal Sweden. An insurer activates 40 surge missions across 12 postcodes within 20 minutes of the event. Zoomers in those zones receive priority push notifications — the missions pay $35 each (vs. $12 standard) with a 3-hour time window. 38 of 40 missions are claimed within 45 minutes.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1929,7 +1930,7 @@ main {
|
||||
<p class="term-long">A verified contributor is a Zoomer who has passed the platform's trust threshold: their identity has been confirmed through government-issued ID and liveness check, their bank account is linked via Stripe Connect, and their account is in good standing (not suspended or restricted). Verification is a one-time process, but good standing is maintained through consistent quality (approval rate above threshold) and compliance with platform policies. Only verified contributors can receive payouts and access Professional-tier missions. Verification status is displayed in the Zoomer's profile.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A new Zoomer completes 5 missions and earns €45. Before they can receive their first payout, they must become a verified contributor by completing KYC. Once verified, their status shows a green checkmark in-app, and they can withdraw earnings and claim premium missions.</p>
|
||||
<p>A new Zoomer completes 5 missions and earns $45. Before they can receive their first payout, they must become a verified contributor by completing KYC. Once verified, their status shows a green checkmark in-app, and they can withdraw earnings and claim premium missions.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -1949,7 +1950,7 @@ main {
|
||||
<p class="term-long">VAT treatment of gig platform income varies by jurisdiction and depends on the Zoomer's status (individual, self-employed, VAT-registered business) and the total income thresholds in their country. In most EU countries, Zoomers earning below the VAT registration threshold are not required to charge or remit VAT. quiXzoom provides transaction records suitable for tax reporting but does not provide tax advice — Zoomers should consult their local tax authority or advisor for their specific obligations. For orderers, quiXzoom's service fees are subject to standard B2B VAT rules.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A Swedish Zoomer earning €8,000/year from quiXzoom (below Sweden's VAT threshold of ~€32,000 for small businesses) is not required to register for or charge VAT. A Zoomer earning above threshold must register, charge VAT on their services, and remit to Skatteverket — quiXzoom's annual earnings summary facilitates this reporting.</p>
|
||||
<p>A Swedish Zoomer earning $8,000/year from quiXzoom (below Sweden's VAT threshold of ~$32,000 for small businesses) is not required to register for or charge VAT. A Zoomer earning above threshold must register, charge VAT on their services, and remit to Skatteverket — quiXzoom's annual earnings summary facilitates this reporting.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -2059,7 +2060,7 @@ main {
|
||||
<p class="term-long">Zoomers are the supply side of the quiXzoom marketplace. Anyone with a compatible smartphone, a willingness to be verified (KYC), and a linked bank account can become a Zoomer. Zoomers work independently, on their own schedule, within their chosen zone. They earn per approved submission — there is no hourly pay, no minimum hours, and no obligation to work any particular number of missions. Zoomers progress through tiers (Supplementary → Active → Professional) as they build volume and quality.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A freelance photographer becomes a quiXzoom Zoomer, completing identity verification and bank account setup in 10 minutes. They begin claiming road survey and building inspection missions near their home, earning €8–15 per approved submission. Within 6 weeks, their consistent quality and volume qualify them for Active tier.</p>
|
||||
<p>A freelance photographer becomes a quiXzoom Zoomer, completing identity verification and bank account setup in 10 minutes. They begin claiming road survey and building inspection missions near their home, earning $8–15 per approved submission. Within 6 weeks, their consistent quality and volume qualify them for Active tier.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -2156,7 +2157,7 @@ sections.forEach(s => observer.observe(s));
|
||||
<p class="term-long">Active tier represents Zoomers who use quiXzoom as a regular supplemental income source. To qualify, a Zoomer must sustain 8+ completed missions per week and maintain an approval rate above 85% over the qualifying window. Active Zoomers access a broader mission pool than Supplementary tier, including most premium mission types and higher-paying specialisation missions. Active tier is the threshold for most specialisation programme eligibility.</p>
|
||||
<div class="term-context">
|
||||
<span class="term-context-label">In quiXzoom</span>
|
||||
<p>A part-time Zoomer completes 10 missions/week around their regular job, maintaining a 91% approval rate. At Active tier, they access infrastructure survey missions (€15–20) that are invisible to Supplementary Zoomers, and have applied for the Bridge & Civil specialisation programme.</p>
|
||||
<p>A part-time Zoomer completes 10 missions/week around their regular job, maintaining a 91% approval rate. At Active tier, they access infrastructure survey missions ($15–20) that are invisible to Supplementary Zoomers, and have applied for the Bridge & Civil specialisation programme.</p>
|
||||
</div>
|
||||
<div class="term-related">
|
||||
<span>Related:</span>
|
||||
@@ -2235,5 +2236,234 @@ sections.forEach(s => observer.observe(s));
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -19,9 +19,239 @@
|
||||
<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="Help — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to <a href="support.html">support centre</a>...</p>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<style>
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -259,6 +259,7 @@
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -393,5 +394,234 @@
|
||||
<p>© 2026 quiXzoom · Operated by Landvex AB · <a href="/privacy.html">Privacy</a> · <a href="/terms.html">Terms</a> · <a href="https://www.quixzoom.com/">quixzoom.com</a></p>
|
||||
</footer>
|
||||
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="/favicon-512x512.png">
|
||||
<link rel="preconnect" href="https://api.quixzoom.com">
|
||||
<script type="application/ld+json">{"@context":"https://schema.org","@graph":[
|
||||
{"@type":["Organization","Corporation"],"@id":"https://www.quixzoom.com/#organization","name":"quiXzoom","legalName":"Landvex AB","alternateName":["quiXzoom","quixzoom","QuiXzoom","QUIXZOOM","quick zoom app","quix zoom","qui xzoom","quiX zoom","quixzoom app","quixzoom platform","quiXzoom platform","quiXzoom Sverige","quiXzoom Sweden"],"url":"https://www.quixzoom.com/","foundingDate":"2026","description":"quiXzoom is a crowdsourced field data collection platform. Independent contractors called Zoomers photograph infrastructure, roads, buildings and urban environments on demand and earn money per approved mission.","logo":{"@type":"ImageObject","url":"https://www.quixzoom.com/favicon-512x512.png","width":512,"height":512},"brand":{"@type":"Brand","name":"quiXzoom","slogan":"Collect reality."},"disambiguatingDescription":"quiXzoom (spelled with a capital X) is a crowdsourced field data platform — not related to Zoom video conferencing or camera zoom features.","sameAs":["https://www.quixzoom.com","https://app.quixzoom.com","https://www.linkedin.com/company/quixzoom","https://www.instagram.com/quixzoom","https://twitter.com/quixzoom","https://x.com/quixzoom","https://www.crunchbase.com/organization/quixzoom"]},
|
||||
{"@type":["Organization","Corporation"],"@id":"https://www.quixzoom.com/#organization","name":"quiXzoom","legalName":"quiXzoom Inc","alternateName":["quiXzoom","quixzoom","QuiXzoom","QUIXZOOM","quick zoom app","quix zoom","qui xzoom","quiX zoom","quixzoom app","quixzoom platform","quiXzoom platform","quiXzoom Sverige","quiXzoom Sweden"],"url":"https://www.quixzoom.com/","foundingDate":"2026","description":"quiXzoom is a crowdsourced field data collection platform. Independent contractors called Zoomers photograph infrastructure, roads, buildings and urban environments on demand and earn money per approved mission.","logo":{"@type":"ImageObject","url":"https://www.quixzoom.com/favicon-512x512.png","width":512,"height":512},"brand":{"@type":"Brand","name":"quiXzoom","slogan":"Collect reality."},"disambiguatingDescription":"quiXzoom (spelled with a capital X) is a crowdsourced field data platform — not related to Zoom video conferencing or camera zoom features.","sameAs":["https://www.quixzoom.com","https://app.quixzoom.com","https://www.linkedin.com/company/quixzoom","https://www.instagram.com/quixzoom","https://twitter.com/quixzoom","https://x.com/quixzoom","https://www.crunchbase.com/organization/quixzoom"]},
|
||||
{"@type":"WebSite","@id":"https://www.quixzoom.com/#website","url":"https://www.quixzoom.com/","name":"quiXzoom","description":"Crowdsourced field data collection — earn money photographing infrastructure and urban environments near you.","publisher":{"@id":"https://www.quixzoom.com/#organization"}},
|
||||
{"@type":"SoftwareApplication","name":"quiXzoom","applicationCategory":"LifestyleApplication","operatingSystem":"iOS, Android","description":"Crowdsourced field data collection platform. Zoomers earn money photographing infrastructure and urban environments on demand.","offers":{"@type":"Offer","price":"0","priceCurrency":"EUR","description":"Free to join — earn per approved mission"}},
|
||||
{"@type":"SoftwareApplication","name":"quiXzoom","applicationCategory":"LifestyleApplication","operatingSystem":"iOS, Android","description":"Crowdsourced field data collection platform. Zoomers earn money photographing infrastructure and urban environments on demand.","offers":{"@type":"Offer","price":"0","priceCurrency":"USD","description":"Free to join — earn per approved mission"}},
|
||||
{"@type":"FAQPage","mainEntity":[
|
||||
{"@type":"Question","name":"How much can I earn as a Zoomer?","acceptedAnswer":{"@type":"Answer","text":"Missions reward 30–120 QX Credits each. Active Zoomers doing 8–12 missions per week typically accumulate 400–1,200 Credits weekly. Credits are redeemable as cash via weekly payout — the exact amount depends on your local payout rate."}},
|
||||
{"@type":"Question","name":"Do I need a professional camera?","acceptedAnswer":{"@type":"Answer","text":"No. iPhone 12 or later, or any Android with 12MP or better, is all you need."}},
|
||||
@@ -137,7 +137,7 @@ nav{position:sticky;top:0;z-index:300;background:var(--bg-white);border-bottom:1
|
||||
#hero{background:var(--bg-off);min-height:calc(100vh - 68px);display:flex;align-items:center;padding:80px 0 96px}
|
||||
.hero-layout{display:grid;grid-template-columns:1fr 420px;gap:64px;align-items:center;width:100%}
|
||||
.hero-inner{max-width:560px}
|
||||
.hero-visual{display:flex;justify-content:center;align-items:center;position:relative;overflow:hidden}
|
||||
.hero-visual{display:flex;justify-content:center;align-items:flex-start;position:relative;overflow:hidden;padding-top:40px}
|
||||
.phone-frame{width:240px;height:480px;background:#1a1a1a;border-radius:36px;border:2px solid rgba(0,0,0,.1);box-shadow:0 40px 80px rgba(0,0,0,.15),0 0 0 1px rgba(0,0,0,.05);position:relative;overflow:hidden;flex-shrink:0}
|
||||
.phone-notch{position:absolute;top:0;left:50%;transform:translateX(-50%);width:80px;height:24px;background:#2a2a2a;border-radius:0 0 16px 16px;z-index:10}
|
||||
.phone-screen{position:absolute;inset:0;background:#fff;border-radius:34px;overflow:hidden;display:flex;flex-direction:column}
|
||||
@@ -646,11 +646,22 @@ html,body{overflow-x:clip}
|
||||
.qx-calc-result-period{font-size:.75rem;color:rgba(10,10,10,.75);margin-top:2px}
|
||||
|
||||
/* GeoIP banner */
|
||||
#qx-geo-banner{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#fff;border:1px solid rgba(0,0,0,.12);border-radius:100px;padding:10px 20px 10px 16px;display:flex;align-items:center;gap:12px;font-size:.8125rem;color:rgba(0,0,0,.85);z-index:800;box-shadow:0 8px 32px rgba(0,0,0,.15);white-space:nowrap;animation:slideUp .3s ease}
|
||||
@keyframes slideUp{from{opacity:0;transform:translateX(-50%) translateY(8px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
|
||||
#qx-geo-banner button.geo-switch{background:#0066FF;color:var(--text-dark);border:none;border-radius:100px;padding:5px 14px;font-size:.75rem;font-weight:700;cursor:pointer}
|
||||
#qx-geo-banner button.geo-dismiss{background:none;border:none;color:rgba(10,10,10,.75);font-size:1rem;cursor:pointer;padding:0 0 0 4px;line-height:1}
|
||||
@media(max-width:480px){#qx-geo-banner{bottom:16px;left:16px;right:16px;transform:none;border-radius:16px;white-space:normal;flex-wrap:wrap}}
|
||||
#qx-geo-banner{position:fixed;bottom:32px;left:50%;transform:translateX(-50%);background:#fff;border:2px solid var(--blue);border-radius:var(--radius-card);padding:24px 32px;display:flex;align-items:center;gap:20px;font-size:1rem;color:var(--text-dark);z-index:800;box-shadow:0 12px 40px rgba(0,102,255,.15);animation:slideUp .3s ease;max-width:480px;width:90%}
|
||||
@keyframes slideUp{from{opacity:0;transform:translateX(-50%) translateY(16px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
|
||||
#qx-geo-banner .geo-icon{width:48px;height:48px;background:var(--blue);border-radius:12px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
|
||||
#qx-geo-banner .geo-icon svg{width:24px;height:24px;stroke:white;stroke-width:2;fill:none}
|
||||
#qx-geo-banner .geo-text{display:flex;flex-direction:column;gap:6px;flex:1}
|
||||
#qx-geo-banner .geo-title{font-size:1.1rem;font-weight:800;color:var(--text-dark);line-height:1.3}
|
||||
#qx-geo-banner .geo-subtitle{font-size:.85rem;color:var(--text-muted);line-height:1.4}
|
||||
#qx-geo-banner .geo-local{font-size:.8rem;color:var(--blue);font-weight:600}
|
||||
#qx-geo-banner .geo-actions{display:flex;flex-direction:column;gap:8px;align-items:flex-end}
|
||||
#qx-geo-banner a.geo-switch{background:var(--blue);color:#fff;border:none;border-radius:var(--radius-lg);padding:10px 20px;font-size:.85rem;font-weight:700;cursor:pointer;text-decoration:none;white-space:nowrap;transition:background .15s;text-align:center}
|
||||
#qx-geo-banner a.geo-switch:hover{background:var(--blue-dark)}
|
||||
#qx-geo-banner button.geo-dismiss{background:none;border:none;color:var(--text-muted);font-size:1.25rem;cursor:pointer;padding:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:background .15s;margin-top:4px}
|
||||
#qx-geo-banner button.geo-dismiss:hover{background:rgba(0,0,0,.08)}
|
||||
@media(max-width:600px){#qx-geo-banner{flex-direction:column;align-items:flex-start;gap:16px;max-width:90%}}
|
||||
@media(max-width:600px){#qx-geo-banner .geo-actions{width:100%;align-items:stretch}}
|
||||
@media(max-width:600px){#qx-geo-banner a.geo-switch{width:100%;text-align:center}}
|
||||
|
||||
/* Tier illustrations */
|
||||
.tier-ill{display:flex;justify-content:center;margin-bottom:16px}
|
||||
@@ -679,6 +690,7 @@ html,body{overflow-x:clip}
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -690,7 +702,6 @@ html,body{overflow-x:clip}
|
||||
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/order/">Order Field Data</a></li>
|
||||
<li><a href="/pricing/">Pricing</a></li>
|
||||
<li><a href="/earn/">Earn as Zoomer</a></li>
|
||||
<li><a href="/for-organisations/">For Organisations</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
@@ -726,7 +737,6 @@ html,body{overflow-x:clip}
|
||||
</nav>
|
||||
<div class="mobile-menu" id="mobile-menu">
|
||||
<a href="/order/" onclick="closeMenu()">Order Field Photography</a>
|
||||
<a href="/pricing/" onclick="closeMenu()">Pricing</a>
|
||||
<a href="/earn/" onclick="closeMenu()">Earn as a Zoomer</a>
|
||||
<a href="/for-organisations/" onclick="closeMenu()">For Organisations</a>
|
||||
<a href="/about" onclick="closeMenu()">About</a>
|
||||
@@ -785,8 +795,29 @@ html,body{overflow-x:clip}
|
||||
</div>
|
||||
<p class="wl-counter" id="wl-counter-wrap" style="display:block"><strong><span id="wl-count">2,400+</span></strong> <a class="gterm" data-gterm="Zoomer" href="/glossary#zoomer">Zoomers</a> already signed up</p>
|
||||
<p class="wl-trust" style="color:rgba(10,10,10,.75);margin-top:12px">Launching August 1, 2026.</p>
|
||||
|
||||
<!-- ECOSYSTEM STRIP -->
|
||||
<div style="margin-top:28px;padding:18px 20px;background:linear-gradient(135deg,rgba(0,102,255,.06) 0%,rgba(0,102,255,.02) 100%);border:1px solid rgba(0,102,255,.15);border-radius:14px">
|
||||
<p style="font-size:.78rem;font-weight:700;color:var(--blue);letter-spacing:.06em;text-transform:uppercase;margin-bottom:10px">The quiXzoom Ecosystem</p>
|
||||
<div style="display:flex;flex-wrap:wrap;gap:12px;align-items:flex-start">
|
||||
<div style="flex:1;min-width:200px">
|
||||
<div style="font-size:.9rem;font-weight:700;color:var(--text-dark);margin-bottom:2px">quiXzoom</div>
|
||||
<div style="font-size:.8rem;color:var(--text-muted);line-height:1.5">Raw field photography. Verified contributors. Geotagged data delivered in hours.</div>
|
||||
</div>
|
||||
<div style="width:1px;background:rgba(0,102,255,.15);min-height:40px"></div>
|
||||
<div style="flex:1;min-width:200px">
|
||||
<div style="font-size:.9rem;font-weight:700;color:var(--text-dark);margin-bottom:2px">AMOS AI</div>
|
||||
<div style="font-size:.8rem;color:var(--text-muted);line-height:1.5">AI analysis of your data. Object detection, anomaly alerts, change detection, risk scoring.</div>
|
||||
</div>
|
||||
<div style="width:1px;background:rgba(0,102,255,.15);min-height:40px"></div>
|
||||
<div style="flex:1;min-width:200px">
|
||||
<div style="font-size:.9rem;font-weight:700;color:var(--text-dark);margin-bottom:2px">Landvex</div>
|
||||
<div style="font-size:.8rem;color:var(--text-muted);line-height:1.5">Business intelligence for enterprise, government & infrastructure owners. Decision support.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-stats-pill">
|
||||
<div class="hstat"><div class="hstat-num">30–120</div><div class="hstat-lbl">credits per mission<br><span style="font-size:.7rem;color:rgba(10,10,10,.55);font-weight:400">1 Credit = €1 · min. payout 250 Credits</span></div></div>
|
||||
<div class="hstat"><div class="hstat-num">30–120</div><div class="hstat-lbl">credits per mission<br><span style="font-size:.7rem;color:rgba(10,10,10,.55);font-weight:400">1 Credit = $1 · min. payout 250 Credits</span></div></div>
|
||||
<div class="hstat"><div class="hstat-num">Sweden</div><div class="hstat-lbl">Launching Aug 2026</div></div>
|
||||
<div class="hstat"><div class="hstat-num">0%</div><div class="hstat-lbl">platform fee</div></div>
|
||||
</div>
|
||||
@@ -838,7 +869,7 @@ html,body{overflow-x:clip}
|
||||
<div class="ps-card-addr">Main Street 14, Stockholm</div>
|
||||
<div class="ps-card-row">
|
||||
<div>
|
||||
<div class="ps-card-reward">€85</div>
|
||||
<div class="ps-card-reward">$85</div>
|
||||
<div class="ps-card-meta">3 photos · ~12 min</div>
|
||||
</div>
|
||||
<span class="ps-card-btn" style="cursor:default;opacity:.7">Claim →</span>
|
||||
@@ -852,12 +883,93 @@ html,body{overflow-x:clip}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- DUAL CTA -->
|
||||
<section style="background:var(--bg-white);padding:48px 0;border-bottom:1px solid rgba(0,0,0,.08)">
|
||||
<div class="container">
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:24px">
|
||||
<!-- CTA: Beställare -->
|
||||
<div style="background:linear-gradient(135deg,rgba(0,102,255,.06) 0%,rgba(0,102,255,.02) 100%);border:2px solid rgba(0,102,255,.15);border-radius:var(--radius-card);padding:36px 32px;text-align:center">
|
||||
<div style="width:64px;height:64px;margin:0 auto 16px;background:var(--blue);border-radius:16px;display:flex;align-items:center;justify-content:center">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="5"/><path d="M12 7v10M7 12h10"/></svg>
|
||||
</div>
|
||||
<h3 style="font-size:1.5rem;font-weight:800;color:var(--text-dark);margin-bottom:8px">Order Field Photography</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-base);line-height:1.6;margin-bottom:12px">Get raw geotagged photos from verified contributors. Delivered in hours.</p>
|
||||
<p style="color:var(--text-muted);font-size:.8rem;line-height:1.5;margin-bottom:24px">Pay with Stripe in the app or on desktop. Enterprise invoicing via <a href="https://www.landvex.com" style="color:var(--blue)">Landvex</a>.</p>
|
||||
<a href="/order/" style="display:inline-flex;align-items:center;justify-content:center;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-lg);padding:16px 32px;font-size:1rem;font-weight:700;text-decoration:none;transition:background .15s">Start Order →</a>
|
||||
</div>
|
||||
<!-- CTA: Zoomer -->
|
||||
<div style="background:linear-gradient(135deg,rgba(0,200,83,.06) 0%,rgba(0,200,83,.02) 100%);border:2px solid rgba(0,200,83,.15);border-radius:var(--radius-card);padding:36px 32px;text-align:center">
|
||||
<div style="width:64px;height:64px;margin:0 auto 16px;background:#00C853;border-radius:16px;display:flex;align-items:center;justify-content:center">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v12M8 10l4-4 4 4M8 14l4 4 4-4"/></svg>
|
||||
</div>
|
||||
<h3 style="font-size:1.5rem;font-weight:800;color:var(--text-dark);margin-bottom:8px">Earn as a Zoomer</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-base);line-height:1.6;margin-bottom:12px">Free to join. No fees. Get paid per approved mission. Work when you want.</p>
|
||||
<p style="color:var(--text-muted);font-size:.8rem;line-height:1.5;margin-bottom:24px">Weekly payouts via Stripe Connect. From $0.50 per mission. 1 QZ Token = $1 USD.</p>
|
||||
<a href="/earn/" style="display:inline-flex;align-items:center;justify-content:center;background:#00C853;color:#fff;border:none;border-radius:var(--radius-lg);padding:16px 32px;font-size:1rem;font-weight:700;text-decoration:none;transition:background .15s">Join as Zoomer →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOR ORGANISATIONS -->
|
||||
<section id="for-organisations" class="section-pad" style="background:var(--bg-off)">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<h2>Order verified field data</h2>
|
||||
<p>Raw geotagged photos from verified contributors. Delivered in hours, not weeks.</p>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:24px;margin-bottom:40px">
|
||||
<div style="background:var(--bg-white);border-radius:var(--radius-card);padding:28px;border:1px solid rgba(0,0,0,.08)">
|
||||
<div style="font-size:2rem;margin-bottom:12px">📸</div>
|
||||
<h3 style="font-size:var(--text-lg);font-weight:800;margin-bottom:8px;color:var(--text-dark)">Raw Field Photography</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm);line-height:1.6">Order specific shots from any location. Geotagged, timestamped, verified. No analysis — just raw visual data.</p>
|
||||
<a href="/order/" style="color:var(--blue);font-weight:600;font-size:var(--text-sm);text-decoration:none">Start order →</a>
|
||||
</div>
|
||||
<div style="background:var(--bg-white);border-radius:var(--radius-card);padding:28px;border:1px solid rgba(0,0,0,.08)">
|
||||
<div style="font-size:2rem;margin-bottom:12px">📋</div>
|
||||
<h3 style="font-size:var(--text-lg);font-weight:800;margin-bottom:8px;color:var(--text-dark)">Lead Packages</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm);line-height:1.6">Buy pre-collected intelligence. Example: "Dirty skylights on Östermalm" — get company names, raw photos, and AI-flagged issues.</p>
|
||||
<a href="/order/#leads" style="color:var(--blue);font-weight:600;font-size:var(--text-sm);text-decoration:none">Browse leads →</a>
|
||||
</div>
|
||||
<div style="background:var(--bg-white);border-radius:var(--radius-card);padding:28px;border:1px solid rgba(0,0,0,.08)">
|
||||
<div style="font-size:2rem;margin-bottom:12px">🤖</div>
|
||||
<h3 style="font-size:var(--text-lg);font-weight:800;margin-bottom:8px;color:var(--text-dark)">AMOS AI Analysis</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm);line-height:1.6">Add AI analysis to your order. Object detection, anomaly alerts, change detection, risk scoring. Automated reports.</p>
|
||||
<a href="/for-organisations/#ai" style="color:var(--blue);font-weight:600;font-size:var(--text-sm);text-decoration:none">Learn more →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ECOSYSTEM CLARIFICATION -->
|
||||
<div style="background:var(--bg-white);border-radius:var(--radius-card);padding:32px;border:2px solid var(--blue);border-opacity:.15">
|
||||
<p style="font-size:.78rem;font-weight:700;color:var(--blue);letter-spacing:.06em;text-transform:uppercase;margin-bottom:20px">The quiXzoom Ecosystem — Three Layers</p>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px">
|
||||
<div style="padding:20px;background:rgba(0,102,255,.04);border-radius:12px;border-left:4px solid var(--blue)">
|
||||
<div style="font-size:1.1rem;font-weight:800;color:var(--text-dark);margin-bottom:6px">quiXzoom</div>
|
||||
<div style="font-size:.85rem;color:var(--text-muted);line-height:1.6;margin-bottom:8px">Raw field data collection. Verified Zoomers. Geotagged photos delivered in hours.</div>
|
||||
<div style="font-size:.75rem;color:var(--blue);font-weight:600">For: Everyone</div>
|
||||
</div>
|
||||
<div style="padding:20px;background:rgba(0,102,255,.04);border-radius:12px;border-left:4px solid #7B61FF">
|
||||
<div style="font-size:1.1rem;font-weight:800;color:var(--text-dark);margin-bottom:6px">AMOS AI</div>
|
||||
<div style="font-size:.85rem;color:var(--text-muted);line-height:1.6;margin-bottom:8px">AI analysis engine. Object detection, anomaly alerts, change detection, risk scoring on your data.</div>
|
||||
<div style="font-size:.75rem;color:#7B61FF;font-weight:600">For: Data owners & developers</div>
|
||||
</div>
|
||||
<div style="padding:20px;background:rgba(0,102,255,.04);border-radius:12px;border-left:4px solid #00C853">
|
||||
<div style="font-size:1.1rem;font-weight:800;color:var(--text-dark);margin-bottom:6px">Landvex</div>
|
||||
<div style="font-size:.85rem;color:var(--text-muted);line-height:1.6;margin-bottom:8px">Business intelligence. Decision support, control intelligence, strategic insights for enterprise & government.</div>
|
||||
<div style="font-size:.75rem;color:#00C853;font-weight:600">For: Enterprise, Government, Infrastructure</div>
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin-top:16px;font-size:.8rem;color:var(--text-muted);text-align:center">Start with quiXzoom raw data → Add AMOS AI analysis → Upgrade to Landvex intelligence</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- STATS -->
|
||||
<section id="stats">
|
||||
<div class="container">
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item fade-in" style="cursor:pointer" onclick="document.getElementById('how').scrollIntoView({behavior:'smooth'})"><span class="stat-value">~30 min</span><span class="stat-label">Average mission time</span></div>
|
||||
<div class="stat-item fade-in" style="cursor:pointer" onclick="document.getElementById('how').scrollIntoView({behavior:'smooth'})"><span class="stat-value">€30–120</span><span class="stat-label">Per mission</span></div>
|
||||
<div class="stat-item fade-in" style="cursor:pointer" onclick="document.getElementById('how').scrollIntoView({behavior:'smooth'})"><span class="stat-value">$30–120</span><span class="stat-label">Per mission</span></div>
|
||||
<div class="stat-item fade-in" style="cursor:pointer" onclick="document.getElementById('how').scrollIntoView({behavior:'smooth'})"><span class="stat-value">Aug 2026</span><span class="stat-label">Launching in Sweden</span></div>
|
||||
<div class="stat-item fade-in" style="cursor:pointer" onclick="document.getElementById('how').scrollIntoView({behavior:'smooth'})"><span class="stat-value">100%</span><span class="stat-label">Paid on approval</span></div>
|
||||
</div>
|
||||
@@ -885,7 +997,7 @@ html,body{overflow-x:clip}
|
||||
<div class="proof-card fade-in">
|
||||
<div style="font-size:2rem;margin-bottom:12px">💰</div>
|
||||
<h3 style="font-size:var(--text-lg);margin-bottom:8px">Weekly payouts</h3>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm)">Reach 250 Credits (€250) and get paid automatically every Monday via Stripe Connect.</p>
|
||||
<p style="color:var(--text-muted);font-size:var(--text-sm)">Reach 250 Credits ($250) and get paid automatically every Monday via Stripe Connect.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="proof-logos">
|
||||
@@ -1165,7 +1277,7 @@ html,body{overflow-x:clip}
|
||||
function qxCalcUpdate(){
|
||||
var m=parseInt(document.getElementById('calc-missions').value);
|
||||
var r=parseInt(document.getElementById('calc-reward').value);
|
||||
var loc=window.QX_CURRENT_LOCALE||{symbol:'€',currency:'EUR'};
|
||||
var loc=window.QX_CURRENT_LOCALE||{symbol:'$',currency:'USD'};
|
||||
var monthly=Math.round(m*r*4.3);
|
||||
document.getElementById('calc-missions-val').textContent=m;
|
||||
document.getElementById('calc-reward-val').textContent=loc.symbol+r;
|
||||
@@ -1403,30 +1515,42 @@ document.addEventListener('qx:market',function(){qxCalcUpdate();});
|
||||
</div>
|
||||
</div><!-- /footer-top -->
|
||||
|
||||
<div style="border-bottom:1px solid rgba(10,10,10,.07)">
|
||||
<div class="footer-bottom" style="padding-top:16px;padding-bottom:16px">
|
||||
<div class="footer-lang-row">
|
||||
<span class="fl-lbl"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10z"/></svg></span>
|
||||
<a href="https://www.quixzoom.com/" class="flang active">EN</a>
|
||||
<a href="/markets/se/" class="flang">🇸🇪 Sverige</a>
|
||||
<a href="/markets/nl/" class="flang">🇳🇱 NL</a>
|
||||
<a href="/markets/th/" class="flang">🇹🇭 TH</a>
|
||||
<a href="/markets/ng/" class="flang">🇳🇬 NG</a>
|
||||
<a href="/markets/ke/" class="flang">🇰🇪 KE</a>
|
||||
<!-- MICROSOFT-STYLE FOOTER -->
|
||||
<div style="border-top:1px solid rgba(10,10,10,.08);padding:20px 32px;max-width:1200px;margin:0 auto">
|
||||
|
||||
<!-- Row 1: Language selector (clean, Microsoft-style) -->
|
||||
<div style="display:flex;align-items:center;gap:16px;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid rgba(10,10,10,.06)">
|
||||
<span style="font-size:.8rem;color:var(--text-muted)">Language:</span>
|
||||
<a href="https://www.quixzoom.com/" style="font-size:.8rem;color:var(--text-dark);font-weight:600;text-decoration:none">English (US)</a>
|
||||
<span style="color:rgba(10,10,10,.2)">|</span>
|
||||
<a href="https://quixzoom.se/" style="font-size:.8rem;color:var(--text-muted);text-decoration:none">Svenska</a>
|
||||
<span style="color:rgba(10,10,10,.2)">|</span>
|
||||
<a href="https://quixzoom.de/" style="font-size:.8rem;color:var(--text-muted);text-decoration:none">Deutsch</a>
|
||||
<span style="color:rgba(10,10,10,.2)">|</span>
|
||||
<a href="https://quixzoom.nl/" style="font-size:.8rem;color:var(--text-muted);text-decoration:none">Nederlands</a>
|
||||
<span style="color:rgba(10,10,10,.2)">|</span>
|
||||
<a href="https://quixzoom.fr/" style="font-size:.8rem;color:var(--text-muted);text-decoration:none">Français</a>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Company info (single, clear message) -->
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:16px">
|
||||
<div>
|
||||
<p style="font-size:.75rem;color:rgba(10,10,10,.55);line-height:1.6;margin:0">
|
||||
<strong style="color:rgba(10,10,10,.75)">quiXzoom Inc</strong> — A US company. Operated in Sweden by Landvex AB.<br>
|
||||
Raw field data collection. <a href="https://www.landvex.com" style="color:var(--blue);text-decoration:none">Need intelligence? → Landvex</a>
|
||||
</p>
|
||||
</div>
|
||||
<div style="display:flex;gap:16px;flex-wrap:wrap">
|
||||
<a href="/privacy/" style="font-size:.75rem;color:rgba(10,10,10,.55);text-decoration:none">Privacy</a>
|
||||
<a href="/terms/" style="font-size:.75rem;color:rgba(10,10,10,.55);text-decoration:none">Terms</a>
|
||||
<a href="/security" style="font-size:.75rem;color:rgba(10,10,10,.55);text-decoration:none">Security</a>
|
||||
<a href="/support" style="font-size:.75rem;color:rgba(10,10,10,.55);text-decoration:none">Support</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border-top:1px solid rgba(10,10,10,.07);padding:20px 32px;max-width:1200px;margin:0 auto">
|
||||
<p style="font-size:.8rem;color:rgba(10,10,10,.55);line-height:1.6"><strong style="color:rgba(10,10,10,.75)">quiXzoom delivers raw field data. Landvex delivers decision intelligence.</strong> Need analysis, not just photos? → <a href="https://www.landvex.com" style="color:var(--blue);font-weight:600;text-decoration:none">landvex.com</a></p>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p class="footer-copy">© 2026 quiXzoom · Operated by Landvex AB · <em>Raw data by quiXzoom. Intelligence by Landvex.</em></p>
|
||||
<div class="footer-legal-links">
|
||||
<a href="/privacy/">Privacy</a>
|
||||
<a href="/terms/">Terms</a>
|
||||
<a href="/security">Security</a>
|
||||
<a href="/support">Support</a>
|
||||
|
||||
<!-- Row 3: Copyright -->
|
||||
<div style="margin-top:12px;padding-top:12px;border-top:1px solid rgba(10,10,10,.06)">
|
||||
<p style="font-size:.7rem;color:rgba(10,10,10,.4);margin:0">© 2026 quiXzoom Inc. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1469,7 +1593,7 @@ const DATA = {
|
||||
2: {
|
||||
type: 'Step 2', title: 'Selecting and claiming a mission',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Missions are displayed on a map as markers. Each marker shows the mission type, distance from you, estimated time, and compensation. You claim the missions you want — no assignment from quiXzoom.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">~30 min</div><div class="layer-kpi-lbl">Average mission</div></div><div class="layer-kpi"><div class="layer-kpi-val">€3–30</div><div class="layer-kpi-lbl">Typical range per mission</div></div><div class="layer-kpi"><div class="layer-kpi-val">Your choice</div><div class="layer-kpi-lbl">No forced assignments</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Missions are displayed on a map as markers. Each marker shows the mission type, distance from you, estimated time, and compensation. You claim the missions you want — no assignment from quiXzoom.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">~30 min</div><div class="layer-kpi-lbl">Average mission</div></div><div class="layer-kpi"><div class="layer-kpi-val">$3–30</div><div class="layer-kpi-lbl">Typical range per mission</div></div><div class="layer-kpi"><div class="layer-kpi-val">Your choice</div><div class="layer-kpi-lbl">No forced assignments</div></div></div>` },
|
||||
{ tab: '2 · The mission brief', html: `<p class="layer-lead">Every mission has a brief that tells you exactly what is required before you claim it. There are no surprises.</p><p class="layer-h3">What the brief contains</p><ul class="layer-ul"><li>Location — exact address or area, shown on map with navigation link</li><li>Documentation type — which angles, elements and coverage are required</li><li>Example images — visual reference for each required shot</li><li>Time window — how long you have to complete and submit</li><li>Compensation — the exact amount you receive on approval</li><li>Difficulty rating — estimated skill level and time required</li><li>Access notes — any known access restrictions or safety guidance</li></ul>` },
|
||||
{ tab: '3 · Claiming', html: `<p class="layer-lead">Claiming reserves the mission for you within a defined time window. Unclaimed missions are visible to all Zoomers in range simultaneously.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Browse the map</p><p class="layer-step-desc">Missions in your zone appear as markers. Filter by type, compensation or distance.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Read the brief</p><p class="layer-step-desc">Review requirements, example images and compensation before claiming. No obligation.</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">Claim</p><p class="layer-step-desc">Claiming starts your time window. The mission is reserved for you. Other Zoomers cannot claim it during your window.</p></div></div><div class="layer-step"><div class="layer-step-num">4</div><div class="layer-step-body"><p class="layer-step-title">Navigate and document</p><p class="layer-step-desc">App guides you to location and shows required shots as you capture them.</p></div></div><div class="layer-step"><div class="layer-step-num">5</div><div class="layer-step-body"><p class="layer-step-title">Submit</p><p class="layer-step-desc">Submit within your time window. AI review begins immediately.</p></div></div></div>` },
|
||||
{ tab: '4 · Competition', html: `<p class="layer-lead">Some missions allow multiple Zoomer submissions — the fastest and highest-quality earns most. Understanding competition dynamics helps you maximise earnings.</p><p class="layer-h3">Mission types by competition model</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Type</th><th>Competition model</th><th>Best strategy</th></tr><tr><td>Standard</td><td>First valid submission wins full fee</td><td>Speed matters — claim and go immediately</td></tr><tr><td>Multi-submission</td><td>Multiple submissions accepted, declining fee</td><td>Quality matters — take your time</td></tr><tr><td>Surge</td><td>Large event, many locations, flat fee</td><td>Volume strategy — complete as many as possible</td></tr><tr><td>Premium</td><td>Single submission, higher fee, quality threshold</td><td>Specialisation required — only for rated Zoomers</td></tr></table></div>` },
|
||||
@@ -1496,10 +1620,10 @@ const DATA = {
|
||||
student: {
|
||||
type: 'Zoomer profile', title: 'Student · Part-time Zoomer',
|
||||
layers: [
|
||||
{ tab: '1 · Profile', html: `<p class="layer-lead">Students are the most active Zoomer demographic in beta testing. Flexible schedules, local knowledge, and smartphone-native — they fit the platform naturally.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">3–4</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">€60–120</div><div class="layer-kpi-lbl">Monthly earnings (est.)</div></div><div class="layer-kpi"><div class="layer-kpi-val">~2h</div><div class="layer-kpi-lbl">Time invested per week</div></div></div>` },
|
||||
{ tab: '2 · Typical week', html: `<p class="layer-lead">A typical student Zoomer fits missions into their existing daily movement — to lectures, to the library, back home.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">Mon</div><div class="layer-step-body"><p class="layer-step-title">Morning: 1 mission on the way to campus</p><p class="layer-step-desc">Infrastructure mission near the route. 20 minutes, €8. Submitted between lectures.</p></div></div><div class="layer-step"><div class="layer-step-num">Wed</div><div class="layer-step-body"><p class="layer-step-title">Lunchtime: 2 missions in town</p><p class="layer-step-desc">Two real estate missions in a nearby block. 40 minutes combined, €14 total.</p></div></div><div class="layer-step"><div class="layer-step-num">Fri</div><div class="layer-step-body"><p class="layer-step-title">Afternoon: 1 surge mission</p><p class="layer-step-desc">Insurance surge event posted — storm damage documentation nearby. Highest pay this week: €22.</p></div></div></div><p class="layer-h3">Total: ~€44 this week, ~2 hours invested</p>` },
|
||||
{ tab: '1 · Profile', html: `<p class="layer-lead">Students are the most active Zoomer demographic in beta testing. Flexible schedules, local knowledge, and smartphone-native — they fit the platform naturally.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">3–4</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">$60–120</div><div class="layer-kpi-lbl">Monthly earnings (est.)</div></div><div class="layer-kpi"><div class="layer-kpi-val">~2h</div><div class="layer-kpi-lbl">Time invested per week</div></div></div>` },
|
||||
{ tab: '2 · Typical week', html: `<p class="layer-lead">A typical student Zoomer fits missions into their existing daily movement — to lectures, to the library, back home.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">Mon</div><div class="layer-step-body"><p class="layer-step-title">Morning: 1 mission on the way to campus</p><p class="layer-step-desc">Infrastructure mission near the route. 20 minutes, $8. Submitted between lectures.</p></div></div><div class="layer-step"><div class="layer-step-num">Wed</div><div class="layer-step-body"><p class="layer-step-title">Lunchtime: 2 missions in town</p><p class="layer-step-desc">Two real estate missions in a nearby block. 40 minutes combined, $14 total.</p></div></div><div class="layer-step"><div class="layer-step-num">Fri</div><div class="layer-step-body"><p class="layer-step-title">Afternoon: 1 surge mission</p><p class="layer-step-desc">Insurance surge event posted — storm damage documentation nearby. Highest pay this week: $22.</p></div></div></div><p class="layer-h3">Total: ~$44 this week, ~2 hours invested</p>` },
|
||||
{ tab: '3 · Equipment used', html: `<p class="layer-lead">iPhone 14 (standard lens). No accessories. No extra apps. Standard camera, standard settings.</p><ul class="layer-ul"><li>Photos shot at maximum native resolution</li><li>Location services always on while using quiXzoom</li><li>2GB free storage maintained</li><li>No tripod, no gimbal — all handheld</li></ul>` },
|
||||
{ tab: '4 · Earnings breakdown', html: `<p class="layer-lead">Earnings vary significantly by mission type selected and regional availability.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Mission type</th><th>Avg. compensation</th><th>Avg. time</th><th>€/hour equivalent</th></tr><tr><td>Infrastructure (road)</td><td>€5</td><td>15 min</td><td>€20/h</td></tr><tr><td>Real estate (façade)</td><td>€8</td><td>20 min</td><td>€24/h</td></tr><tr><td>Insurance (surge)</td><td>€18</td><td>25 min</td><td>€43/h</td></tr><tr><td>Verification</td><td>€4</td><td>10 min</td><td>€24/h</td></tr></table></div>` },
|
||||
{ tab: '4 · Earnings breakdown', html: `<p class="layer-lead">Earnings vary significantly by mission type selected and regional availability.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Mission type</th><th>Avg. compensation</th><th>Avg. time</th><th>$/hour equivalent</th></tr><tr><td>Infrastructure (road)</td><td>$5</td><td>15 min</td><td>$20/h</td></tr><tr><td>Real estate (façade)</td><td>$8</td><td>20 min</td><td>$24/h</td></tr><tr><td>Insurance (surge)</td><td>$18</td><td>25 min</td><td>$43/h</td></tr><tr><td>Verification</td><td>$4</td><td>10 min</td><td>$24/h</td></tr></table></div>` },
|
||||
{ tab: '5 · Tax guide', html: `<p class="layer-lead">Tax rules depend on your country of residence. quiXzoom provides annual earnings statements in all required formats.</p><p id="qx-tax-note" style="margin:12px 0;padding:12px 16px;background:#f7f7f5;border-radius:8px;font-size:.875rem;">Select your market above to see local tax guidance.</p><p class="layer-p"><em>General information only — consult a qualified local tax advisor for your situation.</em></p>` },
|
||||
{ tab: '6 · Rating building', html: `<p class="layer-lead">A high approval rate unlocks better missions. For students starting out, consistency matters more than volume.</p><ul class="layer-ul"><li>Read every brief carefully before claiming</li><li>Never claim if you can't complete within the time window</li><li>When in doubt about quality — retake, don't submit</li><li>Review rejection reasons immediately — patterns emerge quickly</li><li>3 months of consistent quality → Active tier → access to premium missions</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Join thousands of students on the waitlist</p><p class="layer-cta-sub">Register now. Missions go live August 2026. No commitment, no cost.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Register as a Zoomer →</a></div>` }
|
||||
@@ -1508,10 +1632,10 @@ const DATA = {
|
||||
driver: {
|
||||
type: 'Zoomer profile', title: 'Delivery driver · Route-integrated Zoomer',
|
||||
layers: [
|
||||
{ tab: '1 · Profile', html: `<p class="layer-lead">Delivery drivers and couriers are among the highest-earning Zoomer profiles. They pass through multiple mission zones every working day — and the marginal cost of completing a mission on route is nearly zero.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">5–8</div><div class="layer-kpi-lbl">Missions per week (on-route)</div></div><div class="layer-kpi"><div class="layer-kpi-val">€120–280</div><div class="layer-kpi-lbl">Monthly earnings (est.)</div></div><div class="layer-kpi"><div class="layer-kpi-val">~3 min</div><div class="layer-kpi-lbl">Added time per on-route mission</div></div></div>` },
|
||||
{ tab: '1 · Profile', html: `<p class="layer-lead">Delivery drivers and couriers are among the highest-earning Zoomer profiles. They pass through multiple mission zones every working day — and the marginal cost of completing a mission on route is nearly zero.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">5–8</div><div class="layer-kpi-lbl">Missions per week (on-route)</div></div><div class="layer-kpi"><div class="layer-kpi-val">$120–280</div><div class="layer-kpi-lbl">Monthly earnings (est.)</div></div><div class="layer-kpi"><div class="layer-kpi-val">~3 min</div><div class="layer-kpi-lbl">Added time per on-route mission</div></div></div>` },
|
||||
{ tab: '2 · Route integration', html: `<p class="layer-lead">The key insight: missions that are already on your route take near-zero additional time. The mission is there; you're passing it anyway.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Open quiXzoom before your shift</p><p class="layer-step-desc">Browse missions on the map overlaid on your planned route. Identify 3–5 that are directly on path.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Claim as you approach</p><p class="layer-step-desc">Claim each mission when you're 5–10 minutes away. Keeps time window comfortable.</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">Stop, shoot, go</p><p class="layer-step-desc">A simple façade mission: pull up, 4 photos, submit. 3–5 minutes. You're moving again.</p></div></div></div>` },
|
||||
{ tab: '3 · Best mission types', html: `<p class="layer-lead">Not all missions fit the driver workflow equally well. These work best.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Mission type</th><th>Fit for drivers</th><th>Why</th></tr><tr><td>Verification (address check)</td><td>⭐⭐⭐⭐⭐</td><td>Already stopping at addresses</td></tr><tr><td>Real estate (exterior)</td><td>⭐⭐⭐⭐</td><td>Drive-by documentation, no entry needed</td></tr><tr><td>Urban & streets</td><td>⭐⭐⭐⭐</td><td>Pass through these zones anyway</td></tr><tr><td>Infrastructure</td><td>⭐⭐⭐</td><td>Often near delivery zones</td></tr><tr><td>Incident (surge)</td><td>⭐⭐⭐⭐⭐</td><td>Already in the area when events happen</td></tr></table></div>` },
|
||||
{ tab: '4 · Earnings model', html: `<p class="layer-lead">Route-integrated missions have an exceptional earnings-to-time ratio because the travel cost is zero.</p><div class="layer-block"><p class="layer-block-title">Example shift: 8-hour delivery day</p><ul class="layer-ul"><li>5 verification missions on delivery routes: 5 × €4 = €20</li><li>2 exterior documentation missions: 2 × €7 = €14</li><li>1 urban survey cluster (3 adjacent buildings): €18</li><li>Total added: €52 for ~25 minutes of additional activity</li></ul></div>` },
|
||||
{ tab: '4 · Earnings model', html: `<p class="layer-lead">Route-integrated missions have an exceptional earnings-to-time ratio because the travel cost is zero.</p><div class="layer-block"><p class="layer-block-title">Example shift: 8-hour delivery day</p><ul class="layer-ul"><li>5 verification missions on delivery routes: 5 × $4 = $20</li><li>2 exterior documentation missions: 2 × $7 = $14</li><li>1 urban survey cluster (3 adjacent buildings): $18</li><li>Total added: $52 for ~25 minutes of additional activity</li></ul></div>` },
|
||||
{ tab: '5 · Platform rules for drivers', html: `<p class="layer-lead">Using quiXzoom while on a delivery shift: rules to follow.</p><ul class="layer-ul"><li>Never compromise your delivery SLAs for a quiXzoom mission — deliveries first</li><li>Do not stop in prohibited areas to complete a mission</li><li>Never use a handheld device while driving — only stop at safe, legal locations</li><li>Check your employment contract: most delivery platform contracts do not restrict secondary income activities during breaks</li></ul>` },
|
||||
{ tab: '6 · Tier progression', html: `<p class="layer-lead">Regular route-based Zoomers typically reach Active tier within 6–8 weeks, unlocking premium missions.</p><ul class="layer-ul"><li>Supplementary → Active: ~50 approved missions with >85% approval rate</li><li>Active → Professional: ~200 approved missions with >92% approval rate</li><li>Professional tier: access to premium infrastructure missions, priority claim windows, compensation boosts</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Your route is already worth money</p><p class="layer-cta-sub">Register now. The missions will be live on your delivery routes from August 2026.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Register as a Zoomer →</a></div>` }
|
||||
@@ -1520,8 +1644,8 @@ const DATA = {
|
||||
retired: {
|
||||
type: 'Zoomer profile', title: 'Retired · Neighbourhood Zoomer',
|
||||
layers: [
|
||||
{ tab: '1 · Profile', html: `<p class="layer-lead">Retired Zoomers are among the most consistent performers on the platform. Deep local knowledge, predictable daily routines, and patient attention to detail produce high approval rates.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">2–5</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">€40–160</div><div class="layer-kpi-lbl">Monthly earnings (est.)</div></div><div class="layer-kpi"><div class="layer-kpi-val">92%+</div><div class="layer-kpi-lbl">Typical approval rate</div></div></div>` },
|
||||
{ tab: '2 · A typical day', html: `<p class="layer-lead">The quiXzoom approach fits naturally into a walk-based daily routine without changing it materially.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">9:00</div><div class="layer-step-body"><p class="layer-step-title">Morning check</p><p class="layer-step-desc">Open app over coffee. See 2–3 missions within a 1km walk from home. Claim one.</p></div></div><div class="layer-step"><div class="layer-step-num">10:30</div><div class="layer-step-body"><p class="layer-step-title">Morning walk</p><p class="layer-step-desc">Route passes the mission location. Stop, photograph according to brief, submit. Walk continues.</p></div></div><div class="layer-step"><div class="layer-step-num">11:00</div><div class="layer-step-body"><p class="layer-step-title">Review notification</p><p class="layer-step-desc">Submission approved. €7 credited. Payment arrives in bank account tomorrow.</p></div></div></div>` },
|
||||
{ tab: '1 · Profile', html: `<p class="layer-lead">Retired Zoomers are among the most consistent performers on the platform. Deep local knowledge, predictable daily routines, and patient attention to detail produce high approval rates.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">2–5</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">$40–160</div><div class="layer-kpi-lbl">Monthly earnings (est.)</div></div><div class="layer-kpi"><div class="layer-kpi-val">92%+</div><div class="layer-kpi-lbl">Typical approval rate</div></div></div>` },
|
||||
{ tab: '2 · A typical day', html: `<p class="layer-lead">The quiXzoom approach fits naturally into a walk-based daily routine without changing it materially.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">9:00</div><div class="layer-step-body"><p class="layer-step-title">Morning check</p><p class="layer-step-desc">Open app over coffee. See 2–3 missions within a 1km walk from home. Claim one.</p></div></div><div class="layer-step"><div class="layer-step-num">10:30</div><div class="layer-step-body"><p class="layer-step-title">Morning walk</p><p class="layer-step-desc">Route passes the mission location. Stop, photograph according to brief, submit. Walk continues.</p></div></div><div class="layer-step"><div class="layer-step-num">11:00</div><div class="layer-step-body"><p class="layer-step-title">Review notification</p><p class="layer-step-desc">Submission approved. $7 credited. Payment arrives in bank account tomorrow.</p></div></div></div>` },
|
||||
{ tab: '3 · Why approval rates are high', html: `<p class="layer-lead">Retired Zoomers consistently achieve the highest approval rates on the platform. The reasons are structural.</p><ul class="layer-ul"><li>No time pressure — time window is comfortable, no rushing</li><li>Attention to detail — brief is read carefully, all angles completed</li><li>Local knowledge — know exactly where things are, less navigation uncertainty</li><li>Calm capture conditions — not multitasking, images are sharp and well-framed</li><li>Consistency — same routes, same conditions, predictable quality</li></ul>` },
|
||||
{ tab: '4 · Suitable mission types', html: `<p class="layer-lead">Infrastructure and urban missions within walking distance are the natural fit.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Mission type</th><th>Suitability</th><th>Note</th></tr><tr><td>Infrastructure (road, pavement)</td><td>⭐⭐⭐⭐⭐</td><td>Walking speed ideal for pavement-level documentation</td></tr><tr><td>Urban & streets</td><td>⭐⭐⭐⭐⭐</td><td>Local knowledge advantage</td></tr><tr><td>Real estate (exterior)</td><td>⭐⭐⭐⭐</td><td>No access needed, exterior only</td></tr><tr><td>Verification</td><td>⭐⭐⭐⭐</td><td>Local area knowledge invaluable</td></tr></table></div>` },
|
||||
{ tab: '5 · Social aspect', html: `<p class="layer-lead">Several retired beta Zoomers noted an unexpected benefit: purpose and local contribution.</p><div class="layer-quote"><p>"I walk past the same bridge every morning anyway. Now I'm documenting it for the municipality. It feels like I'm contributing something, not just walking."</p></div><div class="layer-quote"><p>"I know this neighbourhood better than anyone. It turns out that local knowledge is worth money. Who knew."</p></div>` },
|
||||
@@ -1535,10 +1659,10 @@ const DATA = {
|
||||
infrastructure: {
|
||||
type: 'Mission category', title: 'Infrastructure',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Infrastructure missions are the backbone of the quiXzoom platform. They are recurring, predictable, and distributed across every populated area. Clients include municipalities, national road agencies, utility operators and bridge authorities.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€5–25</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">20–45 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Recurring</div><div class="layer-kpi-lbl">Same assets, annual cycles</div></div></div><div class="layer-badge-row"><span class="layer-badge">Roads & pavements</span><span class="layer-badge">Bridges</span><span class="layer-badge">Drainage</span><span class="layer-badge">Street lighting</span><span class="layer-badge">Utility cabinets</span><span class="layer-badge">Flood infrastructure</span></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Infrastructure missions are the backbone of the quiXzoom platform. They are recurring, predictable, and distributed across every populated area. Clients include municipalities, national road agencies, utility operators and bridge authorities.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$5–25</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">20–45 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Recurring</div><div class="layer-kpi-lbl">Same assets, annual cycles</div></div></div><div class="layer-badge-row"><span class="layer-badge">Roads & pavements</span><span class="layer-badge">Bridges</span><span class="layer-badge">Drainage</span><span class="layer-badge">Street lighting</span><span class="layer-badge">Utility cabinets</span><span class="layer-badge">Flood infrastructure</span></div>` },
|
||||
{ tab: '2 · What you document', html: `<p class="layer-lead">Infrastructure missions specify exactly which elements need to be captured. No guessing required.</p><p class="layer-h3">Example: Road surface mission</p><ul class="layer-ul"><li>Full-length view down the road segment (both directions)</li><li>Close-up of any visible surface damage (cracks, potholes, deformation)</li><li>Drainage gully condition (cover present, obstruction)</li><li>Kerb condition at both sides</li></ul><p class="layer-h3">Example: Bridge inspection mission</p><ul class="layer-ul"><li>Full span view from each abutment</li><li>Underside if accessible from public area</li><li>Bearing condition (visible from bank)</li><li>Any visible corrosion, cracking or vegetation growth</li></ul>` },
|
||||
{ tab: '3 · Equipment needed', html: `<p class="layer-lead">Standard smartphone. No specialist equipment for most infrastructure missions.</p><ul class="layer-ul"><li>12MP camera or better — standard on all recent phones</li><li>Location services enabled — required for all missions</li><li>Good lighting — cloudy daylight is ideal; avoid harsh direct sun or night</li><li>Steady hands or flat surface for close-up shots</li></ul><p class="layer-h3">Advanced infrastructure missions</p><p class="layer-p">Some higher-compensation missions specify minimum equipment (e.g. bridge inspection requiring access to a specific vantage point). These are clearly marked in the brief and are only available to rated Zoomers.</p>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Infrastructure type</th><th>Typical compensation</th><th>Why</th></tr><tr><td>Road surface (50m segment)</td><td>€4–7</td><td>Standard volume mission</td></tr><tr><td>Bridge (full exterior)</td><td>€12–20</td><td>More complex, higher orderer value</td></tr><tr><td>Utility cabinet</td><td>€3–5</td><td>Quick, high volume</td></tr><tr><td>Flood infrastructure</td><td>€8–15</td><td>Seasonal priority, higher demand</td></tr><tr><td>Premium bridge inspection</td><td>€25–50</td><td>Specialisation required, high orderer SLA</td></tr></table></div>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Infrastructure type</th><th>Typical compensation</th><th>Why</th></tr><tr><td>Road surface (50m segment)</td><td>$4–7</td><td>Standard volume mission</td></tr><tr><td>Bridge (full exterior)</td><td>$12–20</td><td>More complex, higher orderer value</td></tr><tr><td>Utility cabinet</td><td>$3–5</td><td>Quick, high volume</td></tr><tr><td>Flood infrastructure</td><td>$8–15</td><td>Seasonal priority, higher demand</td></tr><tr><td>Premium bridge inspection</td><td>$25–50</td><td>Specialisation required, high orderer SLA</td></tr></table></div>` },
|
||||
{ tab: '5 · Recurring missions', html: `<p class="layer-lead">Infrastructure missions repeat on annual or semi-annual cycles. This means reliable, predictable income for Zoomers who know their local assets.</p><ul class="layer-ul"><li>Same bridges, same roads, same utility cabinets — every year</li><li>Zoomers who documented an asset before have priority access to re-documentation missions (coming Q4 2026)</li><li>Build a specialist route: know every asset in your area, check them annually</li></ul>` },
|
||||
{ tab: '6 · AI review criteria', html: `<p class="layer-lead">Infrastructure AI review is based on specification compliance. Know the criteria before you shoot.</p><ul class="layer-ul"><li>All specified angles present and clearly legible</li><li>Asset occupies ≥40% of frame in required shots</li><li>No obstruction blocking the primary subject</li><li>Capture location within mission perimeter (verified by GPS)</li><li>Image sharpness above threshold (motion blur from handheld is the most common rejection reason)</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Infrastructure missions go live August 2026</p><p class="layer-cta-sub">Register now. Your neighbourhood has more infrastructure missions than you think.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1547,10 +1671,10 @@ const DATA = {
|
||||
realestate: {
|
||||
type: 'Mission category', title: 'Real estate',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Real estate documentation missions cover facades, roofs (from street level), entrances, parking structures and commercial exteriors. Clients include property management companies, insurance firms and lenders.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€6–18</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">15–30 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Urban</div><div class="layer-kpi-lbl">Highest density in cities</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Real estate documentation missions cover facades, roofs (from street level), entrances, parking structures and commercial exteriors. Clients include property management companies, insurance firms and lenders.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$6–18</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">15–30 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Urban</div><div class="layer-kpi-lbl">Highest density in cities</div></div></div>` },
|
||||
{ tab: '2 · What you document', html: `<p class="layer-lead">Real estate missions are exterior-only. You never need to enter a building.</p><ul class="layer-ul"><li>All street-facing facades (usually 1–3 elevations visible from public land)</li><li>Main entrance — door condition, signage, accessibility</li><li>Roof edge if visible from street</li><li>Parking area (if specified)</li><li>Any visible damage, staining, structural concerns</li></ul><p class="layer-h3">What you do NOT need to do</p><ul class="layer-ul"><li>Enter the building</li><li>Speak to residents or tenants</li><li>Access the roof</li><li>Document interiors</li></ul>` },
|
||||
{ tab: '3 · Urban clusters', html: `<p class="layer-lead">Real estate missions cluster in dense urban areas. A single city block can contain 5–10 missions. Plan routes to cover clusters for maximum earnings efficiency.</p><ul class="layer-ul"><li>Filter map by "Real estate" and zoom into your area</li><li>Identify clusters of 3+ adjacent missions</li><li>Claim all before walking — claim window is 2 hours</li><li>Document in sequence along the block</li></ul><p class="layer-h3">Typical cluster earnings</p><p class="layer-p">5 adjacent façade missions at €8 each = €40 for approximately 50 minutes of work in one block.</p>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Property type</th><th>Typical compensation</th><th>Complexity</th></tr><tr><td>Residential terrace</td><td>€5–8</td><td>Low — 1–2 elevations</td></tr><tr><td>Apartment block</td><td>€10–16</td><td>Medium — full exterior circuit</td></tr><tr><td>Commercial premises</td><td>€8–14</td><td>Medium — signage, accessibility</td></tr><tr><td>Car park structure</td><td>€12–20</td><td>Higher — multiple levels visible</td></tr><tr><td>Industrial/warehouse</td><td>€8–18</td><td>Variable — large exterior areas</td></tr></table></div>` },
|
||||
{ tab: '3 · Urban clusters', html: `<p class="layer-lead">Real estate missions cluster in dense urban areas. A single city block can contain 5–10 missions. Plan routes to cover clusters for maximum earnings efficiency.</p><ul class="layer-ul"><li>Filter map by "Real estate" and zoom into your area</li><li>Identify clusters of 3+ adjacent missions</li><li>Claim all before walking — claim window is 2 hours</li><li>Document in sequence along the block</li></ul><p class="layer-h3">Typical cluster earnings</p><p class="layer-p">5 adjacent façade missions at $8 each = $40 for approximately 50 minutes of work in one block.</p>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Property type</th><th>Typical compensation</th><th>Complexity</th></tr><tr><td>Residential terrace</td><td>$5–8</td><td>Low — 1–2 elevations</td></tr><tr><td>Apartment block</td><td>$10–16</td><td>Medium — full exterior circuit</td></tr><tr><td>Commercial premises</td><td>$8–14</td><td>Medium — signage, accessibility</td></tr><tr><td>Car park structure</td><td>$12–20</td><td>Higher — multiple levels visible</td></tr><tr><td>Industrial/warehouse</td><td>$8–18</td><td>Variable — large exterior areas</td></tr></table></div>` },
|
||||
{ tab: '5 · Tips for high approval', html: `<ul class="layer-ul"><li>Shoot in flat, overcast daylight — avoids harsh shadows on facades</li><li>Stand far enough back to fit the full building in frame — the most common rejection reason is partial coverage</li><li>Shoot all elevations — even ones that look fine. The brief requires them</li><li>Include street context in at least one shot — confirms location</li><li>If access is blocked (parked vehicles, scaffolding) — note it in submission notes before submitting</li></ul>` },
|
||||
{ tab: '6 · Insurance surge missions', html: `<p class="layer-lead">After weather events (storms, hail, flooding), insurance companies activate surge documentation missions for residential and commercial properties in the affected area. These pay 2–4× standard rates.</p><ul class="layer-ul"><li>Push notification sent immediately when surge missions activate in your area</li><li>Fastest submissions earn the highest compensation</li><li>No booking required — first valid submission accepted</li><li>Surge missions are time-sensitive — typically expire within 24–48 hours</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Real estate missions in every city</p><p class="layer-cta-sub">Register now. Your neighbourhood's property portfolio is already a waiting mission list.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1559,10 +1683,10 @@ const DATA = {
|
||||
coastline: {
|
||||
type: 'Mission category', title: 'Coastlines & nature',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Coastal and environmental missions document shorelines, beaches, waterways, and natural habitats. Clients include environmental agencies, maritime authorities, conservation organisations and research institutions.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€8–30</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">30–60 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Seasonal</div><div class="layer-kpi-lbl">Higher volume spring/summer</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Coastal and environmental missions document shorelines, beaches, waterways, and natural habitats. Clients include environmental agencies, maritime authorities, conservation organisations and research institutions.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$8–30</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">30–60 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Seasonal</div><div class="layer-kpi-lbl">Higher volume spring/summer</div></div></div>` },
|
||||
{ tab: '2 · What you document', html: `<p class="layer-lead">Coastal missions focus on change detection and condition monitoring over time.</p><ul class="layer-ul"><li>Shoreline condition — erosion, accretion, new features</li><li>Beach profile — width, material, debris</li><li>Coastal structures — sea walls, groynes, breakwaters</li><li>Vegetation line — position and density</li><li>Water clarity (where specified)</li><li>Invasive species markers (flagged in specific missions)</li></ul>` },
|
||||
{ tab: '3 · Who commissions these', html: `<div class="layer-badge-row"><span class="layer-badge">Environmental agencies</span><span class="layer-badge">Maritime authorities</span><span class="layer-badge">Conservation NGOs</span><span class="layer-badge">Research institutions</span><span class="layer-badge">Local councils</span><span class="layer-badge">Insurance (flood risk)</span></div><p class="layer-h3">Use cases</p><ul class="layer-ul"><li>Annual coastal monitoring programmes (same transects, year after year)</li><li>Post-storm damage assessment</li><li>Invasive species early detection surveys</li><li>Flood risk verification for insurance underwriting</li><li>Academic research support</li></ul>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Mission type</th><th>Compensation</th></tr><tr><td>Coastal transect (100m)</td><td>€10–18</td></tr><tr><td>Beach condition survey</td><td>€12–20</td></tr><tr><td>River/waterway segment</td><td>€8–15</td></tr><tr><td>Post-storm coastal assessment</td><td>€20–40</td></tr><tr><td>Habitat survey (specialist)</td><td>€25–50</td></tr></table></div>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Mission type</th><th>Compensation</th></tr><tr><td>Coastal transect (100m)</td><td>$10–18</td></tr><tr><td>Beach condition survey</td><td>$12–20</td></tr><tr><td>River/waterway segment</td><td>$8–15</td></tr><tr><td>Post-storm coastal assessment</td><td>$20–40</td></tr><tr><td>Habitat survey (specialist)</td><td>$25–50</td></tr></table></div>` },
|
||||
{ tab: '5 · Seasonal patterns', html: `<p class="layer-lead">Coastal missions peak in spring (post-winter assessment) and summer (active monitoring season). Plan your Zoomer activity around these peaks for maximum earnings.</p><ul class="layer-ul"><li>March–April: post-winter storm assessment missions — high compensation, surge pricing</li><li>May–August: regular monitoring cycles — consistent volume</li><li>October–November: pre-winter baseline documentation — another surge period</li><li>December–February: lower volume, but ice and frost condition missions available in northern regions</li></ul>` },
|
||||
{ tab: '6 · Safety', html: `<p class="layer-lead">Coastal environments have specific safety considerations. quiXzoom missions require documentation from safe, accessible public areas only.</p><ul class="layer-ul"><li>Never enter the water to complete a mission</li><li>Never approach unstable cliff edges or eroded banks</li><li>All missions specify safe capture positions — stay within these</li><li>Weather check required before coastal missions — wind speed and sea state indicated in brief</li><li>If conditions are unsafe: release the mission without penalty</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">The coastline nearest you is already a mission</p><p class="layer-cta-sub">Register interest. Coastal missions live from August 2026, starting in Sweden.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1571,10 +1695,10 @@ const DATA = {
|
||||
incident: {
|
||||
type: 'Mission category', title: 'Incident & damage',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Incident missions are activated immediately after weather events, fires, floods or other damage-causing events. They are the highest-paying mission type and are time-critical — the fastest Zoomers earn the most.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€15–60</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">Speed</div><div class="layer-kpi-lbl">Fastest submission earns most</div></div><div class="layer-kpi"><div class="layer-kpi-val">Push alert</div><div class="layer-kpi-lbl">Instant notification to local Zoomers</div></div></div>` },
|
||||
{ tab: '2 · How surge works', html: `<p class="layer-lead">When an insurance company detects a weather event or receives a cluster of claims in an area, they trigger a documentation surge campaign. Zoomers in the area receive a push notification immediately.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Event detected</p><p class="layer-step-desc">Storm, flood or fire event triggers campaign creation by insurance client. Happens within minutes of event.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Push notification to local Zoomers</p><p class="layer-step-desc">All Zoomers within the affected area receive alert: "Surge mission active near you — €[amount] per approved submission."</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">First submission wins maximum</p><p class="layer-step-desc">Each address-level mission: first approved submission earns the maximum rate. Speed is directly rewarded.</p></div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Incident missions are activated immediately after weather events, fires, floods or other damage-causing events. They are the highest-paying mission type and are time-critical — the fastest Zoomers earn the most.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$15–60</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">Speed</div><div class="layer-kpi-lbl">Fastest submission earns most</div></div><div class="layer-kpi"><div class="layer-kpi-val">Push alert</div><div class="layer-kpi-lbl">Instant notification to local Zoomers</div></div></div>` },
|
||||
{ tab: '2 · How surge works', html: `<p class="layer-lead">When an insurance company detects a weather event or receives a cluster of claims in an area, they trigger a documentation surge campaign. Zoomers in the area receive a push notification immediately.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Event detected</p><p class="layer-step-desc">Storm, flood or fire event triggers campaign creation by insurance client. Happens within minutes of event.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Push notification to local Zoomers</p><p class="layer-step-desc">All Zoomers within the affected area receive alert: "Surge mission active near you — $[amount] per approved submission."</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">First submission wins maximum</p><p class="layer-step-desc">Each address-level mission: first approved submission earns the maximum rate. Speed is directly rewarded.</p></div></div></div>` },
|
||||
{ tab: '3 · What you document', html: `<p class="layer-lead">Incident missions focus on visible external damage from public areas. No entry to buildings.</p><ul class="layer-ul"><li>Overall building condition from street</li><li>Visible damage: roof, facade, windows, structural elements</li><li>Water ingress evidence: staining, debris lines</li><li>Access road condition</li><li>Adjacent properties if specified</li></ul><p class="layer-h3">What the AI checks specifically</p><ul class="layer-ul"><li>Damage visible in frame</li><li>Capture timestamp within 48h of event</li><li>Location confirmed at the specified address</li><li>Full required angle coverage</li></ul>` },
|
||||
{ tab: '4 · Compensation model', html: `<p class="layer-lead">Incident missions use a decay compensation model. First submission earns maximum; subsequent submissions earn less.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Submission order</th><th>Compensation (example €30 mission)</th></tr><tr><td>1st approved</td><td>€30 (full rate)</td></tr><tr><td>2nd approved</td><td>€18 (60%)</td></tr><tr><td>3rd approved</td><td>€9 (30%)</td></tr><tr><td>4th+</td><td>€4.50 (15%)</td></tr></table></div><p class="layer-p">The decay model incentivises speed. For insurance purposes, the first documentation before damage is cleared is most valuable.</p>` },
|
||||
{ tab: '4 · Compensation model', html: `<p class="layer-lead">Incident missions use a decay compensation model. First submission earns maximum; subsequent submissions earn less.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Submission order</th><th>Compensation (example $30 mission)</th></tr><tr><td>1st approved</td><td>$30 (full rate)</td></tr><tr><td>2nd approved</td><td>$18 (60%)</td></tr><tr><td>3rd approved</td><td>$9 (30%)</td></tr><tr><td>4th+</td><td>$4.50 (15%)</td></tr></table></div><p class="layer-p">The decay model incentivises speed. For insurance purposes, the first documentation before damage is cleared is most valuable.</p>` },
|
||||
{ tab: '5 · Safety rules', html: `<p class="layer-lead">After serious events (floods, fires, structural damage), safety is paramount. quiXzoom incident missions have mandatory safety rules.</p><ul class="layer-ul"><li>Never enter a flood-affected or fire-damaged building</li><li>Maintain safe distance from structurally compromised buildings</li><li>Do not enter areas cordoned by emergency services</li><li>Incident missions activate only in areas where general public access has been confirmed safe</li><li>If unsafe conditions: use "report unsafe" in the app — no penalty, mission is reassigned</li></ul>` },
|
||||
{ tab: '6 · Enabling push alerts', html: `<p class="layer-lead">To receive surge notifications instantly when events happen near you, push alerts must be enabled.</p><ul class="layer-ul"><li>Enable push notifications for quiXzoom in your phone settings</li><li>Set your notification zone radius — larger radius = more surge events</li><li>Keep app installed and location services always-on</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Highest pay. Near you. Right now.</p><p class="layer-cta-sub">Register to get push alerts when surge missions activate in your area from August 2026.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1583,10 +1707,10 @@ const DATA = {
|
||||
verification: {
|
||||
type: 'Mission category', title: 'Verification',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Verification missions confirm that a specific address, premises or entity exists and matches available records. Clients include e-commerce platforms, banks, recruitment agencies and public sector bodies.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€3–8</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">~10 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Highest volume</div><div class="layer-kpi-lbl">Most missions available</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Verification missions confirm that a specific address, premises or entity exists and matches available records. Clients include e-commerce platforms, banks, recruitment agencies and public sector bodies.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$3–8</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">~10 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">Highest volume</div><div class="layer-kpi-lbl">Most missions available</div></div></div>` },
|
||||
{ tab: '2 · What you verify', html: `<ul class="layer-ul"><li>Premises exists at the stated address</li><li>Correct trading name/signage visible</li><li>Business appears to be operating (lights on, activity visible)</li><li>Access point and entry condition</li><li>Any specified reference elements (brand colours, specific signage)</li></ul><p class="layer-h3">What you do NOT do</p><ul class="layer-ul"><li>Enter premises</li><li>Speak to anyone inside</li><li>Verify any financial or identity information</li></ul>` },
|
||||
{ tab: '3 · Route integration', html: `<p class="layer-lead">Verification missions are ideal for route integration — especially for delivery workers already stopping at addresses.</p><p class="layer-p">If you're making a delivery to a street, there may be 2–5 verification missions for neighbouring premises on the same block. Check before leaving — claim them while you're there.</p>` },
|
||||
{ tab: '4 · Compensation', html: `<p class="layer-lead">Lower per-mission but highest volume. Verification missions are optimised for quantity.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Verification type</th><th>Compensation</th></tr><tr><td>Address confirmation</td><td>€3–4</td></tr><tr><td>Business premises check</td><td>€4–6</td></tr><tr><td>Storefront documentation</td><td>€5–8</td></tr><tr><td>Multi-element verification</td><td>€6–10</td></tr></table></div>` },
|
||||
{ tab: '4 · Compensation', html: `<p class="layer-lead">Lower per-mission but highest volume. Verification missions are optimised for quantity.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Verification type</th><th>Compensation</th></tr><tr><td>Address confirmation</td><td>$3–4</td></tr><tr><td>Business premises check</td><td>$4–6</td></tr><tr><td>Storefront documentation</td><td>$5–8</td></tr><tr><td>Multi-element verification</td><td>$6–10</td></tr></table></div>` },
|
||||
{ tab: '5 · Privacy rules', html: `<p class="layer-lead">Verification missions have strict privacy rules. You document premises, not people.</p><ul class="layer-ul"><li>Never photograph identifiable individuals — if people are in shot, do not submit (blur or retake)</li><li>Never document interior spaces through windows</li><li>Only photograph from public land</li><li>Do not attempt to verify the identity of anyone at the premises</li></ul>` },
|
||||
{ tab: '6 · AI review', html: `<p class="layer-lead">Verification AI review is the fastest on the platform — typically under 15 seconds.</p><ul class="layer-ul"><li>Address number or name visible in frame</li><li>Premises legible and identifiable</li><li>GPS confirms capture at the specified address</li><li>No people identifiable in frame</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">The most missions. The fastest approval.</p><p class="layer-cta-sub">Verification missions launch in Sweden August 1, 2026. More markets follow.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1595,10 +1719,10 @@ const DATA = {
|
||||
urban: {
|
||||
type: 'Mission category', title: 'Urban & streets',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Urban missions document street-level conditions: pavement quality, signage, accessibility, retail presence, and general urban environment. Clients include city planners, retail chains and transport authorities.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€5–15</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">15–40 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">City-dense</div><div class="layer-kpi-lbl">Highest density in urban centres</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Urban missions document street-level conditions: pavement quality, signage, accessibility, retail presence, and general urban environment. Clients include city planners, retail chains and transport authorities.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$5–15</div><div class="layer-kpi-lbl">Typical compensation</div></div><div class="layer-kpi"><div class="layer-kpi-val">15–40 min</div><div class="layer-kpi-lbl">Average mission time</div></div><div class="layer-kpi"><div class="layer-kpi-val">City-dense</div><div class="layer-kpi-lbl">Highest density in urban centres</div></div></div>` },
|
||||
{ tab: '2 · What you document', html: `<ul class="layer-ul"><li>Pavement condition — surface quality, trip hazards, accessibility</li><li>Street signage — road signs, wayfinding, condition</li><li>Retail frontage — occupancy status, condition, branding</li><li>Accessibility features — dropped kerbs, tactile paving, ramps</li><li>Street furniture — benches, bins, cycle stands</li><li>Lighting — streetlight present and functional</li></ul>` },
|
||||
{ tab: '3 · Walk-based workflow', html: `<p class="layer-lead">Urban missions are designed for walkers. A single high street can contain 10–20 missions completed in one pass.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Plan a walk route through mission clusters</p><p class="layer-step-desc">Filter map by Urban. Find a street with 5+ adjacent missions.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Claim as you walk</p><p class="layer-step-desc">Claim each mission 2–3 minutes before you arrive. Submit as you pass.</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">One street, €40–80</p><p class="layer-step-desc">A 1km high street walk with 10 missions at €5–8 each = €50–80 in under 1 hour.</p></div></div></div>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Urban mission type</th><th>Compensation</th></tr><tr><td>Pavement condition survey</td><td>€4–7</td></tr><tr><td>Retail frontage check</td><td>€5–8</td></tr><tr><td>Accessibility audit (20m segment)</td><td>€8–12</td></tr><tr><td>High street condition survey</td><td>€12–20</td></tr></table></div>` },
|
||||
{ tab: '3 · Walk-based workflow', html: `<p class="layer-lead">Urban missions are designed for walkers. A single high street can contain 10–20 missions completed in one pass.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Plan a walk route through mission clusters</p><p class="layer-step-desc">Filter map by Urban. Find a street with 5+ adjacent missions.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Claim as you walk</p><p class="layer-step-desc">Claim each mission 2–3 minutes before you arrive. Submit as you pass.</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">One street, $40–80</p><p class="layer-step-desc">A 1km high street walk with 10 missions at $5–8 each = $50–80 in under 1 hour.</p></div></div></div>` },
|
||||
{ tab: '4 · Compensation', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Urban mission type</th><th>Compensation</th></tr><tr><td>Pavement condition survey</td><td>$4–7</td></tr><tr><td>Retail frontage check</td><td>$5–8</td></tr><tr><td>Accessibility audit (20m segment)</td><td>$8–12</td></tr><tr><td>High street condition survey</td><td>$12–20</td></tr></table></div>` },
|
||||
{ tab: '5 · Privacy in urban environments', html: `<p class="layer-lead">Urban environments contain people. quiXzoom's AI review includes pedestrian detection — missions with identifiable individuals in primary focus are flagged.</p><ul class="layer-ul"><li>Capture when pedestrian traffic is low where possible</li><li>Use wide shots that keep people as background elements, not subjects</li><li>If someone is prominently in frame: wait 30 seconds and retake</li><li>You never need to photograph people — the mission is always about the built environment</li></ul>` },
|
||||
{ tab: '6 · City planning missions', html: `<p class="layer-lead">Some urban missions are part of larger city planning studies commissioned by municipalities. These are higher-value and require a structured survey approach.</p><ul class="layer-ul"><li>Accessibility audit missions: document every accessibility feature along a defined route</li><li>Retail vacancy surveys: document occupancy status of every unit on a shopping street</li><li>Before/after infrastructure works: document condition before and after roadworks</li></ul><p class="layer-p">City planning missions are rated-only — available from Active tier upward.</p>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Every street you walk is potential earnings</p><p class="layer-cta-sub">Register interest. Urban missions launch August 2026 in Sweden, with more cities and markets to follow.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1610,11 +1734,11 @@ const DATA = {
|
||||
supplementary: {
|
||||
type: 'Earnings tier', title: 'Supplementary — 2–4 missions/week',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">The Supplementary tier is the entry point. No minimum commitment, no schedule, no performance requirements beyond the standard approval rate.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€20–80</div><div class="layer-kpi-lbl">Typical monthly earnings</div></div><div class="layer-kpi"><div class="layer-kpi-val">2–4</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">~2h</div><div class="layer-kpi-lbl">Time per week</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">The Supplementary tier is the entry point. No minimum commitment, no schedule, no performance requirements beyond the standard approval rate.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$20–80</div><div class="layer-kpi-lbl">Typical monthly earnings</div></div><div class="layer-kpi"><div class="layer-kpi-val">2–4</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">~2h</div><div class="layer-kpi-lbl">Time per week</div></div></div>` },
|
||||
{ tab: '2 · What you can access', html: `<ul class="layer-ul"><li>All standard mission categories (infrastructure, real estate, verification, urban, coastline)</li><li>Incident/surge missions when activated in your area</li><li>Standard compensation rates for all mission types</li></ul><p class="layer-h3">Not yet available (unlocks at Active tier)</p><ul class="layer-ul"><li>Premium missions (higher compensation, stricter specification)</li><li>Priority claim window on high-demand missions</li><li>Specialisation modules</li></ul>` },
|
||||
{ tab: '3 · Path to Active', html: `<p class="layer-lead">Moving from Supplementary to Active tier unlocks significantly higher earnings potential.</p><p class="layer-h3">Requirements for Active tier</p><ul class="layer-ul"><li>50 approved missions (cumulative)</li><li>Approval rate above 85%</li><li>No active claim violations</li></ul><p class="layer-h3">How long does it take?</p><p class="layer-p">At 3 missions per week: approximately 4 months. At 5 missions per week: approximately 10 weeks.</p>` },
|
||||
{ tab: '4 · Realistic earnings', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Missions/week</th><th>Avg. per mission</th><th>Monthly estimate</th></tr><tr><td>2</td><td>€7</td><td>~€56</td></tr><tr><td>3</td><td>€7</td><td>~€84</td></tr><tr><td>4</td><td>€8</td><td>~€128</td></tr></table></div><p class="layer-p">Including surge missions (1–2/month): add €20–60 to monthly estimates.</p>` },
|
||||
{ tab: '5 · Tax threshold guidance', html: `<p class="layer-lead">At Supplementary level, many Zoomers will earn below their country's minimum threshold for mandatory tax registration.</p><ul class="layer-ul"><li>Sweden: Grundavdrag ~17,500 SEK/year (~€1,500/month). Supplementary earnings typically well below this.</li><li>Germany: Kleinunternehmer threshold €22,000/year. Not reached at this tier.</li><li>UK: Personal allowance £12,570/year. Supplementary Zoomer earnings far below this.</li></ul><p class="layer-p"><em>General information only — consult a local tax advisor for your situation.</em></p>` },
|
||||
{ tab: '4 · Realistic earnings', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Missions/week</th><th>Avg. per mission</th><th>Monthly estimate</th></tr><tr><td>2</td><td>$7</td><td>~$56</td></tr><tr><td>3</td><td>$7</td><td>~$84</td></tr><tr><td>4</td><td>$8</td><td>~$128</td></tr></table></div><p class="layer-p">Including surge missions (1–2/month): add $20–60 to monthly estimates.</p>` },
|
||||
{ tab: '5 · Tax threshold guidance', html: `<p class="layer-lead">At Supplementary level, many Zoomers will earn below their country's minimum threshold for mandatory tax registration.</p><ul class="layer-ul"><li>Sweden: Grundavdrag ~17,500 SEK/year (~$1,500/month). Supplementary earnings typically well below this.</li><li>Germany: Kleinunternehmer threshold $22,000/year. Not reached at this tier.</li><li>UK: Personal allowance £12,570/year. Supplementary Zoomer earnings far below this.</li></ul><p class="layer-p"><em>General information only — consult a local tax advisor for your situation.</em></p>` },
|
||||
{ tab: '6 · Getting the most from this tier', html: `<ul class="layer-ul"><li>Focus on approval rate over volume — every rejection at this tier costs relatively more</li><li>Stick to mission types you understand well until your rate is consistently above 85%</li><li>Enable push notifications — surge missions add disproportionate value at this tier</li><li>Plan: 50 approved missions is your target — set it as a goal and track progress</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Start here. No commitment.</p><p class="layer-cta-sub">Register interest. The Supplementary tier is open to everyone from day one — no qualification required.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
]
|
||||
@@ -1622,10 +1746,10 @@ const DATA = {
|
||||
active: {
|
||||
type: 'Earnings tier', title: 'Active — 8–12 missions/week',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Active is the most popular Zoomer tier. It represents a meaningful secondary income stream — €200–400/month — without requiring a full-time commitment.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€200–400</div><div class="layer-kpi-lbl">Typical monthly earnings</div></div><div class="layer-kpi"><div class="layer-kpi-val">8–12</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">5–8h</div><div class="layer-kpi-lbl">Time per week</div></div></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Active is the most popular Zoomer tier. It represents a meaningful secondary income stream — $200–400/month — without requiring a full-time commitment.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$200–400</div><div class="layer-kpi-lbl">Typical monthly earnings</div></div><div class="layer-kpi"><div class="layer-kpi-val">8–12</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">5–8h</div><div class="layer-kpi-lbl">Time per week</div></div></div>` },
|
||||
{ tab: '2 · Unlocked at Active', html: `<ul class="layer-ul"><li>Priority claim window: 5-minute advance access to new high-demand missions before general availability</li><li>Premium missions: higher compensation, stricter specification, more interesting subjects</li><li>Specialisation module access: complete training to unlock bridge inspection, coastal survey, and other high-value categories</li><li>Performance-based compensation boost: approval rate above 92% earns a 5% rate uplift across all missions</li></ul>` },
|
||||
{ tab: '3 · Weekly rhythm', html: `<p class="layer-lead">Active Zoomers typically build a consistent weekly rhythm around mission clusters in their area.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">Mon–Fri</div><div class="layer-step-body"><p class="layer-step-title">2 missions per day (mornings or lunch)</p><p class="layer-step-desc">10 missions at €8 average = €80/week baseline.</p></div></div><div class="layer-step"><div class="layer-step-num">Weekend</div><div class="layer-step-body"><p class="layer-step-title">Surge monitoring + premium missions</p><p class="layer-step-desc">Higher-value targets. Weather events often trigger surge during weekends.</p></div></div></div>` },
|
||||
{ tab: '4 · Realistic earnings', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Scenario</th><th>Missions/week</th><th>Avg. rate</th><th>Monthly</th></tr><tr><td>Conservative Active</td><td>8</td><td>€8</td><td>~€256</td></tr><tr><td>Standard Active</td><td>10</td><td>€9</td><td>~€360</td></tr><tr><td>Active + surge (2/month)</td><td>10 + surge</td><td>€9 + €30</td><td>~€420</td></tr><tr><td>Active + premium</td><td>10 standard + 2 premium</td><td>€9/€20</td><td>~€440</td></tr></table></div>` },
|
||||
{ tab: '3 · Weekly rhythm', html: `<p class="layer-lead">Active Zoomers typically build a consistent weekly rhythm around mission clusters in their area.</p><div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">Mon–Fri</div><div class="layer-step-body"><p class="layer-step-title">2 missions per day (mornings or lunch)</p><p class="layer-step-desc">10 missions at $8 average = $80/week baseline.</p></div></div><div class="layer-step"><div class="layer-step-num">Weekend</div><div class="layer-step-body"><p class="layer-step-title">Surge monitoring + premium missions</p><p class="layer-step-desc">Higher-value targets. Weather events often trigger surge during weekends.</p></div></div></div>` },
|
||||
{ tab: '4 · Realistic earnings', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Scenario</th><th>Missions/week</th><th>Avg. rate</th><th>Monthly</th></tr><tr><td>Conservative Active</td><td>8</td><td>$8</td><td>~$256</td></tr><tr><td>Standard Active</td><td>10</td><td>$9</td><td>~$360</td></tr><tr><td>Active + surge (2/month)</td><td>10 + surge</td><td>$9 + $30</td><td>~$420</td></tr><tr><td>Active + premium</td><td>10 standard + 2 premium</td><td>$9/$20</td><td>~$440</td></tr></table></div>` },
|
||||
{ tab: '5 · How to maintain Active tier', html: `<p class="layer-lead">Active tier is maintained by consistent activity and quality. Falling below the threshold triggers a grace period.</p><ul class="layer-ul"><li>Minimum: 6 approved missions per month to maintain Active status</li><li>Approval rate must stay above 80% (rolling 30-day)</li><li>Inactivity for 60 days: tier paused (reactivates on next approved mission)</li><li>Grace period if dropping below: 14 days to complete 5 approved missions to retain tier</li></ul>` },
|
||||
{ tab: '6 · Specialisations', html: `<p class="layer-lead">Specialisations unlock premium mission categories with higher compensation rates. Available at Active tier.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Specialisation</th><th>Missions unlocked</th><th>Extra compensation</th></tr><tr><td>Bridge & structures</td><td>Bridge inspection premium missions</td><td>+40–80% vs standard</td></tr><tr><td>Coastal survey</td><td>Environmental monitoring missions</td><td>+30–60% vs standard</td></tr><tr><td>Accessibility audit</td><td>Municipal accessibility surveys</td><td>+25–45% vs standard</td></tr><tr><td>Insurance documentation</td><td>Priority surge access</td><td>First-claim advantage</td></tr></table></div>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Active tier: the target for serious Zoomers</p><p class="layer-cta-sub">Register now. Start Supplementary in August 2026. Reach Active within 10–16 weeks.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1634,11 +1758,11 @@ const DATA = {
|
||||
professional: {
|
||||
type: 'Earnings tier', title: 'Professional — 15+ missions/week',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Professional Zoomers treat quiXzoom as a primary or near-primary income source. Reaching this tier requires consistent quality and volume over several months — and delivers proportional earnings.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">€600–1,500+</div><div class="layer-kpi-lbl">Typical monthly earnings</div></div><div class="layer-kpi"><div class="layer-kpi-val">15–25</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">~20h</div><div class="layer-kpi-lbl">Time per week</div></div></div>` },
|
||||
{ tab: '2 · Unlocked at Professional', html: `<ul class="layer-ul"><li>First access to all new premium mission categories as they launch</li><li>Dedicated account manager (for Zoomers earning above €500/month)</li><li>10-minute priority claim window before Active tier Zoomers</li><li>Performance compensation: approval rate above 95% earns 10% uplift across all missions</li><li>Reserved high-value campaign slots: selected Zoomers invited to commissioned campaigns at fixed daily rates</li></ul>` },
|
||||
{ tab: '3 · Campaign work', html: `<p class="layer-lead">At Professional tier, quiXzoom can invite you to dedicated campaign work — typically a fixed-rate day rate for systematic documentation of a specific asset set.</p><p class="layer-h3">Example campaign</p><div class="layer-block"><p class="layer-block-title">Municipal bridge survey — Stockholm</p><ul class="layer-ul"><li>Duration: 5 days</li><li>Scope: 120 bridges in the city</li><li>Rate: €200/day (fixed, not per submission)</li><li>Total: €1,000 for the campaign</li></ul></div><p class="layer-p">Campaign invitations are issued to Professional Zoomers with relevant specialisations in the geographic area. Not guaranteed — depends on campaign availability and matching.</p>` },
|
||||
{ tab: '4 · Tax at Professional level', html: `<p class="layer-lead">At Professional earnings levels, formal tax registration is typically required in all jurisdictions.</p><ul class="layer-ul"><li>Sweden: Register as enskild firma or aktiebolag. F-skatt certification recommended.</li><li>Germany: Gewerbe registration required. Umsatzsteuer (VAT) may apply above €22,000.</li><li>UK: Self-assessment required. National Insurance Class 4 on profits above £12,570.</li><li>EU generally: VAT registration typically required above national thresholds.</li></ul><p class="layer-p"><em>Consult a qualified tax advisor. quiXzoom provides annual earnings statements in all required formats.</em></p>` },
|
||||
{ tab: '5 · Equipment at this level', html: `<p class="layer-lead">Professional Zoomers working on premium infrastructure and environmental missions often invest in modest equipment upgrades.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Equipment</th><th>Benefit</th><th>Cost (approx.)</th></tr><tr><td>Stabiliser/gimbal</td><td>Sharper images, higher approval rate</td><td>€60–120</td></tr><tr><td>Wide-angle lens attachment</td><td>Better façade coverage in tight spaces</td><td>€30–80</td></tr><tr><td>Portable power bank</td><td>Full-day operation without recharge</td><td>€20–40</td></tr><tr><td>Weatherproof phone case</td><td>Coastal and incident missions in all weather</td><td>€20–50</td></tr></table></div>` },
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">Professional Zoomers treat quiXzoom as a primary or near-primary income source. Reaching this tier requires consistent quality and volume over several months — and delivers proportional earnings.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">$600–1,500+</div><div class="layer-kpi-lbl">Typical monthly earnings</div></div><div class="layer-kpi"><div class="layer-kpi-val">15–25</div><div class="layer-kpi-lbl">Missions per week</div></div><div class="layer-kpi"><div class="layer-kpi-val">~20h</div><div class="layer-kpi-lbl">Time per week</div></div></div>` },
|
||||
{ tab: '2 · Unlocked at Professional', html: `<ul class="layer-ul"><li>First access to all new premium mission categories as they launch</li><li>Dedicated account manager (for Zoomers earning above $500/month)</li><li>10-minute priority claim window before Active tier Zoomers</li><li>Performance compensation: approval rate above 95% earns 10% uplift across all missions</li><li>Reserved high-value campaign slots: selected Zoomers invited to commissioned campaigns at fixed daily rates</li></ul>` },
|
||||
{ tab: '3 · Campaign work', html: `<p class="layer-lead">At Professional tier, quiXzoom can invite you to dedicated campaign work — typically a fixed-rate day rate for systematic documentation of a specific asset set.</p><p class="layer-h3">Example campaign</p><div class="layer-block"><p class="layer-block-title">Municipal bridge survey — Stockholm</p><ul class="layer-ul"><li>Duration: 5 days</li><li>Scope: 120 bridges in the city</li><li>Rate: $200/day (fixed, not per submission)</li><li>Total: $1,000 for the campaign</li></ul></div><p class="layer-p">Campaign invitations are issued to Professional Zoomers with relevant specialisations in the geographic area. Not guaranteed — depends on campaign availability and matching.</p>` },
|
||||
{ tab: '4 · Tax at Professional level', html: `<p class="layer-lead">At Professional earnings levels, formal tax registration is typically required in all jurisdictions.</p><ul class="layer-ul"><li>Sweden: Register as enskild firma or aktiebolag. F-skatt certification recommended.</li><li>Germany: Gewerbe registration required. Umsatzsteuer (VAT) may apply above $22,000.</li><li>UK: Self-assessment required. National Insurance Class 4 on profits above £12,570.</li><li>EU generally: VAT registration typically required above national thresholds.</li></ul><p class="layer-p"><em>Consult a qualified tax advisor. quiXzoom provides annual earnings statements in all required formats.</em></p>` },
|
||||
{ tab: '5 · Equipment at this level', html: `<p class="layer-lead">Professional Zoomers working on premium infrastructure and environmental missions often invest in modest equipment upgrades.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Equipment</th><th>Benefit</th><th>Cost (approx.)</th></tr><tr><td>Stabiliser/gimbal</td><td>Sharper images, higher approval rate</td><td>$60–120</td></tr><tr><td>Wide-angle lens attachment</td><td>Better façade coverage in tight spaces</td><td>$30–80</td></tr><tr><td>Portable power bank</td><td>Full-day operation without recharge</td><td>$20–40</td></tr><tr><td>Weatherproof phone case</td><td>Coastal and incident missions in all weather</td><td>$20–50</td></tr></table></div>` },
|
||||
{ tab: '6 · Building to Professional', html: `<p class="layer-lead">Professional tier requires: 200+ approved missions with 90%+ approval rate. Timeline depends on weekly mission volume.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Weekly missions</th><th>Estimated time to Professional</th></tr><tr><td>8–10 (Active pace)</td><td>6–8 months</td></tr><tr><td>12–15</td><td>4–5 months</td></tr><tr><td>15+</td><td>3–4 months</td></tr></table></div>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Professional tier starts with one mission</p><p class="layer-cta-sub">Register now. August 2026 launch. Every mission from day one counts toward Professional.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
]
|
||||
@@ -1651,10 +1775,10 @@ const DATA = {
|
||||
layers: [
|
||||
{ tab: '1 · Minimum spec', html: `<p class="layer-lead">The only requirement is a smartphone with a 12MP or better rear camera. No specialist equipment needed for standard missions.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Device</th><th>Minimum model</th><th>Passes standard missions</th></tr><tr><td>iPhone</td><td>iPhone 12</td><td><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg> Yes</td></tr><tr><td>Samsung Galaxy</td><td>S21 / A53 and newer</td><td><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg> Yes</td></tr><tr><td>Google Pixel</td><td>Pixel 5 and newer</td><td><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg> Yes</td></tr><tr><td>Other Android</td><td>12MP rear camera, Android 10+</td><td><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg> Yes</td></tr></table></div>` },
|
||||
{ tab: '2 · Settings', html: `<ul class="layer-ul"><li>Resolution: maximum native resolution (no digital zoom)</li><li>Location: always on while using the app</li><li>HDR: off (produces inconsistent results for AI review)</li><li>Portrait mode: off (can add artificial blur)</li><li>Video: not accepted for missions (photos only, unless specified)</li></ul>` },
|
||||
{ tab: '3 · Optional equipment', html: `<p class="layer-lead">Optional — none required for standard missions. Useful for higher-volume or premium missions.</p><ul class="layer-ul"><li>Gimbal stabiliser: reduces rejection from motion blur. ~€60–120. Useful for Active tier upward.</li><li>Wide-angle clip-on lens: better building coverage in tight streets. ~€30–80.</li><li>Power bank: for full-day shifts. 10,000mAh, ~€20.</li><li>Weatherproof case: coastal and incident missions. ~€20–50.</li></ul>` },
|
||||
{ tab: '3 · Optional equipment', html: `<p class="layer-lead">Optional — none required for standard missions. Useful for higher-volume or premium missions.</p><ul class="layer-ul"><li>Gimbal stabiliser: reduces rejection from motion blur. ~$60–120. Useful for Active tier upward.</li><li>Wide-angle clip-on lens: better building coverage in tight streets. ~$30–80.</li><li>Power bank: for full-day shifts. 10,000mAh, ~$20.</li><li>Weatherproof case: coastal and incident missions. ~$20–50.</li></ul>` },
|
||||
{ tab: '4 · Common photo errors', html: `<p class="layer-h3">Most common AI rejection reasons</p><ul class="layer-ul"><li>Motion blur — move slower, hold breath before shot, or use a surface to stabilise</li><li>Subject too small — step back until asset fills at least 40% of frame</li><li>Obstruction — parked vehicle, scaffolding, tree in front of subject</li><li>Overexposure in direct sunlight — shoot in shade or turn back to the sun</li><li>Wrong angle — re-read the brief and match example images exactly</li></ul>` },
|
||||
{ tab: '5 · Drones', html: `<p class="layer-lead">Drone support is planned for Q4 2026 for specific mission types (coastal, large-scale infrastructure).</p><ul class="layer-ul"><li>Drone missions will require verified drone licence (A1/A3 certification in EU)</li><li>Higher compensation: drone missions pay 3–8× standard ground-level missions</li><li>Register drone capability in your profile to be notified when drone missions launch</li></ul>` },
|
||||
{ tab: '6 · Professional kit', html: `<p class="layer-lead">Full equipment guide for Professional tier Zoomers operating in all mission categories.</p><ul class="layer-ul"><li>Primary phone: iPhone 15 Pro or Samsung S24 Ultra — best camera systems available</li><li>Stabiliser: DJI OM6 or Insta360 Flow — ~€100</li><li>Lenses: Moment wide (18mm) for buildings, tele for detail — ~€80 each</li><li>Power: Anker 20,000mAh with fast charge — ~€35</li><li>Weatherproofing: UAG or Catalyst case — ~€40</li><li>Total investment: ~€300. Payback at Active tier: approximately 5 weeks.</li></ul>` },
|
||||
{ tab: '6 · Professional kit', html: `<p class="layer-lead">Full equipment guide for Professional tier Zoomers operating in all mission categories.</p><ul class="layer-ul"><li>Primary phone: iPhone 15 Pro or Samsung S24 Ultra — best camera systems available</li><li>Stabiliser: DJI OM6 or Insta360 Flow — ~$100</li><li>Lenses: Moment wide (18mm) for buildings, tele for detail — ~$80 each</li><li>Power: Anker 20,000mAh with fast charge — ~$35</li><li>Weatherproofing: UAG or Catalyst case — ~$40</li><li>Total investment: ~$300. Payback at Active tier: approximately 5 weeks.</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Your current phone is enough to start</p><p class="layer-cta-sub">Register now. No equipment purchase needed to begin earning in August 2026.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
]
|
||||
},
|
||||
@@ -1664,7 +1788,7 @@ const DATA = {
|
||||
{ tab: '1 · The basics', html: `<p class="layer-lead">Payment is automatic. When your submission is approved, Stripe Connect initiates a transfer to your linked bank account. You don't do anything.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">Automatic</div><div class="layer-kpi-lbl">No action needed</div></div><div class="layer-kpi"><div class="layer-kpi-val">0%</div><div class="layer-kpi-lbl">Platform fee to Zoomers</div></div><div class="layer-kpi"><div class="layer-kpi-val">1–3 days</div><div class="layer-kpi-lbl">Bank transfer time</div></div></div>` },
|
||||
{ tab: '2 · Setting up payouts', html: `<div class="layer-step-list"><div class="layer-step"><div class="layer-step-num">1</div><div class="layer-step-body"><p class="layer-step-title">Identity verification</p><p class="layer-step-desc">Required once. Takes 3–5 minutes. Government ID + selfie. Processed by Stripe's KYC provider.</p></div></div><div class="layer-step"><div class="layer-step-num">2</div><div class="layer-step-body"><p class="layer-step-title">Link bank account</p><p class="layer-step-desc">IBAN + bank name. Processed via Stripe Connect. quiXzoom never sees your bank details.</p></div></div><div class="layer-step"><div class="layer-step-num">3</div><div class="layer-step-body"><p class="layer-step-title">Done</p><p class="layer-step-desc">All future payments route automatically to this account. Change at any time in settings.</p></div></div></div>` },
|
||||
{ tab: '3 · Transfer times by country', html: `<div class="layer-table-wrap"><table class="layer-table"><tr><th>Region</th><th>Method</th><th>Time</th></tr><tr><td>Sweden / EU</td><td>SEPA Credit Transfer</td><td>1–2 business days</td></tr><tr><td>UK</td><td>Faster Payments</td><td>Next business day</td></tr><tr><td>US</td><td>ACH</td><td>2–3 business days</td></tr><tr><td>Other</td><td>SWIFT</td><td>2–5 business days</td></tr></table></div>` },
|
||||
{ tab: '4 · Currency', html: `<p class="layer-lead">You receive payment in your local currency. Conversion at mid-market rate (Stripe) where applicable.</p><ul class="layer-ul"><li>Swedish Zoomers: receive SEK</li><li>EU Zoomers: receive EUR</li><li>UK Zoomers: receive GBP</li><li>Missions are priced in EUR — local currency conversion applied at payout</li></ul>` },
|
||||
{ tab: '4 · Currency', html: `<p class="layer-lead">You receive payment in your local currency. Conversion at mid-market rate (Stripe) where applicable.</p><ul class="layer-ul"><li>Swedish Zoomers: receive SEK</li><li>EU Zoomers: receive USD</li><li>UK Zoomers: receive GBP</li><li>Missions are priced in USD — local currency conversion applied at payout</li></ul>` },
|
||||
{ tab: '5 · Minimum payout', html: `<p class="layer-lead">Payouts require a minimum Credit balance. Credits accumulate until the threshold is reached, then transfer automatically on the weekly payout cycle.</p><ul class="layer-ul"><li>Minimum payout: 250 QX Credits (or local equivalent)</li><li>Credits below the threshold accumulate in your balance — they never expire</li><li>Automatic weekly transfer when balance meets or exceeds the minimum</li><li>Current balance, threshold status and estimated payout visible in your earnings dashboard</li></ul>` },
|
||||
{ tab: '6 · Annual statement', html: `<p class="layer-lead">At the end of each calendar year, quiXzoom provides an earnings statement for tax purposes.</p><ul class="layer-ul"><li>Total earnings for the year</li><li>Mission-by-mission transaction log</li><li>Downloadable as PDF (formatted) and CSV (for accounting software)</li><li>Available in your dashboard from January 15 each year</li><li>Covers all approved missions in the calendar year</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">Zero fees. Automatic payment. Start in August.</p><p class="layer-cta-sub">Register your interest now. Payment setup takes 5 minutes and is only needed once.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
@@ -1687,7 +1811,7 @@ const DATA = {
|
||||
layers: [
|
||||
{ tab: '1 · The basics', html: `<p class="layer-lead">quiXzoom earnings are income. You are responsible for declaring and paying any applicable taxes. quiXzoom does not withhold taxes on your behalf.</p><p class="layer-h3">What quiXzoom provides</p><ul class="layer-ul"><li>Annual earnings statement (PDF + CSV) from January 15 each year</li><li>Transaction-level records for every approved mission</li><li>Stripe Connect payment records (accessible via your Stripe dashboard)</li></ul>` },
|
||||
{ tab: '2 · Sweden', html: `<p class="layer-lead">Sweden has straightforward rules for occasional income and self-employment.</p><ul class="layer-ul"><li>Declare quiXzoom earnings as inkomst av tjänst (occasional) or inkomst av näringsverksamhet (if regular)</li><li>Grundavdrag (basic deduction): ~17,500 SEK — if total income below this, no income tax</li><li>For regular Zoomer activity: register as enskild firma with Skatteverket (simple, low-cost)</li><li>F-skatt: recommended if earning above ~5,000 SEK/month from quiXzoom</li></ul><p class="layer-p"><em>General information. Consult Skatteverket or a Swedish tax advisor for your situation.</em></p>` },
|
||||
{ tab: '3 · Germany', html: `<ul class="layer-ul"><li>Under €256/year: tax-free (Freigrenze für Einkünfte aus sonstigen Leistungen in some interpretations — verify with a Steuerberater)</li><li>Kleinunternehmer: if annual turnover below €22,000, simplified VAT rules apply</li><li>Above €22,000: Umsatzsteuer (VAT) registration likely required</li><li>Gewerbesteuer: may apply depending on activity classification</li></ul><p class="layer-p"><em>Germany's tax rules for gig income are complex. A Steuerberater consultation is recommended.</em></p>` },
|
||||
{ tab: '3 · Germany', html: `<ul class="layer-ul"><li>Under $256/year: tax-free (Freigrenze für Einkünfte aus sonstigen Leistungen in some interpretations — verify with a Steuerberater)</li><li>Kleinunternehmer: if annual turnover below $22,000, simplified VAT rules apply</li><li>Above $22,000: Umsatzsteuer (VAT) registration likely required</li><li>Gewerbesteuer: may apply depending on activity classification</li></ul><p class="layer-p"><em>Germany's tax rules for gig income are complex. A Steuerberater consultation is recommended.</em></p>` },
|
||||
{ tab: '4 · UK', html: `<ul class="layer-ul"><li>Self-assessment required if quiXzoom income plus other untaxed income exceeds £1,000/year</li><li>Personal allowance: £12,570 — income below this threshold: no income tax</li><li>National Insurance: Class 2 and 4 apply above threshold on self-employed profits</li><li>Sole trader registration with HMRC recommended for active Zoomers</li></ul><p class="layer-p"><em>Consult HMRC's self-employment guidance or an accountant for your situation.</em></p>` },
|
||||
{ tab: '5 · Other EU countries', html: `<p class="layer-lead">EU countries generally follow similar principles: declare freelance income, register as self-employed if earnings are regular, pay applicable income tax and social contributions.</p><p class="layer-p">quiXzoom will publish country-specific guidance pages for all launch markets by August 2026. These will cover registration requirements, thresholds, and documentation needed for common tax scenarios.</p>` },
|
||||
{ tab: '6 · Record keeping', html: `<p class="layer-lead">Good records make tax filing simple. Keep these:</p><ul class="layer-ul"><li>quiXzoom annual earnings statement (download from your dashboard)</li><li>Stripe Connect payout records (download from your Stripe dashboard)</li><li>Equipment purchase receipts (deductible in many jurisdictions)</li><li>Travel expenses related to missions (deductible in some jurisdictions)</li></ul>` },
|
||||
@@ -1710,10 +1834,10 @@ const DATA = {
|
||||
type: 'Earnings scenarios', title: 'Working part-time — real scenarios',
|
||||
layers: [
|
||||
{ tab: '1 · Overview', html: `<p class="layer-lead">quiXzoom is designed for part-time work. No schedule, no minimum hours, no commitment. You work when you want — which is exactly how most Zoomers use it.</p><div class="layer-kpi-row"><div class="layer-kpi"><div class="layer-kpi-val">0</div><div class="layer-kpi-lbl">Minimum missions required</div></div><div class="layer-kpi"><div class="layer-kpi-val">Yours</div><div class="layer-kpi-lbl">You set your own schedule</div></div><div class="layer-kpi"><div class="layer-kpi-val">Unlimited</div><div class="layer-kpi-lbl">No earning cap</div></div></div>` },
|
||||
{ tab: '2 · Lunch break scenario', html: `<p class="layer-lead">One of the most common Zoomer patterns: 1–2 missions during a lunch break.</p><div class="layer-block"><p class="layer-block-title">Typical lunch break (45 min)</p><ul class="layer-ul"><li>Walk to nearest mission cluster: 5 minutes</li><li>Mission 1 (infrastructure, €5): 15 minutes</li><li>Mission 2 (verification, €4): 10 minutes</li><li>Walk back: 5 minutes</li><li>Total: 35 minutes. Earnings: €9. Still had 10 minutes to eat.</li></ul></div><p class="layer-h3">5 days/week = €45/week = ~€180/month</p>` },
|
||||
{ tab: '3 · Weekend scenario', html: `<p class="layer-lead">Weekend walks with missions integrated into existing activity.</p><div class="layer-block"><p class="layer-block-title">Saturday morning walk (2 hours)</p><ul class="layer-ul"><li>Route planned around 6 adjacent missions in local area</li><li>Mix: 3 infrastructure + 2 real estate + 1 urban survey</li><li>Average €7 per mission: €42</li><li>Same walk you'd have done anyway</li></ul></div><p class="layer-h3">Two weekend sessions/week = ~€320/month</p>` },
|
||||
{ tab: '4 · Evening scenario', html: `<p class="layer-lead">Short evening sessions after work — 30–45 minutes, 2–3 missions.</p><div class="layer-block"><p class="layer-block-title">Weekday evening (30 min, 3×/week)</p><ul class="layer-ul"><li>3 evenings × 2 missions × €6.50 average = €39/week</li><li>Monthly: ~€156</li><li>Time invested: 1.5 hours/week</li></ul></div>` },
|
||||
{ tab: '5 · Combining with existing work', html: `<p class="layer-lead">quiXzoom income compounds with other part-time and flexible work. Scenarios from beta testers.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Profile</th><th>Existing work</th><th>quiXzoom addition</th></tr><tr><td>Delivery worker</td><td>On-route missions</td><td>+€150–300/month, ~15 min/day extra</td></tr><tr><td>Dog walker</td><td>Morning walks</td><td>+€60–120/month, same route</td></tr><tr><td>Part-time retail</td><td>Fixed days off</td><td>+€80–200/month on free days</td></tr><tr><td>Student</td><td>Campus movement</td><td>+€60–150/month, between lectures</td></tr></table></div>` },
|
||||
{ tab: '2 · Lunch break scenario', html: `<p class="layer-lead">One of the most common Zoomer patterns: 1–2 missions during a lunch break.</p><div class="layer-block"><p class="layer-block-title">Typical lunch break (45 min)</p><ul class="layer-ul"><li>Walk to nearest mission cluster: 5 minutes</li><li>Mission 1 (infrastructure, $5): 15 minutes</li><li>Mission 2 (verification, $4): 10 minutes</li><li>Walk back: 5 minutes</li><li>Total: 35 minutes. Earnings: $9. Still had 10 minutes to eat.</li></ul></div><p class="layer-h3">5 days/week = $45/week = ~$180/month</p>` },
|
||||
{ tab: '3 · Weekend scenario', html: `<p class="layer-lead">Weekend walks with missions integrated into existing activity.</p><div class="layer-block"><p class="layer-block-title">Saturday morning walk (2 hours)</p><ul class="layer-ul"><li>Route planned around 6 adjacent missions in local area</li><li>Mix: 3 infrastructure + 2 real estate + 1 urban survey</li><li>Average $7 per mission: $42</li><li>Same walk you'd have done anyway</li></ul></div><p class="layer-h3">Two weekend sessions/week = ~$320/month</p>` },
|
||||
{ tab: '4 · Evening scenario', html: `<p class="layer-lead">Short evening sessions after work — 30–45 minutes, 2–3 missions.</p><div class="layer-block"><p class="layer-block-title">Weekday evening (30 min, 3×/week)</p><ul class="layer-ul"><li>3 evenings × 2 missions × $6.50 average = $39/week</li><li>Monthly: ~$156</li><li>Time invested: 1.5 hours/week</li></ul></div>` },
|
||||
{ tab: '5 · Combining with existing work', html: `<p class="layer-lead">quiXzoom income compounds with other part-time and flexible work. Scenarios from beta testers.</p><div class="layer-table-wrap"><table class="layer-table"><tr><th>Profile</th><th>Existing work</th><th>quiXzoom addition</th></tr><tr><td>Delivery worker</td><td>On-route missions</td><td>+$150–300/month, ~15 min/day extra</td></tr><tr><td>Dog walker</td><td>Morning walks</td><td>+$60–120/month, same route</td></tr><tr><td>Part-time retail</td><td>Fixed days off</td><td>+$80–200/month on free days</td></tr><tr><td>Student</td><td>Campus movement</td><td>+$60–150/month, between lectures</td></tr></table></div>` },
|
||||
{ tab: '6 · When to work more', html: `<p class="layer-lead">Some weeks offer higher earnings opportunities than others. Knowing when to increase your activity maximises returns.</p><ul class="layer-ul"><li>After significant weather events: insurance surge missions activate — highest pay of the year</li><li>Spring (March–April): annual municipal inspection season begins — high volume</li><li>Late autumn (October): pre-winter surveys — another surge period</li><li>Urban centre missions refresh regularly — check Monday mornings for new weekly batches</li></ul>` },
|
||||
{ tab: '7 · Get started', html: `<div class="layer-cta-box"><p class="layer-cta-title">One mission a day. Real income.</p><p class="layer-cta-sub">Register now. No commitment to how much or how often. Start when you want, stop when you want.</p><a href="#waitlist-form-top" class="layer-cta-btn" onclick="closeOverlay()">Join waitlist →</a></div>` }
|
||||
]
|
||||
@@ -1871,7 +1995,7 @@ const GLOSSARY = {
|
||||
'API': { short: 'Application Programming Interface — how orderers programmatically access mission data.', slug: 'api' },
|
||||
'webhook': { short: 'Automatic HTTP notification sent to orderers when a submission is approved.', slug: 'webhook' },
|
||||
'IBAN': { short: 'International Bank Account Number — required for Stripe Connect payout setup.', slug: 'iban' },
|
||||
'SEPA': { short: 'Single Euro Payments Area — the European bank transfer network used for EUR payouts.', slug: 'sepa' },
|
||||
'SEPA': { short: 'Single Euro Payments Area — the European bank transfer network used for USD payouts.', slug: 'sepa' },
|
||||
'VAT': { short: 'Value Added Tax — Zoomers are responsible for their own VAT obligations where applicable.', slug: 'vat' },
|
||||
'payout': { short: 'Weekly transfer of QX Credits converted to cash, to your linked bank account. Requires reaching the minimum Credit threshold.', slug: 'payout' },
|
||||
'rating': { short: 'Your accumulated quality score on the platform, affecting tier and mission access.', slug: 'rating' },
|
||||
@@ -1998,13 +2122,257 @@ document.addEventListener('qx:market', function(e){
|
||||
'Asia/Bangkok':'th','Asia/Tokyo':'ja','Asia/Seoul':'ko','Asia/Shanghai':'zh-cn','Asia/Jakarta':'id','Asia/Kuala_Lumpur':'ms','Asia/Ho_Chi_Minh':'vi'
|
||||
};
|
||||
var names={'sv-se':'Sweden','nl-nl':'Netherlands','de-de':'Germany','fr-fr':'France','it-it':'Italy','es-es':'Spain','th':'Thailand','ja':'Japan','ko':'Korea','zh-cn':'China','id':'Indonesia','ms':'Malaysia','vi':'Vietnam'};
|
||||
var localNames={'sv-se':'Sverige','nl-nl':'Nederland','de-de':'Deutschland','fr-fr':'France','it-it':'Italia','es-es':'España','th':'ประเทศไทย','ja':'日本','ko':'한국','zh-cn':'中国','id':'Indonesia','ms':'Malaysia','vi':'Việt Nam'};
|
||||
var detected=map[tz];
|
||||
if(!detected||detected==='sv-se')return;
|
||||
var b=document.createElement('div');
|
||||
b.id='qx-geo-banner';
|
||||
b.innerHTML='<span>Looks like you're in '+names[detected]+'</span><a href="/'+detected+'/" class="geo-switch">Switch to '+names[detected]+'</a><button class="geo-dismiss" onclick="sessionStorage.setItem(\'qx_geo_dismissed\',1);this.closest(\'#qx-geo-banner\').remove()" aria-label="Dismiss">×</button>';
|
||||
var isThai=detected==='th';
|
||||
var html='<div class="geo-icon"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/><path d="M2 12h20"/></svg></div>';
|
||||
html+='<div class="geo-text">';
|
||||
html+='<span class="geo-title">Looks like you\'re in '+names[detected]+'</span>';
|
||||
if(isThai) html+='<span class="geo-local">ดูเหมือนคุณอยู่ใน'+localNames[detected]+'</span>';
|
||||
else if(localNames[detected]) html+='<span class="geo-local">'+localNames[detected]+'</span>';
|
||||
html+='<span class="geo-subtitle">Switch to your local site for better experience</span>';
|
||||
if(isThai) html+='<span class="geo-subtitle">เปลี่ยนไปยังเว็บไซต์ท้องถิ่นเพื่อประสบการณ์ที่ดีขึ้น</span>';
|
||||
html+='</div>';
|
||||
html+='<div class="geo-actions">';
|
||||
html+='<button class="geo-dismiss" onclick="sessionStorage.setItem(\'qx_geo_dismissed\',1);this.closest(\'#qx-geo-banner\').remove()" aria-label="Dismiss">×</button>';
|
||||
html+='<a href="/'+detected+'/" class="geo-switch">Switch to '+names[detected]+' →</a>';
|
||||
if(isThai) html+='<a href="/'+detected+'/" class="geo-switch" style="background:rgba(0,102,255,.1);color:var(--blue)">ไปยัง'+localNames[detected]+' →</a>';
|
||||
html+='</div>';
|
||||
b.innerHTML=html;
|
||||
document.addEventListener('DOMContentLoaded',function(){document.body.appendChild(b);});
|
||||
}());
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -194,6 +194,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">iOS Integration Guide for quiXzoom</h1>
|
||||
@@ -702,5 +703,234 @@ class</span> <span class="tp">LocationService</span>: <span class="tp">NSObject<
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -387,6 +387,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">iOS Readiness Dashboard</h1>
|
||||
@@ -790,5 +791,234 @@ setInterval(() => {
|
||||
document.getElementById('navTime').textContent = str;
|
||||
}, 1000);
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Become a Zoomer</h1>
|
||||
@@ -160,7 +161,7 @@
|
||||
<div class="feature-icon">💰</div>
|
||||
<div class="feature-text">
|
||||
<h3>Get Paid</h3>
|
||||
<p>Earn €5-50 per mission, paid every Monday</p>
|
||||
<p>Earn $5-50 per mission, paid every Monday</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
@@ -254,5 +255,234 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -7,4 +7,234 @@
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</head><body><h1>Identitetsverifiering</h1><p>quiXzoom KYC ar under uppbyggnad. Kontakta support for manuell verifiering.</p></body></html>
|
||||
|
||||
@@ -242,6 +242,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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="Launch Campaign — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">quiXzoom Launch Campaign</h1>
|
||||
@@ -583,5 +584,234 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
navigator.clipboard?.writeText(pr).then(() => alert('Pressrelease kopierad!')).catch(() => alert('Kopiering misslyckades'));
|
||||
}
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -428,6 +428,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">Launch Checklist for quiXzoom</h1>
|
||||
@@ -648,7 +649,7 @@ const CATEGORIES = [
|
||||
{ title: 'DAC7 TIN-verifiering per Zoomer', owner: 'Legal', status: 'done', deadline: 'Juni 10', note: 'POST /compliance/dac7/sellers/:sellerId/verify' },
|
||||
{ title: 'DAC7 XML rapport genereras (Skatteverket-format)', owner: 'Legal', status: 'done', deadline: 'Juni 10', note: 'urn:oecd:ties:dpi:v1 · messageRefId QZ-DAC7-*' },
|
||||
{ title: 'DAC7 status-dashboard live', owner: 'Legal', status: 'done', deadline: 'Juni 10', note: '/quixzoom/dac7-report.html + /dac7-reporting.html' },
|
||||
{ title: 'Rapporteringsgräns 30 transaktioner / 2 000 EUR per år', owner: 'Legal', status: 'done', deadline: 'Juni 5', note: 'Art. 8ac DAC trösk implementerad i dac7.mjs:175' },
|
||||
{ title: 'Rapporteringsgräns 30 transaktioner / 2 000 USD per år', owner: 'Legal', status: 'done', deadline: 'Juni 5', note: 'Art. 8ac DAC trösk implementerad i dac7.mjs:175' },
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -688,7 +689,7 @@ const CATEGORIES = [
|
||||
{ title: 'Privacy Policy + Terms of Service publicerade', owner: 'Legal', status: 'done', deadline: 'Maj 15' },
|
||||
{ title: 'GDPR Data Deletion-flow verifierat', owner: 'Legal', status: 'done', deadline: 'Juni 5' },
|
||||
{ title: 'KYC/AML-process granskad av extern jurist', owner: 'Legal', status: 'done', deadline: 'Juni 5' },
|
||||
{ title: 'Liability Framework (T545) konfigurerad', owner: 'Legal', status: 'done', deadline: 'Juni 5', note: 'Free €100 / Plus €500 / Pro 12mo / Enterprise €5M floor' },
|
||||
{ title: 'Liability Framework (T545) konfigurerad', owner: 'Legal', status: 'done', deadline: 'Juni 5', note: 'Free $100 / Plus $500 / Pro 12mo / Enterprise $5M floor' },
|
||||
{ title: 'Dispute resolution 3-steg (T546) aktiverat', owner: 'Legal', status: 'done', deadline: 'Juni 5' },
|
||||
]
|
||||
},
|
||||
@@ -833,5 +834,234 @@ function render() {
|
||||
|
||||
render();
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -391,6 +391,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -657,5 +658,234 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
loadData();
|
||||
setInterval(loadData, 60000);
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -498,6 +498,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -910,5 +911,234 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
setInterval(updateHero, 60000);
|
||||
</script>
|
||||
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -484,6 +484,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/mission-map.jpg">
|
||||
<meta name="twitter:title" content="quiXzoom — Mission Map">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -256,6 +256,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/infrastructure-scan.jpg">
|
||||
<meta name="twitter:title" content="Uppdragsdetaljer — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -634,5 +635,234 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
|
||||
load();
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -127,6 +127,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/mission-map.jpg">
|
||||
<meta name="twitter:title" content="quiXzoom — Live Mission Map">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -377,5 +378,234 @@ const focusMission = urlParams.get('mission');
|
||||
// Start
|
||||
connect();
|
||||
</script>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
.footer-tagline{font-size:.8rem;color:rgba(10,10,10,.55);margin-top:8px;font-style:italic}
|
||||
@media(max-width:768px){.footer-inner{flex-direction:column;align-items:flex-start}}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -253,7 +254,7 @@
|
||||
<div class="pricing-card">
|
||||
<h3>Per Mission</h3>
|
||||
<p class="pricing-sub">Best for targeted tasks</p>
|
||||
<div class="price">€8–80<span>/mission</span></div>
|
||||
<div class="price">$8–80<span>/mission</span></div>
|
||||
<ul>
|
||||
<li>Single location, defined shot list</li>
|
||||
<li>You set the budget</li>
|
||||
@@ -265,7 +266,7 @@
|
||||
<div class="pricing-card featured">
|
||||
<h3>Per Photo</h3>
|
||||
<p class="pricing-sub">Best for volume documentation</p>
|
||||
<div class="price">€2–5<span>/photo</span></div>
|
||||
<div class="price">$2–5<span>/photo</span></div>
|
||||
<ul>
|
||||
<li>Large-scale photo collection</li>
|
||||
<li>Consistent framing requirements</li>
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
.footer-tagline{font-size:.8rem;color:rgba(10,10,10,.55);margin-top:8px;font-style:italic}
|
||||
@media(max-width:768px){.footer-inner{flex-direction:column;align-items:flex-start}}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -163,7 +164,7 @@
|
||||
<div class="pricing-card">
|
||||
<h3>Per Mission</h3>
|
||||
<p class="pricing-sub">Best for targeted tasks</p>
|
||||
<div class="price">€8–80</div>
|
||||
<div class="price">$8–80</div>
|
||||
<p class="price-note">per mission</p>
|
||||
<ul>
|
||||
<li>Single location, defined shot list</li>
|
||||
@@ -179,7 +180,7 @@
|
||||
<div class="badge">MOST POPULAR</div>
|
||||
<h3>Per Photo</h3>
|
||||
<p class="pricing-sub">Best for volume documentation</p>
|
||||
<div class="price">€2–5</div>
|
||||
<div class="price">$2–5</div>
|
||||
<p class="price-note">per photo</p>
|
||||
<ul>
|
||||
<li>Large-scale photo collection</li>
|
||||
@@ -251,7 +252,7 @@
|
||||
<div style="max-width:760px;margin:0 auto">
|
||||
<details class="faq-item">
|
||||
<summary>What does a mission cost?</summary>
|
||||
<div class="faq-answer">Mission costs vary by complexity and location. Simple verification missions start at €8. Complex documentation missions range from €25–80. You set the budget when creating a mission.</div>
|
||||
<div class="faq-answer">Mission costs vary by complexity and location. Simple verification missions start at $8. Complex documentation missions range from $25–80. You set the budget when creating a mission.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Are there any fees for Zoomers?</summary>
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/og-image.jpg">
|
||||
<meta name="twitter:title" content="Privacy Policy – quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -334,5 +335,234 @@
|
||||
<p style="margin-top:4px;">quiXzoom is a trademark of Landvex AB</p>
|
||||
</footer>
|
||||
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -19,6 +19,7 @@ p{margin-bottom:1rem}
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Privacy Policy</h1>
|
||||
@@ -39,5 +40,234 @@ p{margin-bottom:1rem}
|
||||
<p>For privacy questions, contact us at <a href="mailto:support@quixzoom.com">support@quixzoom.com</a>.</p>
|
||||
|
||||
<p><a href="/">← Back to QUIXZOOM</a></p>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -112,6 +112,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</style>
|
||||
<script src="i18n.js"></script>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="widget">
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="logo">quiXzoom</div>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Join as Zoomer — quiXzoom</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<meta name="description" content="Join quiXzoom as a Zoomer. Free to join, no fees. Get paid per approved mission.">
|
||||
<style>
|
||||
:root{
|
||||
--bg-white:#FFFFFF;--bg-off:#F7F7F5;--blue:#0066FF;--blue-dark:#0052CC;
|
||||
--text-dark:#1a1a1a;--text-body:#3a3a3a;--text-muted:#6B6B6B;
|
||||
--radius-card:16px;--radius-lg:24px;
|
||||
}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Inter','Helvetica Neue',sans-serif;font-size:1rem;line-height:1.65;color:var(--text-body);background:var(--bg-white);-webkit-font-smoothing:antialiased;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px}
|
||||
.register-card{background:var(--bg-off);border-radius:var(--radius-card);padding:48px;width:100%;max-width:420px}
|
||||
.logo{font-size:2rem;font-weight:900;color:var(--text-dark);text-align:center;margin-bottom:8px}
|
||||
.logo .x{color:var(--blue)}
|
||||
.subtitle{color:var(--text-muted);text-align:center;margin-bottom:32px;font-size:.9rem}
|
||||
.form-group{margin-bottom:20px}
|
||||
.form-label{display:block;font-size:.875rem;font-weight:600;color:var(--text-dark);margin-bottom:6px}
|
||||
.form-input{width:100%;padding:12px 16px;border:1.5px solid rgba(0,0,0,.1);border-radius:12px;font-size:1rem;background:#fff;color:var(--text-dark);transition:border-color .15s}
|
||||
.form-input:focus{outline:none;border-color:var(--blue)}
|
||||
.form-input::placeholder{color:rgba(10,10,10,.35)}
|
||||
.btn-submit{width:100%;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-lg);padding:14px 24px;font-size:1rem;font-weight:700;cursor:pointer;transition:background .15s;margin-top:8px}
|
||||
.btn-submit:hover{background:var(--blue-dark)}
|
||||
.login-link{text-align:center;margin-top:20px;font-size:.875rem;color:var(--text-muted)}
|
||||
.login-link a{color:var(--blue);font-weight:600;text-decoration:none}
|
||||
.features{display:flex;justify-content:center;gap:32px;margin-top:32px;padding-top:24px;border-top:1px solid rgba(0,0,0,.08)}
|
||||
.feature{text-align:center}
|
||||
.feature-icon{width:40px;height:40px;margin:0 auto 8px;background:var(--blue);border-radius:10px;display:flex;align-items:center;justify-content:center}
|
||||
.feature-icon svg{width:20px;height:20px;stroke:white;stroke-width:2;fill:none}
|
||||
.feature-label{font-size:.75rem;color:var(--text-muted);font-weight:600}
|
||||
@media(max-width:480px){.register-card{padding:32px 24px}}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="register-card">
|
||||
<div class="logo">qui<span class="x">X</span>zoom</div>
|
||||
<p class="subtitle">Join as Zoomer — Free, no fees</p>
|
||||
|
||||
<form onsubmit="event.preventDefault();alert('Registration opens August 2026. Join the waitlist!');">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Full Name</label>
|
||||
<input type="text" class="form-input" placeholder="Anna Svensson" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" class="form-input" placeholder="anna@example.com" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Phone Number</label>
|
||||
<input type="tel" class="form-input" placeholder="+46 70 123 45 67" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" class="form-input" placeholder="Min. 8 characters" required minlength="8">
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Create Account</button>
|
||||
</form>
|
||||
|
||||
<p class="login-link">Already have an account? <a href="/login">Log in</a></p>
|
||||
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<div class="feature-icon">
|
||||
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="5"/></svg>
|
||||
</div>
|
||||
<div class="feature-label">Photograph</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 6v12M8 10l4-4 4 4"/></svg>
|
||||
</div>
|
||||
<div class="feature-label">Earn Money</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 8v8M8 12h8"/></svg>
|
||||
</div>
|
||||
<div class="feature-label">Flexible</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -162,6 +162,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/mission-map.jpg">
|
||||
<meta name="twitter:title" content="quiXzoom — Beställ ett uppdrag">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -4,363 +4,313 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Resources — quiXzoom</title>
|
||||
<meta name="description" content="Documentation, API guides and use-case resources for organisations using quiXzoom field data.">
|
||||
<link rel="canonical" href="https://www.quixzoom.com/resources/">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://www.quixzoom.com/resources/">
|
||||
<meta property="og:title" content="Resources — quiXzoom">
|
||||
<meta property="og:description" content="Documentation, API guides and use-case resources for organisations using quiXzoom field data.">
|
||||
<meta property="og:image" content="https://www.quixzoom.com/images/hero-zoomer-city.jpg">
|
||||
<meta property="og:image:width" content="1200">
|
||||
<meta property="og:image:height" content="630">
|
||||
<meta property="og:image:alt" content="quiXzoom — Collect Reality. Get Paid.">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Resources — quiXzoom">
|
||||
<meta name="twitter:description" content="Documentation, API guides and use-case resources for organisations using quiXzoom field data.">
|
||||
|
||||
<meta name="description" content="quiXzoom resources: documentation, guides, API references, and more.">
|
||||
<style>
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
:root{
|
||||
--bg-dark:#0a0a0a;--bg-white:#FFFFFF;--bg-off:#F7F7F5;
|
||||
--blue:#0066FF;--blue-dark:#0052CC;--blue-pale:rgba(0,102,255,.07);
|
||||
--green:#00C853;--text-dark:#0a0a0a;--text-body:#3a3a3a;--text-muted:#6B6B6B;
|
||||
--text-xs:.75rem;--text-sm:.875rem;--text-base:1rem;
|
||||
--text-lg:1.125rem;--text-xl:1.375rem;--text-2xl:1.75rem;
|
||||
--radius-sm:8px;--radius-md:14px;--radius-lg:24px;--radius-full:100px;
|
||||
--radius-card:16px;
|
||||
--qx-blue:#0066FF;
|
||||
}
|
||||
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Inter','Helvetica Neue',sans-serif;font-size:1rem;line-height:1.65;color:var(--text-body);background:var(--bg-white);-webkit-font-smoothing:antialiased;overflow-x:hidden}
|
||||
a{color:inherit;text-decoration:none}
|
||||
ul{list-style:none}
|
||||
button{cursor:pointer;font-family:inherit}
|
||||
.container{width:100%;max-width:1160px;margin:0 auto;padding:0 24px}
|
||||
|
||||
/* NAV */
|
||||
nav{position:sticky;top:0;z-index:300;background:var(--bg-dark);border-bottom:1px solid rgba(255,255,255,.06)}
|
||||
.nav-inner{display:flex;align-items:center;justify-content:space-between;height:68px;gap:32px}
|
||||
.nav-logo{font-size:1.375rem;font-weight:900;color:#fff;letter-spacing:-.04em}
|
||||
.nav-logo .x{color:var(--blue)}
|
||||
.nav-links{display:flex;align-items:center;gap:32px;flex:1;justify-content:center;list-style:none}
|
||||
.nav-links a{color:rgba(255,255,255,.75);font-size:var(--text-base);font-weight:500;transition:color .15s}
|
||||
.nav-links a:hover{color:#fff}
|
||||
.btn-nav{background:var(--blue);color:#fff;border:none;border-radius: var(--radius-lg);padding:10px 24px;font-size:var(--text-base);font-weight:700;transition:background .15s;white-space:nowrap;flex-shrink:0;text-decoration:none;display:inline-block}
|
||||
.btn-nav:hover{background:var(--blue-dark)}
|
||||
@media(max-width:768px){.nav-links{display:none}.btn-nav{display:none}}
|
||||
|
||||
/* PAGE HERO */
|
||||
.page-hero{background:var(--bg-dark);padding:72px 0 80px;color:#fff}
|
||||
.breadcrumb{font-size:.8rem;color:rgba(255,255,255,.75);margin-bottom:16px}
|
||||
.breadcrumb a{color:var(--blue)}
|
||||
.breadcrumb a:hover{color:#fff}
|
||||
.page-hero h1{font-size:clamp(2rem,5vw,3.5rem);font-weight:900;letter-spacing:-.05em;line-height:1.1;margin-bottom:16px}
|
||||
.page-hero p{font-size:var(--text-lg);color:rgba(255,255,255,.65);max-width:560px;line-height:1.6}
|
||||
|
||||
/* RESOURCE SECTIONS */
|
||||
.resources-body{padding:72px 0 96px;background:var(--bg-white)}
|
||||
|
||||
.resource-group{margin-bottom:64px}
|
||||
.resource-group:last-child{margin-bottom:0}
|
||||
|
||||
.group-label{
|
||||
font-size:.7rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;
|
||||
color:rgba(0,0,0,.3);margin-bottom:20px;
|
||||
}
|
||||
|
||||
.resource-list{display:flex;flex-direction:column;gap:12px}
|
||||
|
||||
.resource-row{
|
||||
display:flex;align-items:center;justify-content:space-between;gap:24px;
|
||||
background:#fff;border:1.5px solid #EAEAEA;border-radius: var(--radius-md);
|
||||
padding:20px 24px;
|
||||
transition:border-color .2s,box-shadow .2s,transform .2s;
|
||||
}
|
||||
.resource-row:hover{
|
||||
border-color:rgba(0,102,255,.25);
|
||||
box-shadow:0 8px 24px rgba(0,0,0,.07);
|
||||
transform:translateY(-1px);
|
||||
}
|
||||
|
||||
.resource-info{flex:1}
|
||||
.resource-info h3{font-size:1rem;font-weight:700;color:var(--text-dark);margin-bottom:4px;line-height:1.3}
|
||||
.resource-info p{font-size:.875rem;color:var(--text-muted);line-height:1.5}
|
||||
|
||||
.resource-tag{
|
||||
flex-shrink:0;
|
||||
font-size:.7rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
|
||||
background:var(--blue-pale);color:var(--blue);border-radius: var(--radius-sm);padding:4px 10px;
|
||||
}
|
||||
|
||||
.resource-arrow{
|
||||
flex-shrink:0;color:var(--blue);font-weight:700;font-size:.875rem;
|
||||
display:flex;align-items:center;gap:4px;
|
||||
}
|
||||
|
||||
/* FOOTER */
|
||||
footer{background:#0a0a0a;color:#fff;padding:0;border-top:none}
|
||||
.footer-top{padding:56px 0 48px;border-bottom:1px solid rgba(255,255,255,.1)}
|
||||
.footer-grid{display:grid;grid-template-columns:200px repeat(5,1fr);gap:32px;max-width:1200px;margin:0 auto;padding:0 32px}
|
||||
.footer-brand-col .footer-logo{font-size:1.3rem;font-weight:800;letter-spacing:-.04em;color:#fff;text-decoration:none;display:block;margin-bottom:12px}
|
||||
.footer-brand-col .footer-logo .x{color:var(--qx-blue,#0066FF)}
|
||||
.footer-brand-col .footer-desc{font-size:.8rem;color:rgba(255,255,255,.75);line-height:1.6;margin-bottom:20px}
|
||||
.footer-social-row{display:flex;gap:10px}
|
||||
.footer-soc-btn{width:36px;height:36px;border-radius: var(--radius-md);background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.7);text-decoration:none;transition:background .15s,color .15s}
|
||||
.footer-soc-btn:hover{background:rgba(255,255,255,.16);color:#fff}
|
||||
.footer-col-title{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:rgba(255,255,255,.75);margin-bottom:16px}
|
||||
.footer-col-links{list-style:none;padding:0;margin:0}
|
||||
.footer-col-links li{margin-bottom:10px}
|
||||
.footer-col-links a{font-size:.875rem;color:rgba(255,255,255,.65);text-decoration:none;transition:color .15s}
|
||||
.footer-col-links a:hover{color:#fff}
|
||||
.badge-new{font-size:.65rem;font-weight:700;background:var(--qx-blue,#0066FF);color:#fff;padding:1px 5px;border-radius: var(--radius-sm);margin-left:5px;vertical-align:middle}
|
||||
.footer-trust{display:flex;align-items:center;gap:16px;margin-top:20px;flex-wrap:wrap}
|
||||
.trust-badge{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius: var(--radius-md);padding:8px 12px;font-size:.72rem;font-weight:700;color:rgba(255,255,255,.7);display:flex;align-items:center;gap:6px;letter-spacing:.02em}
|
||||
.footer-bottom{max-width:1200px;margin:0 auto;padding:20px 32px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}
|
||||
.footer-copy{font-size:.8rem;color:rgba(255,255,255,.75)}
|
||||
.footer-legal-links{display:flex;gap:20px}
|
||||
.footer-legal-links a{font-size:.8rem;color:rgba(255,255,255,.75);text-decoration:none}
|
||||
.footer-legal-links a:hover{color:rgba(255,255,255,.8)}
|
||||
.footer-lang-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
||||
.footer-lang-row .fl-lbl{font-size:.72rem;color:rgba(255,255,255,.75)}
|
||||
.flang{font-size:.78rem;color:rgba(255,255,255,.75);text-decoration:none;padding:3px 6px;border-radius: var(--radius-sm)}
|
||||
.flang:hover,.flang.active{background:rgba(255,255,255,.1);color:#fff}
|
||||
|
||||
@media(max-width:1024px){.footer-grid{grid-template-columns:1fr 1fr 1fr}.footer-brand-col{grid-column:1/-1}}
|
||||
@media(max-width:768px){.resource-row{flex-wrap:wrap}.resource-tag{display:none}}
|
||||
@media(max-width:600px){.footer-grid{grid-template-columns:1fr 1fr;padding:0 16px}.footer-bottom{flex-direction:column;align-items:flex-start;gap:10px}}
|
||||
@media(max-width:480px){.footer-grid{grid-template-columns:1fr;padding:0 12px}}
|
||||
:root{--bg-white:#FFFFFF;--bg-off:#F7F7F5;--blue:#0066FF;--text-dark:#1a1a1a;--text-body:#3a3a3a;--text-muted:#6B6B6B;--radius-card:16px;}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'Inter',sans-serif;background:var(--bg-white);color:var(--text-body);line-height:1.65}
|
||||
.container{max-width:900px;margin:0 auto;padding:48px 24px}
|
||||
h1{font-size:2.5rem;font-weight:900;color:var(--text-dark);margin-bottom:16px}
|
||||
h2{font-size:1.3rem;font-weight:800;color:var(--text-dark);margin:32px 0 16px}
|
||||
p{margin-bottom:16px;color:var(--text-body)}
|
||||
.back{margin-bottom:24px;display:inline-block;color:var(--blue);text-decoration:none;font-weight:600}
|
||||
.back:hover{text-decoration:underline}
|
||||
.resource-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px}
|
||||
.resource-card{background:var(--bg-off);border-radius:var(--radius-card);padding:24px;text-decoration:none;color:inherit;display:block;transition:transform .15s,box-shadow .15s}
|
||||
.resource-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,.1)}
|
||||
.resource-icon{font-size:2rem;margin-bottom:12px}
|
||||
.resource-title{font-size:1.1rem;font-weight:700;color:var(--text-dark);margin-bottom:8px}
|
||||
.resource-desc{font-size:.9rem;color:var(--text-muted)}
|
||||
</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="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<div class="container">
|
||||
<div class="nav-inner">
|
||||
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/#how">How it works</a></li>
|
||||
<li><a href="/for-organisations/">For organisations</a></li>
|
||||
<li><a href="/resources/">Resources</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
</ul>
|
||||
<a href="/for-organisations/#contact" class="btn-nav">Request a demo →</a>
|
||||
<a href="/" class="back">← Back to quiXzoom</a>
|
||||
<h1>Resources</h1>
|
||||
<p>Documentation, guides, and references for the quiXzoom platform.</p>
|
||||
|
||||
<h2>For Zoomers</h2>
|
||||
<div class="resource-grid">
|
||||
<a href="/glossary" class="resource-card">
|
||||
<div class="resource-icon">📖</div>
|
||||
<div class="resource-title">Glossary</div>
|
||||
<div class="resource-desc">Terms and definitions used on the quiXzoom platform.</div>
|
||||
</a>
|
||||
<a href="/academy/" class="resource-card">
|
||||
<div class="resource-icon">🎓</div>
|
||||
<div class="resource-title">Zoomer Academy</div>
|
||||
<div class="resource-desc">Training materials and best practices for field photography.</div>
|
||||
</a>
|
||||
<a href="/support" class="resource-card">
|
||||
<div class="resource-icon">❓</div>
|
||||
<div class="resource-title">Support Center</div>
|
||||
<div class="resource-desc">FAQs, troubleshooting, and help articles.</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>For Developers</h2>
|
||||
<div class="resource-grid">
|
||||
<a href="/developer/openapi.json" class="resource-card">
|
||||
<div class="resource-icon">🔌</div>
|
||||
<div class="resource-title">API Reference</div>
|
||||
<div class="resource-desc">OpenAPI specification for the quiXzoom platform.</div>
|
||||
</a>
|
||||
<a href="/changelog" class="resource-card">
|
||||
<div class="resource-icon">📝</div>
|
||||
<div class="resource-title">Changelog</div>
|
||||
<div class="resource-desc">Latest updates and platform improvements.</div>
|
||||
</a>
|
||||
<a href="/status" class="resource-card">
|
||||
<div class="resource-icon">📊</div>
|
||||
<div class="resource-title">System Status</div>
|
||||
<div class="resource-desc">Platform uptime and service health.</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>For Organisations</h2>
|
||||
<div class="resource-grid">
|
||||
<a href="/for-organisations/" class="resource-card">
|
||||
<div class="resource-icon">🏢</div>
|
||||
<div class="resource-title">Enterprise Guide</div>
|
||||
<div class="resource-desc">How to deploy photo missions at scale.</div>
|
||||
</a>
|
||||
<a href="/pricing/" class="resource-card">
|
||||
<div class="resource-icon">💰</div>
|
||||
<div class="resource-title">Pricing</div>
|
||||
<div class="resource-desc">Mission pricing, lead packages, and enterprise plans.</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<div class="page-hero">
|
||||
<div class="container">
|
||||
<div class="breadcrumb"><a href="/">quiXzoom</a> / Resources</div>
|
||||
<h1>Resources for organisations</h1>
|
||||
<p>Documentation, API guides and use-case resources for organisations using quiXzoom field data.</p>
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="resources-body">
|
||||
<div class="container">
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
<div class="resource-group">
|
||||
<div class="group-label">Platform</div>
|
||||
<div class="resource-list">
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
<a href="/for-organisations/" class="resource-row">
|
||||
<div class="resource-info">
|
||||
<h3>For organisations</h3>
|
||||
<p>Overview of quiXzoom's field data platform for municipalities, infrastructure operators, insurers and property managers.</p>
|
||||
</div>
|
||||
<span class="resource-tag">Overview</span>
|
||||
<span class="resource-arrow">View →</span>
|
||||
</a>
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
<a href="/use-cases/road-inspection/sweden/" class="resource-row">
|
||||
<div class="resource-info">
|
||||
<h3>Use case: Road inspection in Sweden</h3>
|
||||
<p>How Swedish municipalities use quiXzoom missions to collect pavement condition data at scale.</p>
|
||||
</div>
|
||||
<span class="resource-tag">Use case</span>
|
||||
<span class="resource-arrow">View →</span>
|
||||
</a>
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="resource-group">
|
||||
<div class="group-label">Developer</div>
|
||||
<div class="resource-list">
|
||||
|
||||
<a href="/developer/openapi.json" class="resource-row">
|
||||
<div class="resource-info">
|
||||
<h3>API documentation (OpenAPI spec)</h3>
|
||||
<p>Full reference for the quiXzoom REST API. Authentication, endpoints, webhooks and rate limits.</p>
|
||||
</div>
|
||||
<span class="resource-tag">Docs</span>
|
||||
<span class="resource-arrow">View →</span>
|
||||
</a>
|
||||
|
||||
<a href="/changelog" class="resource-row">
|
||||
<div class="resource-info">
|
||||
<h3>Changelog</h3>
|
||||
<p>Platform updates, API version history and upcoming changes.</p>
|
||||
</div>
|
||||
<span class="resource-tag">Updates</span>
|
||||
<span class="resource-arrow">View →</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="resource-group">
|
||||
<div class="group-label">Trust & compliance</div>
|
||||
<div class="resource-list">
|
||||
|
||||
<a href="/security" class="resource-row">
|
||||
<div class="resource-info">
|
||||
<h3>Security & trust</h3>
|
||||
<p>Data handling practices, GDPR compliance, EU data residency and responsible disclosure policy.</p>
|
||||
</div>
|
||||
<span class="resource-tag">Security</span>
|
||||
<span class="resource-arrow">View →</span>
|
||||
</a>
|
||||
|
||||
<a href="privacy/index.html" class="resource-row">
|
||||
<div class="resource-info">
|
||||
<h3>Privacy policy</h3>
|
||||
<p>How quiXzoom collects, processes and protects personal data for Zoomers, clients and end users.</p>
|
||||
</div>
|
||||
<span class="resource-tag">Legal</span>
|
||||
<span class="resource-arrow">View →</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="footer-top">
|
||||
<div class="footer-grid">
|
||||
|
||||
<div class="footer-brand-col">
|
||||
<a href="/" class="footer-logo">qui<span class="x">X</span>zoom</a>
|
||||
<p class="footer-desc">Crowdsourced field data collection. Zoomers photograph infrastructure, real estate and urban environments on demand.</p>
|
||||
<div class="footer-social-row">
|
||||
<a href="https://www.linkedin.com/company/quixzoom" class="footer-soc-btn" aria-label="LinkedIn" target="_blank" rel="noopener">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||
</a>
|
||||
<a href="https://twitter.com/quixzoom" class="footer-soc-btn" aria-label="X / Twitter" target="_blank" rel="noopener">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
||||
</a>
|
||||
<a href="https://www.instagram.com/quixzoom" class="footer-soc-btn" aria-label="Instagram" target="_blank" rel="noopener">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="footer-trust">
|
||||
<div class="trust-badge"><span class="tb-icon"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span>GDPR compliant</div>
|
||||
<div class="trust-badge"><span class="tb-icon"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10z"/></svg></span>EU data residency</div>
|
||||
<div class="trust-badge"><span class="tb-icon"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg></span>Stripe payouts</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="footer-col-title">Platform</div>
|
||||
<ul class="footer-col-links">
|
||||
<li><a href="#how">How it works</a></li>
|
||||
<li><a href="#missions">Mission types</a></li>
|
||||
<li><a href="#earnings">Zoomer earnings</a></li>
|
||||
<li><a href="/markets/se/">Sverige launch <span class="badge-new">Aug 2026</span></a></li>
|
||||
<li><a href="/markets/">All markets</a></li>
|
||||
<li><a href="https://app.quixzoom.com/join">Join as Zoomer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="footer-col-title">Solutions</div>
|
||||
<ul class="footer-col-links">
|
||||
<li><a href="/for-organisations/">For organisations</a></li>
|
||||
<li><a href="/for-organisations/#use-cases">Use cases</a></li>
|
||||
<li><a href="/for-organisations/#enterprise">Enterprise</a></li>
|
||||
<li><a href="/pricing">Pricing</a></li>
|
||||
<li><a href="/for-organisations/#contact">Request a demo</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="footer-col-title">Developers</div>
|
||||
<ul class="footer-col-links">
|
||||
<li><a href="/developer/openapi.json">OpenAPI spec</a></li>
|
||||
<li><a href="/changelog">Changelog</a></li>
|
||||
<li><a href="/status">System status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="footer-col-title">Resources</div>
|
||||
<ul class="footer-col-links">
|
||||
<li><a href="/academy/">Zoomer Academy</a></li>
|
||||
<li><a href="/resources/">Resources</a></li>
|
||||
<li><a href="/glossary">Glossary</a></li>
|
||||
<li><a href="/security">Security & trust</a></li>
|
||||
<li><a href="/careers/">Careers</a></li>
|
||||
<li><a href="/about">About quiXzoom</a></li>
|
||||
<li><a href="support.html">Support centre</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="footer-col-title">Legal</div>
|
||||
<ul class="footer-col-links">
|
||||
<li><a href="privacy/index.html">Privacy policy</a></li>
|
||||
<li><a href="terms/index.html">Terms of service</a></li>
|
||||
<li><a href="/terms#DAC7B2>DAC7 reporting</a></li>
|
||||
<li><a href="/privacy#gdpr">GDPR rights</a></li>
|
||||
<li><a href="/security#responsible">Responsible disclosure</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border-bottom:1px solid rgba(255,255,255,.07)">
|
||||
<div class="footer-bottom" style="padding-top:16px;padding-bottom:16px">
|
||||
<div class="footer-lang-row">
|
||||
<span class="fl-lbl"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10z"/></svg></span>
|
||||
<a href="https://www.quixzoom.com/" class="flang active">EN</a>
|
||||
<a href="/markets/se/" class="flang">SV</a>
|
||||
<a href="/markets/nl/" class="flang">NL</a>
|
||||
<span class="flang" style="opacity:.35;cursor:default" title="Coming soon">DE</span>
|
||||
<span class="flang" style="opacity:.35;cursor:default" title="Coming soon">FR</span>
|
||||
<span class="flang" style="opacity:.35;cursor:default" title="Coming soon">IT</span>
|
||||
<span class="flang" style="opacity:.35;cursor:default" title="Coming soon">ES</span>
|
||||
<span class="flang" style="opacity:.35;cursor:default" title="Coming soon">FI</span>
|
||||
<span class="flang" style="opacity:.35;cursor:default" title="Coming soon">DK</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<p class="footer-copy">© 2026 quiXzoom · All rights reserved</p>
|
||||
<div class="footer-legal-links">
|
||||
<a href="privacy/index.html">Privacy</a>
|
||||
<a href="terms/index.html">Terms</a>
|
||||
<a href="/security">Security</a>
|
||||
<a href="support.html">Support</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -135,6 +135,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
@@ -351,7 +352,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
</div>
|
||||
|
||||
<div style="background:#fffbeb;border:1px solid #fde68a;border-radius: var(--radius-md);padding:14px;font-size:12px;color:#854d0e">
|
||||
<strong><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> DAC7 deadline:</strong> Rapportering för kalenderår 2026 ska lämnas till Skatteverket senast <strong>31 januari 2027</strong>. Zoomers med intäkter > 2 000 EUR eller > 25 transaktioner måste ha verifierat TIN/PNR.
|
||||
<strong><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> DAC7 deadline:</strong> Rapportering för kalenderår 2026 ska lämnas till Skatteverket senast <strong>31 januari 2027</strong>. Zoomers med intäkter > 2 000 USD eller > 25 transaktioner måste ha verifierat TIN/PNR.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -253,6 +253,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/zoomer-earnings.jpg">
|
||||
<meta name="twitter:title" content="quiXzoom — Reviews & Ratings">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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="Support – quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -511,6 +511,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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 Sverige — Launch 1 augusti 2026">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -758,7 +759,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<h3>DAC7 — EU Direktiv 2021/514</h3>
|
||||
<ul class="compliance-list">
|
||||
<li><span class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Automatisk rapportering av zoomer-inkomster till Skatteverket</li>
|
||||
<li><span class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Rapporteringströskel: >30 uppdrag ELLER >2 000 EUR per år</li>
|
||||
<li><span class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Rapporteringströskel: >30 uppdrag ELLER >2 000 USD per år</li>
|
||||
<li><span class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Personnummer-verifiering via Skatteverket SPAR</li>
|
||||
<li><span class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Årsrapport skickas senast 31 januari varje år</li>
|
||||
<li><span class="check"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span> Du får en kopia som skatteunderlag</li>
|
||||
|
||||
@@ -169,6 +169,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/og-image.jpg">
|
||||
<meta name="twitter:title" content="Terms of Service – quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ p{margin-bottom:1rem}
|
||||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Terms of Service</h1>
|
||||
@@ -36,5 +37,234 @@ p{margin-bottom:1rem}
|
||||
<p>For questions about these terms, contact us at <a href="mailto:support@quixzoom.com">support@quixzoom.com</a>.</p>
|
||||
|
||||
<p><a href="/">← Back to QUIXZOOM</a></p>
|
||||
<!-- quiXzoom Mina Sidor - Universal Header Component -->
|
||||
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
|
||||
|
||||
<style>
|
||||
.qz-auth-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #0A0A0A;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
z-index: 9999;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.qz-auth-bar .qz-logo {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
.qz-auth-bar .qz-logo span {
|
||||
color: #007AFF;
|
||||
}
|
||||
.qz-auth-bar .qz-nav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.qz-auth-bar .qz-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-primary:hover {
|
||||
background: #0056CC;
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost {
|
||||
background: transparent;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
.qz-auth-bar .qz-btn-ghost:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
.qz-auth-bar .qz-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #007AFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
background: #1C1C1E;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
min-width: 200px;
|
||||
display: none;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-menu.active {
|
||||
display: block;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-item:hover {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.qz-auth-bar .qz-dropdown-divider {
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
margin: 8px 0;
|
||||
}
|
||||
.qz-auth-bar .qz-balance {
|
||||
background: rgba(0,122,255,0.15);
|
||||
color: #007AFF;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="qz-auth-bar" class="qz-auth-bar">
|
||||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||||
<div class="qz-nav">
|
||||
<div id="qz-auth-guest">
|
||||
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
|
||||
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
|
||||
</div>
|
||||
<div id="qz-auth-user" style="display:none;">
|
||||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||||
<div class="qz-dropdown">
|
||||
<button class="qz-btn qz-btn-ghost qz-user" onclick="qzToggleDropdown()">
|
||||
<div class="qz-avatar" id="qz-avatar">E</div>
|
||||
<span id="qz-user-name">Erik</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="qz-dropdown-menu" id="qz-dropdown-menu">
|
||||
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/></svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/></svg>
|
||||
Plånbok
|
||||
</a>
|
||||
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
|
||||
Mina uppdrag
|
||||
</a>
|
||||
<div class="qz-dropdown-divider"></div>
|
||||
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
Inställningar
|
||||
</a>
|
||||
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
||||
Logga ut
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||||
const APP_BASE = 'https://app.quixzoom.com';
|
||||
|
||||
// Check auth status
|
||||
async function qzCheckAuth() {
|
||||
try {
|
||||
const res = await fetch(SSO_BASE + '/auth/check', {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.authenticated) {
|
||||
document.getElementById('qz-auth-guest').style.display = 'none';
|
||||
document.getElementById('qz-auth-user').style.display = 'flex';
|
||||
document.getElementById('qz-user-name').textContent = data.user.name || data.user.email;
|
||||
document.getElementById('qz-avatar').textContent = (data.user.name || data.user.email)[0].toUpperCase();
|
||||
if (data.user.balance !== undefined) {
|
||||
document.getElementById('qz-balance').textContent = '$' + data.user.balance.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail - show guest state
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle dropdown
|
||||
window.qzToggleDropdown = function() {
|
||||
document.getElementById('qz-dropdown-menu').classList.toggle('active');
|
||||
};
|
||||
|
||||
// Close dropdown on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.qz-dropdown')) {
|
||||
document.getElementById('qz-dropdown-menu').classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Logout
|
||||
window.qzLogout = async function() {
|
||||
try {
|
||||
await fetch(SSO_BASE + '/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
} catch (e) {}
|
||||
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Init
|
||||
qzCheckAuth();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -104,6 +104,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/mission-map.jpg">
|
||||
<meta name="twitter:title" content="quiXzoom Urban Intelligence Map">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">Urban Map - quiXzoom</h1>
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
||||
@@ -468,6 +468,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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="quiXzoom — Zoomer App">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">Zoomer App - quiXzoom</h1>
|
||||
|
||||
@@ -227,6 +227,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/zoomer-earnings.jpg">
|
||||
<meta name="twitter:title" content="Earnings — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">Zoomer Earnings - quiXzoom</h1>
|
||||
|
||||
@@ -500,6 +500,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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="Zoomer Onboarding — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">Zoomer Onboarding Flow</h1>
|
||||
|
||||
@@ -405,6 +405,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="https://www.quixzoom.com/images/zoomer-earnings.jpg">
|
||||
<meta name="twitter:title" content="Profil — quiXzoom">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="position:absolute;left:-9999px;">Zoomer Profile - quiXzoom</h1>
|
||||
|
||||
@@ -433,6 +433,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
||||
<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">
|
||||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user