4fe11e124a
The stats webhook now returns a 'code' field (e.g. 'fr', 'usa') alongside the display 'server' name. The option value now uses s.code and falls back to slugified server name for backward compat.
707 lines
31 KiB
HTML
707 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();
|
|
// Handle both {data: [...]} and flat array/object
|
|
const list = raw && Array.isArray(raw.data) ? raw.data : Array.isArray(raw) ? raw : [raw].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> |