Files
veilix.app/design/handoff/project/MassAppTG.dc.html
T
2026-10-21 03:30:38 +02:00

807 lines
60 KiB
HTML
Raw 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-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="{&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, 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>