Files
nixpanel-website/views/pricing.php
T
NixReaper 29c18c5af8 fix: rename setInterval to setBilling, add deploy.sh
- pricing.php: rename setInterval() to setBilling() to stop shadowing
  the native window.setInterval — the billing toggle was silently broken
- deploy.sh: full automated deploy script for nixpanel.io
  - DocumentRoot set to public/ (fixes /pricing, /login 404s)
  - AllowOverride All so public/.htaccess handles all routing
  - PHP 8.3-FPM via Apache mod_proxy_fcgi
  - composer install, DB migrations, .env setup, SSL via certbot
2026-04-25 05:37:31 -05:00

124 lines
7.2 KiB
PHP

<?php $pageTitle = 'Pricing — NixPanel'; ?>
<?php
$plans = [
['key' => 'free', 'name' => 'Free', 'monthly' => 0, 'annual' => 0, 'highlighted' => false,
'limits' => ['Domains' => '1', 'Accounts' => '1', 'Emails' => '5', 'Databases' => '2', 'Disk' => '5 GB'],
'features' => ['Community support', "Let's Encrypt SSL"]],
['key' => 'solo', 'name' => 'Solo', 'monthly' => 9.99, 'annual' => 95.90, 'highlighted' => false,
'limits' => ['Domains' => '5', 'Accounts' => '5', 'Emails' => '50', 'Databases' => '10', 'Disk' => '50 GB'],
'features' => ['Email support', "Let's Encrypt SSL", 'WP one-click install']],
['key' => 'host', 'name' => 'Host', 'monthly' => 29.99, 'annual' => 287.90, 'highlighted' => true,
'limits' => ['Domains' => '50', 'Accounts' => '50', 'Emails' => '500', 'Databases' => '100', 'Disk' => '250 GB'],
'features' => ['Priority support', "Let's Encrypt SSL", 'WP one-click install', 'NixServer reseller panel']],
['key' => 'agency', 'name' => 'Agency', 'monthly' => 79.99, 'annual' => 767.90, 'highlighted' => false,
'limits' => ['Domains' => 'Unlimited', 'Accounts' => 'Unlimited', 'Emails' => 'Unlimited', 'Databases' => 'Unlimited', 'Disk' => 'Unlimited'],
'features' => ['Priority support', "Let's Encrypt SSL", 'WP one-click install', 'NixServer reseller panel', 'White-label ready']],
];
?>
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-20">
<div class="text-center mb-12">
<h1 class="text-4xl font-bold text-white mb-4">Simple, transparent pricing</h1>
<p class="text-gray-400">Tied to your server IP. No per-user fees. Cancel anytime.</p>
<div class="inline-flex items-center gap-1 bg-gray-900 border border-gray-800 rounded-lg p-1 mt-8">
<button id="btn-monthly" onclick="setBilling('monthly')"
class="px-4 py-2 rounded-md text-sm font-medium transition-colors bg-brand-600 text-white">
Monthly
</button>
<button id="btn-annual" onclick="setBilling('annual')"
class="px-4 py-2 rounded-md text-sm font-medium transition-colors text-gray-400 hover:text-white">
Annual
<span class="ml-2 text-xs bg-green-500/20 text-green-400 px-1.5 py-0.5 rounded">2 months free</span>
</button>
</div>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-6">
<?php foreach ($plans as $plan): ?>
<div class="flex flex-col rounded-xl border p-6 <?= $plan['highlighted'] ? 'bg-brand-900/20 border-brand-500/40' : 'bg-gray-900/50 border-gray-800' ?>">
<?php if ($plan['highlighted']): ?>
<div class="text-xs font-semibold text-brand-400 bg-brand-500/10 border border-brand-500/20 rounded-full px-3 py-1 w-fit mb-4">Most popular</div>
<?php endif ?>
<h3 class="text-white font-bold text-xl mb-1"><?= e($plan['name']) ?></h3>
<div class="mb-4">
<span class="price-monthly <?= $plan['monthly'] === 0 ? '' : 'hidden' ?>" data-plan="<?= $plan['key'] ?>">
<span class="text-4xl font-extrabold text-white">$0</span>
<span class="text-gray-400 text-sm">/mo</span>
</span>
<?php if ($plan['monthly'] > 0): ?>
<span class="price-monthly" data-plan="<?= $plan['key'] ?>">
<span class="text-4xl font-extrabold text-white">$<?= number_format($plan['monthly'], 2) ?></span>
<span class="text-gray-400 text-sm">/mo</span>
</span>
<span class="price-annual hidden" data-plan="<?= $plan['key'] ?>">
<span class="text-4xl font-extrabold text-white">$<?= number_format($plan['annual'] / 12, 2) ?></span>
<span class="text-gray-400 text-sm">/mo</span>
<div class="text-xs text-gray-500 mt-0.5">$<?= number_format($plan['annual'], 2) ?>/yr</div>
</span>
<?php endif ?>
</div>
<ul class="space-y-1.5 mb-5 text-sm text-gray-400">
<?php foreach ($plan['limits'] as $label => $value): ?>
<li class="flex justify-between">
<span><?= e($label) ?></span>
<span class="text-white font-medium"><?= e($value) ?></span>
</li>
<?php endforeach ?>
</ul>
<ul class="space-y-2 mb-6 text-sm text-gray-400 flex-1">
<?php foreach ($plan['features'] as $feat): ?>
<li class="flex items-center gap-2"><span class="text-emerald-500 flex-shrink-0">✓</span><?= e($feat) ?></li>
<?php endforeach ?>
</ul>
<?php if ($plan['key'] === 'free'): ?>
<a href="/register" class="<?= $plan['highlighted'] ? 'btn-primary' : 'btn-secondary' ?> w-full justify-center">Get started free</a>
<?php else: ?>
<?php if (\NixPanel\Auth::isLoggedIn()): ?>
<form method="POST" action="/checkout">
<?= csrf_field() ?>
<input type="hidden" name="plan_key" value="<?= e($plan['key']) ?>">
<input type="hidden" name="interval" id="interval-<?= $plan['key'] ?>" value="monthly">
<div class="mb-3">
<input type="text" name="server_ip" placeholder="Your server IP (e.g. 1.2.3.4)"
class="input text-xs" pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
title="Valid IPv4 address" required>
</div>
<button type="submit" class="<?= $plan['highlighted'] ? 'btn-primary' : 'btn-secondary' ?> w-full justify-center">
Get started
</button>
</form>
<?php else: ?>
<a href="/register" class="<?= $plan['highlighted'] ? 'btn-primary' : 'btn-secondary' ?> w-full justify-center">Get started</a>
<?php endif ?>
<?php endif ?>
</div>
<?php endforeach ?>
</div>
<p class="text-center text-sm text-gray-500 mt-10">
All plans include 1 server IP. Need multiple servers?
<a href="mailto:[email protected]" class="text-brand-400 hover:underline">Contact us.</a>
</p>
</div>
<script>
function setBilling(val) {
document.querySelectorAll('.price-monthly').forEach(el => el.classList.toggle('hidden', val !== 'monthly'));
document.querySelectorAll('.price-annual').forEach(el => el.classList.toggle('hidden', val !== 'annual'));
document.querySelectorAll('[id^="interval-"]').forEach(el => el.value = val);
document.getElementById('btn-monthly').className = 'px-4 py-2 rounded-md text-sm font-medium transition-colors ' + (val === 'monthly' ? 'bg-brand-600 text-white' : 'text-gray-400 hover:text-white');
document.getElementById('btn-annual').className = 'px-4 py-2 rounded-md text-sm font-medium transition-colors ' + (val === 'annual' ? 'bg-brand-600 text-white' : 'text-gray-400 hover:text-white');
}
// Hide the duplicate free $0 span
document.querySelectorAll('.price-monthly[data-plan="free"]').forEach((el, i) => { if (i > 0) el.remove(); });
</script>