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
This commit is contained in:
Bernt
2026-07-14 15:27:33 +00:00
parent 3c522e39f0
commit 1a12fb870b
6296 changed files with 911440 additions and 55607 deletions
+620 -41
View File
@@ -182,6 +182,103 @@
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -202,6 +299,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="quiXzoom — 3D Atlas">
@@ -564,43 +758,376 @@ setInterval(refresh, 30000);
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -609,47 +1136,72 @@ setInterval(refresh, 30000);
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -657,13 +1209,40 @@ setInterval(refresh, 30000);
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();
};
// Init
// 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>
+552 -50
View File
@@ -3,9 +3,17 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About quiXzoom — Global Field Intelligence Network</title>
<title>About quiXzoom</title>
<meta name="description" content="quiXzoom is a global field intelligence network. Learn about our mission, team, and how we're building the world's largest observation network.">
<link rel="canonical" href="https://www.quixzoom.com/about">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta property="og:type" content="website">
<meta property="og:title" content="About — quiXzoom">
<meta property="og:description" content="quiXzoom is a global field intelligence network. Learn about our mission, team, and how we're building the world's largest observation network.">
<meta property="og:url" content="https://www.quixzoom.com/about">
<meta property="og:image" content="https://www.quixzoom.com/images/og-image.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://www.quixzoom.com/images/og-image.jpg">
<style>
:root {
--bg-dark: #f5f5f7;
@@ -21,7 +29,7 @@
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Helvetica Neue', sans-serif;
background: var(--bg-dark);
background: #ffffff;
color: var(--text-dark);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
@@ -42,6 +50,8 @@
.nav-links a { color: var(--text-muted); text-decoration: none; font-weight: 500; transition: color 0.15s; }
.nav-links a:hover { color: var(--text-dark); }
.nav-links a.active { color: var(--blue); }
.btn-nav { background: var(--blue); color: #fff; border: none; border-radius: 24px; padding: 10px 24px; font-size: 0.9375rem; font-weight: 700; transition: background 0.15s; text-decoration: none; }
.btn-nav:hover { background: var(--blue-dark); }
/* Hero */
.hero { padding: 80px 0 60px; text-align: center; background: var(--surface); }
@@ -87,22 +97,129 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<nav>
<nav class="qz-nav">
<div class="container">
<div class="nav-inner">
<a href="/" class="nav-logo">qui<span>X</span>zoom</a>
<div class="nav-links">
<a href="/">Home</a>
<a href="/order/">Order</a>
<a href="/pricing/">Pricing</a>
<a href="/earn/">Earn as Zoomer</a>
<a href="/for-organisations/">For Organisations</a>
<a href="/about" class="active">About</a>
<a href="/pricing">Pricing</a>
<a href="/features">Features</a>
</div>
</div>
<a href="/order/" class="btn-nav">Start Order →</a>
</div>
</nav>
@@ -170,13 +287,13 @@
</div>
</section>
<footer>
<footer class="qz-footer">
<div class="container">
<div class="footer-inner">
<div>© 2026 quiXzoom · Operated by Landvex AB</div>
<div class="footer-links">
<a href="privacy/index.html">Privacy</a>
<a href="terms/index.html">Terms</a>
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
<a href="/support">Support</a>
</div>
</div>
@@ -309,43 +426,376 @@
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -354,47 +804,72 @@
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -402,13 +877,40 @@
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();
};
// Init
// 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>
+620 -41
View File
@@ -30,6 +30,103 @@ body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;background:#F2F2F7;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
/* AAMOS GLOBAL CONTRAST ENFORCEMENT — WCAG AA (4.5:1 minimum) */
@@ -49,6 +146,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head><body>
@@ -232,43 +426,376 @@ load();
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -277,47 +804,72 @@ load();
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -325,13 +877,40 @@ load();
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();
};
// Init
// 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>
</script></body></html>
+621 -42
View File
@@ -132,6 +132,103 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,'SF Pro Te
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -152,6 +249,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -159,7 +353,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="layout">
<!-- SIDEBAR -->
<nav class="sidebar">
<nav class="qz-nav" class="sidebar">
<div class="sidebar-logo">
<div class="logo-brand">quiXzoom</div>
<div class="logo-name">Analytics</div>
@@ -947,43 +1141,376 @@ window.addEventListener('load', ()=>{
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -992,47 +1519,72 @@ window.addEventListener('load', ()=>{
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -1040,13 +1592,40 @@ window.addEventListener('load', ()=>{
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();
};
// Init
// 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>
+620 -41
View File
@@ -407,6 +407,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -428,6 +525,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -1169,43 +1363,376 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -1214,47 +1741,72 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -1262,13 +1814,40 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
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();
};
// Init
// 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>
+620 -41
View File
@@ -203,6 +203,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -224,6 +321,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="App Store Guide — quiXzoom">
@@ -794,43 +988,376 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -839,47 +1366,72 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -887,13 +1439,40 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
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();
};
// Init
// 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>
+105
View File
@@ -22,8 +22,113 @@
.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}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<div class="container">
+105
View File
@@ -23,8 +23,113 @@
.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}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<div class="container">
+621 -42
View File
@@ -127,6 +127,103 @@ body{font-family:-apple-system,'SF Pro Text',system-ui,sans-serif;background:#F2
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -147,6 +244,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -215,7 +409,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<!-- Tab bar -->
<nav class="tab-bar">
<nav class="qz-nav" class="tab-bar">
<div class="tab-item active" onclick="showTab('dashboard',this,'Hem')"><div class="tab-icon"></div><div class="tab-label">Hem</div></div>
<div class="tab-item" onclick="showTab('history',this,'Historik')"><div class="tab-icon"><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 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div><div class="tab-label">Historik</div></div>
<div class="tab-item" onclick="showTab('billing',this,'Fakturering')"><div class="tab-icon"></div><div class="tab-label">Fakturering</div></div>
@@ -612,43 +806,376 @@ loadAll();
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -657,47 +1184,72 @@ loadAll();
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -705,13 +1257,40 @@ loadAll();
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();
};
// Init
// 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>
+621 -42
View File
@@ -504,6 +504,103 @@ html, body {
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -524,13 +621,110 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<!-- Navbar -->
<nav class="navbar">
<nav class="qz-nav" class="navbar">
<div class="navbar-logo"></div>
<div class="navbar-title">DAC7 Rapportering</div>
<div class="navbar-badge">quiXzoom</div>
@@ -1032,43 +1226,376 @@ loadData();
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -1077,47 +1604,72 @@ loadData();
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -1125,13 +1677,40 @@ loadData();
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();
};
// Init
// 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>
+620 -41
View File
@@ -459,6 +459,103 @@ body {
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -479,6 +576,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -950,43 +1144,376 @@ function generateDAC7XML() {
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -995,47 +1522,72 @@ function generateDAC7XML() {
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -1043,13 +1595,40 @@ function generateDAC7XML() {
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();
};
// Init
// 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>
@@ -0,0 +1,556 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>quiXzoom Developer API — Programmatic Photo Ordering</title>
<meta name="description" content="REST API for ordering real-world geotagged photography. Fresh photos, AI-verified, delivered in hours. Perfect for websites, apps, and digital signage.">
<link rel="canonical" href="https://www.quixzoom.com/developers/">
<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:680px;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-dev{background:var(--bg-off);padding:80px 0 64px}
.hero-dev-inner{max-width:720px}
.hero-dev h1{color:var(--text-dark);margin-bottom:24px}
.hero-dev 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)}
/* CODE BLOCKS */
.code-block{background:#1a1a1a;border-radius:var(--radius-md);padding:24px;overflow-x:auto;margin:20px 0}
.code-block pre{color:#e4e4e4;font-family:'SF Mono',Monaco,monospace;font-size:.875rem;line-height:1.6;margin:0}
.code-block .comment{color:#6B6B6B}
.code-block .keyword{color:#ff7b72}
.code-block .string{color:#a5d6ff}
.code-block .number{color:#79c0ff}
.code-block .function{color:#d2a8ff}
/* FEATURES */
.features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:40px}
.feature-card{background:var(--bg-white);border:1.5px solid #E8E8E8;border-radius:var(--radius-card);padding:32px 28px}
.feature-card h3{font-size:1.125rem;font-weight:800;color:var(--text-dark);margin-bottom:10px}
.feature-card p{font-size:var(--text-sm);color:var(--text-body);line-height:1.6}
.feature-icon{font-size:2rem;margin-bottom:12px}
@media(max-width:900px){.features-grid{grid-template-columns:1fr 1fr}}
@media(max-width:600px){.features-grid{grid-template-columns:1fr}}
/* USE CASES */
.usecase-card{background:var(--bg-off);border-radius:var(--radius-card);padding:32px;margin-bottom:24px}
.usecase-card h3{color:var(--text-dark);margin-bottom:12px;font-size:1.25rem}
.usecase-card p{color:var(--text-body);margin-bottom:16px}
.usecase-meta{display:flex;gap:16px;flex-wrap:wrap;margin-top:16px}
.usecase-tag{background:var(--blue-pale);color:var(--blue);padding:4px 12px;border-radius:100px;font-size:.8rem;font-weight:600}
/* PRICING */
.pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:40px}
.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-sub{font-size:.9rem;color:var(--text-muted);margin-bottom:20px}
.price{font-size:2.5rem;font-weight:900;color:var(--text-dark);letter-spacing:-.04em;margin-bottom:8px}
.price span{font-size:1rem;font-weight:500;color:var(--text-muted)}
.pricing-card ul{text-align:left;margin:24px 0}
.pricing-card ul li{display:flex;align-items:flex-start;gap:10px;font-size:var(--text-base);padding:8px 0}
.pricing-card ul 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:900px){.pricing-grid{grid-template-columns:1fr}}
/* SDK */
.sdk-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:32px}
.sdk-card{background:var(--bg-white);border:1.5px solid #E8E8E8;border-radius:var(--radius-sm);padding:20px;text-align:center}
.sdk-card h4{font-size:1rem;font-weight:700;color:var(--text-dark);margin-bottom:4px}
.sdk-card p{font-size:.8rem;color:var(--text-muted)}
@media(max-width:768px){.sdk-grid{grid-template-columns:repeat(2,1fr)}}
/* 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>
</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/">Earn as Zoomer</a>
<a href="/for-organisations/">For Organisations</a>
<a href="/about">About</a>
</div>
<a href="#get-started" class="btn-nav">Get API Key →</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="#get-started" onclick="document.getElementById('mobile-menu').classList.remove('open')">Get API Key →</a>
</div>
<main>
<!-- HERO -->
<section class="hero-dev">
<div class="container">
<div class="hero-dev-inner">
<div class="hero-badge">REST API v1.0</div>
<h1>Photos via API.<br>Real places. Real time.</h1>
<p>Order fresh geotagged photography programmatically. AI-verified, structured metadata, delivered in hours. Build websites, apps, and dashboards with current visual data — not stale stock archives.</p>
<div class="hero-cta">
<a href="#get-started" class="btn-primary">Get API Key →</a>
<a href="#examples" class="btn-ghost">View Examples</a>
</div>
</div>
</div>
</section>
<!-- FEATURES -->
<section class="section-pad">
<div class="container">
<div class="section-header">
<h2>Built for developers</h2>
<p>Everything you need to integrate real-world photography into your applications.</p>
</div>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon"></div>
<h3>Fast Delivery</h3>
<p>Most missions complete within 4-48 hours. Real-time status updates via webhooks. No scheduling, no travel coordination.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🤖</div>
<h3>AI Verification</h3>
<p>Every photo is checked by AI for location accuracy, angle match, quality, and obstructions. Failed photos are automatically retaken.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔄</div>
<h3>Recurring Missions</h3>
<p>Set up monthly, weekly, or daily photo updates. Seasonal adjustments. Automatic pause for holidays or extreme weather.</p>
</div>
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>Structured Data</h3>
<p>Every photo includes GPS coordinates, timestamp, weather conditions, device info, and optional Landvex AI analysis.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔔</div>
<h3>Webhooks</h3>
<p>Get notified when missions are assigned, completed, verified, or delivered. Build reactive workflows without polling.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🌍</div>
<h3>Global Coverage</h3>
<p>Zoomer network in 40+ countries. Urban centers have same-day coverage. Remote areas available with flexible timelines.</p>
</div>
</div>
</div>
</section>
<!-- QUICK START -->
<section class="section-pad" style="background:var(--bg-off)" id="get-started">
<div class="container">
<div class="section-header">
<h2>Quick start</h2>
<p>Order your first photos in 3 API calls.</p>
</div>
<h3 style="margin-bottom:16px">1. Create an order</h3>
<div class="code-block">
<pre><span class="keyword">POST</span> <span class="string">https://api.quixzoom.com/v1/orders</span>
<span class="comment">Authorization: Bearer qz_live_abc123...</span>
{
<span class="string">"client_reference"</span>: <span class="string">"malmo-museums-2026"</span>,
<span class="string">"category"</span>: <span class="string">"cultural_exterior"</span>,
<span class="string">"description"</span>: <span class="string">"Exterior photos of all museums in Malmö"</span>,
<span class="string">"locations"</span>: [
{
<span class="string">"location_id"</span>: <span class="string">"loc_001"</span>,
<span class="string">"name"</span>: <span class="string">"Malmö Konstmuseum"</span>,
<span class="string">"coordinates"</span>: {<span class="string">"lat"</span>: <span class="number">55.6050</span>, <span class="string">"lng"</span>: <span class="number">13.0038</span>},
<span class="string">"requirements"</span>: {
<span class="string">"angles"</span>: [<span class="string">"front"</span>, <span class="string">"entrance"</span>],
<span class="string">"time_of_day"</span>: <span class="string">"daylight"</span>
}
}
],
<span class="string">"recurring"</span>: {
<span class="string">"enabled"</span>: <span class="keyword">true</span>,
<span class="string">"frequency"</span>: <span class="string">"monthly"</span>
}
}</pre>
</div>
<h3 style="margin:32px 0 16px">2. Confirm and pay</h3>
<div class="code-block">
<pre><span class="keyword">POST</span> <span class="string">https://api.quixzoom.com/v1/orders/ord_7f8a9b2c/confirm</span>
<span class="comment">Authorization: Bearer qz_live_abc123...</span>
{
<span class="string">"payment_method"</span>: <span class="string">"card"</span>
}
<span class="comment">// Response: Missions dispatched to Zoomers</span></pre>
</div>
<h3 style="margin:32px 0 16px">3. Receive photos via webhook</h3>
<div class="code-block">
<pre><span class="comment">// Webhook POST to your endpoint:</span>
{
<span class="string">"event"</span>: <span class="string">"photos.delivered"</span>,
<span class="string">"order_id"</span>: <span class="string">"ord_7f8a9b2c"</span>,
<span class="string">"mission_id"</span>: <span class="string">"mis_a3b4c5d6"</span>,
<span class="string">"photos"</span>: [
{
<span class="string">"photo_id"</span>: <span class="string">"img_x7y8z9"</span>,
<span class="string">"url"</span>: <span class="string">"https://cdn.quixzoom.com/v1/photos/img_x7y8z9.jpg"</span>,
<span class="string">"thumbnail"</span>: <span class="string">"https://cdn.quixzoom.com/v1/photos/img_x7y8z9_thumb.jpg"</span>,
<span class="string">"angle"</span>: <span class="string">"front"</span>,
<span class="string">"metadata"</span>: {
<span class="string">"captured_at"</span>: <span class="string">"2026-07-15T11:42:18Z"</span>,
<span class="string">"gps"</span>: {<span class="string">"lat"</span>: <span class="number">55.6051</span>, <span class="string">"lng"</span>: <span class="number">13.0039</span>},
<span class="string">"weather"</span>: {<span class="string">"condition"</span>: <span class="string">"partly_cloudy"</span>, <span class="string">"temp_c"</span>: <span class="number">22</span>}
},
<span class="string">"verification"</span>: {
<span class="string">"status"</span>: <span class="string">"approved"</span>,
<span class="string">"ai_checks"</span>: {
<span class="string">"location_match"</span>: {<span class="string">"score"</span>: <span class="number">0.98</span>, <span class="string">"passed"</span>: <span class="keyword">true</span>},
<span class="string">"angle_match"</span>: {<span class="string">"score"</span>: <span class="number">0.94</span>, <span class="string">"passed"</span>: <span class="keyword">true</span>}
}
}
}
]
}</pre>
</div>
</div>
</section>
<!-- USE CASES -->
<section class="section-pad" id="examples">
<div class="container">
<div class="section-header">
<h2>Use cases</h2>
<p>How developers and agencies use quiXzoom API.</p>
</div>
<div class="usecase-card">
<h3>🏛️ Municipal Websites</h3>
<p>A marketing agency rebuilds Malmö Stad's tourism site. Instead of using 2019 archive photos, they order fresh exterior shots of all 15 museums, 40 parks, and 8 beaches — updated monthly automatically. The website always shows current conditions, seasonal changes, and real crowd levels.</p>
<div class="code-block">
<pre><span class="comment">// Monthly auto-update for all public facilities</span>
<span class="keyword">POST</span> <span class="string">/v1/orders</span>
{
<span class="string">"category"</span>: <span class="string">"cultural_exterior"</span>,
<span class="string">"locations"</span>: museums,
<span class="string">"recurring"</span>: {<span class="string">"enabled"</span>: <span class="keyword">true</span>, <span class="string">"frequency"</span>: <span class="string">"monthly"</span>},
<span class="string">"delivery"</span>: {<span class="string">"webhook_url"</span>: <span class="string">"https://api.malmocity.se/photos/update"</span>}
}</pre>
</div>
<div class="usecase-meta">
<span class="usecase-tag">15 locations</span>
<span class="usecase-tag">$120/month</span>
<span class="usecase-tag">4h delivery</span>
</div>
</div>
<div class="usecase-card">
<h3>🍽️ Restaurant Discovery Apps</h3>
<p>A food app wants real ambiance photos, not staged marketing shots. They order weekly evening photos of trending restaurants — capturing actual crowd levels, lighting, and atmosphere. Users see "busy but welcoming" or "quiet romantic spot" based on real data.</p>
<div class="code-block">
<pre><span class="comment">// Weekly vibe capture for restaurant listings</span>
<span class="keyword">POST</span> <span class="string">/v1/orders</span>
{
<span class="string">"category"</span>: <span class="string">"restaurant_ambiance"</span>,
<span class="string">"requirements"</span>: {
<span class="string">"angles"</span>: [<span class="string">"interior"</span>, <span class="string">"outdoor_seating"</span>],
<span class="string">"time_of_day"</span>: <span class="string">"evening"</span>,
<span class="string">"minimum_crowd"</span>: <span class="string">"moderate"</span>
},
<span class="string">"recurring"</span>: {<span class="string">"enabled"</span>: <span class="keyword">true</span>, <span class="string">"frequency"</span>: <span class="string">"weekly"</span>, <span class="string">"day_of_week"</span>: <span class="number">5</span>}
}</pre>
</div>
<div class="usecase-meta">
<span class="usecase-tag">25 restaurants</span>
<span class="usecase-tag">$200/week</span>
<span class="usecase-tag">AI sentiment analysis</span>
</div>
</div>
<div class="usecase-card">
<h3>🏖️ Beach & Facility Status</h3>
<p>A city app shows real-time beach conditions. Daily photos during summer capture water quality flags, lifeguard presence, crowd density, and facility status. Parents check if the beach is too crowded before leaving home.</p>
<div class="code-block">
<pre><span class="comment">// Daily summer monitoring</span>
<span class="keyword">POST</span> <span class="string">/v1/orders</span>
{
<span class="string">"category"</span>: <span class="string">"beach_facility_status"</span>,
<span class="string">"requirements"</span>: {
<span class="string">"angles"</span>: [<span class="string">"overview"</span>, <span class="string">"water_quality_flag"</span>, <span class="string">"facilities"</span>],
<span class="string">"time_of_day"</span>: <span class="string">"midday"</span>
},
<span class="string">"recurring"</span>: {
<span class="string">"enabled"</span>: <span class="keyword">true</span>,
<span class="string">"frequency"</span>: <span class="string">"daily"</span>,
<span class="string">"season"</span>: <span class="string">"summer_only"</span>,
<span class="string">"pause_conditions"</span>: {<span class="string">"weather_extreme"</span>: <span class="keyword">true</span>}
}
}</pre>
</div>
<div class="usecase-meta">
<span class="usecase-tag">8 beaches</span>
<span class="usecase-tag">$80/day</span>
<span class="usecase-tag">JuneAugust</span>
</div>
</div>
<div class="usecase-card">
<h3>🏪 Retail Compliance</h3>
<p>A franchise chain verifies that all 200 stores display current promotions correctly. Monthly photos check storefront signage, opening hours displays, and promotional materials. AI flags non-compliant locations automatically.</p>
<div class="code-block">
<pre><span class="comment">// Monthly compliance audit</span>
<span class="keyword">POST</span> <span class="string">/v1/orders</span>
{
<span class="string">"category"</span>: <span class="string">"retail_compliance"</span>,
<span class="string">"requirements"</span>: {
<span class="string">"angles"</span>: [<span class="string">"storefront"</span>, <span class="string">"promotional_display"</span>],
<span class="string">"brand_match"</span>: <span class="string">"ICA"</span>
},
<span class="string">"recurring"</span>: {<span class="string">"enabled"</span>: <span class="keyword">true</span>, <span class="string">"frequency"</span>: <span class="string">"monthly"</span>, <span class="string">"deliver_before"</span>: <span class="string">"5th"</span>}
}</pre>
</div>
<div class="usecase-meta">
<span class="usecase-tag">200 stores</span>
<span class="usecase-tag">$600/month</span>
<span class="usecase-tag">AI brand verification</span>
</div>
</div>
<div class="usecase-card">
<h3>🎵 Event Venue Monitoring</h3>
<p>An event app shows real-time crowd levels at concerts and festivals. Photos every 30 minutes during events capture queue lengths, parking availability, and crowd density. Attendees decide when to arrive.</p>
<div class="code-block">
<pre><span class="comment">// Event night monitoring</span>
<span class="keyword">POST</span> <span class="string">/v1/orders</span>
{
<span class="string">"category"</span>: <span class="string">"event_venue_status"</span>,
<span class="string">"requirements"</span>: {
<span class="string">"angles"</span>: [<span class="string">"entrance_queue"</span>, <span class="string">"parking_lot"</span>],
<span class="string">"time_of_day"</span>: <span class="string">"evening"</span>
},
<span class="string">"recurring"</span>: {
<span class="string">"enabled"</span>: <span class="keyword">true</span>,
<span class="string">"frequency"</span>: <span class="string">"daily"</span>,
<span class="string">"time_window"</span>: <span class="string">"19:00-23:00"</span>,
<span class="string">"event_nights_only"</span>: <span class="keyword">true</span>
}
}</pre>
</div>
<div class="usecase-meta">
<span class="usecase-tag">5 venues</span>
<span class="usecase-tag">$50/event</span>
<span class="usecase-tag">Real-time crowd data</span>
</div>
</div>
</div>
</section>
<!-- PRICING -->
<section class="section-pad" style="background:var(--bg-off)">
<div class="container">
<div class="section-header">
<h2>Developer pricing</h2>
<p>Start free. Scale as you grow. No hidden fees.</p>
</div>
<div class="pricing-grid">
<div class="pricing-card">
<h3>Developer</h3>
<p class="pricing-sub">For side projects & startups</p>
<div class="price">$3<span>/photo</span></div>
<ul>
<li>Up to 1,000 photos/month</li>
<li>48-hour delivery</li>
<li>Basic AI verification</li>
<li>60 requests/minute</li>
<li>Email support</li>
</ul>
<a href="#" class="btn-ghost" style="width:100%;text-align:center;display:block">Start Free Trial</a>
</div>
<div class="pricing-card featured">
<h3>Business</h3>
<p class="pricing-sub">For production applications</p>
<div class="price">$2<span>/photo</span></div>
<ul>
<li>Up to 10,000 photos/month</li>
<li>Priority 24-hour delivery</li>
<li>Advanced AI analysis</li>
<li>Webhooks & callbacks</li>
<li>300 requests/minute</li>
<li>Slack support</li>
</ul>
<a href="#" class="btn-primary" style="width:100%;text-align:center;display:block">Get Started</a>
</div>
<div class="pricing-card">
<h3>Enterprise</h3>
<p class="pricing-sub">For large-scale platforms</p>
<div class="price">Custom</div>
<ul>
<li>Unlimited photos</li>
<li>SLA guarantees</li>
<li>Custom AI models</li>
<li>Dedicated account manager</li>
<li>Unlimited API rate</li>
<li>24/7 phone support</li>
</ul>
<a href="#" class="btn-ghost" style="width:100%;text-align:center;display:block">Contact Sales</a>
</div>
</div>
<p style="text-align:center;margin-top:32px;color:var(--text-muted)">Recurring missions: 10% discount (monthly), 15% discount (weekly)</p>
</div>
</section>
<!-- SDKs -->
<section class="section-pad">
<div class="container">
<div class="section-header">
<h2>SDKs & Libraries</h2>
<p>Official client libraries for popular languages.</p>
</div>
<div class="sdk-grid">
<div class="sdk-card">
<h4>JavaScript</h4>
<p>npm install @quixzoom/sdk</p>
</div>
<div class="sdk-card">
<h4>Python</h4>
<p>pip install quixzoom-python</p>
</div>
<div class="sdk-card">
<h4>Go</h4>
<p>go get github.com/quixzoom/go-sdk</p>
</div>
<div class="sdk-card">
<h4>Ruby</h4>
<p>Coming Q3 2026</p>
</div>
</div>
<div class="code-block" style="margin-top:32px">
<pre><span class="comment">// JavaScript SDK example</span>
<span class="keyword">import</span> { QuixzoomClient } <span class="keyword">from</span> <span class="string">'@quixzoom/sdk'</span>;
<span class="keyword">const</span> quixzoom = <span class="keyword">new</span> QuixzoomClient({
apiKey: process.env.QUIXZOOM_API_KEY,
environment: <span class="string">'production'</span>
});
<span class="comment">// Create a recurring order</span>
<span class="keyword">const</span> order = <span class="keyword">await</span> quixzoom.orders.create({
category: <span class="string">'cultural_exterior'</span>,
locations: [{ name: <span class="string">'Malmö Konstmuseum'</span>, coordinates: { lat: <span class="number">55.605</span>, lng: <span class="number">13.004</span> } }],
recurring: { enabled: <span class="keyword">true</span>, frequency: <span class="string">'monthly'</span> }
});
<span class="comment">// Listen for delivery via webhook or poll</span>
quixzoom.webhooks.on(<span class="string">'photos.delivered'</span>, (event) => {
console.log(<span class="string">'New photos:'</span>, event.photos);
});</pre>
</div>
</div>
</section>
<!-- DOCS LINK -->
<section class="section-pad" style="background:var(--bg-off)">
<div class="container" style="text-align:center">
<h2 style="margin-bottom:16px">Full API Reference</h2>
<p style="color:var(--text-muted);margin-bottom:32px;max-width:600px;margin-left:auto;margin-right:auto">Complete OpenAPI specification, authentication guides, webhook setup, and error handling.</p>
<a href="/developers/docs/" class="btn-primary">View API Documentation →</a>
<p style="margin-top:24px;font-size:var(--text-sm);color:var(--text-muted)">
<a href="/developers/openapi.yaml" style="color:var(--blue);font-weight:600" download>Download OpenAPI Spec (YAML)</a>
</p>
</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="/pricing/">Pricing</a>
<a href="/earn/">Earn</a>
<a href="/privacy/">Privacy</a>
<a href="/terms/">Terms</a>
</div>
</div>
</div>
</footer>
</body>
</html>
+232 -41
View File
@@ -398,41 +398,180 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -441,47 +580,72 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -489,13 +653,40 @@ footer{background:var(--bg-white);border-top:1px solid rgba(0,0,0,.08);padding:4
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();
};
// Init
// 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>
+318 -107
View File
@@ -3,9 +3,17 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Features — quiXzoom Field Intelligence Platform</title>
<title>Features — quiXzoom</title>
<meta name="description" content="Discover quiXzoom features: AI-powered quality review, real-time tracking, global coverage, and enterprise-grade security.">
<link rel="canonical" href="https://www.quixzoom.com/features">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta property="og:type" content="website">
<meta property="og:title" content="Features — quiXzoom">
<meta property="og:description" content="Discover quiXzoom features: AI-powered quality review, real-time tracking, global coverage, and enterprise-grade security.">
<meta property="og:url" content="https://www.quixzoom.com/features">
<meta property="og:image" content="https://www.quixzoom.com/images/og-image.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://www.quixzoom.com/images/og-image.jpg">
<style>
:root {
--bg-dark: #f5f5f7;
@@ -22,7 +30,7 @@
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Helvetica Neue', sans-serif;
background: var(--bg-dark);
background: #ffffff;
color: var(--text-dark);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
@@ -43,6 +51,8 @@
.nav-links a { color: var(--text-muted); text-decoration: none; font-weight: 500; transition: color 0.15s; }
.nav-links a:hover { color: var(--text-dark); }
.nav-links a.active { color: var(--blue); }
.btn-nav { background: var(--blue); color: #fff; border: none; border-radius: 24px; padding: 10px 24px; font-size: 0.9375rem; font-weight: 700; transition: background 0.15s; text-decoration: none; }
.btn-nav:hover { background: var(--blue-dark); }
/* Hero */
.hero { padding: 80px 0 60px; text-align: center; background: var(--surface); }
@@ -139,6 +149,103 @@
.feature-row { grid-template-columns: 1fr; gap: 24px; }
.feature-row.reverse { direction: ltr; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style>
@media (max-width: 640px) {
@@ -148,21 +255,128 @@
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<nav>
<nav class="qz-nav">
<div class="container">
<div class="nav-inner">
<a href="/" class="nav-logo">qui<span>X</span>zoom</a>
<div class="nav-links">
<a href="/">Home</a>
<a href="/order/">Order</a>
<a href="/pricing/">Pricing</a>
<a href="/earn/">Earn as Zoomer</a>
<a href="/for-organisations/">For Organisations</a>
<a href="/about">About</a>
<a href="/pricing">Pricing</a>
<a href="/features" class="active">Features</a>
</div>
</div>
<a href="/order/" class="btn-nav">Start Order →</a>
</div>
</nav>
@@ -283,7 +497,7 @@
</div>
</section>
<footer>
<footer class="qz-footer">
<div class="container">
<div class="footer-inner">
<div>© 2026 quiXzoom · Operated by Landvex AB</div>
@@ -422,107 +636,104 @@
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { 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>
+296 -5
View File
@@ -367,6 +367,103 @@ main {
/* HIDDEN during search */
.term-card.hidden { display: none; }
.letter-section.hidden { display: none; }
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<link rel="canonical" href="https://www.quixzoom.com/glossary">
<meta name="robots" content="index, follow">
@@ -389,12 +486,109 @@ main {
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<nav class="nav">
<nav class="qz-nav" class="nav">
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
<div class="nav-divider"></div>
<span class="nav-title">Glossary</span>
@@ -405,7 +599,7 @@ main {
</svg>
<input type="search" id="glossary-search" placeholder="Search terms…" autocomplete="off" aria-label="Search glossary">
</div>
<nav class="nav-az" id="az-nav" aria-label="Jump to letter">
<nav class="qz-nav" class="nav-az" id="az-nav" aria-label="Jump to letter">
<a href="#a">A</a><a href="#b" class="empty">B</a><a href="#c">C</a><a href="#d">D</a>
<a href="#e">E</a><a href="#f">F</a><a href="#g">G</a><a href="#h">H</a>
<a href="#i">I</a><a href="#j">J</a><a href="#k">K</a><a href="#l">L</a>
@@ -417,7 +611,7 @@ main {
</nav>
<main>
<header class="page-header">
<header class="qz-nav" class="page-header">
<h1>Every term.<br>Defined.</h1>
<p>The complete ecosystem reference — from field observations to decision intelligence. Covering quiXzoom, AMOS, and Landvex.</p>
</header>
@@ -2218,10 +2412,10 @@ sections.forEach(s => observer.observe(s));
})();
</script>
<footer style="background:#0d0d10;border-top:1px solid rgba(255,255,255,.07);padding:32px 24px;margin-top:48px">
<footer class="qz-footer" style="background:#0d0d10;border-top:1px solid rgba(255,255,255,.07);padding:32px 24px;margin-top:48px">
<div style="max-width:1080px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px">
<a href="/" style="font-size:1.1rem;font-weight:800;color:#fff;letter-spacing:-.03em;text-decoration:none">qui<span style="color:#3b82f6">X</span>zoom</a>
<nav style="display:flex;gap:20px;flex-wrap:wrap">
<nav class="qz-nav" style="display:flex;gap:20px;flex-wrap:wrap">
<a href="/" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">For Zoomers</a>
<a href="/for-organisations/" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">For Organisations</a>
<a href="/pricing" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Pricing</a>
@@ -2363,6 +2557,103 @@ sections.forEach(s => observer.observe(s));
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+233 -41
View File
@@ -22,6 +22,7 @@
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<h1 style="position:absolute;left:-9999px;">Help — quiXzoom</h1>
<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 -->
@@ -152,41 +153,180 @@
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -195,47 +335,72 @@
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -243,13 +408,40 @@
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();
};
// Init
// 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>
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — दुनिया को कैप्चर करें। पैसे कमाएं। | Zoomer बनें</title>
<meta name="description" content="quiXzoom आपको इंफ्रास्ट्रक्चर, प्रकृति और शहरी वातावरण की तस्वीरें लेने के लिए पैसे देता है। कोई प्लेटफॉर्म शुल्क नहीं। अगस्त 2026 में लॉन्च।">
<link rel="canonical" href="https://quixzoom.asia/hi/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/hi/">
<link rel="alternate" hreflang="hi" href="https://quixzoom.asia/hi/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — Abadikan Dunia. Dapatkan Bayaran. | Jadi Zoomer</title>
<meta name="description" content="quiXzoom membayar Anda untuk memotret infrastruktur, alam, dan lingkungan perkotaan. Tanpa biaya platform. Diluncurkan Agustus 2026.">
<link rel="canonical" href="https://quixzoom.asia/id/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/id/">
<link rel="alternate" hreflang="id" href="https://quixzoom.asia/id/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+196 -2
View File
@@ -257,13 +257,110 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<nav>
<nav class="qz-nav">
<a href="https://www.quixzoom.com/" class="logo">qui<span>X</span>zoom</a>
<a href="https://www.quixzoom.com/join.html" class="nav-cta">Join as Zoomer</a>
</nav>
@@ -390,7 +487,7 @@
</div>
</div>
<footer>
<footer class="qz-footer">
<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>
@@ -521,6 +618,103 @@
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+294 -3
View File
@@ -679,6 +679,103 @@ html,body{overflow-x:clip}
@media(max-width:400px){.hero-stats-pill{flex-wrap:wrap;gap:16px 24px}}
@media(max-width:520px){.qx-calc-row{flex-wrap:wrap}.qx-calc input[type=range]{min-width:0;width:100%}.qx-calc-result{flex-direction:column;gap:8px;text-align:center}.qx-calc-result-amount{font-size:1.375rem}}
@media(max-width:360px){.market-selector{display:none}}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="/locale-data.js"></script>
<style>
@@ -689,6 +786,103 @@ html,body{overflow-x:clip}
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -696,7 +890,7 @@ html,body{overflow-x:clip}
<a href="#main-content" style="position:absolute;top:-100px;left:16px;background:var(--blue);color:var(--text-dark);padding:10px 20px;border-radius:0 0 8px 8px;font-weight:700;font-size:.9rem;z-index:9999;transition:top .2s" onfocus="this.style.top='0'" onblur="this.style.top='-100px'">Skip to main content</a>
<nav>
<nav class="qz-nav">
<div class="container">
<div class="nav-inner">
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
@@ -1423,7 +1617,7 @@ document.addEventListener('qx:market',function(){qxCalcUpdate();});
</main>
<footer>
<footer class="qz-footer">
<div class="footer-top">
<div class="footer-grid">
@@ -1564,7 +1758,7 @@ document.addEventListener('qx:market',function(){qxCalcUpdate();});
<p class="overlay-breadcrumb">quiXzoom / <span id="overlay-breadcrumb-sector"></span></p>
<p class="overlay-type" id="overlay-type-label"></p>
<h2 class="overlay-title" id="overlay-title"></h2>
<div class="layer-nav-wrap"><nav class="layer-nav" id="layer-nav"></nav></div>
<div class="layer-nav-wrap"><nav class="qz-nav" class="layer-nav" id="layer-nav"></nav></div>
</div>
<div class="overlay-body" id="overlay-body"></div>
</div>
@@ -2272,6 +2466,103 @@ document.addEventListener('qx:market', function(e){
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
@@ -172,6 +172,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -193,13 +290,110 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</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>
<nav class="nav-bar">
<nav class="qz-nav" class="nav-bar">
<button class="nav-back" onclick="history.back()"> Tillbaka</button>
<div class="nav-title">iOS-integrationsguide</div>
</nav>
@@ -830,6 +1024,103 @@ class</span> <span class="tp">LocationService</span>: <span class="tp">NSObject<
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
@@ -365,6 +365,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -386,6 +483,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -918,6 +1112,103 @@ setInterval(() => {
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — | Zoomer </title>
<meta name="description" content="quiXzoom 2026820">
<link rel="canonical" href="https://quixzoom.asia/ja/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/ja/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ko" href="https://quixzoom.asia/ko/">
+202
View File
@@ -142,8 +142,113 @@
.success-msg.show {
display: block;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<h1>Become a Zoomer</h1>
@@ -382,6 +487,103 @@
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — . . | Zoomer </title>
<meta name="description" content="quiXzoom , , . . 2026 8 . 20 .">
<link rel="canonical" href="https://quixzoom.asia/ko/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/ko/">
<link rel="alternate" hreflang="ko" href="https://quixzoom.asia/ko/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+194
View File
@@ -6,6 +6,103 @@
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<!-- quiXzoom Mina Sidor - Universal Header Component -->
@@ -135,6 +232,103 @@
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+291
View File
@@ -209,6 +209,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -230,6 +327,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="Launch Campaign — quiXzoom">
@@ -711,6 +905,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
@@ -406,6 +406,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -427,6 +524,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -961,6 +1155,103 @@ render();
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+291
View File
@@ -369,6 +369,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -390,6 +487,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
@@ -785,6 +979,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+292 -1
View File
@@ -465,6 +465,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -486,6 +583,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="quiXzoom Launch — Sverige 2026">
@@ -503,7 +697,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<body>
<!-- Nav Bar -->
<nav class="nav-bar">
<nav class="qz-nav" class="nav-bar">
<a class="nav-back" href="aamos-hub/index.html">
<svg width="10" height="17" viewBox="0 0 10 17" fill="none"><path d="M9 1L1 8.5L9 16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
Hub
@@ -1038,6 +1232,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+194
View File
@@ -450,6 +450,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -471,6 +568,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
+299
View File
@@ -222,6 +222,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -243,6 +340,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -257,6 +451,14 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
@@ -762,6 +964,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+300
View File
@@ -95,6 +95,103 @@
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -115,6 +212,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="quiXzoom — Live Mission Map">
@@ -128,8 +322,17 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<h1 style="position:absolute;left:-9999px;">Mission Map — quiXzoom</h1>
<div class="header">
<div class="header-left">
@@ -505,6 +708,103 @@ connect();
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — Rakam Dunia. Dapatkan Bayaran. | Jadi Zoomer</title>
<meta name="description" content="quiXzoom membayar anda untuk merakam infrastruktur, alam semula jadi, dan persekitaran bandar. Tiada yuran platform. Dilancarkan Ogos 2026.">
<link rel="canonical" href="https://quixzoom.asia/ms/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/ms/">
<link rel="alternate" hreflang="ms" href="https://quixzoom.asia/ms/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+107 -2
View File
@@ -114,12 +114,117 @@
.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}}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<nav>
<nav class="qz-nav">
<div class="container">
<div class="nav-inner">
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
@@ -292,7 +397,7 @@
</main>
<footer>
<footer class="qz-footer">
<div class="container">
<div class="footer-inner">
<div>
+206 -3
View File
@@ -2,9 +2,10 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="canonical" href="https://www.quixzoom.com/privacy.html">
<link rel="canonical" href="https://www.quixzoom.com/privacy.html">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Privacy Policy quiXzoom</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta name="description" content="quiXzoom Privacy Policy — how we collect, use, and protect your personal data.">
<style>
:root {
@@ -70,6 +71,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
@@ -84,10 +182,18 @@
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<header>
<header class="qz-nav">
<div class="logo">quiXzoom</div>
<h1>Privacy Policy</h1>
<p>Last updated: 18 June 2026 &nbsp;·&nbsp; Version 1.1</p>
@@ -330,7 +436,7 @@
</div>
</div>
<footer>
<footer class="qz-footer">
<p>© 2026 Landvex AB · <a href="terms/index.html">Terms of Service</a> · <a href="privacy/index.html">Privacy Policy</a></p>
<p style="margin-top:4px;">quiXzoom is a trademark of Landvex AB</p>
</footer>
@@ -462,6 +568,103 @@
display: none;
}
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
+232 -41
View File
@@ -169,41 +169,180 @@ p{margin-bottom:1rem}
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -212,47 +351,72 @@ p{margin-bottom:1rem}
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -260,13 +424,40 @@ p{margin-bottom:1rem}
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();
};
// Init
// 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>
+194
View File
@@ -90,6 +90,103 @@
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -110,6 +207,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
+105
View File
@@ -109,8 +109,113 @@
font-size: 24px;
margin-bottom: 4px;
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<div class="logo">quiXzoom</div>
+194
View File
@@ -129,6 +129,103 @@
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -149,6 +246,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
+232 -41
View File
@@ -212,41 +212,180 @@
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -255,47 +394,72 @@
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -303,13 +467,40 @@
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();
};
// Init
// 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>
+195 -1
View File
@@ -114,6 +114,103 @@ td.mono{font-variant-numeric:tabular-nums;font-family:'SF Mono',monospace;font-s
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -134,13 +231,110 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<div class="layout">
<!-- SIDEBAR -->
<nav class="sidebar">
<nav class="qz-nav" class="sidebar">
<div class="sidebar-logo">
<div class="brand">AAMOS · quiXzoom</div>
<div class="name">quiX<span>zoom</span></div>
+194
View File
@@ -221,6 +221,103 @@ body { font-family: var(--font-text); background: var(--background-secondary); c
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -241,6 +338,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="quiXzoom — Reviews & Ratings">
+99 -2
View File
@@ -164,13 +164,110 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<nav>
<nav class="qz-nav">
<span class="nav-logo">qui<span class="x">X</span>zoom</span>
</nav>
@@ -263,7 +360,7 @@
</div>
</main>
<footer>
<footer class="qz-footer">
<p>© 2026 quiXzoom · <a href="/">quixzoom.com</a> · <a href="/privacy/">Privacy</a> · <a href="/terms/">Terms</a></p>
<p style="margin-top:8px;">quiXzoom — also known as: quixzoom · quickzoom · quix zoom · quick zoom · quizoom · QuiXzoom</p>
</footer>
+204 -2
View File
@@ -197,6 +197,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -218,6 +315,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -232,10 +426,18 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<header>
<header class="qz-nav">
<div class="logo">quiXzoom</div>
<h1>Support</h1>
<p>
@@ -395,7 +597,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<footer>
<footer class="qz-footer">
<p>© 2026 quiXzoom · <a href="terms/index.html">Terms of Service</a> · <a href="privacy/index.html">Privacy Policy</a></p>
</footer>
+197 -3
View File
@@ -486,6 +486,103 @@
.hero { padding: 3rem 1.5rem 3rem; }
.countdown-container { gap: 1rem; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<style id="wcag-contrast-enforcement">
@@ -499,6 +596,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
[style*="rgba(255,255,255,0.3)"] { color: rgba(255,255,255,0.72) !important; }
[style*="opacity: 0.4"] { opacity: 0.72 !important; }
[style*="opacity: 0.3"] { opacity: 0.72 !important; }
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="quiXzoom Sverige — Launch 1 augusti 2026">
@@ -516,7 +710,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<body>
<!-- Nav -->
<nav>
<nav class="qz-nav">
<div class="nav-logo">qui<span>X</span>zoom</div>
<div class="nav-links">
<a href="#how-it-works">Hur det fungerar</a>
@@ -528,7 +722,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</nav>
<!-- Hero -->
<header class="hero">
<header class="qz-nav" class="hero">
<div class="launch-badge">
<span class="dot"></span>
Sverige-launch — 1 augusti 2026
@@ -800,7 +994,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</section>
<!-- Footer -->
<footer>
<footer class="qz-footer">
<div class="footer-logo">qui<span>X</span>zoom Sverige</div>
<div style="margin-bottom:0.75rem">
<a href="/terms/">Användarvillkor</a>
+206 -3
View File
@@ -2,9 +2,10 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="canonical" href="https://www.quixzoom.com/terms.html">
<link rel="canonical" href="https://www.quixzoom.com/terms.html">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Terms of Service quiXzoom</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta name="description" content="quiXzoom Terms of Service — rules and guidelines for using the quiXzoom platform.">
<style>
:root {
@@ -136,6 +137,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -157,6 +255,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="Terms of Service quiXzoom">
@@ -170,10 +365,18 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<header>
<header class="qz-nav">
<div class="logo">quiXzoom</div>
<h1>Terms of Service</h1>
<p>Last updated: 18 May 2026 &nbsp;·&nbsp; Version 1.0</p>
@@ -362,7 +565,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
</div>
<footer>
<footer class="qz-footer">
<p>© 2026 quiXzoom · Operated by Landvex AB · <a href="terms/index.html">Terms of Service</a> · <a href="privacy/index.html">Privacy Policy</a></p>
</footer>
+232 -41
View File
@@ -166,41 +166,180 @@ p{margin-bottom:1rem}
}
</style>
<div id="qz-auth-bar" class="qz-auth-bar">
<!--
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">
<div id="qz-auth-guest">
<!-- 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>
<div id="qz-auth-user" style="display:none;">
<!-- Authenticated view -->
<div id="qz-user-view" 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>
<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-menu" id="qz-dropdown-menu">
<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>
<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>
<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>
<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>
<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>
<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>
@@ -209,47 +348,72 @@ p{margin-bottom:1rem}
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
const APP_BASE = 'https://app.quixzoom.com';
// Check auth status
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/check', {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
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);
}
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) {
// Silent fail - show guest state
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
// Toggle dropdown
window.qzToggleDropdown = function() {
document.getElementById('qz-dropdown-menu').classList.toggle('active');
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');
};
// 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', {
@@ -257,13 +421,40 @@ p{margin-bottom:1rem}
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();
};
// Init
// 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>
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — ถ่ายภาพโลก รับรายได้ | เป็น Zoomer</title>
<meta name="description" content="quiXzoom จ่ายเงินให้คุณถ่ายภาพโครงสร้างพื้นฐาน ธรรมชาติ และสภาพแวดล้อมในเมือง ไม่มีค่าธรรมเนียมแพลตฟอร์ม เปิดตัวสิงหาคม 2026">
<link rel="canonical" href="https://quixzoom.asia/th/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/th/">
<link rel="alternate" hreflang="th" href="https://quixzoom.asia/th/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+97
View File
@@ -78,6 +78,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<meta property="og:type" content="website">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — Chụp ảnh thế giới. Nhận thù lao. | Trở thành Zoomer</title>
<meta name="description" content="quiXzoom trả tiền cho bạn chụp ảnh cơ sở hạ tầng, thiên nhiên và môi trường đô thị. Không phí nền tảng. Ra mắt tháng 8 năm 2026.">
<link rel="canonical" href="https://quixzoom.asia/vi/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/vi/">
<link rel="alternate" hreflang="vi" href="https://quixzoom.asia/vi/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — | Zoomer</title>
<meta name="description" content="quiXzoom 2026820">
<link rel="canonical" href="https://quixzoom.asia/zh-cn/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
<link rel="alternate" hreflang="ko" href="https://quixzoom.asia/ko/">
+1
View File
@@ -6,6 +6,7 @@
<title>quiXzoom — | Zoomer</title>
<meta name="description" content="quiXzoom 2026820">
<link rel="canonical" href="https://quixzoom.asia/zh-tw/">
<meta http-equiv="refresh" content="0; url=https://quixzoom.asia/zh-tw/">
<link rel="alternate" hreflang="zh-TW" href="https://quixzoom.asia/zh-tw/">
<link rel="alternate" hreflang="zh-CN" href="https://quixzoom.asia/zh-cn/">
<link rel="alternate" hreflang="ja" href="https://quixzoom.asia/ja/">
+207 -5
View File
@@ -434,6 +434,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -455,6 +552,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -469,13 +663,21 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<h1 style="position:absolute;left:-9999px;">Zoomer App - quiXzoom</h1>
<!-- KARTA TAB -->
<div id="page-karta" class="tab-page active">
<nav class="nav-bar">
<nav class="qz-nav" class="nav-bar">
<span class="nav-title nav-logo">quiXzoom</span>
<button class="nav-btn-sm" onclick="refreshMap()"></button>
</nav>
@@ -513,7 +715,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- MINA UPPDRAG TAB -->
<div id="page-mina" class="tab-page">
<nav class="nav-bar">
<nav class="qz-nav" class="nav-bar">
<span class="nav-title">Mina uppdrag</span>
</nav>
<div class="segment-wrap">
@@ -540,7 +742,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- INTÄKTER TAB -->
<div id="page-intakter" class="tab-page">
<nav class="nav-bar">
<nav class="qz-nav" class="nav-bar">
<span class="nav-title">Intäkter</span>
</nav>
<div class="earnings-hero">
@@ -605,7 +807,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<!-- LEADERBOARD TAB -->
<div id="page-leaderboard" class="tab-page">
<nav class="nav-bar">
<nav class="qz-nav" class="nav-bar">
<span class="nav-title">Leaderboard</span>
</nav>
<div class="lb-hero">
@@ -627,7 +829,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
</div>
<!-- TAB BAR -->
<nav class="tab-bar">
<nav class="qz-nav" class="tab-bar">
<button class="tab-item active" id="tab-karta" onclick="switchTab('karta')">
<span class="tab-icon"></span>
<span class="tab-label">Karta</span>
+203 -1
View File
@@ -193,6 +193,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -214,6 +311,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -228,6 +422,14 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<h1 style="position:absolute;left:-9999px;">Zoomer Earnings - quiXzoom</h1>
@@ -320,7 +522,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="toast" id="toast"></div>
<!-- Tab Bar -->
<nav class="tab-bar">
<nav class="qz-nav" class="tab-bar">
<a class="tab-item" href="zoomer-app.html">
<span class="tab-icon"></span>
<span class="tab-label">Missions</span>
@@ -466,6 +466,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -487,6 +584,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -501,6 +695,14 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<h1 style="position:absolute;left:-9999px;">Zoomer Onboarding Flow</h1>
+203 -1
View File
@@ -371,6 +371,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -392,6 +489,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -406,6 +600,14 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>
<h1 style="position:absolute;left:-9999px;">Zoomer Profile - quiXzoom</h1>
@@ -630,7 +832,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<div class="toast" id="toast"></div>
<!-- Tab Bar -->
<nav class="tab-bar">
<nav class="qz-nav" class="tab-bar">
<a class="tab-item" href="zoomer-app.html">
<span class="tab-icon"></span>
<span class="tab-label">Uppdrag</span>
+202
View File
@@ -399,6 +399,103 @@
.card { padding: 1.5rem !important; }
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
@@ -420,6 +517,103 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
.grid { grid-template-columns: 1fr !important; }
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
}
/* QUIXZOOM STANDARD — Dark Theme */
:root {
--qz-bg: #0a0a0a;
--qz-surface: #111111;
--qz-surface-2: #1a1a1a;
--qz-border: rgba(255,255,255,0.08);
--qz-text: #e8e8e8;
--qz-text-dim: #a0a0a0;
--qz-text-muted: #6b6b6b;
--qz-blue: #007AFF;
--qz-blue-dark: #0056CC;
--qz-radius: 10px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
background: var(--qz-bg);
color: var(--qz-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
/* Navigation */
.qz-nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 56px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--qz-border);
}
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
.qz-nav-links a:hover { color: #fff; }
/* Buttons */
.qz-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 12px 24px; border-radius: var(--qz-radius);
font-size: 15px; font-weight: 600; text-decoration: none;
transition: all 0.2s; cursor: pointer; border: none;
}
.qz-btn-primary {
background: var(--qz-blue); color: #fff;
}
.qz-btn-primary:hover {
background: var(--qz-blue-dark); transform: translateY(-1px);
}
.qz-btn-ghost {
background: transparent; color: var(--qz-text);
border: 1px solid var(--qz-border);
}
.qz-btn-ghost:hover {
border-color: var(--qz-blue); color: var(--qz-blue);
}
/* Cards */
.qz-card {
background: var(--qz-surface);
border: 1px solid var(--qz-border);
border-radius: var(--qz-radius);
padding: 24px;
transition: transform 0.2s, border-color 0.2s;
}
.qz-card:hover {
transform: translateY(-2px);
border-color: rgba(0,122,255,0.3);
}
/* Footer */
.qz-footer {
border-top: 1px solid var(--qz-border);
padding: 40px 24px;
text-align: center;
color: var(--qz-text-dim);
font-size: 14px;
}
/* Banner */
.qz-banner {
background: #001E50;
color: rgba(255,255,255,0.55);
text-align: center;
padding: 10px;
font-size: 12px;
}
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
@media (max-width: 768px) {
.qz-nav-links { display: none; }
}
</style>
<script src="i18n.js"></script>
<meta property="og:type" content="website">
@@ -434,6 +628,14 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
<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>
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
</head>
<body>