1a12fb870b
- New /developers/ page with API docs, SDKs, pricing, use cases - OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics - Case study: Glasskiosken i Flatenbadet — complete ROI analysis - Updated /order/ with recurring missions and frequency dropdown
694 lines
27 KiB
HTML
694 lines
27 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Earn as a Zoomer — quiXzoom</title>
|
||
<meta name="description" content="Earn money as a quiXzoom Zoomer. Free to join. No fees. Take photo missions near you and get paid per approved mission.">
|
||
<link rel="canonical" href="https://www.quixzoom.com/earn/">
|
||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||
<style>
|
||
:root{
|
||
--bg-dark:#1a1a1a;--bg-white:#FFFFFF;--bg-off:#F7F7F5;
|
||
--blue:#0066FF;--blue-dark:#0052CC;--blue-pale:rgba(0,102,255,.07);
|
||
--green:#00C853;--text-dark:#1a1a1a;--text-body:#3a3a3a;--text-muted:#6B6B6B;
|
||
--radius-card:16px;--shadow-hover:0 16px 48px rgba(0,0,0,.14);
|
||
--transition:.22s cubic-bezier(.25,.46,.45,.94);
|
||
--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;
|
||
}
|
||
*,*::before,*::after{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;overflow-x:hidden}
|
||
a{color:inherit;text-decoration:none}
|
||
ul{list-style:none}
|
||
button{cursor:pointer;font-family:inherit}
|
||
h1{font-size:clamp(2.5rem,5vw,4rem);font-weight:900;letter-spacing:-.05em;line-height:1}
|
||
h2{font-size:clamp(1.75rem,3vw,2.5rem);font-weight:800;letter-spacing:-.04em;line-height:1.1}
|
||
h3{font-size:var(--text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.3}
|
||
.container{width:100%;max-width:1160px;margin:0 auto;padding:0 24px}
|
||
.section-pad{padding:80px 0}
|
||
.section-header{text-align:center;margin-bottom:56px}
|
||
.section-header h2{color:var(--text-dark);margin-bottom:16px}
|
||
.section-header p{font-size:var(--text-lg);color:var(--text-muted);max-width:580px;margin:0 auto}
|
||
|
||
/* NAV */
|
||
nav{position:sticky;top:0;z-index:300;background:var(--bg-white);border-bottom:1px solid rgba(0,0,0,.08);box-shadow:0 1px 3px rgba(0,0,0,.05)}
|
||
.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:var(--text-dark);letter-spacing:-.04em}
|
||
.nav-logo .x{color:var(--blue)}
|
||
.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:var(--text-dark);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)}
|
||
.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}
|
||
.mobile-menu .btn-mobile{display:block;margin-top:24px;background:var(--blue);color:var(--text-dark);text-align:center;border-radius:var(--radius-lg);padding:16px 24px;font-size:1rem;font-weight:700}
|
||
}
|
||
|
||
/* HERO */
|
||
.hero-earn{background:var(--bg-off);padding:80px 0 64px}
|
||
.hero-earn-inner{max-width:720px}
|
||
.hero-earn h1{color:var(--text-dark);margin-bottom:24px}
|
||
.hero-earn 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-cta{display:flex;gap:12px;flex-wrap:wrap}
|
||
.btn-primary{background:var(--blue);color:var(--text-dark);border:none;border-radius:var(--radius-lg);padding:14px 32px;font-size:1rem;font-weight:700;transition:background .15s;text-decoration:none;display:inline-block}
|
||
.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;text-decoration:none;display:inline-block}
|
||
.btn-ghost:hover{border-color:var(--blue);background:var(--blue-pale)}
|
||
|
||
/* 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(10,10,10,.3)}
|
||
.stat-value{font-size:clamp(1.75rem,3vw,2.5rem);font-weight:900;color:var(--text-dark);letter-spacing:-.04em;line-height:1;display:block;margin-bottom:8px}
|
||
.stat-label{font-size:.9rem;color:rgba(10,10,10,.9);font-weight:500}
|
||
@media(max-width:600px){.stats-grid{grid-template-columns:repeat(2,1fr);gap:1px;background:rgba(10,10,10,.2)}.stat-item{background:var(--blue)}.stat-item::after{display:none}}
|
||
|
||
/* STEPS */
|
||
.steps-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;margin-top:40px}
|
||
.step-card{background:var(--bg-off);border-radius:var(--radius-card);padding:40px 36px}
|
||
.step-number{display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;background:var(--blue);color:var(--text-dark);border-radius:50%;font-size:var(--text-xl);font-weight:900;margin-bottom:24px}
|
||
.step-card h3{color:var(--text-dark);margin-bottom:12px;font-size:var(--text-xl)}
|
||
.step-card p{color:var(--text-muted);font-size:.9625rem;line-height:1.6}
|
||
@media(max-width:768px){.steps-grid{grid-template-columns:1fr;gap:20px}}
|
||
|
||
/* EARNINGS */
|
||
.earnings-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:40px}
|
||
.tier-card{background:var(--bg-off);border:2px solid #E8E8E8;border-radius:var(--radius-card);padding:36px 28px;text-align:center}
|
||
.tier-card.featured{border-color:var(--blue);transform:scale(1.03)}
|
||
.tier-card h3{font-size:1.5rem;font-weight:900;margin-bottom:4px;color:var(--text-dark)}
|
||
.tier-missions{font-size:.9rem;color:var(--text-muted);margin-bottom:20px}
|
||
.tier-earnings{font-size:1.375rem;font-weight:900;color:var(--blue);letter-spacing:-.03em;margin-bottom:16px}
|
||
.tier-features{text-align:left}
|
||
.tier-features li{display:flex;align-items:flex-start;gap:10px;font-size:var(--text-base);padding:6px 0}
|
||
.tier-features li::before{content:'';display:block;width:18px;height:18px;min-width:18px;background:var(--green) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='8' viewBox='0 0 10 8'%3E%3Cpath d='M1 4l2.5 2.5L9 1' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E") no-repeat center;border-radius:50%;margin-top:2px}
|
||
@media(max-width:768px){.earnings-grid{grid-template-columns:1fr}}
|
||
|
||
/* CTA */
|
||
.cta-box{background:var(--bg-off);border-radius:var(--radius-card);padding:48px 32px;text-align:center}
|
||
.cta-box h3{font-size:var(--text-2xl);color:var(--text-dark);margin-bottom:12px}
|
||
.cta-box p{color:var(--text-muted);margin-bottom:24px}
|
||
|
||
/* FOOTER */
|
||
footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:48px 0 32px}
|
||
.footer-inner{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}
|
||
.footer-copy{color:rgba(10,10,10,.6);font-size:var(--text-sm)}
|
||
.footer-links{display:flex;gap:20px}
|
||
.footer-links a{color:rgba(10,10,10,.6);font-size:var(--text-sm);transition:color .15s}
|
||
.footer-links a:hover{color:var(--text-dark)}
|
||
.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>
|
||
|
||
<nav>
|
||
<div class="container">
|
||
<div class="nav-inner">
|
||
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
|
||
<div class="nav-links">
|
||
<a href="/order/">Order</a>
|
||
<a href="/pricing/">Pricing</a>
|
||
<a href="/earn/" style="color:var(--text-dark);font-weight:600">Earn as Zoomer</a>
|
||
<a href="/for-organisations/">For Organisations</a>
|
||
<a href="/about">About</a>
|
||
</div>
|
||
<a href="https://app.quixzoom.com/join" class="btn-nav">Join Free →</a>
|
||
<button class="hamburger" id="hamburger" aria-label="Menu" onclick="document.getElementById('mobile-menu').classList.toggle('open')">
|
||
<span></span><span></span><span></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
<div class="mobile-menu" id="mobile-menu">
|
||
<a href="/order/" onclick="document.getElementById('mobile-menu').classList.remove('open')">Order Field Photography</a>
|
||
<a href="/pricing/" onclick="document.getElementById('mobile-menu').classList.remove('open')">Pricing</a>
|
||
<a href="/earn/" onclick="document.getElementById('mobile-menu').classList.remove('open')">Earn as a Zoomer</a>
|
||
<a href="/for-organisations/" onclick="document.getElementById('mobile-menu').classList.remove('open')">For Organisations</a>
|
||
<a href="/about" onclick="document.getElementById('mobile-menu').classList.remove('open')">About</a>
|
||
<a class="btn-mobile" href="https://app.quixzoom.com/join" onclick="document.getElementById('mobile-menu').classList.remove('open')">Join Free →</a>
|
||
</div>
|
||
|
||
<main>
|
||
|
||
<!-- HERO -->
|
||
<section class="hero-earn">
|
||
<div class="container">
|
||
<div class="hero-earn-inner">
|
||
<div class="hero-badge">Free to Join · No Fees</div>
|
||
<h1>Earn as a Zoomer</h1>
|
||
<p>Take photo missions near you. Get paid per approved mission. No platform fee — you keep 100% of your earnings. Free to join, no commitment.</p>
|
||
<div class="hero-cta">
|
||
<a href="https://app.quixzoom.com/join" class="btn-primary">Join Free →</a>
|
||
<a href="/" class="btn-ghost">Learn More</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- STATS -->
|
||
<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">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>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- HOW IT WORKS -->
|
||
<section class="section-pad">
|
||
<div class="container">
|
||
<div class="section-header">
|
||
<h2>How it works</h2>
|
||
<p>Three steps from signup to payout.</p>
|
||
</div>
|
||
<div class="steps-grid">
|
||
<div class="step-card">
|
||
<div class="step-number">1</div>
|
||
<h3>Register</h3>
|
||
<p>Sign up in under 60 seconds. Verify your identity once. Link your bank account via Stripe Connect. No fees, no commitment.</p>
|
||
</div>
|
||
<div class="step-card">
|
||
<div class="step-number">2</div>
|
||
<h3>Claim Missions</h3>
|
||
<p>Browse active missions on the map. Each shows location, required shots, and exact payment. Claim the ones you want — no forced assignments.</p>
|
||
</div>
|
||
<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>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- EARNINGS -->
|
||
<section class="section-pad" style="background:var(--bg-off)">
|
||
<div class="container">
|
||
<div class="section-header">
|
||
<h2>How much can you earn?</h2>
|
||
<p>Your earnings depend on how many missions you complete. No hourly rate — you control your schedule.</p>
|
||
</div>
|
||
<div class="earnings-grid">
|
||
<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>
|
||
<ul class="tier-features">
|
||
<li>Supplementary income</li>
|
||
<li>No minimum hours</li>
|
||
<li>All standard missions</li>
|
||
</ul>
|
||
</div>
|
||
<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>
|
||
<ul class="tier-features">
|
||
<li>Consistent volume</li>
|
||
<li>Priority mission access</li>
|
||
<li>Build platform rating</li>
|
||
</ul>
|
||
</div>
|
||
<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>
|
||
<ul class="tier-features">
|
||
<li>Maximum volume</li>
|
||
<li>Premium missions</li>
|
||
<li>Performance bonuses</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- CTA -->
|
||
<section class="section-pad">
|
||
<div class="container">
|
||
<div class="cta-box">
|
||
<h3>Ready to start earning?</h3>
|
||
<p>Join free. No fees. No commitment. Launching August 2026.</p>
|
||
<a href="https://app.quixzoom.com/join" class="btn-primary">Join as a Zoomer →</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
|
||
<footer>
|
||
<div class="container">
|
||
<div class="footer-inner">
|
||
<div>
|
||
<p class="footer-copy">© 2026 quiXzoom · Operated by Landvex AB</p>
|
||
<p class="footer-tagline">Raw data by quiXzoom. Intelligence by Landvex.</p>
|
||
</div>
|
||
<div class="footer-links">
|
||
<a href="/">Home</a>
|
||
<a href="/order/">Order</a>
|
||
<a href="/pricing/">Pricing</a>
|
||
<a href="/privacy/">Privacy</a>
|
||
<a href="/terms/">Terms</a>
|
||
</div>
|
||
</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>
|
||
|
||
<!--
|
||
quiXzoom Universal Auth Snippet v2.0
|
||
Add this to ALL quiXzoom sites for seamless cross-domain login
|
||
|
||
Features:
|
||
- Silent login (auto-refresh tokens)
|
||
- Cross-domain cookie sync
|
||
- Real-time auth state updates
|
||
- Mobile app compatible
|
||
-->
|
||
|
||
<!-- Auth bar styles -->
|
||
<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-menu {
|
||
position: relative;
|
||
}
|
||
#qz-auth-bar .qz-dropdown {
|
||
position: absolute;
|
||
top: 40px;
|
||
right: 0;
|
||
background: #1C1C1E;
|
||
border: 1px solid rgba(255,255,255,0.1);
|
||
border-radius: 12px;
|
||
padding: 8px;
|
||
min-width: 220px;
|
||
display: none;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||
}
|
||
#qz-auth-bar .qz-dropdown.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-avatar {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
background: #007AFF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
color: white;
|
||
}
|
||
#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;
|
||
}
|
||
</style>
|
||
|
||
<!-- Auth bar HTML -->
|
||
<div id="qz-auth-bar">
|
||
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
|
||
<div class="qz-nav">
|
||
<!-- Guest view -->
|
||
<div id="qz-guest-view">
|
||
<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>
|
||
|
||
<!-- Authenticated view -->
|
||
<div id="qz-user-view" style="display:none;">
|
||
<span class="qz-balance" id="qz-balance">$0.00</span>
|
||
<div class="qz-user-menu">
|
||
<button class="qz-btn qz-btn-ghost" onclick="qzToggleMenu()" style="display:flex;align-items:center;gap:8px;">
|
||
<div class="qz-avatar" id="qz-avatar">?</div>
|
||
<span id="qz-user-name" style="color:white;">User</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" id="qz-dropdown">
|
||
<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>
|
||
|
||
<!-- SSO Client Script -->
|
||
<script>
|
||
(function() {
|
||
const SSO_BASE = 'https://auth.quixzoom.com';
|
||
|
||
// Check auth status on load
|
||
async function qzCheckAuth() {
|
||
try {
|
||
const res = await fetch(SSO_BASE + '/auth/silent', {
|
||
method: 'GET',
|
||
credentials: 'include',
|
||
});
|
||
|
||
if (res.ok) {
|
||
const data = await res.json();
|
||
if (data.authenticated) {
|
||
qzShowUser(data.user);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Try check endpoint
|
||
const checkRes = await fetch(SSO_BASE + '/auth/check', {
|
||
credentials: 'include',
|
||
});
|
||
|
||
if (checkRes.ok) {
|
||
const checkData = await checkRes.json();
|
||
if (checkData.authenticated && checkData.user) {
|
||
qzShowUser(checkData.user);
|
||
return;
|
||
}
|
||
}
|
||
|
||
qzShowGuest();
|
||
} catch (e) {
|
||
console.log('[QZAuth] Check failed, showing guest');
|
||
qzShowGuest();
|
||
}
|
||
}
|
||
|
||
function qzShowUser(user) {
|
||
document.getElementById('qz-guest-view').style.display = 'none';
|
||
document.getElementById('qz-user-view').style.display = 'flex';
|
||
document.getElementById('qz-user-name').textContent = user.name || user.email;
|
||
document.getElementById('qz-avatar').textContent = (user.name || user.email)[0].toUpperCase();
|
||
|
||
// Dispatch event for other scripts
|
||
window.dispatchEvent(new CustomEvent('qz-auth-change', {
|
||
detail: { authenticated: true, user }
|
||
}));
|
||
}
|
||
|
||
function qzShowGuest() {
|
||
document.getElementById('qz-guest-view').style.display = 'flex';
|
||
document.getElementById('qz-user-view').style.display = 'none';
|
||
|
||
window.dispatchEvent(new CustomEvent('qz-auth-change', {
|
||
detail: { authenticated: false }
|
||
}));
|
||
}
|
||
|
||
window.qzToggleMenu = function() {
|
||
document.getElementById('qz-dropdown').classList.toggle('active');
|
||
};
|
||
|
||
window.qzLogout = async function() {
|
||
try {
|
||
await fetch(SSO_BASE + '/auth/logout', {
|
||
method: 'POST',
|
||
credentials: 'include',
|
||
});
|
||
} catch (e) {}
|
||
|
||
// Clear all cookies
|
||
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=/;';
|
||
document.cookie = 'qz_auth=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
|
||
|
||
qzShowGuest();
|
||
window.location.reload();
|
||
};
|
||
|
||
// Close dropdown on outside click
|
||
document.addEventListener('click', function(e) {
|
||
if (!e.target.closest('.qz-user-menu')) {
|
||
document.getElementById('qz-dropdown').classList.remove('active');
|
||
}
|
||
});
|
||
|
||
// Check auth on page load
|
||
qzCheckAuth();
|
||
|
||
// Re-check auth periodically (every 5 minutes)
|
||
setInterval(qzCheckAuth, 5 * 60 * 1000);
|
||
|
||
// Listen for storage events (login from other tabs)
|
||
window.addEventListener('storage', function(e) {
|
||
if (e.key === 'qz_auth_event') {
|
||
const event = JSON.parse(e.newValue || '{}');
|
||
if (event.type === 'login') {
|
||
qzCheckAuth();
|
||
} else if (event.type === 'logout') {
|
||
qzShowGuest();
|
||
}
|
||
}
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|