Files
2026-10-21 03:30:38 +02:00

816 lines
63 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
<style>
:root{
--bg: oklch(99% 0.005 296);
--surface: oklch(100% 0 0);
--surface-2: oklch(97.5% 0.01 296);
--ink: oklch(24% 0.04 296);
--ink-2: oklch(45% 0.03 296);
--ink-3: oklch(60% 0.025 296);
--border: oklch(92% 0.014 296);
--accent: oklch(45% 0.17 296);
--accent-soft: oklch(95.5% 0.028 296);
--on-accent: oklch(99% 0.01 296);
--glow-3: oklch(91% 0.08 332);
--veil: oklch(100% 0 0 / 0.55);
--warn: oklch(62% 0.14 62);
--tg: oklch(62% 0.15 250);
}
:root[data-veilix-theme="dark"]{
--bg: oklch(17% 0.022 296);
--surface: oklch(21% 0.026 296);
--surface-2: oklch(24% 0.03 296);
--ink: oklch(96% 0.01 296);
--ink-2: oklch(79% 0.02 296);
--ink-3: oklch(63% 0.025 296);
--border: oklch(31% 0.032 296);
--accent: oklch(74% 0.15 296);
--accent-soft: oklch(27% 0.05 296);
--on-accent: oklch(17% 0.03 296);
--glow-3: oklch(42% 0.13 332);
--veil: oklch(100% 0 0 / 0.07);
--warn: oklch(78% 0.13 62);
--tg: oklch(70% 0.13 250);
}
*{ box-sizing: border-box; }
input, textarea, select{ font-size: 16px; }
[hidden]{ display: none !important; }
body{ margin:0; background: oklch(95% 0.008 296); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
:root[data-veilix-theme="dark"] body{ background: oklch(13% 0.018 296); }
a{ color: var(--accent); text-decoration: none; }
input::placeholder, textarea::placeholder{ color: var(--ink-3); }
</style>
</helmet>
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
</svg>
</template>
<div style="min-height: 100vh; display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: 32px; padding: 40px 24px 64px;">
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx?v=20260915" hint-size="393px,852px">
<div style="display: flex; flex-direction: column; height: 100%; padding-top: 62px; padding-bottom: 34px; background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; letter-spacing: -0.01em;">
<div style="display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--border); background: var(--surface);">
<span style="position: relative; width: 24px; height: 24px; display: block; flex: none;">
<span style="position: absolute; left: 0; top: 3px; width: 18px; height: 18px; border-radius: 6px; background: linear-gradient(140deg, var(--accent), color-mix(in oklab, var(--accent) 55%, var(--glow-3))); display: block;"></span>
<span style="position: absolute; right: 0; bottom: 3px; width: 18px; height: 18px; border-radius: 6px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
</span>
<span style="display: grid; gap: 1px; flex: 1; min-width: 0;">
<span style="font-size: 15px; font-weight: 500; letter-spacing: -0.02em;">Вейликс</span>
<span style="font-size: 11px; color: var(--tg);">мини-приложение</span>
</span>
<span style="font-size: 15px; color: var(--tg);">Закрыть</span>
</div>
<div style="flex: 1; min-height: 0; overflow: auto; padding: 16px 16px 108px;">
<div hidden="{{ buyOff }}" style="display: grid; gap: 12px;">
<div style="display: grid; gap: 4px; padding: 2px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Ваш личный VPN</div>
<div style="font-size: 13px; color: var(--ink-2);">От 4 900 ₽ первый месяц, далее от 2 499 ₽.</div>
</div>
<div style="display: grid; gap: 9px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
<span>Индивидуальный IP в выбранной стране</span>
</div>
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
<span>Выделенный RU-адрес в комплекте</span>
</div>
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
<span>До 5 устройств</span>
</div>
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
<span style="display: grid; gap: 5px;">
<span>2 типа подключения:</span>
<span style="display: flex; gap: 8px; color: var(--ink-2);">
<span style="flex: none;">—</span>
<span>WireGuard / Amnezia WG</span>
</span>
<span style="display: flex; gap: 8px; color: var(--ink-2);">
<span style="flex: none;">—</span>
<span>Xray: VLESS, Hysteria2, XHTTP</span>
</span>
</span>
</div>
</div>
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 8px 2px 0;">
<span style="font-size: 16px; font-weight: 500;">Локации</span>
<span style="font-size: 12px; color: var(--ink-3);">{{ locCount }}</span>
</div>
<sc-for list="{{ locations }}" as="loc" hint-placeholder-count="8">
<div style="display: grid; gap: 11px; padding: 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: flex; align-items: center; gap: 11px;">
{{ loc.flag }}
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
<span style="font-size: 15px; font-weight: 500;">{{ loc.country }}</span>
<span style="font-size: 12px; color: {{ loc.statusInk }};">{{ loc.sub }}</span>
</span>
<button type="button" onClick="{{ loc.buy }}" style="flex: none; min-height: 44px; padding: 0 16px; border-radius: 12px; border: 0; background: {{ loc.btnBg }}; color: {{ loc.btnInk }}; font-family: inherit; font-size: 14px; font-weight: 500; cursor: pointer;">{{ loc.btn }}</button>
</div>
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; padding-top: 11px; border-top: 1px solid var(--border); font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
<span style="font-size: 15px; font-weight: 500; color: var(--ink);">{{ loc.setup }}</span>
<span>первый месяц · далее</span>
<span style="font-weight: 500; color: var(--ink);">{{ loc.month }}</span>
</div>
</div>
</sc-for>
<div style="display: grid; gap: 12px; margin-top: 8px; padding: 16px; border-radius: 18px; background: var(--surface-2); border: 1px solid var(--border);">
<div style="display: grid; gap: 5px;">
<div style="font-size: 16px; font-weight: 500;">Нужны индивидуальные условия</div>
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Команда, несколько адресов, редкая страна или свой сценарий маршрутизации. Оставьте заявку — ответим в течение дня.</div>
</div>
<input type="text" placeholder="Имя" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
<input type="text" placeholder="Telegram или почта" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; resize: vertical; width: 100%;"></textarea>
<button type="button" onClick="{{ sendLead }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">{{ leadButton }}</button>
</div>
</div>
<div hidden="{{ faqOff }}" style="display: grid; gap: 10px;">
<div style="display: grid; gap: 4px; padding: 2px 2px 6px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Частые вопросы</div>
<div style="font-size: 13px; color: var(--ink-2);">Если ответа нет — напишите в поддержку.</div>
</div>
<sc-for list="{{ faq }}" as="item" hint-placeholder-count="6">
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
<button type="button" onClick="{{ item.toggle }}" style="display: flex; align-items: center; gap: 12px; width: 100%; min-height: 52px; padding: 14px 16px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 15px; font-weight: 500; text-align: left; cursor: pointer;">
<span style="flex: 1;">{{ item.q }}</span>
<span style="flex: none; width: 8px; height: 8px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ item.rot }}; transition: transform 180ms ease; display: block;"></span>
</button>
<div hidden="{{ item.off }}" style="padding: 0 16px 16px; font-size: 14px; line-height: 1.55; color: var(--ink-2);">{{ item.a }}</div>
</div>
</sc-for>
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); font-size: 15px;">
<span style="flex: 1;">Остался вопрос</span>
<span style="color: var(--tg);">@veilix_support</span>
</div>
</div>
<div hidden="{{ keysOff }}" style="display: grid; gap: 12px;">
<div style="display: grid; gap: 4px; padding: 2px 2px 4px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Ваши ключи</div>
<div style="font-size: 13px; color: var(--ink-2);">Нажмите, чтобы открыть подключение</div>
</div>
<sc-for list="{{ keys }}" as="key" hint-placeholder-count="2">
<button type="button" onClick="{{ key.open }}" style="display: grid; gap: 10px; width: 100%; min-height: 44px; padding: 16px; border-radius: 18px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;">
<span style="display: flex; align-items: center; gap: 8px;">
<span style="flex: 1; font-size: 16px; font-weight: 500;">{{ key.title }}</span>
<span style="padding: 3px 9px; border-radius: 7px; background: {{ key.badgeBg }}; color: {{ key.badgeInk }}; font-size: 11px;">{{ key.badge }}</span>
</span>
<span style="display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
<span>{{ key.ip }}</span>
<span>{{ key.untilLine }}</span>
</span>
</button>
</sc-for>
<button type="button" onClick="{{ goBuy }}" style="display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 50px; border-radius: 16px; border: 1px dashed var(--border); background: var(--surface-2); color: var(--ink-2); font-family: inherit; font-size: 15px; cursor: pointer;">Купить ещё один ключ</button>
</div>
<div hidden="{{ keyOff }}" style="display: grid; gap: 14px;">
<button type="button" onClick="{{ goKeys }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ключи</button>
<div style="display: grid; gap: 4px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">{{ cur.title }}</div>
<div style="font-size: 13px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ cur.ip }}</div>
</div>
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
<span style="font-size: 13px; color: var(--ink-3);">Действует до</span>
<span style="font-size: 16px; font-weight: 500;">{{ cur.until }}</span>
</div>
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
<span style="font-size: 13px; color: var(--ink-3);">Списание</span>
<span style="font-size: 15px; font-variant-numeric: tabular-nums;">2 499 ₽ · со счёта</span>
</div>
<div style="font-size: 13px; color: {{ cur.leftInk }};">{{ cur.left }}</div>
<button type="button" onClick="{{ goBilling }}" style="min-height: 44px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">Счёт и платежи</button>
</div>
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
<span style="font-size: 16px; font-weight: 500;">WireGuard</span>
<span style="font-size: 12px; color: var(--ink-3);">5 устройств</span>
</div>
<sc-for list="{{ configs }}" as="cfg" hint-placeholder-count="5">
<div style="display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 14px; background: var(--surface-2);">
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
<span style="font-size: 15px;">{{ cfg.name }}</span>
<span style="font-size: 12px; color: var(--ink-3);">{{ cfg.state }}</span>
</span>
<button type="button" onClick="{{ cfg.showQr }}" style="min-height: 44px; padding: 0 14px; border-radius: 11px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 14px; cursor: pointer;">QR</button>
<button type="button" style="min-height: 44px; padding: 0 14px; border-radius: 11px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;">Файл</button>
</div>
</sc-for>
</div>
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
<span style="font-size: 16px; font-weight: 500;">Xray</span>
<span style="font-size: 12px; color: var(--ink-3);">Happ и другие</span>
</div>
<div style="padding: 12px; border-radius: 12px; background: var(--surface-2); font-size: 12px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ cur.xray }}</div>
<div style="display: flex; gap: 8px;">
<button type="button" onClick="{{ copyXray }}" style="flex: 1; min-height: 46px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;">{{ copyLabel }}</button>
<button type="button" onClick="{{ showXrayQr }}" style="min-height: 46px; padding: 0 18px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">QR</button>
</div>
</div>
</div>
<div hidden="{{ moreOff }}" style="display: grid; gap: 12px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em; padding: 2px;">Ещё</div>
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
<button type="button" onClick="{{ goBilling }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
<span style="flex: 1;">Счёт и платежи</span>
<span style="color: var(--ink-3); font-variant-numeric: tabular-nums;">1 250 ₽</span>
<span style="color: var(--ink-3);">›</span>
</button>
<button type="button" onClick="{{ goRef }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
<span style="flex: 1;">Реферальная программа</span>
<span style="color: var(--ink-3);">{{ refStats }}</span>
<span style="color: var(--ink-3);">›</span>
</button>
<button type="button" onClick="{{ goLead }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
<span style="flex: 1;">Индивидуальные условия</span>
<span style="color: var(--ink-3);">›</span>
</button>
<button type="button" onClick="{{ goFaq }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
<span style="flex: 1;">Частые вопросы</span>
<span style="color: var(--ink-3);">›</span>
</button>
</div>
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
<span style="flex: 1;">Написать в поддержку</span>
<span style="color: var(--tg);">@veilix_support</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
<span style="flex: 1;">Почта аккаунта</span>
<span style="color: var(--ink-3);">anna@example.com</span>
</div>
</div>
<div style="display: grid; gap: 12px; padding: 14px 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
<span style="font-size: 15px;">Тема</span>
<span style="display: flex; flex: none; padding: 3px; border-radius: 11px; border: 1px solid var(--border);">
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 34px; border: 0; border-radius: 8px; background: {{ lightBg }}; color: var(--ink); cursor: pointer;">
<span style="position: relative; width: 16px; height: 16px; display: block;">
<span style="position: absolute; left: 4px; top: 4px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span>
<span style="position: absolute; left: 0; top: 0; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; opacity: 0.45; display: block;"></span>
</span>
</button>
<button type="button" onClick="{{ setDark }}" title="Тёмная тема" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 34px; border: 0; border-radius: 8px; background: {{ darkBg }}; color: var(--ink); cursor: pointer;">
<span style="width: 16px; height: 16px; border-radius: 50%; box-shadow: inset -5px -2px 0 0 currentColor; display: block;"></span>
</button>
</span>
</div>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid var(--border);">
<span style="font-size: 15px;">Язык</span>
<span style="display: flex; flex: none; padding: 3px; border-radius: 11px; border: 1px solid var(--border);">
<button type="button" onClick="{{ setRu }}" style="height: 34px; padding: 0 16px; border: 0; border-radius: 8px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 14px; cursor: pointer;">RU</button>
<button type="button" onClick="{{ setEn }}" style="height: 34px; padding: 0 16px; border: 0; border-radius: 8px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 14px; cursor: pointer;">EN</button>
</span>
</div>
<div style="font-size: 12px; color: var(--ink-3); line-height: 1.45;">По умолчанию тема подстраивается под Telegram.</div>
</div>
</div>
<div hidden="{{ billingOff }}" style="display: grid; gap: 12px;">
<button type="button" onClick="{{ goMore }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ещё</button>
<div style="display: grid; gap: 4px; padding: 0 2px 2px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Счёт и платежи</div>
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Карта привязана к аккаунту, а не к отдельному ключу. Списания за все ключи идут с неё или с баланса.</div>
</div>
<div style="display: grid; gap: 12px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="display: grid; gap: 3px;">
<span style="font-size: 13px; color: var(--ink-3);">Баланс</span>
<span style="font-size: 28px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">1 250 ₽</span>
</div>
<button type="button" onClick="{{ openTopup }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">Пополнить баланс</button>
</div>
<div hidden="{{ topupOff }}" style="display: grid; gap: 12px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--accent);">
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
<span style="font-size: 16px; font-weight: 500;">Пополнение</span>
<button type="button" onClick="{{ closeTopup }}" style="border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">Свернуть</button>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
<sc-for list="{{ sums }}" as="sum" hint-placeholder-count="3">
<button type="button" onClick="{{ sum.pick }}" style="min-height: 44px; padding: 0 16px; border-radius: 12px; border: 1px solid {{ sum.border }}; background: {{ sum.bg }}; color: var(--ink); font-family: inherit; font-size: 15px; font-variant-numeric: tabular-nums; cursor: pointer;">{{ sum.label }}</button>
</sc-for>
</div>
<div style="display: grid; gap: 6px;">
<sc-for list="{{ methods }}" as="m" hint-placeholder-count="3">
<button type="button" onClick="{{ m.pick }}" style="display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid {{ m.border }}; background: {{ m.bg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
<span style="flex: 1;">{{ m.label }}</span>
<span style="font-size: 12px; color: var(--ink-3);">{{ m.note }}</span>
</button>
</sc-for>
</div>
<div hidden="{{ cryptoOff }}" style="padding: 12px 14px; border-radius: 12px; background: var(--surface-2); font-size: 13px; line-height: 1.5; color: var(--ink-2);">USDT (TRC-20), BTC, ETH. Покажем адрес и сумму в монете, курс фиксируется на 15 минут. Баланс пополнится после одного подтверждения сети.</div>
<button type="button" onClick="{{ sendTopup }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">{{ topupButton }}</button>
</div>
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
<span style="flex: 1;">Карта аккаунта</span>
<span style="color: var(--ink-3); font-variant-numeric: tabular-nums;">···· 4242</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
<span style="flex: 1;">Следующее списание</span>
<span style="color: var(--ink-3); font-variant-numeric: tabular-nums;">2 499 ₽ · 21 сент.</span>
</div>
</div>
<div style="font-size: 16px; font-weight: 500; padding: 8px 2px 0;">История платежей</div>
<sc-for list="{{ payments }}" as="p" hint-placeholder-count="5">
<div style="display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
<span style="flex: 1; min-width: 0; display: grid; gap: 3px;">
<span style="font-size: 14px;">{{ p.title }}</span>
<span style="font-size: 12px; color: var(--ink-3);">{{ p.meta }}</span>
</span>
<span style="flex: none; font-size: 15px; font-weight: 500; color: {{ p.ink }}; font-variant-numeric: tabular-nums;">{{ p.amount }}</span>
</div>
</sc-for>
</div>
<div hidden="{{ refOff }}" style="display: grid; gap: 12px;">
<button type="button" onClick="{{ goMore }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ещё</button>
<div style="display: grid; gap: 4px; padding: 0 2px 2px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Реферальная программа</div>
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Приведите друга — вы оба получите по месяцу в подарок.</div>
</div>
<div style="display: flex; gap: 8px;">
<div style="flex: 1; display: grid; gap: 4px; padding: 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
<span style="font-size: 12px; color: var(--ink-3);">Приглашено</span>
<span style="font-size: 24px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">3</span>
</div>
<div style="flex: 1; display: grid; gap: 4px; padding: 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
<span style="font-size: 12px; color: var(--ink-3);">Месяцев</span>
<span style="font-size: 24px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">2</span>
</div>
</div>
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<div style="font-size: 15px; font-weight: 500;">Ваша ссылка</div>
<div style="padding: 12px; border-radius: 12px; background: var(--surface-2); font-size: 13px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">t.me/veilix_bot?start=anna7f2c</div>
<div style="display: flex; gap: 8px;">
<button type="button" onClick="{{ copyRef }}" style="flex: 1; min-height: 46px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;">{{ refLabel }}</button>
<button type="button" style="min-height: 46px; padding: 0 18px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">Поделиться</button>
</div>
<div style="font-size: 12px; line-height: 1.45; color: var(--ink-3);">Месяц начисляется после первой оплаты друга и применяется к ближайшему списанию.</div>
</div>
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
<div style="display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 12px 16px; background: var(--surface); font-size: 14px;">
<span style="flex: 1;">m···@gmail.com</span>
<span style="color: var(--accent);">Месяц начислен</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 12px 16px; background: var(--surface); font-size: 14px;">
<span style="flex: 1;">d···@yandex.ru</span>
<span style="color: var(--accent);">Месяц начислен</span>
</div>
<div style="display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 12px 16px; background: var(--surface); font-size: 14px;">
<span style="flex: 1;">k···@icloud.com</span>
<span style="color: var(--ink-3);">Ждём оплаты</span>
</div>
</div>
</div>
<div hidden="{{ leadOff }}" style="display: grid; gap: 12px;">
<button type="button" onClick="{{ goMore }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ещё</button>
<div style="display: grid; gap: 4px; padding: 0 2px 2px;">
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Индивидуальные условия</div>
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Команда, несколько адресов, редкая страна или свой сценарий маршрутизации. Ответим в течение дня.</div>
</div>
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
<input type="text" placeholder="Имя" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
<input type="text" placeholder="Telegram или почта" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
<input type="text" placeholder="Сколько адресов и в каких странах" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
<textarea rows="4" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; resize: vertical; width: 100%;"></textarea>
<button type="button" onClick="{{ sendLead }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">{{ leadButton }}</button>
</div>
</div>
</div>
<div style="position: absolute; left: 50%; bottom: 46px; transform: translateX(-50%); z-index: 15; display: flex; gap: 2px; padding: 6px; border-radius: 999px; background: color-mix(in oklab, var(--surface) 62%, transparent); backdrop-filter: blur(24px) saturate(1.8); -webkit-backdrop-filter: blur(24px) saturate(1.8); border: 1px solid color-mix(in oklab, var(--ink) 8%, transparent); box-shadow: 0 10px 30px -10px color-mix(in oklab, var(--ink) 40%, transparent);">
<button type="button" onClick="{{ goBuy }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabBuyBg }}; color: {{ tabBuyInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
<span style="position: relative; width: 20px; height: 20px; display: block;">
<span style="position: absolute; left: 8px; top: 0; width: 4px; height: 20px; border-radius: 2px; background: currentColor; display: block;"></span>
<span style="position: absolute; left: 0; top: 8px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
</span>
Купить
</button>
<button type="button" onClick="{{ goFaq }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabFaqBg }}; color: {{ tabFaqInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
<span style="display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; border: 2px solid currentColor; font-size: 13px; font-weight: 600; line-height: 1;">?</span>
FAQ
</button>
<button type="button" onClick="{{ goKeys }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabKeysBg }}; color: {{ tabKeysInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
<span style="position: relative; width: 20px; height: 20px; display: block;">
<span style="position: absolute; left: 0; top: 2px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
<span style="position: absolute; left: 0; top: 9px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.6; display: block;"></span>
<span style="position: absolute; left: 0; top: 16px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.3; display: block;"></span>
</span>
Ключи
</button>
<button type="button" onClick="{{ goMore }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabMoreBg }}; color: {{ tabMoreInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
<span style="position: relative; width: 20px; height: 20px; display: block;">
<span style="position: absolute; left: 0; top: 8px; width: 4px; height: 4px; border-radius: 50%; background: currentColor; display: block;"></span>
<span style="position: absolute; left: 8px; top: 8px; width: 4px; height: 4px; border-radius: 50%; background: currentColor; display: block;"></span>
<span style="position: absolute; left: 16px; top: 8px; width: 4px; height: 4px; border-radius: 50%; background: currentColor; display: block;"></span>
</span>
Ещё
</button>
</div>
<div hidden="{{ payOff }}" onClick="{{ closePay }}" style="position: absolute; left: 0; right: 0; top: -62px; bottom: -34px; z-index: 22; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 34px; background: color-mix(in oklab, var(--ink) 45%, transparent);">
<div onClick="{{ stopPay }}" style="width: 100%; display: grid; gap: 14px; padding: 22px 20px 34px; border-radius: 22px 22px 0 0; background: var(--surface); border-top: 1px solid var(--border);">
<span style="justify-self: center; width: 36px; height: 4px; border-radius: 2px; background: var(--border); display: block;"></span>
<div style="display: flex; align-items: center; gap: 12px;">
{{ payFlag }}
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
<span style="font-size: 17px; font-weight: 600; letter-spacing: -0.02em;">{{ payTitle }}</span>
<span style="font-size: 12px; color: var(--accent);">{{ paySub }}</span>
</span>
</div>
<div style="display: grid; gap: 8px; padding: 14px 16px; border-radius: 14px; background: var(--surface-2);">
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px;">
<span style="color: var(--ink-2);">Первый месяц</span>
<span style="font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums;">{{ paySetup }}</span>
</div>
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px;">
<span style="color: var(--ink-2);">Далее каждый месяц</span>
<span style="font-variant-numeric: tabular-nums;">{{ payMonth }}</span>
</div>
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px; padding-top: 8px; border-top: 1px solid var(--border);">
<span style="color: var(--ink-2);">В комплекте</span>
<span>RU-адрес · 5 устройств</span>
</div>
</div>
<button type="button" onClick="{{ confirmPay }}" style="min-height: 52px; border-radius: 14px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;">{{ payButton }}</button>
<div style="font-size: 12px; line-height: 1.45; color: var(--ink-3); text-align: center;">Оплата пройдёт внутри Telegram. Ключ появится во вкладке «Ключи» сразу после подтверждения.</div>
</div>
</div>
<div hidden="{{ qrOff }}" onClick="{{ closeQr }}" style="position: absolute; left: 0; right: 0; top: -62px; bottom: -34px; z-index: 20; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 34px; background: color-mix(in oklab, var(--ink) 45%, transparent);">
<div style="width: 100%; display: grid; gap: 14px; justify-items: center; padding: 22px 20px 34px; border-radius: 22px 22px 0 0; background: var(--surface); border-top: 1px solid var(--border);">
<span style="width: 36px; height: 4px; border-radius: 2px; background: var(--border); display: block;"></span>
<span style="font-size: 16px; font-weight: 500;">{{ qrTitle }}</span>
{{ qrImage }}
<span style="font-size: 12px; color: var(--ink-3); text-align: center; max-width: 240px; line-height: 1.45;">Наведите камеру телефона на код в приложении WireGuard или Happ.</span>
</div>
</div>
</div>
</x-import>
<div style="max-width: 320px; display: grid; gap: 14px; align-content: start; padding-top: 12px;">
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.03em; color: var(--ink);">Мини-приложение в Telegram</div>
<p style="margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">В Telegram приложение работает и как витрина, и как кабинет: человек может попасть сюда, ни разу не открыв сайт. Поэтому первая вкладка — «Купить»: тезисы, все локации с ценами и кнопкой покупки у каждой, форма на индивидуальные условия внизу.</p>
<p style="margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Вкладки: Купить, FAQ, Ключи, Ещё. В «Ещё» — счёт и платежи, реферальная программа, индивидуальный заказ, тема и язык.</p>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;defaultTheme&quot;:{&quot;editor&quot;:&quot;enum&quot;,&quot;options&quot;:[&quot;light&quot;,&quot;dark&quot;,&quot;system&quot;],&quot;default&quot;:&quot;light&quot;,&quot;tsType&quot;:&quot;'light' | 'dark' | 'system'&quot;,&quot;section&quot;:&quot;Тема&quot;}}">
class Component extends DCLogic {
state = { theme: null, sysDark: false, view: "buy", key: 0, qr: null, copied: false, lang: "ru", refCopied: false, faq: -1, leadSent: false, topup: false, sum: 1, method: 0, topupSent: false, pay: null, paid: false };
componentDidMount() {
const mq = window.matchMedia("(prefers-color-scheme: dark)");
this.mq = mq;
this.onSys = () => this.setState({ sysDark: mq.matches });
mq.addEventListener("change", this.onSys);
let saved = null;
try { saved = localStorage.getItem("veilix.theme"); } catch (e) {}
let savedLang = null;
try { savedLang = localStorage.getItem("veilix.lang"); } catch (e) {}
this.setState({ sysDark: mq.matches, theme: saved || this.props.defaultTheme || "light", lang: savedLang || "ru" }, () => { this.apply(); this.applyLang(); });
}
applyLang() {
const lang = this.state.lang || "ru";
if (window.VEILIX_APPLY_LANG) window.VEILIX_APPLY_LANG(document.body, lang);
else setTimeout(() => this.applyLang(), 150);
}
setLang(l) {
try { localStorage.setItem("veilix.lang", l); } catch (e) {}
this.setState({ lang: l }, () => this.applyLang());
}
componentWillUnmount() { if (this.mq) this.mq.removeEventListener("change", this.onSys); }
componentDidUpdate() { this.apply(); this.applyLang(); }
apply() {
const theme = this.state.theme || this.props.defaultTheme || "light";
const dark = theme === "dark" || (theme === "system" && this.state.sysDark);
document.documentElement.setAttribute("data-veilix-theme", dark ? "dark" : "light");
}
setTheme(t) {
try { localStorage.setItem("veilix.theme", t); } catch (e) {}
this.setState({ theme: t }, () => this.apply());
}
data() {
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
const L = (country, city, code, servers, tier) => ({ country, city, code, servers, setup: tier.setup, month: tier.month });
return [
L("Нидерланды", "Амстердам", "nl", 12, base),
L("Германия", "Франкфурт", "de", 10, base),
L("США", "Нью-Йорк", "us", 9, base),
L("Великобритания", "Лондон", "gb", 8, base),
L("Франция", "Париж", "fr", 7, base),
L("Финляндия", "Хельсинки", "fi", 6, base),
L("Швеция", "Стокгольм", "se", 5, base),
L("Норвегия", "Осло", "no", 0, base),
L("Дания", "Копенгаген", "dk", 0, base),
L("Швейцария", "Цюрих", "ch", 4, rare),
L("Австрия", "Вена", "at", 5, base),
L("Испания", "Мадрид", "es", 6, base),
L("Италия", "Милан", "it", 6, base),
L("Польша", "Варшава", "pl", 7, base),
L("Чехия", "Прага", "cz", 5, base),
L("Румыния", "Бухарест", "ro", 0, base),
L("Литва", "Вильнюс", "lt", 4, base),
L("Латвия", "Рига", "lv", 4, base),
L("Эстония", "Таллин", "ee", 0, base),
L("Турция", "Стамбул", "tr", 6, base),
L("Казахстан", "Алматы", "kz", 0, base),
L("Армения", "Ереван", "am", 0, base),
L("Грузия", "Тбилиси", "ge", 0, base),
L("ОАЭ", "Дубай", "ae", 3, rare),
L("Сингапур", "Сингапур", "sg", 4, rare),
L("Япония", "Токио", "jp", 4, rare),
L("Южная Корея", "Сеул", "kr", 0, rare),
L("Канада", "Торонто", "ca", 6, base),
L("Австралия", "Сидней", "au", 0, far),
L("Бразилия", "Сан-Паулу", "br", 0, far)
];
}
flagSpecs() {
return {
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 19, h: 15, bg: "#3C3B6E" }] },
gb: { bg: "#012169", marks: [{ l: 15, t: 0, w: 10, h: 28, bg: "#FFFFFF" }, { l: 0, t: 9, w: 40, h: 10, bg: "#FFFFFF" }, { l: 17, t: 0, w: 5, h: 28, bg: "#C8102E" }, { l: 0, t: 11, w: 40, h: 5, bg: "#C8102E" }] },
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
fi: { bg: "#FFFFFF", marks: [{ l: 11, t: 0, w: 7, h: 28, bg: "#003580" }, { l: 0, t: 10, w: 40, h: 7, bg: "#003580" }] },
se: { bg: "#006AA7", marks: [{ l: 11, t: 0, w: 5, h: 28, bg: "#FECC00" }, { l: 0, t: 11, w: 40, h: 5, bg: "#FECC00" }] },
no: { bg: "#BA0C2F", marks: [{ l: 10, t: 0, w: 9, h: 28, bg: "#FFFFFF" }, { l: 0, t: 9, w: 40, h: 9, bg: "#FFFFFF" }, { l: 13, t: 0, w: 3, h: 28, bg: "#00205B" }, { l: 0, t: 12, w: 40, h: 3, bg: "#00205B" }] },
dk: { bg: "#C8102E", marks: [{ l: 11, t: 0, w: 5, h: 28, bg: "#FFFFFF" }, { l: 0, t: 11, w: 40, h: 5, bg: "#FFFFFF" }] },
ch: { bg: "#D52B1E", marks: [{ l: 17, t: 7, w: 5, h: 14, bg: "#FFFFFF" }, { l: 13, t: 11, w: 14, h: 5, bg: "#FFFFFF" }] },
at: { rows: ["#ED2939", "#FFFFFF", "#ED2939"] },
es: { rows: ["#AA151B", "#F1BF00", "#AA151B"], sizes: "1fr 2fr 1fr" },
it: { cols: ["#008C45", "#F4F5F0", "#CD212A"] },
pl: { rows: ["#FFFFFF", "#DC143C"] },
cz: { rows: ["#FFFFFF", "#D7141A"], marks: [{ l: 0, t: 0, w: 16, h: 28, bg: "#11457E", clip: "polygon(0 0, 100% 50%, 0 100%)" }] },
ro: { cols: ["#002B7F", "#FCD116", "#CE1126"] },
lt: { rows: ["#FDB913", "#006A44", "#C1272D"] },
lv: { rows: ["#9E3039", "#FFFFFF", "#9E3039"], sizes: "2fr 1fr 2fr" },
ee: { rows: ["#0072CE", "#000000", "#FFFFFF"] },
tr: { bg: "#E30A17", marks: [{ l: 9, t: 7, w: 14, h: 14, r: "50%", bg: "#FFFFFF" }, { l: 13, t: 7, w: 14, h: 14, r: "50%", bg: "#E30A17" }] },
kz: { bg: "#00AFCA", marks: [{ l: 14, t: 7, w: 13, h: 13, r: "50%", bg: "#FEC50C" }] },
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
ge: { bg: "#FFFFFF", marks: [{ l: 17, t: 0, w: 5, h: 28, bg: "#FF0000" }, { l: 0, t: 11, w: 40, h: 5, bg: "#FF0000" }] },
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 10, h: 28, bg: "#FF0000" }] },
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 4, t: 3, w: 10, h: 10, r: "50%", bg: "#FFFFFF" }, { l: 8, t: 3, w: 10, h: 10, r: "50%", bg: "#EF3340" }] },
jp: { bg: "#FFFFFF", marks: [{ l: 14, t: 8, w: 12, h: 12, r: "50%", bg: "#BC002D" }] },
kr: { bg: "#FFFFFF", marks: [{ l: 14, t: 8, w: 12, h: 12, r: "50%", bg: "#CD2E3A" }, { l: 14, t: 8, w: 12, h: 12, r: "50%", bg: "#0047A0", clip: "polygon(0 50%, 100% 50%, 100% 100%, 0 100%)" }] },
ca: { cols: ["#D80621", "#FFFFFF", "#D80621"], sizes: "1fr 2fr 1fr", marks: [{ l: 17, t: 8, w: 6, h: 12, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
au: { bg: "#00247D", marks: [{ l: 8, t: 0, w: 3, h: 14, bg: "#FFFFFF" }, { l: 0, t: 5, w: 19, h: 3, bg: "#FFFFFF" }, { l: 26, t: 17, w: 6, h: 6, r: "50%", bg: "#FFFFFF" }] },
br: { bg: "#009739", marks: [{ l: 8, t: 3, w: 24, h: 22, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 15, t: 10, w: 9, h: 9, r: "50%", bg: "#012169" }] }
};
}
flag(code) {
const h = React.createElement;
const spec = this.flagSpecs()[code] || {};
const style = { width: "40px", height: "28px", borderRadius: "5px", overflow: "hidden", display: "grid", border: "1px solid var(--border)", position: "relative", flex: "none" };
let bands = [];
if (spec.rows) {
bands = spec.rows;
style.gridTemplateRows = spec.sizes || "repeat(" + bands.length + ", 1fr)";
} else if (spec.cols) {
bands = spec.cols;
style.gridAutoFlow = "column";
style.gridTemplateColumns = spec.sizes || "repeat(" + bands.length + ", 1fr)";
} else {
style.background = spec.bg || "var(--surface-2)";
}
const kids = bands.map((c, i) => h("span", { key: "b" + i, style: { background: c, display: "block" } }));
(spec.marks || []).forEach((m, i) => kids.push(h("span", {
key: "m" + i,
style: { position: "absolute", left: m.l + "px", top: m.t + "px", width: m.w + "px", height: m.h + "px", borderRadius: m.r || 0, background: m.bg, clipPath: m.clip || "none", display: "block" }
})));
return h("span", { style: style }, kids);
}
qrImage(seed) {
const n = 21;
let h = 0;
for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
const cells = [];
const finder = (r, c) => (r < 7 && c < 7) || (r < 7 && c >= n - 7) || (r >= n - 7 && c < 7);
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
let on;
if (finder(r, c)) {
const rr = r < 7 ? r : n - 1 - r;
const cc = c < 7 ? c : n - 1 - c;
on = Math.max(Math.abs(rr - 3), Math.abs(cc - 3)) !== 2;
} else {
h = (h * 1103515245 + 12345) >>> 0;
on = ((h >>> 16) & 1) === 1;
}
cells.push(React.createElement("span", { key: r + "-" + c, style: { background: on ? "var(--ink)" : "transparent", display: "block" } }));
}
}
return React.createElement("div", {
style: { display: "grid", gridTemplateColumns: "repeat(" + n + ", 1fr)", width: "196px", height: "196px", padding: "10px", borderRadius: "14px", background: "var(--bg)", border: "1px solid var(--border)" }
}, cells);
}
plural(n) {
const m10 = n % 10, m100 = n % 100;
if (m10 === 1 && m100 !== 11) return "сервер";
if (m10 >= 2 && m10 <= 4 && (m100 < 10 || m100 >= 20)) return "сервера";
return "серверов";
}
renderVals() {
const s = this.state;
const sel = "var(--accent-soft)";
const theme = s.theme || this.props.defaultTheme || "light";
const keyData = [
{ title: "Нидерланды · Амстердам", ip: "185.72.14.203", until: s.lang === "en" ? "14 October" : "14 октября", badge: "Активен", warn: false, left: "Продлится автоматически через 31 день", xray: "vless://8f2a…@nl1.veilix.net:443?type=xhttp#Veilix-NL" },
{ title: "США · Нью-Йорк", ip: "23.105.61.44", until: s.lang === "en" ? "21 September" : "21 сентября", badge: "Истекает", warn: true, left: "Осталось 8 дней", xray: "vless://c41d…@us1.veilix.net:443?type=xhttp#Veilix-US" }
];
const cur = keyData[s.key];
const cfgNames = [["Ноутбук", "подключено"], ["Телефон", "подключено"], ["Планшет", "подключено"], ["Рабочий ноутбук", "не занят"], ["Свободный слот", "не занят"]];
const faqData = [
["Чем это отличается от обычного VPN?", "У вас личный IP-адрес, который не делят с другими. Его не банят за чужую активность, и сервисы видят вас как обычного жителя выбранной страны."],
["Что входит в цену?", "Индивидуальный IP в выбранной стране, выделенный RU-адрес, до 5 устройств, WireGuard и Amnezia WG, а также Xray с VLESS, Hysteria2 и XHTTP."],
["Сколько устройств можно подключить?", "До пяти. На каждое — своя конфигурация WireGuard, IP-адрес при этом один и тот же."],
["Как быстро выдаётся доступ?", "Если в локации есть свободные серверы — сразу после оплаты. Если нет, поднимем сервер под вас в течение 24 часов."],
["Что если страна перестала подходить?", "Напишите в поддержку: перенесём ключ в другую локацию, доплата только за разницу в тарифе."],
["Как платить и как отменить?", "Карта привязана к аккаунту, можно держать баланс и пополнять его картой, через СБП или криптой. Отмена — в разделе «Счёт и платежи», ключ доработает оплаченный месяц."]
];
return {
locCount: s.lang === "en" ? "30 countries" : "30 стран",
locations: this.data().map((d, i) => ({
country: d.country,
flag: this.flag(d.code),
sub: d.servers > 0 ? d.city + " · " + d.servers + " " + this.plural(d.servers) + " свободно" : d.city + " · установим за 24 часа",
statusInk: d.servers > 0 ? "var(--accent)" : "var(--ink-3)",
setup: d.setup,
month: d.month,
btn: d.servers > 0 ? "Купить" : "Заказать",
btnBg: d.servers > 0 ? "var(--accent)" : "var(--surface-2)",
btnInk: d.servers > 0 ? "var(--on-accent)" : "var(--ink-2)",
buy: () => d.servers > 0 ? this.setState({ pay: d, paid: false }) : this.setState({ view: "lead" })
})),
faq: faqData.map((row, i) => ({
q: row[0],
a: row[1],
off: s.faq !== i,
rot: s.faq === i ? "rotate(-135deg) translateY(2px)" : "rotate(45deg) translateY(-2px)",
toggle: () => this.setState(st => ({ faq: st.faq === i ? -1 : i }))
})),
keys: keyData.map((k, i) => ({
title: k.title,
ip: k.ip,
untilLine: (s.lang === "en" ? "until " : "до ") + k.until,
badge: k.badge,
badgeBg: k.warn ? "color-mix(in oklab, var(--warn) 18%, transparent)" : sel,
badgeInk: k.warn ? "var(--warn)" : "var(--accent)",
open: () => this.setState({ view: "key", key: i })
})),
cur: {
title: cur.title, ip: cur.ip, until: cur.until,
left: cur.left, leftInk: cur.warn ? "var(--warn)" : "var(--ink-2)", xray: cur.xray
},
configs: cfgNames.map(c => ({
name: c[0],
state: c[1],
showQr: () => this.setState({ qr: { title: "WireGuard · " + c[0], seed: cur.ip + c[0] } })
})),
goBuy: () => this.setState({ view: "buy" }),
goFaq: () => this.setState({ view: "faq" }),
goKeys: () => this.setState({ view: "keys" }),
goMore: () => this.setState({ view: "more" }),
goRef: () => this.setState({ view: "ref" }),
goBilling: () => this.setState({ view: "billing" }),
goLead: () => this.setState({ view: "lead" }),
buyOff: s.view !== "buy",
faqOff: s.view !== "faq",
keysOff: s.view !== "keys",
keyOff: s.view !== "key",
moreOff: s.view !== "more",
refOff: s.view !== "ref",
billingOff: s.view !== "billing",
leadOff: s.view !== "lead",
tabBuyBg: s.view === "buy" ? sel : "transparent",
tabFaqBg: s.view === "faq" ? sel : "transparent",
tabKeysBg: (s.view === "keys" || s.view === "key") ? sel : "transparent",
tabMoreBg: (s.view === "more" || s.view === "ref" || s.view === "billing" || s.view === "lead") ? sel : "transparent",
tabBuyInk: s.view === "buy" ? "var(--accent)" : "var(--ink-2)",
tabFaqInk: s.view === "faq" ? "var(--accent)" : "var(--ink-2)",
tabKeysInk: (s.view === "keys" || s.view === "key") ? "var(--accent)" : "var(--ink-2)",
tabMoreInk: (s.view === "more" || s.view === "ref" || s.view === "billing" || s.view === "lead") ? "var(--accent)" : "var(--ink-2)",
openTopup: () => this.setState({ topup: true }),
closeTopup: () => this.setState({ topup: false }),
topupOff: !s.topup,
sums: ["2 500 ₽", "5 000 ₽", "10 000 ₽"].map((label, i) => ({
label: label,
bg: s.sum === i ? sel : "transparent",
border: s.sum === i ? "var(--accent)" : "var(--border)",
pick: () => this.setState({ sum: i })
})),
methods: [
{ label: "Карта ···· 4242", note: "мгновенно" },
{ label: "СБП", note: "мгновенно" },
{ label: "Криптовалюта", note: "USDT, BTC, ETH" }
].map((m, i) => ({
label: m.label,
note: m.note,
bg: s.method === i ? sel : "transparent",
border: s.method === i ? "var(--accent)" : "var(--border)",
pick: () => this.setState({ method: i })
})),
cryptoOff: s.method !== 2,
sendTopup: () => this.setState({ topupSent: true }),
topupButton: s.topupSent ? "Счёт выставлен" : "Пополнить",
payments: [
{ title: "Продление · Нидерланды", meta: "14 сентября · карта ···· 4242", amount: "−2 499 ₽", ink: "var(--ink)" },
{ title: "Пополнение баланса", meta: "6 сентября · USDT (TRC-20)", amount: "+5 000 ₽", ink: "var(--accent)" },
{ title: "Первый месяц · США", meta: "21 августа · карта ···· 4242", amount: "−4 900 ₽", ink: "var(--ink)" },
{ title: "Продление · Нидерланды", meta: "14 августа · с баланса", amount: "−2 499 ₽", ink: "var(--ink)" },
{ title: "Реферальный бонус", meta: "2 августа · месяц в подарок", amount: "+2 499 ₽", ink: "var(--accent)" }
],
sendLead: () => this.setState({ leadSent: true }),
leadButton: s.leadSent ? "Заявка отправлена" : "Отправить заявку",
refStats: s.lang === "en" ? "3 invited" : "3 друга",
copyRef: () => this.setState({ refCopied: true }),
refLabel: s.refCopied ? "Ссылка скопирована" : "Копировать ссылку",
copyXray: () => this.setState({ copied: true }),
copyLabel: s.copied ? "Скопировано" : "Копировать ключ",
showXrayQr: () => this.setState({ qr: { title: "Xray · Happ", seed: cur.xray } }),
payOff: !s.pay,
payFlag: s.pay ? this.flag(s.pay.code) : null,
payTitle: s.pay ? s.pay.country + " · " + s.pay.city : "",
paySub: s.pay ? s.pay.servers + " " + this.plural(s.pay.servers) + " свободно · доступ сразу" : "",
paySetup: s.pay ? s.pay.setup : "",
payMonth: s.pay ? s.pay.month : "",
payButton: s.paid ? "Оплата подтверждена" : (s.pay ? "Оплатить " + s.pay.setup : "Оплатить"),
confirmPay: () => this.setState({ paid: true }),
closePay: () => this.setState({ pay: null, paid: false }),
stopPay: (e) => e.stopPropagation(),
qrOff: !s.qr,
qrTitle: s.qr ? s.qr.title : "",
qrImage: s.qr ? this.qrImage(s.qr.seed) : null,
closeQr: () => this.setState({ qr: null }),
setRu: () => this.setLang("ru"),
setEn: () => this.setLang("en"),
ruBg: s.lang === "en" ? "transparent" : sel,
enBg: s.lang === "en" ? sel : "transparent",
ruInk: s.lang === "en" ? "var(--ink-3)" : "var(--ink)",
enInk: s.lang === "en" ? "var(--ink)" : "var(--ink-3)",
setLight: () => this.setTheme("light"),
setDark: () => this.setTheme("dark"),
lightBg: theme === "light" ? sel : "transparent",
darkBg: theme === "dark" ? sel : "transparent"
};
}
}
</script>
</body>
</html>