Files
boc/quixzoom-landing-fixed/earn/index.html
T
Bernt 1a12fb870b dev-api: add developer portal, OpenAPI spec, and Flatenbadet case study
- 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
2026-07-14 15:27:33 +00:00

694 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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">$330</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">24 missions/week</p>
<p class="tier-earnings">$60480<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">812 missions/week</p>
<p class="tier-earnings">$2401,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">$4502,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>