807 lines
60 KiB
HTML
807 lines
60 KiB
HTML
<!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-mass.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.6% 0.0015 215);
|
||
--surface: oklch(100% 0 0);
|
||
--surface-2: oklch(98.4% 0.003 215);
|
||
--ink: oklch(24% 0.04 204);
|
||
--ink-2: oklch(45% 0.03 204);
|
||
--ink-3: oklch(60% 0.025 204);
|
||
--border: oklch(92% 0.014 200);
|
||
--accent: oklch(48% 0.105 178);
|
||
--accent-soft: oklch(95.5% 0.032 180);
|
||
--sel: oklch(96.5% 0.016 190);
|
||
--on-accent: oklch(99% 0.01 180);
|
||
--glow-3: oklch(89% 0.07 172);
|
||
--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(23% 0.026 210);
|
||
--surface: oklch(26.5% 0.028 210);
|
||
--surface-2: oklch(30% 0.03 210);
|
||
--ink: oklch(96% 0.012 196);
|
||
--ink-2: oklch(80% 0.02 196);
|
||
--ink-3: oklch(65% 0.025 196);
|
||
--border: oklch(36% 0.04 204);
|
||
--accent: oklch(76% 0.105 190);
|
||
--accent-soft: oklch(31% 0.05 190);
|
||
--sel: oklch(30% 0.03 195);
|
||
--on-accent: oklch(20% 0.04 204);
|
||
--glow-3: oklch(50% 0.08 180);
|
||
--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="#e3f4f1">
|
||
<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="#00705e" />
|
||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#00705e" 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>
|
||
|
||
<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>Все 30 локаций в подписке</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>Российский адрес в комплекте</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>Современный протокол XRAY (VLESS, Hysteria2, XHTTP)</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="display: flex; gap: 8px;">
|
||
<sc-for list="{{ plans }}" as="plan" hint-placeholder-count="2">
|
||
<button type="button" onClick="{{ plan.pick }}" style="flex: 1; display: grid; align-content: start; gap: 4px; min-height: 44px; padding: 14px; border-radius: 16px; border: 1px solid {{ plan.border }}; background: {{ plan.bg }}; color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;">
|
||
<span style="font-size: 13px; color: var(--ink-3);">{{ plan.name }}</span>
|
||
<span style="font-size: 20px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">{{ plan.price }}</span>
|
||
<span style="font-size: 12px; color: {{ plan.noteInk }};">{{ plan.note }}</span>
|
||
</button>
|
||
</sc-for>
|
||
</div>
|
||
<button type="button" onClick="{{ confirmPay }}" style="min-height: 52px; border-radius: 16px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;">{{ payButton }}</button>
|
||
|
||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 12px 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: flex; align-items: center; gap: 11px; padding: 12px 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||
{{ 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: var(--ink-3);">{{ loc.city }}</span>
|
||
</span>
|
||
<span style="flex: none; font-size: 12px; color: {{ loc.statusInk }};">{{ loc.status }}</span>
|
||
</div>
|
||
</sc-for>
|
||
|
||
<a href="Main.dc.html" style="display: grid; gap: 6px; margin-top: 8px; padding: 16px; border-radius: 18px; background: var(--surface-2); border: 1px solid var(--border); color: var(--ink);">
|
||
<span style="font-size: 16px; font-weight: 500;">Нужен свой IP-адрес</span>
|
||
<span style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Если сервисы требуют постоянный адрес, есть отдельный тариф с индивидуальным IP в выбранной стране.</span>
|
||
<span style="font-size: 13px; color: var(--accent);">Посмотреть индивидуальный тариф →</span>
|
||
</a>
|
||
</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: 10px;">
|
||
{{ key.flag }}
|
||
<span style="flex: 1; min-width: 0; 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.devices }}</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 style="font-size: 12px; line-height: 1.5; color: var(--ink-3); padding: 0 4px;">Каждая подписка — свои пять устройств и своя страна. Оплата общая, со счёта аккаунта.</div>
|
||
</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;">{{ curTitle }}</div>
|
||
<div style="font-size: 13px; color: var(--ink-3);">{{ curPlan }}</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: center; gap: 11px;">
|
||
{{ curFlag }}
|
||
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
|
||
<span style="font-size: 16px; font-weight: 500;">{{ curCountry }}</span>
|
||
<span style="font-size: 12px; color: var(--accent);">Подключено сейчас</span>
|
||
</span>
|
||
</div>
|
||
<button type="button" onClick="{{ goSwitch }}" style="min-height: 46px; 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: 13px; color: var(--ink-3);">Действует до</span>
|
||
<span style="font-size: 16px; font-weight: 500;">{{ curUntil }}</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;">{{ curPlan }} · со счёта</span>
|
||
</div>
|
||
<div style="font-size: 13px; color: {{ curLeftInk }};">{{ curLeft }}</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;">Устройства</span>
|
||
<span style="font-size: 12px; color: var(--ink-3);">{{ curDevices }}</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;">{{ curXray }}</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="{{ switchOff }}" style="display: grid; gap: 12px;">
|
||
<button type="button" onClick="{{ goKey }}" 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>
|
||
<sc-for list="{{ pickList }}" as="loc" hint-placeholder-count="8">
|
||
<button type="button" onClick="{{ loc.pick }}" style="display: flex; align-items: center; gap: 11px; width: 100%; min-height: 44px; padding: 12px 14px; border-radius: 16px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;">
|
||
{{ 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>
|
||
<span style="width: 10px; height: 10px; border-radius: 50%; background: {{ loc.dot }}; display: block; flex: none;"></span>
|
||
</button>
|
||
</sc-for>
|
||
</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;">350 ₽</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>
|
||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; background: var(--surface); color: var(--ink); font-size: 15px;">
|
||
<span style="flex: 1;">Нужен свой IP-адрес</span>
|
||
<span style="color: var(--ink-3);">›</span>
|
||
</a>
|
||
<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;">350 ₽</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;">699 ₽ · 14 окт.</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(--accent);">6 990 ₽</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/veilixvpn_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>
|
||
|
||
<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="{{ 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;">Наведите камеру телефона на код в приложении Happ или другом клиенте XRAY.</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, Подписка, Ещё. В «Ещё» — счёт и платежи, реферальная программа, свой IP-адрес, тема и язык.</p>
|
||
</div>
|
||
</div>
|
||
</x-dc>
|
||
<script type="text/x-dc" data-dc-script data-props="{"defaultTheme":{"editor":"enum","options":["light","dark","system"],"default":"light","tsType":"'light' | 'dark' | 'system'","section":"Тема"}}">
|
||
class Component extends DCLogic {
|
||
state = { theme: null, sysDark: false, view: "buy", key: 0, locs: [0, 19], loc: 0, qr: null, copied: false, lang: "ru", refCopied: false, faq: -1, plan: 0, paid: false, topup: false, sum: 1, method: 0, topupSent: 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 L = (country, city, code, ready) => ({ country, city, code, ready });
|
||
return [
|
||
L("Нидерланды", "Амстердам", "nl", true),
|
||
L("Германия", "Франкфурт", "de", true),
|
||
L("США", "Нью-Йорк", "us", true),
|
||
L("Великобритания", "Лондон", "gb", true),
|
||
L("Франция", "Париж", "fr", true),
|
||
L("Финляндия", "Хельсинки", "fi", true),
|
||
L("Швеция", "Стокгольм", "se", true),
|
||
L("Норвегия", "Осло", "no", false),
|
||
L("Дания", "Копенгаген", "dk", false),
|
||
L("Швейцария", "Цюрих", "ch", true),
|
||
L("Австрия", "Вена", "at", true),
|
||
L("Испания", "Мадрид", "es", true),
|
||
L("Италия", "Милан", "it", true),
|
||
L("Польша", "Варшава", "pl", true),
|
||
L("Чехия", "Прага", "cz", true),
|
||
L("Румыния", "Бухарест", "ro", false),
|
||
L("Литва", "Вильнюс", "lt", true),
|
||
L("Латвия", "Рига", "lv", true),
|
||
L("Эстония", "Таллин", "ee", false),
|
||
L("Турция", "Стамбул", "tr", true),
|
||
L("Казахстан", "Алматы", "kz", false),
|
||
L("Армения", "Ереван", "am", false),
|
||
L("Грузия", "Тбилиси", "ge", false),
|
||
L("ОАЭ", "Дубай", "ae", true),
|
||
L("Сингапур", "Сингапур", "sg", true),
|
||
L("Япония", "Токио", "jp", true),
|
||
L("Южная Корея", "Сеул", "kr", false),
|
||
L("Канада", "Торонто", "ca", true),
|
||
L("Австралия", "Сидней", "au", false),
|
||
L("Бразилия", "Сан-Паулу", "br", false)
|
||
];
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
renderVals() {
|
||
const s = this.state;
|
||
const sel = "var(--sel)";
|
||
const theme = s.theme || this.props.defaultTheme || "light";
|
||
const all = this.data();
|
||
const keyData = [
|
||
{ title: "Основная подписка", plan: "699 ₽ в месяц", devices: "3 из 5 устройств", until: s.lang === "en" ? "14 October" : "14 октября", badge: "Активна", warn: false, left: "Продлится автоматически через 24 дня" },
|
||
{ title: "Вторая подписка", plan: "6 990 ₽ в год", devices: "2 из 5 устройств", until: s.lang === "en" ? "21 September" : "21 сентября", badge: "Истекает", warn: true, left: "Осталось 8 дней" }
|
||
];
|
||
const locs = s.locs || [0, 19];
|
||
const k = keyData[s.key] || keyData[0];
|
||
const cur = all[locs[s.key] || 0] || all[0];
|
||
const cfgNames = [["Ноутбук", "подключено"], ["Телефон", "подключено"], ["Планшет", "подключено"], ["Рабочий ноутбук", "не занят"], ["Свободный слот", "не занят"]];
|
||
const faqData = [
|
||
["Что входит в подписку?", "Все тридцать локаций, включая российскую, пять устройств и протокол XRAY. Отдельной платы за страну нет, переключение бесплатное."],
|
||
["Можно менять страну сколько угодно?", "Да. Переключение занимает пару секунд и ничего не стоит: список локаций открыт целиком на всё время подписки."],
|
||
["Сколько устройств можно подключить?", "До пяти одновременно. Каждое может быть подключено к своей стране."],
|
||
["На каком протоколе работает подключение?", "На XRAY — VLESS, Hysteria2 и XHTTP. Трафик неотличим от обычного HTTPS, поэтому подключение стабильно работает там, где старые протоколы блокируют."],
|
||
["Чем это отличается от индивидуального IP?", "Здесь сервер общий: он дешевле и этого достаточно большинству. Если сервисы требуют постоянный адрес, посмотрите тариф с индивидуальным IP."],
|
||
["Как платить и как отменить?", "Карта привязана к аккаунту, можно держать баланс и пополнять его картой, через СБП или криптой. Отмена — в разделе «Счёт и платежи», подписка доработает оплаченный период."]
|
||
];
|
||
|
||
return {
|
||
locCount: s.lang === "en" ? "30 countries" : "30 стран",
|
||
locations: all.map(d => ({
|
||
country: d.country,
|
||
city: d.city,
|
||
flag: this.flag(d.code),
|
||
status: d.ready ? "В подписке" : "Скоро",
|
||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)"
|
||
})),
|
||
pickList: all.map((d, i) => ({
|
||
country: d.country,
|
||
flag: this.flag(d.code),
|
||
sub: d.ready ? d.city + " · доступно" : d.city + " · скоро",
|
||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||
bg: locs[s.key] === i ? sel : "var(--surface)",
|
||
border: locs[s.key] === i ? "var(--accent)" : "var(--border)",
|
||
dot: locs[s.key] === i ? "var(--accent)" : "transparent",
|
||
pick: () => this.setState(st => {
|
||
const next = (st.locs || [0, 19]).slice();
|
||
next[st.key] = i;
|
||
return { locs: next, view: "key" };
|
||
})
|
||
})),
|
||
keys: keyData.map((kk, i) => ({
|
||
title: kk.title,
|
||
flag: this.flag(all[locs[i] || 0].code),
|
||
devices: kk.devices,
|
||
untilLine: (s.lang === "en" ? "until " : "до ") + kk.until,
|
||
badge: kk.badge,
|
||
badgeBg: kk.warn ? "color-mix(in oklab, var(--warn) 18%, transparent)" : sel,
|
||
badgeInk: kk.warn ? "var(--warn)" : "var(--accent)",
|
||
open: () => this.setState({ view: "key", key: i })
|
||
})),
|
||
curTitle: k.title,
|
||
curPlan: k.plan,
|
||
curDevices: k.devices,
|
||
curUntil: k.until,
|
||
curLeft: k.left,
|
||
curLeftInk: k.warn ? "var(--warn)" : "var(--ink-2)",
|
||
keyOff: s.view !== "key",
|
||
curFlag: this.flag(cur.code),
|
||
curCountry: cur.country + " · " + cur.city,
|
||
curXray: "vless://8f2a…@" + cur.code + "1.veilixvpn.net:443?type=xhttp#VeilixVPN",
|
||
plans: [
|
||
{ name: "Месяц", price: "699 ₽", note: "списание раз в месяц", accent: false },
|
||
{ name: "Год", price: "6 990 ₽", note: "два месяца в подарок", accent: true }
|
||
].map((p, i) => ({
|
||
name: p.name,
|
||
price: p.price,
|
||
note: p.note,
|
||
noteInk: p.accent ? "var(--accent)" : "var(--ink-3)",
|
||
bg: s.plan === i ? sel : "var(--surface)",
|
||
border: s.plan === i ? "var(--accent)" : "var(--border)",
|
||
pick: () => this.setState({ plan: i, paid: false })
|
||
})),
|
||
payButton: s.paid ? "Оплата подтверждена" : (s.plan === 1 ? "Оплатить 6 990 ₽" : "Оплатить 699 ₽"),
|
||
confirmPay: () => this.setState({ paid: true }),
|
||
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 }))
|
||
})),
|
||
configs: cfgNames.map(c => ({
|
||
name: c[0],
|
||
state: c[1],
|
||
showQr: () => this.setState({ qr: { title: "XRAY · " + c[0], seed: cur.code + c[0] } })
|
||
})),
|
||
goBuy: () => this.setState({ view: "buy" }),
|
||
goFaq: () => this.setState({ view: "faq" }),
|
||
goKeys: () => this.setState({ view: "keys" }),
|
||
goSwitch: () => this.setState({ view: "switch" }),
|
||
goMore: () => this.setState({ view: "more" }),
|
||
goRef: () => this.setState({ view: "ref" }),
|
||
goBilling: () => this.setState({ view: "billing" }),
|
||
buyOff: s.view !== "buy",
|
||
faqOff: s.view !== "faq",
|
||
keysOff: s.view !== "keys",
|
||
switchOff: s.view !== "switch",
|
||
moreOff: s.view !== "more",
|
||
refOff: s.view !== "ref",
|
||
billingOff: s.view !== "billing",
|
||
tabBuyBg: s.view === "buy" ? sel : "transparent",
|
||
tabFaqBg: s.view === "faq" ? sel : "transparent",
|
||
goKey: () => this.setState({ view: "key" }),
|
||
tabKeysBg: (s.view === "keys" || s.view === "key" || s.view === "switch") ? sel : "transparent",
|
||
tabMoreBg: (s.view === "more" || s.view === "ref" || s.view === "billing") ? 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" || s.view === "switch") ? "var(--accent)" : "var(--ink-2)",
|
||
tabMoreInk: (s.view === "more" || s.view === "ref" || s.view === "billing") ? "var(--accent)" : "var(--ink-2)",
|
||
openTopup: () => this.setState({ topup: true }),
|
||
closeTopup: () => this.setState({ topup: false }),
|
||
topupOff: !s.topup,
|
||
sums: ["700 ₽", "2 100 ₽", "6 990 ₽"].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: "−699 ₽", ink: "var(--ink)" },
|
||
{ title: "Пополнение баланса", meta: "6 сентября · USDT (TRC-20)", amount: "+2 100 ₽", ink: "var(--accent)" },
|
||
{ title: "Подписка на месяц", meta: "14 августа · с баланса", amount: "−699 ₽", ink: "var(--ink)" },
|
||
{ title: "Подписка на месяц", meta: "14 июля · карта ···· 4242", amount: "−699 ₽", ink: "var(--ink)" },
|
||
{ title: "Реферальный бонус", meta: "2 июля · месяц в подарок", amount: "+699 ₽", ink: "var(--accent)" }
|
||
],
|
||
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.code + "xray" } }),
|
||
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>
|