e0b66ae824
The new webhook wraps data in an outer array: [{data: [...]}].
The old parsing only handled {data: [...]} directly, so .data was
undefined and the server list ended up empty — causing the location
to fall through to the slugified default.
712 lines
31 KiB
HTML
712 lines
31 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Add Agent — derez.ai</title>
|
|
<meta name="robots" content="noindex, nofollow" />
|
|
<style>
|
|
/* ═══════════════════════════════════════════════════════════════
|
|
DESIGN TOKENS (matches app.derez.ai design system)
|
|
═══════════════════════════════════════════════════════════════ */
|
|
:root {
|
|
--bg: #000810;
|
|
--bg-card: #010f20;
|
|
--bg-inner: #000d1a;
|
|
--bg-hover: #001a30;
|
|
--bg-input: #000813;
|
|
--border: #0a3060;
|
|
--border-hi: rgba(0,245,255,0.45);
|
|
--accent: #00f5ff;
|
|
--accent-dim: #002535;
|
|
--danger: #ff3b3b;
|
|
--danger-dim: #3b0000;
|
|
--success: #00ff88;
|
|
--warning: #ffaa00;
|
|
--text: #c8f0ff;
|
|
--text-muted: #2e6a80;
|
|
--radius: 8px;
|
|
--radius-card: 18px;
|
|
--shadow: 0 0 0 1px rgba(0,245,255,0.25), 0 0 60px rgba(0,245,255,0.12), 0 28px 72px rgba(0,0,0,0.9);
|
|
--max-width: 1120px;
|
|
}
|
|
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
html {
|
|
scroll-behavior: smooth;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
body {
|
|
font-family: "Courier New", Courier, monospace;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Ambient glow */
|
|
body::before {
|
|
content: "";
|
|
position: fixed;
|
|
inset: 0;
|
|
background:
|
|
radial-gradient(ellipse 60% 50% at 25% 30%, rgba(0, 245, 255, 0.06) 0%, transparent 70%),
|
|
radial-gradient(ellipse 50% 60% at 75% 70%, rgba(0, 200, 255, 0.04) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
|
|
h1, h2, h3, h4 { font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; }
|
|
h2 { font-size: clamp(1.6rem, 3.5vw, 2.4rem); margin-bottom: 0.6em; }
|
|
p { color: var(--text-muted); font-size: 14px; }
|
|
|
|
.container {
|
|
max-width: var(--max-width);
|
|
margin: 0 auto;
|
|
padding: 0 24px;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* ── Buttons ─────────────────────────────────────────────── */
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 22px;
|
|
border-radius: var(--radius);
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
text-decoration: none;
|
|
transition: opacity 0.15s, background 0.15s, transform 0.1s;
|
|
}
|
|
.btn:hover { opacity: 0.85; text-decoration: none; }
|
|
.btn:active { transform: scale(0.97); }
|
|
|
|
.btn-primary {
|
|
background: transparent;
|
|
color: #fff;
|
|
border: 1px solid var(--accent);
|
|
box-shadow: 0 0 8px rgba(0,245,255,0.35), inset 0 0 8px rgba(0,245,255,0.04);
|
|
}
|
|
.btn-primary:hover {
|
|
background: rgba(0,245,255,0.08);
|
|
box-shadow: 0 0 14px rgba(0,245,255,0.5), inset 0 0 10px rgba(0,245,255,0.06);
|
|
}
|
|
.btn-lg {
|
|
padding: 12px 28px;
|
|
font-size: 13px;
|
|
border-radius: 8px;
|
|
}
|
|
.btn-block {
|
|
width: 100%;
|
|
justify-content: center;
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════════════════
|
|
PRICING (1:1 copy from index.html)
|
|
══════════════════════════════════════════════════════════ */
|
|
section {
|
|
padding: 80px 0;
|
|
}
|
|
|
|
.section-title {
|
|
text-align: center;
|
|
max-width: 640px;
|
|
margin: 0 auto 16px;
|
|
}
|
|
.section-sub {
|
|
text-align: center;
|
|
max-width: 560px;
|
|
margin: 0 auto 50px;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.pricing-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 20px;
|
|
max-width: 960px;
|
|
margin: 0 auto;
|
|
}
|
|
.pricing-card {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-card);
|
|
padding: 36px 28px;
|
|
text-align: center;
|
|
box-shadow: var(--shadow);
|
|
position: relative;
|
|
transition: border-color 0.2s, transform 0.2s;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.pricing-card.featured {
|
|
border-color: var(--accent);
|
|
background: linear-gradient(180deg, rgba(0, 245, 255, 0.05) 0%, var(--bg-card) 100%);
|
|
transform: scale(1.04);
|
|
}
|
|
.pricing-perks {
|
|
list-style: none;
|
|
text-align: left;
|
|
margin: 14px 0 24px;
|
|
flex: 1;
|
|
}
|
|
.pricing-card .btn-block {
|
|
margin-top: auto;
|
|
}
|
|
.pricing-badge {
|
|
position: absolute;
|
|
top: -11px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: var(--accent);
|
|
color: #000;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
padding: 3px 18px;
|
|
border-radius: 20px;
|
|
}
|
|
.pricing-badge-coupon {
|
|
display: none;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: #f59e0b;
|
|
background: rgba(245, 158, 11, 0.1);
|
|
border: 1px solid rgba(245, 158, 11, 0.25);
|
|
border-radius: 6px;
|
|
padding: 3px 10px;
|
|
margin-top: 8px;
|
|
margin-bottom: 4px;
|
|
line-height: 1.4;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
.pricing-badge-coupon.visible { display: inline-block; }
|
|
.pricing-badge-coupon.free {
|
|
color: #22c55e;
|
|
background: rgba(34, 197, 94, 0.1);
|
|
border-color: rgba(34, 197, 94, 0.25);
|
|
}
|
|
.pricing-plan-name {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
color: var(--accent);
|
|
margin-bottom: 6px;
|
|
}
|
|
.pricing-amount {
|
|
font-size: 3rem;
|
|
font-weight: 800;
|
|
color: var(--text);
|
|
line-height: 1;
|
|
}
|
|
.pricing-amount .pricing-currency { font-size: 1.4rem; vertical-align: super; }
|
|
.pricing-period {
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
color: var(--text-muted);
|
|
}
|
|
.pricing-yearly-bonus {
|
|
display: none;
|
|
font-size: 12px;
|
|
color: var(--success);
|
|
font-weight: 600;
|
|
margin-top: 4px;
|
|
}
|
|
.pricing-yearly-bonus.show { display: block; }
|
|
.pricing-plan-desc {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
margin: 10px 0 14px;
|
|
line-height: 1.5;
|
|
}
|
|
.pricing-plan-quota {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
padding: 8px 0;
|
|
border-top: 1px solid var(--border);
|
|
border-bottom: 1px solid var(--border);
|
|
margin-bottom: 14px;
|
|
}
|
|
.pricing-perks li {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 8px;
|
|
padding: 4px 0;
|
|
font-size: 13px;
|
|
color: var(--text);
|
|
}
|
|
.pricing-perks li svg {
|
|
flex-shrink: 0;
|
|
margin-top: 3px;
|
|
color: var(--text-muted);
|
|
}
|
|
.pricing-perks li .plus-icon {
|
|
color: var(--success);
|
|
font-weight: 700;
|
|
font-size: 15px;
|
|
line-height: 1;
|
|
margin-top: 3px;
|
|
flex-shrink: 0;
|
|
}
|
|
.pricing-perks li.new-feature { color: var(--text); }
|
|
|
|
/* ── Config box ─────────────────────────────── */
|
|
.pricing-config-box {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 12px;
|
|
margin-bottom: 36px;
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-card);
|
|
padding: 14px 24px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.pricing-config-label {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
}
|
|
.pricing-config-select {
|
|
appearance: none;
|
|
-webkit-appearance: none;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
font-size: 13px;
|
|
padding: 6px 30px 6px 12px;
|
|
cursor: pointer;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 8px center;
|
|
min-width: 110px;
|
|
}
|
|
.pricing-config-select:hover { border-color: var(--accent); }
|
|
.pricing-config-select:focus { border-color: var(--accent); }
|
|
.pricing-config-select.no-seats { border-color: var(--danger); box-shadow: 0 0 0 2px rgba(255, 59, 59, 0.2); }
|
|
.pricing-config-input {
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text);
|
|
font-size: 13px;
|
|
padding: 6px 12px;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
width: 120px;
|
|
}
|
|
.pricing-config-input:focus { border-color: var(--accent); }
|
|
.pricing-config-input::placeholder { color: var(--text-muted); opacity: 0.6; }
|
|
#pricing-email { width: 156px; }
|
|
#pricing-email.invalid { border-color: var(--danger); box-shadow: 0 0 0 2px rgba(255, 59, 59, 0.2); }
|
|
|
|
.pricing-toast {
|
|
position: fixed;
|
|
bottom: 100px;
|
|
left: 50%;
|
|
transform: translateX(-50%) translateY(20px);
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--danger);
|
|
border-radius: var(--radius-card);
|
|
padding: 16px 24px;
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
|
font-size: 14px;
|
|
color: var(--text);
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: opacity 0.3s, transform 0.3s, visibility 0.3s;
|
|
z-index: 9999;
|
|
max-width: 420px;
|
|
text-align: center;
|
|
pointer-events: none;
|
|
}
|
|
.pricing-toast.show {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
transform: translateX(-50%) translateY(0);
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.pricing-grid { grid-template-columns: 1fr; max-width: 420px; }
|
|
.pricing-card.featured { transform: none; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<section id="pricing">
|
|
<div class="container">
|
|
|
|
<!-- ── Config: agent, billing, location ── -->
|
|
<div class="pricing-config-box">
|
|
<span class="pricing-config-label">Configure and buy your agent</span>
|
|
<select class="pricing-config-select" id="pricing-agent">
|
|
<option value="hermes" selected>Hermes</option>
|
|
</select>
|
|
<select class="pricing-config-select" id="pricing-period">
|
|
<option value="monthly" selected>Monthly</option>
|
|
<option value="yearly">Yearly</option>
|
|
</select>
|
|
<select class="pricing-config-select" id="pricing-location">
|
|
<option value="">Loading locations...</option>
|
|
</select>
|
|
<input type="email" class="pricing-config-input" id="pricing-email" placeholder="Email" autocomplete="email" />
|
|
<input type="text" class="pricing-config-input" id="pricing-coupon" placeholder="Coupon code" />
|
|
<input type="hidden" id="utm_source" />
|
|
<input type="hidden" id="utm_medium" />
|
|
<input type="hidden" id="utm_campaign" />
|
|
<input type="hidden" id="utm_term" />
|
|
<input type="hidden" id="utm_content" />
|
|
</div>
|
|
|
|
<!-- ── Plans grid ── -->
|
|
<div class="pricing-grid">
|
|
|
|
<!-- Trainee -->
|
|
<div class="pricing-card">
|
|
<div class="pricing-plan-name">Trainee</div>
|
|
<div class="pricing-amount" id="amount-trainee">
|
|
<span class="pricing-currency">$</span>4<span class="pricing-decimal">.99</span>
|
|
</div>
|
|
<div class="pricing-period" id="period-trainee">/ month</div>
|
|
<div class="pricing-badge-coupon" id="badge-trainee">First Month <span class="badge-price">—</span></div>
|
|
<div class="pricing-yearly-bonus" id="bonus-trainee">✨ +$4.99 OpenRouter credits</div>
|
|
<p class="pricing-plan-desc">For getting started with your first agent.</p>
|
|
<div class="pricing-plan-quota">1GB RAM · 10GB HDD</div>
|
|
<ul class="pricing-perks">
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Your Agent's Office
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Management dashboard
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Daily automatic backups
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
OpenRouter key included
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Prompt Builder & Skills
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Support chat with human escalation
|
|
</li>
|
|
</ul>
|
|
<button class="btn btn-primary btn-block" onclick="hireAgent('trainee')">Continue with Stripe</button>
|
|
</div>
|
|
|
|
<!-- Junior -->
|
|
<div class="pricing-card featured">
|
|
<span class="pricing-badge">Popular</span>
|
|
<div class="pricing-plan-name">Junior</div>
|
|
<div class="pricing-amount" id="amount-junior">
|
|
<span class="pricing-currency">$</span>9<span class="pricing-decimal">.50</span>
|
|
</div>
|
|
<div class="pricing-period" id="period-junior">/ month</div>
|
|
<div class="pricing-badge-coupon" id="badge-junior">First Month <span class="badge-price">—</span></div>
|
|
<div class="pricing-yearly-bonus" id="bonus-junior">✨ +$9.50 OpenRouter credits</div>
|
|
<p class="pricing-plan-desc">The sweet spot for most users.</p>
|
|
<div class="pricing-plan-quota">2GB RAM · 20GB HDD</div>
|
|
<ul class="pricing-perks">
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Your Agent's Office
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Management dashboard
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Daily automatic backups
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
OpenRouter key included
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Prompt Builder & Skills
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Support chat with human escalation
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
SSH access to your container
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
On-demand backups
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Higher API key credit allocation
|
|
</li>
|
|
</ul>
|
|
<button class="btn btn-primary btn-block" onclick="hireAgent('junior')">Continue with Stripe</button>
|
|
</div>
|
|
|
|
<!-- Senior -->
|
|
<div class="pricing-card">
|
|
<div class="pricing-plan-name">Senior</div>
|
|
<div class="pricing-amount" id="amount-senior">
|
|
<span class="pricing-currency">$</span>17<span class="pricing-decimal">.50</span>
|
|
</div>
|
|
<div class="pricing-period" id="period-senior">/ month</div>
|
|
<div class="pricing-badge-coupon" id="badge-senior">First Month <span class="badge-price">—</span></div>
|
|
<div class="pricing-yearly-bonus" id="bonus-senior">✨ +$17.50 OpenRouter credits</div>
|
|
<p class="pricing-plan-desc">For power users and teams.</p>
|
|
<div class="pricing-plan-quota">3GB RAM · 30GB HDD</div>
|
|
<ul class="pricing-perks">
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Your Agent's Office
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Management dashboard
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Daily automatic backups
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
OpenRouter key included
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Prompt Builder & Skills
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Support chat with human escalation
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
SSH access to your container
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
On-demand backups
|
|
</li>
|
|
<li>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
Higher API key credit allocation
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Email access for your agent
|
|
</li>
|
|
<li class="new-feature">
|
|
<span class="plus-icon">+</span>
|
|
Highest API key credit allocation
|
|
</li>
|
|
</ul>
|
|
<button class="btn btn-primary btn-block" onclick="hireAgent('senior')">Continue with Stripe</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="pricing-toast" id="pricing-toast"></div>
|
|
|
|
<script>
|
|
// ── Signup Webhook URLs (matching app.derez.ai) ──────────
|
|
const ORDER_WEBHOOK = "https://n8n.derez.ai/webhook/payment";
|
|
const SERVER_WEBHOOK = "https://n8n.derez.ai/webhook/server";
|
|
|
|
async function initServerLocations() {
|
|
const sel = document.getElementById('pricing-location');
|
|
if (!sel) return;
|
|
try {
|
|
const res = await fetch(SERVER_WEBHOOK);
|
|
const raw = await res.json();
|
|
// Unwrap [{data: [...]}] → {data: [...]}
|
|
let data = raw;
|
|
if (Array.isArray(raw) && raw.length > 0 && raw[0] && Array.isArray(raw[0].data)) {
|
|
data = raw[0];
|
|
}
|
|
// Handle {data: [...]}, flat array, or single object
|
|
const list = data && Array.isArray(data.data) ? data.data : Array.isArray(data) ? data : [data].filter(Boolean);
|
|
const servers = list.filter(function(s) { return s && s.server; });
|
|
sel.innerHTML = '';
|
|
// Determine overall select styling based on lowest availability
|
|
var minAvail = Infinity;
|
|
servers.forEach(function(s) {
|
|
var a = parseInt(s['Seats Available'], 10) || 0;
|
|
if (a < minAvail) minAvail = a;
|
|
});
|
|
sel.classList.remove('low-seats', 'no-seats');
|
|
if (minAvail <= 0) {
|
|
sel.classList.add('no-seats');
|
|
}
|
|
servers.forEach(function(s) {
|
|
const server = s.server || '';
|
|
const serverCode = s.code || server.toLowerCase().replace(/\s+/g, '-');
|
|
const available = parseInt(s['Seats Available'], 10) || 0;
|
|
const label = server.charAt(0).toUpperCase() + server.slice(1);
|
|
const opt = document.createElement('option');
|
|
opt.value = serverCode;
|
|
if (available <= 0) {
|
|
opt.textContent = label + ' (sold out)';
|
|
opt.disabled = true;
|
|
opt.style.color = '#ff4444';
|
|
opt.style.fontWeight = '600';
|
|
} else {
|
|
opt.textContent = label;
|
|
opt.style.color = '';
|
|
opt.style.fontWeight = '';
|
|
}
|
|
sel.appendChild(opt);
|
|
});
|
|
for (var i = 0; i < sel.options.length; i++) {
|
|
if (!sel.options[i].disabled) { sel.selectedIndex = i; break; }
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to load server locations:', err);
|
|
sel.innerHTML = '<option value="france" style="color:#ff9900;font-weight:600">France (available ?)</option>';
|
|
}
|
|
}
|
|
|
|
/* ── Period / billing update ──────────────────────────── */
|
|
function updatePeriodDisplay() {
|
|
const sel = document.getElementById('pricing-period');
|
|
const yearly = sel.value === 'yearly';
|
|
document.getElementById('bonus-trainee').classList.toggle('show', yearly);
|
|
document.getElementById('bonus-junior').classList.toggle('show', yearly);
|
|
document.getElementById('bonus-senior').classList.toggle('show', yearly);
|
|
document.getElementById('period-trainee').textContent = yearly ? '/ month ✦ yearly' : '/ month';
|
|
document.getElementById('period-junior').textContent = yearly ? '/ month ✦ yearly' : '/ month';
|
|
document.getElementById('period-senior').textContent = yearly ? '/ month ✦ yearly' : '/ month';
|
|
}
|
|
document.getElementById('pricing-period').addEventListener('change', updatePeriodDisplay);
|
|
updatePeriodDisplay();
|
|
initServerLocations();
|
|
|
|
/* ── Capture UTM params from URL ──────────────────────── */
|
|
(function () {
|
|
const params = new URLSearchParams(window.location.search);
|
|
['source', 'medium', 'campaign', 'term', 'content'].forEach(function (key) {
|
|
const val = params.get('utm_' + key);
|
|
if (val) document.getElementById('utm_' + key).value = val;
|
|
});
|
|
})();
|
|
|
|
/* ── Buy Agent Webhook ────────────────────────────────── */
|
|
function showPricingToast(msg) {
|
|
const el = document.getElementById('pricing-toast');
|
|
el.textContent = msg;
|
|
el.classList.add('show');
|
|
if (window._pricingToastTimer) clearTimeout(window._pricingToastTimer);
|
|
window._pricingToastTimer = setTimeout(function () {
|
|
el.classList.remove('show');
|
|
}, 20000);
|
|
}
|
|
|
|
async function hireAgent(product) {
|
|
const agent = document.getElementById('pricing-agent').value;
|
|
const period = document.getElementById('pricing-period').value;
|
|
const location = document.getElementById('pricing-location').value;
|
|
const email = document.getElementById('pricing-email').value.trim();
|
|
const coupon = document.getElementById('pricing-coupon').value.trim();
|
|
|
|
// Validate email
|
|
const emailInput = document.getElementById('pricing-email');
|
|
emailInput.classList.remove('invalid');
|
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
emailInput.classList.add('invalid');
|
|
showPricingToast('Please configure your agent and provide a valid email address.');
|
|
emailInput.focus();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const form = document.createElement('form');
|
|
form.method = 'POST';
|
|
form.action = ORDER_WEBHOOK;
|
|
form.target = '_self';
|
|
form.style.display = 'none';
|
|
|
|
const fields = {
|
|
product: product,
|
|
agent: agent,
|
|
period: period,
|
|
location: location,
|
|
email: email || '',
|
|
coupon: coupon || '',
|
|
utm_source: document.getElementById('utm_source').value || '',
|
|
utm_medium: document.getElementById('utm_medium').value || '',
|
|
utm_campaign: document.getElementById('utm_campaign').value || '',
|
|
utm_term: document.getElementById('utm_term').value || '',
|
|
utm_content: document.getElementById('utm_content').value || '',
|
|
};
|
|
|
|
Object.keys(fields).forEach(function (key) {
|
|
const inp = document.createElement('input');
|
|
inp.type = 'hidden';
|
|
inp.name = key;
|
|
inp.value = fields[key];
|
|
form.appendChild(inp);
|
|
});
|
|
|
|
document.body.appendChild(form);
|
|
form.submit();
|
|
document.body.removeChild(form);
|
|
} catch (err) {
|
|
console.error('Order webhook failed:', err);
|
|
}
|
|
}
|
|
|
|
/* ── Coupon Badges ──────────────────────────────────────── */
|
|
const COUPONS = { friends499: 499, friends950: 950, creator950: 950, friends1750: 1750, blog499: 499, blog950: 950, blog1750: 1750 };
|
|
const PLAN_PRICES = { trainee: 499, junior: 950, senior: 1750 };
|
|
function centsToDollar(c) { return '$' + (c / 100).toFixed(2); }
|
|
function updateCouponBadges() {
|
|
var code = document.getElementById('pricing-coupon').value.trim().toLowerCase();
|
|
var discount = COUPONS[code];
|
|
['trainee','junior','senior'].forEach(function(plan) {
|
|
var badge = document.getElementById('badge-' + plan);
|
|
if (discount !== undefined) {
|
|
var firstMonth = Math.max(0, PLAN_PRICES[plan] - discount);
|
|
badge.querySelector('.badge-price').textContent = centsToDollar(firstMonth);
|
|
badge.classList.add('visible');
|
|
if (firstMonth === 0) badge.classList.add('free');
|
|
else badge.classList.remove('free');
|
|
} else {
|
|
badge.classList.remove('visible', 'free');
|
|
badge.querySelector('.badge-price').textContent = '—';
|
|
}
|
|
});
|
|
}
|
|
document.getElementById('pricing-coupon').addEventListener('input', updateCouponBadges);
|
|
</script>
|
|
|
|
</body>
|
|
</html> |