Этап 0: документация и макеты
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No files matched your search
@@ -0,0 +1,22 @@
|
||||
# CODING AGENTS: READ THIS FIRST
|
||||
|
||||
This is a **handoff bundle** from Claude Design (claude.ai/design).
|
||||
|
||||
A user mocked up designs in HTML/CSS/JS using an AI design tool, then exported this bundle so a coding agent can implement the designs for real.
|
||||
|
||||
## What you should do — IMPORTANT
|
||||
|
||||
**Read `veilix/project/AppTG.dc.html` in full.** The user had this file open when they triggered the handoff, so it's almost certainly the primary design they want built. Read it top to bottom — don't skim. Then **follow its imports**: open every file it pulls in (shared components, CSS, scripts) so you understand how the pieces fit together before you start implementing.
|
||||
|
||||
**If anything is ambiguous, ask the user to confirm before you start implementing.** It's much cheaper to clarify scope up front than to build the wrong thing.
|
||||
|
||||
## About the design files
|
||||
|
||||
The design medium is **HTML/CSS/JS** — these are prototypes, not production code. Your job is to **recreate them pixel-perfectly** in whatever technology makes sense for the target codebase (React, Vue, native, whatever fits). Match the visual output; don't copy the prototype's internal structure unless it happens to fit.
|
||||
|
||||
**Don't render these files in a browser or take screenshots unless the user asks you to.** Everything you need — dimensions, colors, layout rules — is spelled out in the source. Read the HTML and CSS directly; a screenshot won't tell you anything they don't.
|
||||
|
||||
## Bundle contents
|
||||
|
||||
- `veilix/README.md` — this file
|
||||
- `veilix/project/` — the `VEILIX` project files (HTML prototypes, assets, components)
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,991 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-hover: oklch(38% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-1: oklch(86% 0.1 296);
|
||||
--glow-2: oklch(89% 0.09 266);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--warn: oklch(62% 0.14 62);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 296 / 0.05), 0 12px 32px -12px oklch(24% 0.04 296 / 0.14);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-hover: oklch(80% 0.13 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-1: oklch(45% 0.16 296);
|
||||
--glow-2: oklch(40% 0.14 266);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--warn: oklch(78% 0.13 62);
|
||||
--shadow: 0 1px 2px oklch(10% 0.02 296 / 0.4), 0 16px 40px -16px oklch(8% 0.02 296 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
input::placeholder{ color: var(--ink-3); }
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
<div style="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<div hidden="{{ appOff }}" style="display: grid; grid-template-rows: 1fr auto; min-height: 100vh;">
|
||||
<div style="display: flex; align-items: center; justify-content: center; padding: 24px;">
|
||||
<div style="width: 100%; max-width: 420px; padding: 36px; border-radius: 24px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 26px;">
|
||||
<span style="position: relative; width: 26px; height: 26px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 19px; height: 19px; 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: 19px; height: 19px; border-radius: 6px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 16px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</div>
|
||||
<h1 style="margin: 0 0 10px; font-size: 26px; font-weight: 600; letter-spacing: -0.03em;">Вход в кабинет</h1>
|
||||
<p style="margin: 0 0 22px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Введите почту, пришлём одноразовый код. Отдельной регистрации нет — кабинет создастся сам, если вы здесь впервые.</p>
|
||||
<div hidden="{{ otpSent }}" style="display: grid; gap: 10px;">
|
||||
<input type="email" placeholder="you@example.com" style="height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<button type="button" onClick="{{ sendOtp }}" style="height: 50px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Прислать код</button>
|
||||
</div>
|
||||
<div hidden="{{ otpNotSent }}" style="display: grid; gap: 10px;">
|
||||
<input type="text" inputmode="numeric" placeholder="6 цифр" style="height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 17px; letter-spacing: 0.28em; font-variant-numeric: tabular-nums; width: 100%;" />
|
||||
<button type="button" onClick="{{ signIn }}" style="height: 50px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Войти</button>
|
||||
<button type="button" onClick="{{ backToEmail }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 4px 0;">Другая почта</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; padding: 14px 24px; border-top: 1px solid var(--border); font-size: 13px; color: var(--ink-3);">
|
||||
<span>© 2026 Вейликс</span>
|
||||
<a href="#legal" style="color: var(--ink-3);" style-hover="color: var(--ink);">Условия</a>
|
||||
<a href="#legal" style="color: var(--ink-3);" style-hover="color: var(--ink);">Конфиденциальность</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-3);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
<div style="margin-left: auto; display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; width: max-content; padding: 3px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 6px 11px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 6px 11px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
<div style="display: flex; width: max-content; padding: 3px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;">
|
||||
<span style="position: relative; width: 16px; height: 16px; display: block;">
|
||||
<span style="position: absolute; left: 3.5px; top: 3.5px; width: 9px; height: 9px; 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: 32px; height: 32px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; box-shadow: inset -5px -2px 0 0 currentColor; display: block;"></span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ loginOff }}" style="display: grid; grid-template-columns: {{ shellCols }}; min-height: 100vh;">
|
||||
|
||||
<div hidden="{{ topbarOff }}" style="position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: 10px; padding: 12px 16px; background: color-mix(in oklab, var(--bg) 85%, transparent); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); border-bottom: 1px solid var(--border);">
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink);">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<aside hidden="{{ asideOff }}" style="display: flex; flex-direction: column; gap: 24px; padding: 22px 16px; border-right: 1px solid var(--border); background: var(--surface-2); position: sticky; top: 0; height: 100vh;">
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; padding: 4px 8px; color: var(--ink);">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</a>
|
||||
|
||||
<nav style="display: grid; gap: 4px;">
|
||||
<button type="button" onClick="{{ goHome }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ homeBg }}; color: {{ homeInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; bottom: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; bottom: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Главная
|
||||
</button>
|
||||
<button type="button" onClick="{{ goKeys }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ keysBg }}; color: {{ keysInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 2px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 8px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.6; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 14px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.3; display: block;"></span>
|
||||
</span>
|
||||
Список ключей
|
||||
</button>
|
||||
<button type="button" onClick="{{ goBuy }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ buyBg }}; color: {{ buyInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 7px; top: 0; width: 4px; height: 18px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 7px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Купить ключ
|
||||
</button>
|
||||
<button type="button" onClick="{{ goBilling }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ billingBg }}; color: {{ billingInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 2px; width: 18px; height: 14px; border-radius: 4px; border: 2px solid currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 6px; width: 18px; height: 3px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Счёт и платежи
|
||||
</button>
|
||||
<button type="button" onClick="{{ goRef }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ refBg }}; color: {{ refInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 5px; width: 8px; height: 8px; border-radius: 50%; border: 2px solid currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 0; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 2px; bottom: 0; width: 8px; height: 8px; border-radius: 50%; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
</span>
|
||||
Реферальная программа
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div style="margin-top: auto; display: grid; gap: 14px; padding: 0 4px;">
|
||||
<a href="Support.dc.html" style="display: flex; align-items: center; gap: 12px; padding: 11px 8px; border-radius: 12px; color: var(--ink-2); font-size: 15px;" style-hover="background: var(--surface); color: var(--ink);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 16px; height: 16px; border-radius: 8px 8px 0 0; border: 2px solid currentColor; border-bottom: 0; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 8px; width: 5px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 8px; width: 5px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Поддержка
|
||||
</a>
|
||||
<div>
|
||||
<div style="font-size: 12px; color: var(--ink-3); margin-bottom: 6px; padding: 0 4px;">Тема</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px; width: max-content; max-width: 100%; align-self: start; padding: 3px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border: 0; border-radius: 9px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;">
|
||||
<span style="position: relative; width: 18px; height: 18px; display: block;">
|
||||
<span style="position: absolute; left: 4px; top: 4px; width: 10px; height: 10px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 0; width: 18px; height: 18px; 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: 36px; height: 36px; border: 0; border-radius: 9px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;">
|
||||
<span style="width: 18px; height: 18px; border-radius: 50%; box-shadow: inset -6px -2px 0 0 currentColor; display: block;"></span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border: 0; border-radius: 9px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;">
|
||||
<span style="width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 12px; color: var(--ink-3); margin-bottom: 6px; padding: 0 4px;">Язык</div>
|
||||
<div style="display: flex; flex-wrap: wrap; width: max-content; max-width: 100%; align-self: start; padding: 3px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 8px 14px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 13px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 8px 14px; border: 0; border-radius: 9px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 13px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ signOut }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 13px; cursor: pointer; padding: 4px 8px;">Выйти · anna@example.com</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main style="padding: {{ mainPad }}; max-width: 960px;">
|
||||
|
||||
<div hidden="{{ homeOff }}" style="display: grid; gap: 28px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Ваши ключи</h1>
|
||||
<p style="margin: 0; font-size: 15px; color: var(--ink-2); font-weight: 300;">Два активных тарифа, каждый со своим индивидуальным IP.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<sc-for list="{{ keys }}" as="key" hint-placeholder-count="2">
|
||||
<button type="button" onClick="{{ key.open }}" style="display: grid; gap: 16px; padding: 24px; border-radius: 20px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow); color: var(--ink); font-family: inherit; text-align: left; cursor: pointer; width: 100%;" style-hover="border-color: var(--accent);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px;">
|
||||
<span style="font-size: 19px; font-weight: 500; letter-spacing: -0.02em;">{{ key.title }}</span>
|
||||
<span style="padding: 4px 10px; border-radius: 8px; background: {{ key.badgeBg }}; color: {{ key.badgeInk }}; font-size: 12px;">{{ key.badge }}</span>
|
||||
<span style="margin-left: auto; font-size: 14px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ key.ip }}</span>
|
||||
</span>
|
||||
<span style="display: flex; flex-wrap: wrap; gap: 8px 28px; font-size: 14px; color: var(--ink-2);">
|
||||
<span>Действует до {{ key.until }}</span>
|
||||
<span>{{ key.devices }}</span>
|
||||
<span>{{ key.payment }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; padding: 24px; border-radius: 20px; background: var(--surface-2); border: 1px dashed var(--border);">
|
||||
<div style="display: grid; gap: 4px;">
|
||||
<div style="font-size: 17px; font-weight: 500;">Нужен ещё один IP или другая страна</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2); font-weight: 300;">Новый ключ появится здесь же, оплата отдельная.</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ goBuy }}" style="height: 46px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Купить ключ</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<h2 style="margin: 0 0 4px; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Короткие ответы</h2>
|
||||
<sc-for list="{{ faq }}" as="item" hint-placeholder-count="3">
|
||||
<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: 16px; width: 100%; padding: 17px 20px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 16px; font-weight: 500; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">{{ item.q }}</span>
|
||||
<span style="flex: none; width: 9px; height: 9px; 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 20px 20px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">{{ item.a }}</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ keysOff }}" style="display: grid; gap: 20px;">
|
||||
<h1 style="margin: 0; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Список ключей</h1>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<sc-for list="{{ allKeys }}" as="key" hint-placeholder-count="3">
|
||||
<button type="button" onClick="{{ key.open }}" style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; padding: 20px 24px; border-radius: 18px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; text-align: left; cursor: pointer; width: 100%;" style-hover="border-color: var(--accent);">
|
||||
<span style="font-size: 17px; font-weight: 500;">{{ key.title }}</span>
|
||||
<span style="padding: 4px 10px; border-radius: 8px; background: {{ key.badgeBg }}; color: {{ key.badgeInk }}; font-size: 12px;">{{ key.badge }}</span>
|
||||
<span style="margin-left: auto; font-size: 14px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ key.ip }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-2); font-variant-numeric: tabular-nums;">{{ key.untilLine }}</span>
|
||||
</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ keyOff }}" style="display: grid; gap: 22px;">
|
||||
<button type="button" onClick="{{ goKeys }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">← Все ключи</button>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px 20px;">
|
||||
<h1 style="margin: 0; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">{{ cur.title }}</h1>
|
||||
<span style="font-size: 15px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ cur.ip }}</span>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;">
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Действует до</div>
|
||||
<div style="font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">{{ cur.until }}</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; color: {{ cur.leftInk }};">{{ cur.left }}</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Оплата</div>
|
||||
<div style="font-size: 18px; font-variant-numeric: tabular-nums;">{{ cur.card }}</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; color: var(--ink-2);">2 499 ₽ в месяц · карта аккаунта</div>
|
||||
<button type="button" onClick="{{ goBilling }}" style="margin-top: 14px; height: 40px; padding: 0 16px; border-radius: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="border-color: var(--ink-3);">Счёт и платежи</button>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Устройства</div>
|
||||
<div style="font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">{{ cur.devices }}</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; color: var(--ink-2);">Один и тот же IP на всех</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 6px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">WireGuard</h2>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">5 конфигураций — по одной на устройство</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 18px; font-size: 14px; color: var(--ink-2); font-weight: 300; max-width: 60ch;">На компьютере скачайте файл и откройте его в приложении. На телефоне — отсканируйте QR-код.</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px;">
|
||||
<sc-for list="{{ configs }}" as="cfg" hint-placeholder-count="5">
|
||||
<div style="display: grid; gap: 12px; padding: 16px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 15px; font-weight: 500;">{{ cfg.name }}</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">{{ cfg.state }}</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button type="button" style="flex: 1; height: 38px; border-radius: 10px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="background: var(--accent-hover);">Скачать</button>
|
||||
<button type="button" onClick="{{ cfg.showQr }}" style="height: 38px; padding: 0 14px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="border-color: var(--ink-3);">QR</button>
|
||||
</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 6px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Xray</h2>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Для Happ и других клиентов</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 18px; font-size: 14px; color: var(--ink-2); font-weight: 300; max-width: 60ch;">Запасной способ подключения. Скопируйте ссылку или отсканируйте код — настройки те же.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px; align-items: center;">
|
||||
<div style="flex: 1; min-width: 240px; padding: 14px 16px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border); font-size: 13px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums;">{{ cur.xray }}</div>
|
||||
<button type="button" onClick="{{ copyXray }}" style="height: 46px; padding: 0 20px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ copyLabel }}</button>
|
||||
<button type="button" onClick="{{ showXrayQr }}" style="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;" style-hover="border-color: var(--ink-3);">QR</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ buyOff }}" style="display: grid; gap: 22px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Купить ключ</h1>
|
||||
<p style="margin: 0; font-size: 15px; color: var(--ink-2); font-weight: 300;">Базовая цена: 4 900 ₽ первый месяц, далее 2 499 ₽. В редких локациях — выше.</p>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px;">
|
||||
<sc-for list="{{ locations }}" as="loc" hint-placeholder-count="12">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: grid; align-content: start; gap: 14px; padding: 20px; border-radius: 18px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
{{ loc.flag }}
|
||||
<span style="display: flex; align-items: center; gap: 12px; flex: none; min-height: 32px;">
|
||||
<span hidden="{{ loc.buyOff }}" style="display: flex; align-items: center; justify-content: center; height: 32px; padding: 0 16px; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 14px; font-weight: 500; cursor: pointer;">Купить</span>
|
||||
<span hidden="{{ loc.dotOff }}" style="width: 11px; height: 11px; border-radius: 50%; background: {{ loc.dot }}; display: block; flex: none;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span style="font-size: 17px; font-weight: 500; letter-spacing: -0.02em;">{{ loc.country }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
<span style="font-size: 13px; color: {{ loc.statusInk }};">{{ loc.status }}</span>
|
||||
<span style="display: grid; gap: 12px; padding-top: 14px; border-top: 1px solid var(--border);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
|
||||
<span style="font-size: 17px; font-weight: 500; color: var(--ink);">{{ loc.setup }}</span>
|
||||
первый месяц · далее
|
||||
<span style="font-weight: 500; color: var(--ink);">{{ loc.month }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
<div style="padding: 24px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<h2 id="lead" style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em; scroll-margin-top: 24px;">Нужны индивидуальные условия</h2>
|
||||
<p style="margin: 0 0 18px; max-width: 60ch; font-size: 14px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Команда, много адресов, редкая страна или свой сценарий маршрутизации. Оставьте заявку — ответим в течение дня.</p>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: inline-flex; align-items: center; height: 46px; padding: 0 20px; border-radius: 12px; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-soft);">Оставить заявку</button>
|
||||
<div hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 460px;">
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ billingOff }}" style="display: grid; gap: 22px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Счёт и платежи</h1>
|
||||
<p style="margin: 0; max-width: 62ch; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Карта привязана к аккаунту, а не к отдельному ключу. Списания за все ключи идут с неё — или с баланса, если на нём есть деньги.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px;">
|
||||
<div style="display: grid; align-content: start; gap: 8px; padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Баланс</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">1 250 ₽</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2);">Списывается в первую очередь</div>
|
||||
<button type="button" onClick="{{ openTopup }}" style="justify-self: start; margin-top: 6px; height: 40px; padding: 0 16px; border-radius: 10px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 14px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Пополнить</button>
|
||||
</div>
|
||||
<div style="display: grid; align-content: start; gap: 8px; padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Способ оплаты</div>
|
||||
<div style="font-size: 20px; font-weight: 500; font-variant-numeric: tabular-nums;">Карта ···· 4242</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2);">Основная для всех ключей аккаунта</div>
|
||||
<button type="button" style="justify-self: start; margin-top: 6px; height: 40px; padding: 0 16px; border-radius: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="border-color: var(--ink-3);">Изменить карту</button>
|
||||
</div>
|
||||
<div style="display: grid; align-content: start; gap: 8px; padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Следующее списание</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">2 499 ₽</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2);">21 сентября · США · Нью-Йорк</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Далее 14 октября · Нидерланды</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ topupOff }}" style="display: grid; gap: 16px; padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Пополнить баланс</h2>
|
||||
<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: grid; gap: 8px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Сумма</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<sc-for list="{{ sums }}" as="sum" hint-placeholder-count="4">
|
||||
<button type="button" onClick="{{ sum.pick }}" style="height: 44px; padding: 0 18px; 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>
|
||||
<div style="display: grid; gap: 8px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Способ</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<sc-for list="{{ methods }}" as="m" hint-placeholder-count="3">
|
||||
<button type="button" onClick="{{ m.pick }}" style="height: 44px; padding: 0 18px; border-radius: 12px; border: 1px solid {{ m.border }}; background: {{ m.bg }}; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">{{ m.label }}</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
<div hidden="{{ cryptoOff }}" style="display: grid; gap: 10px; padding: 18px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 15px; font-weight: 500;">USDT (TRC-20), BTC, ETH</div>
|
||||
<p style="margin: 0; max-width: 60ch; font-size: 14px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Покажем адрес и сумму в выбранной монете. Курс фиксируется на 15 минут, баланс пополнится после одного подтверждения сети.</p>
|
||||
</div>
|
||||
<button type="button" onClick="{{ sendTopup }}" style="justify-self: start; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ topupButton }}</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<h2 style="margin: 0 0 4px; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">История платежей</h2>
|
||||
<sc-for list="{{ payments }}" as="p" hint-placeholder-count="5">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; padding: 18px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="width: 116px; flex: none; font-size: 14px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ p.date }}</span>
|
||||
<span style="flex: 1; min-width: 180px; display: grid; gap: 3px;">
|
||||
<span style="font-size: 15px;">{{ p.title }}</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">{{ p.method }}</span>
|
||||
</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">{{ p.status }}</span>
|
||||
<span style="width: 104px; flex: none; text-align: right; font-size: 16px; font-weight: 500; color: {{ p.ink }}; font-variant-numeric: tabular-nums;">{{ p.amount }}</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ refOff }}" style="display: grid; gap: 20px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Реферальная программа</h1>
|
||||
<p style="margin: 0; max-width: 62ch; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Приведите друга — вы оба получите по месяцу в подарок.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;">
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Приглашено</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">3</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Месяцев начислено</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">2</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Ожидает подтверждения</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">1</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 14px; padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 16px; font-weight: 500;">Ваша ссылка</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px; align-items: center;">
|
||||
<div style="flex: 1; min-width: 220px; padding: 14px 16px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border); font-size: 14px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">veilix.com/r/anna-7f2c</div>
|
||||
<button type="button" onClick="{{ copyRef }}" style="height: 46px; padding: 0 20px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ refLabel }}</button>
|
||||
</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3); line-height: 1.5;">Месяц начисляется вам и другу после его первой оплаты и применяется к ближайшему списанию.</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<div style="font-size: 16px; font-weight: 500;">Приглашённые</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">m···@gmail.com</span>
|
||||
<span style="font-size: 14px; color: var(--accent);">Месяц начислен</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">d···@yandex.ru</span>
|
||||
<span style="font-size: 14px; color: var(--accent);">Месяц начислен</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">k···@icloud.com</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">Ждём оплаты</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ moreOff }}" style="display: grid; gap: 16px;">
|
||||
<h1 style="margin: 0; font-size: 26px; font-weight: 600; letter-spacing: -0.03em;">Ещё</h1>
|
||||
<button type="button" onClick="{{ goBilling }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; font-size: 16px; text-align: left; cursor: pointer;">
|
||||
<span>Счёт и платежи</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ goRef }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; font-size: 16px; text-align: left; cursor: pointer;">
|
||||
<span>Реферальная программа</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<a href="Support.dc.html" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-size: 16px;">
|
||||
<span>Поддержка</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</a>
|
||||
<div style="display: grid; gap: 12px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
||||
<div style="font-size: 15px;">Тема</div>
|
||||
<div style="display: flex; padding: 3px; border-radius: 11px; border: 1px solid var(--border); flex: none;">
|
||||
<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>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 34px; border: 0; border-radius: 8px; background: {{ systemBg }}; color: var(--ink); cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid var(--border);">
|
||||
<div style="font-size: 15px;">Язык</div>
|
||||
<div style="display: flex; padding: 3px; border-radius: 11px; border: 1px solid var(--border); flex: none;">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ signOut }}" style="justify-self: start; min-height: 48px; padding: 0 16px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink-2); font-family: inherit; font-size: 15px; cursor: pointer;">Выйти · anna@example.com</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ pillOff }}" style="position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 50; display: flex; gap: 4px; 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="{{ goHome }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; border: 0; border-radius: 999px; background: {{ tabHomeBg }}; color: {{ tabHomeInk }}; 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: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; bottom: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; bottom: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Главная
|
||||
</button>
|
||||
<button type="button" onClick="{{ goKeys }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; 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="{{ goBuy }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; 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="{{ goMore }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; 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: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 24px; background: color-mix(in oklab, var(--ink) 45%, transparent); backdrop-filter: blur(6px);">
|
||||
<div style="display: grid; gap: 16px; justify-items: center; padding: 28px; border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 16px; font-weight: 500;">{{ qrTitle }}</div>
|
||||
{{ qrImage }}
|
||||
<div style="max-width: 260px; text-align: center; font-size: 13px; line-height: 1.5; color: var(--ink-3);">Откройте приложение на телефоне и наведите камеру на код.</div>
|
||||
</div>
|
||||
</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: "home", key: 0, faq: 0, loc: 0, lead: false, leadSent: false, qr: null, copied: false, signedIn: true, otp: false, lang: "ru", narrow: false, refCopied: false, topup: false, sum: 1, method: 0, topupSent: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, view: this.state.view === "more" && !n ? "home" : this.state.view });
|
||||
if (!n && this.state.view === "more") this.setState({ view: "home" });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
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;
|
||||
const d = Math.max(Math.abs(rr - 3), Math.abs(cc - 3));
|
||||
on = d !== 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: "220px",
|
||||
height: "220px",
|
||||
padding: "12px",
|
||||
borderRadius: "14px",
|
||||
background: "var(--bg)",
|
||||
border: "1px solid var(--border)"
|
||||
}
|
||||
}, cells);
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
const sel = "var(--accent-soft)";
|
||||
const theme = s.theme || this.props.defaultTheme || "light";
|
||||
const keyData = [
|
||||
{ title: "Нидерланды · Амстердам", ip: "185.72.14.203", until: s.lang === "en" ? "14 October 2026" : "14 октября 2026", badge: "Активен", warn: false, devices: "3 из 5 устройств", payment: "Карта ···· 4242", card: "···· 4242", left: "Продлится автоматически через 31 день", xray: "vless://8f2a…@nl1.veilix.net:443?type=xhttp#Veilix-NL" },
|
||||
{ title: "США · Нью-Йорк", ip: "23.105.61.44", until: s.lang === "en" ? "21 September 2026" : "21 сентября 2026", badge: "Истекает", warn: true, devices: "1 из 5 устройств", payment: "Карта ···· 8801", card: "···· 8801", left: "Осталось 8 дней", xray: "vless://c41d…@us1.veilix.net:443?type=xhttp#Veilix-US" }
|
||||
];
|
||||
const cur = keyData[s.key];
|
||||
const mk = (i) => ({
|
||||
title: keyData[i].title,
|
||||
ip: keyData[i].ip,
|
||||
until: keyData[i].until,
|
||||
untilLine: (s.lang === "en" ? "until " : "до ") + keyData[i].until,
|
||||
badge: keyData[i].badge,
|
||||
devices: keyData[i].devices,
|
||||
payment: keyData[i].payment,
|
||||
badgeBg: keyData[i].warn ? "color-mix(in oklab, var(--warn) 18%, transparent)" : sel,
|
||||
badgeInk: keyData[i].warn ? "var(--warn)" : "var(--accent)",
|
||||
open: () => this.setState({ view: "key", key: i })
|
||||
});
|
||||
const faqData = [
|
||||
["Как подключить новое устройство?", "Откройте ключ, скачайте любую свободную конфигурацию WireGuard или отсканируйте её QR-код. Каждая конфигурация рассчитана на одно устройство."],
|
||||
["Что делать, если подключение перестало работать?", "Переключитесь на Xray в том же ключе — он настроен заранее. Если не помогло, напишите в поддержку, ответим в течение часа."],
|
||||
["Как отменить продление?", "На странице ключа нажмите «Изменить карту» и отвяжите её. Ключ доработает оплаченный месяц."]
|
||||
];
|
||||
const locData = [
|
||||
["Нидерланды", "Амстердам", true],
|
||||
["Германия", "Франкфурт", true],
|
||||
["США", "Нью-Йорк", true],
|
||||
["Финляндия", "Хельсинки", false],
|
||||
["Турция", "Стамбул", false],
|
||||
["Япония", "Токио", false]
|
||||
];
|
||||
const cfgNames = [
|
||||
["Ноутбук", "подключено"],
|
||||
["Телефон", "подключено"],
|
||||
["Планшет", "подключено"],
|
||||
["Рабочий ноутбук", "не занят"],
|
||||
["Свободный слот", "не занят"]
|
||||
];
|
||||
|
||||
return {
|
||||
appOff: s.signedIn,
|
||||
loginOff: !s.signedIn,
|
||||
otpSent: s.otp,
|
||||
otpNotSent: !s.otp,
|
||||
sendOtp: () => this.setState({ otp: true }),
|
||||
signIn: () => this.setState({ signedIn: true, otp: false, view: "home" }),
|
||||
backToEmail: () => this.setState({ otp: false }),
|
||||
signOut: () => this.setState({ signedIn: false, otp: false }),
|
||||
|
||||
goHome: () => this.setState({ view: "home" }),
|
||||
goKeys: () => this.setState({ view: "keys" }),
|
||||
goBuy: () => this.setState({ view: "buy" }),
|
||||
goMore: () => this.setState({ view: "more" }),
|
||||
goBilling: () => this.setState({ view: "billing" }),
|
||||
billingOff: s.view !== "billing",
|
||||
billingBg: s.view === "billing" ? sel : "transparent",
|
||||
billingInk: s.view === "billing" ? "var(--accent)" : "var(--ink-2)",
|
||||
openTopup: () => this.setState({ topup: true }),
|
||||
closeTopup: () => this.setState({ topup: false }),
|
||||
topupOff: !s.topup,
|
||||
sums: ["2 500 ₽", "5 000 ₽", "10 000 ₽", "Своя сумма"].map((label, i) => ({
|
||||
label: label,
|
||||
bg: s.sum === i ? sel : "transparent",
|
||||
border: s.sum === i ? "var(--accent)" : "var(--border)",
|
||||
pick: () => this.setState({ sum: i })
|
||||
})),
|
||||
methods: ["Карта ···· 4242", "СБП", "Криптовалюта"].map((label, i) => ({
|
||||
label: label,
|
||||
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: [
|
||||
{ date: "14 сентября", title: "Продление · Нидерланды · Амстердам", method: "Карта ···· 4242", status: "Оплачено", amount: "−2 499 ₽", ink: "var(--ink)" },
|
||||
{ date: "6 сентября", title: "Пополнение баланса", method: "USDT (TRC-20)", status: "Зачислено", amount: "+5 000 ₽", ink: "var(--accent)" },
|
||||
{ date: "21 августа", title: "Первый месяц · США · Нью-Йорк", method: "Карта ···· 4242", status: "Оплачено", amount: "−4 900 ₽", ink: "var(--ink)" },
|
||||
{ date: "14 августа", title: "Продление · Нидерланды · Амстердам", method: "С баланса", status: "Оплачено", amount: "−2 499 ₽", ink: "var(--ink)" },
|
||||
{ date: "2 августа", title: "Реферальный бонус", method: "Месяц в подарок", status: "Начислено", amount: "+2 499 ₽", ink: "var(--accent)" }
|
||||
],
|
||||
goRef: () => this.setState({ view: "ref" }),
|
||||
refOff: s.view !== "ref",
|
||||
refBg: s.view === "ref" ? sel : "transparent",
|
||||
refInk: s.view === "ref" ? "var(--accent)" : "var(--ink-2)",
|
||||
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)",
|
||||
homeOff: s.view !== "home",
|
||||
keysOff: s.view !== "keys",
|
||||
keyOff: s.view !== "key",
|
||||
buyOff: s.view !== "buy",
|
||||
moreOff: s.view !== "more",
|
||||
shellCols: s.narrow ? "minmax(0, 1fr)" : "248px minmax(0, 1fr)",
|
||||
mainPad: s.narrow ? "20px 16px 110px" : "36px 40px 64px",
|
||||
asideOff: s.narrow,
|
||||
topbarOff: !s.narrow,
|
||||
pillOff: !s.narrow,
|
||||
tabHomeBg: s.view === "home" ? sel : "transparent",
|
||||
tabKeysBg: (s.view === "keys" || s.view === "key") ? sel : "transparent",
|
||||
tabBuyBg: s.view === "buy" ? sel : "transparent",
|
||||
tabMoreBg: s.view === "more" ? sel : "transparent",
|
||||
tabHomeInk: s.view === "home" ? "var(--accent)" : "var(--ink-2)",
|
||||
tabKeysInk: (s.view === "keys" || s.view === "key") ? "var(--accent)" : "var(--ink-2)",
|
||||
tabBuyInk: s.view === "buy" ? "var(--accent)" : "var(--ink-2)",
|
||||
tabMoreInk: s.view === "more" ? "var(--accent)" : "var(--ink-2)",
|
||||
homeBg: s.view === "home" ? sel : "transparent",
|
||||
keysBg: (s.view === "keys" || s.view === "key") ? sel : "transparent",
|
||||
buyBg: s.view === "buy" ? sel : "transparent",
|
||||
homeInk: s.view === "home" ? "var(--accent)" : "var(--ink-2)",
|
||||
keysInk: (s.view === "keys" || s.view === "key") ? "var(--accent)" : "var(--ink-2)",
|
||||
buyInk: s.view === "buy" ? "var(--accent)" : "var(--ink-2)",
|
||||
|
||||
keys: [mk(0), mk(1)],
|
||||
allKeys: [mk(0), mk(1)],
|
||||
cur: {
|
||||
title: cur.title,
|
||||
ip: cur.ip,
|
||||
until: cur.until,
|
||||
card: cur.card,
|
||||
devices: cur.devices,
|
||||
left: cur.left,
|
||||
leftInk: cur.warn ? "var(--warn)" : "var(--ink-2)",
|
||||
xray: cur.xray
|
||||
},
|
||||
configs: cfgNames.map((c, i) => ({
|
||||
name: c[0],
|
||||
state: c[1],
|
||||
showQr: () => this.setState({ qr: { title: "WireGuard · " + c[0], seed: cur.ip + c[0] } })
|
||||
})),
|
||||
refStats: s.lang === "en" ? "Invited: 3 · Months earned: 2" : "Приглашено: 3 · Месяцев начислено: 2",
|
||||
copyRef: () => this.setState({ refCopied: true }),
|
||||
refLabel: s.refCopied ? "Ссылка скопирована" : "Копировать ссылку",
|
||||
copyXray: () => this.setState({ copied: true }),
|
||||
copyLabel: s.copied ? "Скопировано" : "Копировать",
|
||||
showXrayQr: () => this.setState({ qr: { title: "Xray · Happ", seed: cur.xray } }),
|
||||
qrOff: !s.qr,
|
||||
qrTitle: s.qr ? s.qr.title : "",
|
||||
qrImage: s.qr ? this.qrImage(s.qr.seed) : null,
|
||||
closeQr: () => this.setState({ qr: null }),
|
||||
|
||||
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 }))
|
||||
})),
|
||||
|
||||
locations: this.data().map((d, i) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
status: d.ready ? "Серверы в наличии" : "Установим за 24 часа",
|
||||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||||
bg: s.loc === i ? sel : "var(--surface)",
|
||||
border: s.loc === i ? "var(--accent)" : "var(--border)",
|
||||
dot: s.loc === i ? "var(--accent)" : "transparent",
|
||||
dotOff: s.loc === i,
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
buyOff: s.loc !== i,
|
||||
pick: () => this.setState({ loc: i })
|
||||
})),
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: s.lead,
|
||||
leadClosed: !s.lead,
|
||||
leadButton: s.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
pickedTitle: locData[s.loc][0] + " · " + locData[s.loc][1],
|
||||
pickedHint: locData[s.loc][2] ? "Доступ выдадим сразу после оплаты" : "Установим сервер под вас, доступ в течение 24 часов",
|
||||
|
||||
toggleLang: () => this.setLang(s.lang === "ru" ? "en" : "ru"),
|
||||
langTitle: s.lang === "ru" ? "Язык: русский" : "Язык: английский",
|
||||
langLabel: s.lang === "ru" ? "RU" : "EN",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,815 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--warn: oklch(62% 0.14 62);
|
||||
--tg: oklch(62% 0.15 250);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--warn: oklch(78% 0.13 62);
|
||||
--tg: oklch(70% 0.13 250);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: oklch(95% 0.008 296); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
:root[data-veilix-theme="dark"] body{ background: oklch(13% 0.018 296); }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
input::placeholder, textarea::placeholder{ color: var(--ink-3); }
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
<div style="min-height: 100vh; display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: 32px; padding: 40px 24px 64px;">
|
||||
|
||||
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx?v=20260915" hint-size="393px,852px">
|
||||
<div style="display: flex; flex-direction: column; height: 100%; padding-top: 62px; padding-bottom: 34px; background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; letter-spacing: -0.01em;">
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--border); background: var(--surface);">
|
||||
<span style="position: relative; width: 24px; height: 24px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 18px; height: 18px; border-radius: 6px; background: linear-gradient(140deg, var(--accent), color-mix(in oklab, var(--accent) 55%, var(--glow-3))); display: block;"></span>
|
||||
<span style="position: absolute; right: 0; bottom: 3px; width: 18px; height: 18px; border-radius: 6px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 1px; flex: 1; min-width: 0;">
|
||||
<span style="font-size: 15px; font-weight: 500; letter-spacing: -0.02em;">Вейликс</span>
|
||||
<span style="font-size: 11px; color: var(--tg);">мини-приложение</span>
|
||||
</span>
|
||||
<span style="font-size: 15px; color: var(--tg);">Закрыть</span>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; min-height: 0; overflow: auto; padding: 16px 16px 108px;">
|
||||
|
||||
<div hidden="{{ buyOff }}" style="display: grid; gap: 12px;">
|
||||
<div style="display: grid; gap: 4px; padding: 2px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Ваш личный VPN</div>
|
||||
<div style="font-size: 13px; color: var(--ink-2);">От 4 900 ₽ первый месяц, далее от 2 499 ₽.</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 9px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
|
||||
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
|
||||
<span>Индивидуальный IP в выбранной стране</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
|
||||
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
|
||||
<span>Выделенный RU-адрес в комплекте</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
|
||||
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
|
||||
<span>До 5 устройств</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; font-size: 14px; line-height: 1.45;">
|
||||
<span style="width: 6px; height: 6px; margin-top: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span>2 типа подключения:</span>
|
||||
<span style="display: flex; gap: 8px; color: var(--ink-2);">
|
||||
<span style="flex: none;">—</span>
|
||||
<span>WireGuard / Amnezia WG</span>
|
||||
</span>
|
||||
<span style="display: flex; gap: 8px; color: var(--ink-2);">
|
||||
<span style="flex: none;">—</span>
|
||||
<span>Xray: VLESS, Hysteria2, XHTTP</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 8px 2px 0;">
|
||||
<span style="font-size: 16px; font-weight: 500;">Локации</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">{{ locCount }}</span>
|
||||
</div>
|
||||
|
||||
<sc-for list="{{ locations }}" as="loc" hint-placeholder-count="8">
|
||||
<div style="display: grid; gap: 11px; padding: 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: center; gap: 11px;">
|
||||
{{ loc.flag }}
|
||||
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
|
||||
<span style="font-size: 15px; font-weight: 500;">{{ loc.country }}</span>
|
||||
<span style="font-size: 12px; color: {{ loc.statusInk }};">{{ loc.sub }}</span>
|
||||
</span>
|
||||
<button type="button" onClick="{{ loc.buy }}" style="flex: none; min-height: 44px; padding: 0 16px; border-radius: 12px; border: 0; background: {{ loc.btnBg }}; color: {{ loc.btnInk }}; font-family: inherit; font-size: 14px; font-weight: 500; cursor: pointer;">{{ loc.btn }}</button>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; padding-top: 11px; border-top: 1px solid var(--border); font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
|
||||
<span style="font-size: 15px; font-weight: 500; color: var(--ink);">{{ loc.setup }}</span>
|
||||
<span>первый месяц · далее</span>
|
||||
<span style="font-weight: 500; color: var(--ink);">{{ loc.month }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
|
||||
<div style="display: grid; gap: 12px; margin-top: 8px; padding: 16px; border-radius: 18px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="display: grid; gap: 5px;">
|
||||
<div style="font-size: 16px; font-weight: 500;">Нужны индивидуальные условия</div>
|
||||
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Команда, несколько адресов, редкая страна или свой сценарий маршрутизации. Оставьте заявку — ответим в течение дня.</div>
|
||||
</div>
|
||||
<input type="text" placeholder="Имя" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
|
||||
<input type="text" placeholder="Telegram или почта" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">{{ leadButton }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div hidden="{{ faqOff }}" style="display: grid; gap: 10px;">
|
||||
<div style="display: grid; gap: 4px; padding: 2px 2px 6px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Частые вопросы</div>
|
||||
<div style="font-size: 13px; color: var(--ink-2);">Если ответа нет — напишите в поддержку.</div>
|
||||
</div>
|
||||
<sc-for list="{{ faq }}" as="item" hint-placeholder-count="6">
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ item.toggle }}" style="display: flex; align-items: center; gap: 12px; width: 100%; min-height: 52px; padding: 14px 16px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 15px; font-weight: 500; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">{{ item.q }}</span>
|
||||
<span style="flex: none; width: 8px; height: 8px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ item.rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ item.off }}" style="padding: 0 16px 16px; font-size: 14px; line-height: 1.55; color: var(--ink-2);">{{ item.a }}</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); font-size: 15px;">
|
||||
<span style="flex: 1;">Остался вопрос</span>
|
||||
<span style="color: var(--tg);">@veilix_support</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ keysOff }}" style="display: grid; gap: 12px;">
|
||||
<div style="display: grid; gap: 4px; padding: 2px 2px 4px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Ваши ключи</div>
|
||||
<div style="font-size: 13px; color: var(--ink-2);">Нажмите, чтобы открыть подключение</div>
|
||||
</div>
|
||||
<sc-for list="{{ keys }}" as="key" hint-placeholder-count="2">
|
||||
<button type="button" onClick="{{ key.open }}" style="display: grid; gap: 10px; width: 100%; min-height: 44px; padding: 16px; border-radius: 18px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;">
|
||||
<span style="display: flex; align-items: center; gap: 8px;">
|
||||
<span style="flex: 1; font-size: 16px; font-weight: 500;">{{ key.title }}</span>
|
||||
<span style="padding: 3px 9px; border-radius: 7px; background: {{ key.badgeBg }}; color: {{ key.badgeInk }}; font-size: 11px;">{{ key.badge }}</span>
|
||||
</span>
|
||||
<span style="display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
|
||||
<span>{{ key.ip }}</span>
|
||||
<span>{{ key.untilLine }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</sc-for>
|
||||
<button type="button" onClick="{{ goBuy }}" style="display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 50px; border-radius: 16px; border: 1px dashed var(--border); background: var(--surface-2); color: var(--ink-2); font-family: inherit; font-size: 15px; cursor: pointer;">Купить ещё один ключ</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ keyOff }}" style="display: grid; gap: 14px;">
|
||||
<button type="button" onClick="{{ goKeys }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ключи</button>
|
||||
<div style="display: grid; gap: 4px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">{{ cur.title }}</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ cur.ip }}</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Действует до</span>
|
||||
<span style="font-size: 16px; font-weight: 500;">{{ cur.until }}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Списание</span>
|
||||
<span style="font-size: 15px; font-variant-numeric: tabular-nums;">2 499 ₽ · со счёта</span>
|
||||
</div>
|
||||
<div style="font-size: 13px; color: {{ cur.leftInk }};">{{ cur.left }}</div>
|
||||
<button type="button" onClick="{{ goBilling }}" style="min-height: 44px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">Счёт и платежи</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 16px; font-weight: 500;">WireGuard</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">5 устройств</span>
|
||||
</div>
|
||||
<sc-for list="{{ configs }}" as="cfg" hint-placeholder-count="5">
|
||||
<div style="display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 14px; background: var(--surface-2);">
|
||||
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
|
||||
<span style="font-size: 15px;">{{ cfg.name }}</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">{{ cfg.state }}</span>
|
||||
</span>
|
||||
<button type="button" onClick="{{ cfg.showQr }}" style="min-height: 44px; padding: 0 14px; border-radius: 11px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 14px; cursor: pointer;">QR</button>
|
||||
<button type="button" style="min-height: 44px; padding: 0 14px; border-radius: 11px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;">Файл</button>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 16px; font-weight: 500;">Xray</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">Happ и другие</span>
|
||||
</div>
|
||||
<div style="padding: 12px; border-radius: 12px; background: var(--surface-2); font-size: 12px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ cur.xray }}</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button type="button" onClick="{{ copyXray }}" style="flex: 1; min-height: 46px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;">{{ copyLabel }}</button>
|
||||
<button type="button" onClick="{{ showXrayQr }}" style="min-height: 46px; padding: 0 18px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">QR</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ moreOff }}" style="display: grid; gap: 12px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em; padding: 2px;">Ещё</div>
|
||||
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
|
||||
<button type="button" onClick="{{ goBilling }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">Счёт и платежи</span>
|
||||
<span style="color: var(--ink-3); font-variant-numeric: tabular-nums;">1 250 ₽</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ goRef }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">Реферальная программа</span>
|
||||
<span style="color: var(--ink-3);">{{ refStats }}</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ goLead }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">Индивидуальные условия</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ goFaq }}" style="display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 12px 16px; border: 0; background: var(--surface); color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">Частые вопросы</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
|
||||
<span style="flex: 1;">Написать в поддержку</span>
|
||||
<span style="color: var(--tg);">@veilix_support</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
|
||||
<span style="flex: 1;">Почта аккаунта</span>
|
||||
<span style="color: var(--ink-3);">anna@example.com</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 12px; padding: 14px 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
||||
<span style="font-size: 15px;">Тема</span>
|
||||
<span style="display: flex; flex: none; padding: 3px; border-radius: 11px; border: 1px solid var(--border);">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 34px; border: 0; border-radius: 8px; background: {{ lightBg }}; color: var(--ink); cursor: pointer;">
|
||||
<span style="position: relative; width: 16px; height: 16px; display: block;">
|
||||
<span style="position: absolute; left: 4px; top: 4px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 0; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; opacity: 0.45; display: block;"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setDark }}" title="Тёмная тема" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 34px; border: 0; border-radius: 8px; background: {{ darkBg }}; color: var(--ink); cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; box-shadow: inset -5px -2px 0 0 currentColor; display: block;"></span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">Язык</span>
|
||||
<span style="display: flex; flex: none; padding: 3px; border-radius: 11px; border: 1px solid var(--border);">
|
||||
<button type="button" onClick="{{ setRu }}" style="height: 34px; padding: 0 16px; border: 0; border-radius: 8px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 14px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="height: 34px; padding: 0 16px; border: 0; border-radius: 8px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 14px; cursor: pointer;">EN</button>
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: var(--ink-3); line-height: 1.45;">По умолчанию тема подстраивается под Telegram.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ billingOff }}" style="display: grid; gap: 12px;">
|
||||
<button type="button" onClick="{{ goMore }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ещё</button>
|
||||
<div style="display: grid; gap: 4px; padding: 0 2px 2px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Счёт и платежи</div>
|
||||
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Карта привязана к аккаунту, а не к отдельному ключу. Списания за все ключи идут с неё или с баланса.</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 12px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="display: grid; gap: 3px;">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Баланс</span>
|
||||
<span style="font-size: 28px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">1 250 ₽</span>
|
||||
</div>
|
||||
<button type="button" onClick="{{ openTopup }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">Пополнить баланс</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ topupOff }}" style="display: grid; gap: 12px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--accent);">
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 16px; font-weight: 500;">Пополнение</span>
|
||||
<button type="button" onClick="{{ closeTopup }}" style="border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">Свернуть</button>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<sc-for list="{{ sums }}" as="sum" hint-placeholder-count="3">
|
||||
<button type="button" onClick="{{ sum.pick }}" style="min-height: 44px; padding: 0 16px; border-radius: 12px; border: 1px solid {{ sum.border }}; background: {{ sum.bg }}; color: var(--ink); font-family: inherit; font-size: 15px; font-variant-numeric: tabular-nums; cursor: pointer;">{{ sum.label }}</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
<div style="display: grid; gap: 6px;">
|
||||
<sc-for list="{{ methods }}" as="m" hint-placeholder-count="3">
|
||||
<button type="button" onClick="{{ m.pick }}" style="display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid {{ m.border }}; background: {{ m.bg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">{{ m.label }}</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">{{ m.note }}</span>
|
||||
</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
<div hidden="{{ cryptoOff }}" style="padding: 12px 14px; border-radius: 12px; background: var(--surface-2); font-size: 13px; line-height: 1.5; color: var(--ink-2);">USDT (TRC-20), BTC, ETH. Покажем адрес и сумму в монете, курс фиксируется на 15 минут. Баланс пополнится после одного подтверждения сети.</div>
|
||||
<button type="button" onClick="{{ sendTopup }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">{{ topupButton }}</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
|
||||
<span style="flex: 1;">Карта аккаунта</span>
|
||||
<span style="color: var(--ink-3); font-variant-numeric: tabular-nums;">···· 4242</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 12px 16px; background: var(--surface); font-size: 15px;">
|
||||
<span style="flex: 1;">Следующее списание</span>
|
||||
<span style="color: var(--ink-3); font-variant-numeric: tabular-nums;">2 499 ₽ · 21 сент.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 16px; font-weight: 500; padding: 8px 2px 0;">История платежей</div>
|
||||
<sc-for list="{{ payments }}" as="p" hint-placeholder-count="5">
|
||||
<div style="display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="flex: 1; min-width: 0; display: grid; gap: 3px;">
|
||||
<span style="font-size: 14px;">{{ p.title }}</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">{{ p.meta }}</span>
|
||||
</span>
|
||||
<span style="flex: none; font-size: 15px; font-weight: 500; color: {{ p.ink }}; font-variant-numeric: tabular-nums;">{{ p.amount }}</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ refOff }}" style="display: grid; gap: 12px;">
|
||||
<button type="button" onClick="{{ goMore }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ещё</button>
|
||||
<div style="display: grid; gap: 4px; padding: 0 2px 2px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Реферальная программа</div>
|
||||
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Приведите друга — вы оба получите по месяцу в подарок.</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<div style="flex: 1; display: grid; gap: 4px; padding: 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 12px; color: var(--ink-3);">Приглашено</span>
|
||||
<span style="font-size: 24px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">3</span>
|
||||
</div>
|
||||
<div style="flex: 1; display: grid; gap: 4px; padding: 14px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 12px; color: var(--ink-3);">Месяцев</span>
|
||||
<span style="font-size: 24px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">2</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<div style="font-size: 15px; font-weight: 500;">Ваша ссылка</div>
|
||||
<div style="padding: 12px; border-radius: 12px; background: var(--surface-2); font-size: 13px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">t.me/veilix_bot?start=anna7f2c</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button type="button" onClick="{{ copyRef }}" style="flex: 1; min-height: 46px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;">{{ refLabel }}</button>
|
||||
<button type="button" style="min-height: 46px; padding: 0 18px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">Поделиться</button>
|
||||
</div>
|
||||
<div style="font-size: 12px; line-height: 1.45; color: var(--ink-3);">Месяц начисляется после первой оплаты друга и применяется к ближайшему списанию.</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 1px; border-radius: 18px; overflow: hidden; border: 1px solid var(--border); background: var(--border);">
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 12px 16px; background: var(--surface); font-size: 14px;">
|
||||
<span style="flex: 1;">m···@gmail.com</span>
|
||||
<span style="color: var(--accent);">Месяц начислен</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 12px 16px; background: var(--surface); font-size: 14px;">
|
||||
<span style="flex: 1;">d···@yandex.ru</span>
|
||||
<span style="color: var(--accent);">Месяц начислен</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 12px; min-height: 50px; padding: 12px 16px; background: var(--surface); font-size: 14px;">
|
||||
<span style="flex: 1;">k···@icloud.com</span>
|
||||
<span style="color: var(--ink-3);">Ждём оплаты</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ leadOff }}" style="display: grid; gap: 12px;">
|
||||
<button type="button" onClick="{{ goMore }}" style="justify-self: start; min-height: 44px; border: 0; background: transparent; color: var(--tg); font-family: inherit; font-size: 15px; cursor: pointer; padding: 0;">← Ещё</button>
|
||||
<div style="display: grid; gap: 4px; padding: 0 2px 2px;">
|
||||
<div style="font-size: 22px; font-weight: 600; letter-spacing: -0.03em;">Индивидуальные условия</div>
|
||||
<div style="font-size: 13px; line-height: 1.5; color: var(--ink-2);">Команда, несколько адресов, редкая страна или свой сценарий маршрутизации. Ответим в течение дня.</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 10px; padding: 16px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<input type="text" placeholder="Имя" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
|
||||
<input type="text" placeholder="Telegram или почта" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
|
||||
<input type="text" placeholder="Сколько адресов и в каких странах" style="min-height: 46px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; width: 100%;" />
|
||||
<textarea rows="4" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="min-height: 48px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; left: 50%; bottom: 46px; transform: translateX(-50%); z-index: 15; display: flex; gap: 2px; padding: 6px; border-radius: 999px; background: color-mix(in oklab, var(--surface) 62%, transparent); backdrop-filter: blur(24px) saturate(1.8); -webkit-backdrop-filter: blur(24px) saturate(1.8); border: 1px solid color-mix(in oklab, var(--ink) 8%, transparent); box-shadow: 0 10px 30px -10px color-mix(in oklab, var(--ink) 40%, transparent);">
|
||||
<button type="button" onClick="{{ goBuy }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabBuyBg }}; color: {{ tabBuyInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
|
||||
<span style="position: relative; width: 20px; height: 20px; display: block;">
|
||||
<span style="position: absolute; left: 8px; top: 0; width: 4px; height: 20px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 8px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Купить
|
||||
</button>
|
||||
<button type="button" onClick="{{ goFaq }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabFaqBg }}; color: {{ tabFaqInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; border: 2px solid currentColor; font-size: 13px; font-weight: 600; line-height: 1;">?</span>
|
||||
FAQ
|
||||
</button>
|
||||
<button type="button" onClick="{{ goKeys }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabKeysBg }}; color: {{ tabKeysInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
|
||||
<span style="position: relative; width: 20px; height: 20px; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 2px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 9px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.6; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 16px; width: 20px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.3; display: block;"></span>
|
||||
</span>
|
||||
Ключи
|
||||
</button>
|
||||
<button type="button" onClick="{{ goMore }}" style="display: grid; justify-items: center; gap: 4px; min-width: 72px; min-height: 52px; padding: 8px 10px; border: 0; border-radius: 999px; background: {{ tabMoreBg }}; color: {{ tabMoreInk }}; font-family: inherit; font-size: 11px; cursor: pointer;">
|
||||
<span style="position: relative; width: 20px; height: 20px; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 8px; width: 4px; height: 4px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 8px; top: 8px; width: 4px; height: 4px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 16px; top: 8px; width: 4px; height: 4px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Ещё
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ payOff }}" onClick="{{ closePay }}" style="position: absolute; left: 0; right: 0; top: -62px; bottom: -34px; z-index: 22; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 34px; background: color-mix(in oklab, var(--ink) 45%, transparent);">
|
||||
<div onClick="{{ stopPay }}" style="width: 100%; display: grid; gap: 14px; padding: 22px 20px 34px; border-radius: 22px 22px 0 0; background: var(--surface); border-top: 1px solid var(--border);">
|
||||
<span style="justify-self: center; width: 36px; height: 4px; border-radius: 2px; background: var(--border); display: block;"></span>
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
{{ payFlag }}
|
||||
<span style="flex: 1; min-width: 0; display: grid; gap: 2px;">
|
||||
<span style="font-size: 17px; font-weight: 600; letter-spacing: -0.02em;">{{ payTitle }}</span>
|
||||
<span style="font-size: 12px; color: var(--accent);">{{ paySub }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div style="display: grid; gap: 8px; padding: 14px 16px; border-radius: 14px; background: var(--surface-2);">
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px;">
|
||||
<span style="color: var(--ink-2);">Первый месяц</span>
|
||||
<span style="font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums;">{{ paySetup }}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px;">
|
||||
<span style="color: var(--ink-2);">Далее каждый месяц</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ payMonth }}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px; padding-top: 8px; border-top: 1px solid var(--border);">
|
||||
<span style="color: var(--ink-2);">В комплекте</span>
|
||||
<span>RU-адрес · 5 устройств</span>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ confirmPay }}" style="min-height: 52px; border-radius: 14px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;">{{ payButton }}</button>
|
||||
<div style="font-size: 12px; line-height: 1.45; color: var(--ink-3); text-align: center;">Оплата пройдёт внутри Telegram. Ключ появится во вкладке «Ключи» сразу после подтверждения.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ qrOff }}" onClick="{{ closeQr }}" style="position: absolute; left: 0; right: 0; top: -62px; bottom: -34px; z-index: 20; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 34px; background: color-mix(in oklab, var(--ink) 45%, transparent);">
|
||||
<div style="width: 100%; display: grid; gap: 14px; justify-items: center; padding: 22px 20px 34px; border-radius: 22px 22px 0 0; background: var(--surface); border-top: 1px solid var(--border);">
|
||||
<span style="width: 36px; height: 4px; border-radius: 2px; background: var(--border); display: block;"></span>
|
||||
<span style="font-size: 16px; font-weight: 500;">{{ qrTitle }}</span>
|
||||
{{ qrImage }}
|
||||
<span style="font-size: 12px; color: var(--ink-3); text-align: center; max-width: 240px; line-height: 1.45;">Наведите камеру телефона на код в приложении WireGuard или Happ.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-import>
|
||||
|
||||
<div style="max-width: 320px; display: grid; gap: 14px; align-content: start; padding-top: 12px;">
|
||||
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.03em; color: var(--ink);">Мини-приложение в Telegram</div>
|
||||
<p style="margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">В Telegram приложение работает и как витрина, и как кабинет: человек может попасть сюда, ни разу не открыв сайт. Поэтому первая вкладка — «Купить»: тезисы, все локации с ценами и кнопкой покупки у каждой, форма на индивидуальные условия внизу.</p>
|
||||
<p style="margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Вкладки: Купить, FAQ, Ключи, Ещё. В «Ещё» — счёт и платежи, реферальная программа, индивидуальный заказ, тема и язык.</p>
|
||||
</div>
|
||||
</div>
|
||||
</x-dc>
|
||||
<script type="text/x-dc" data-dc-script data-props="{"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, qr: null, copied: false, lang: "ru", refCopied: false, faq: -1, leadSent: false, topup: false, sum: 1, method: 0, topupSent: false, pay: null, paid: false };
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
let saved = null;
|
||||
try { saved = localStorage.getItem("veilix.theme"); } catch (e) {}
|
||||
let savedLang = null;
|
||||
try { savedLang = localStorage.getItem("veilix.lang"); } catch (e) {}
|
||||
this.setState({ sysDark: mq.matches, theme: saved || this.props.defaultTheme || "light", lang: savedLang || "ru" }, () => { this.apply(); this.applyLang(); });
|
||||
}
|
||||
|
||||
applyLang() {
|
||||
const lang = this.state.lang || "ru";
|
||||
if (window.VEILIX_APPLY_LANG) window.VEILIX_APPLY_LANG(document.body, lang);
|
||||
else setTimeout(() => this.applyLang(), 150);
|
||||
}
|
||||
|
||||
setLang(l) {
|
||||
try { localStorage.setItem("veilix.lang", l); } catch (e) {}
|
||||
this.setState({ lang: l }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() { if (this.mq) this.mq.removeEventListener("change", this.onSys); }
|
||||
componentDidUpdate() { this.apply(); this.applyLang(); }
|
||||
|
||||
apply() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const dark = theme === "dark" || (theme === "system" && this.state.sysDark);
|
||||
document.documentElement.setAttribute("data-veilix-theme", dark ? "dark" : "light");
|
||||
}
|
||||
|
||||
setTheme(t) {
|
||||
try { localStorage.setItem("veilix.theme", t); } catch (e) {}
|
||||
this.setState({ theme: t }, () => this.apply());
|
||||
}
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, servers, tier) => ({ country, city, code, servers, setup: tier.setup, month: tier.month });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 12, base),
|
||||
L("Германия", "Франкфурт", "de", 10, base),
|
||||
L("США", "Нью-Йорк", "us", 9, base),
|
||||
L("Великобритания", "Лондон", "gb", 8, base),
|
||||
L("Франция", "Париж", "fr", 7, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 6, base),
|
||||
L("Швеция", "Стокгольм", "se", 5, base),
|
||||
L("Норвегия", "Осло", "no", 0, base),
|
||||
L("Дания", "Копенгаген", "dk", 0, base),
|
||||
L("Швейцария", "Цюрих", "ch", 4, rare),
|
||||
L("Австрия", "Вена", "at", 5, base),
|
||||
L("Испания", "Мадрид", "es", 6, base),
|
||||
L("Италия", "Милан", "it", 6, base),
|
||||
L("Польша", "Варшава", "pl", 7, base),
|
||||
L("Чехия", "Прага", "cz", 5, base),
|
||||
L("Румыния", "Бухарест", "ro", 0, base),
|
||||
L("Литва", "Вильнюс", "lt", 4, base),
|
||||
L("Латвия", "Рига", "lv", 4, base),
|
||||
L("Эстония", "Таллин", "ee", 0, base),
|
||||
L("Турция", "Стамбул", "tr", 6, base),
|
||||
L("Казахстан", "Алматы", "kz", 0, base),
|
||||
L("Армения", "Ереван", "am", 0, base),
|
||||
L("Грузия", "Тбилиси", "ge", 0, base),
|
||||
L("ОАЭ", "Дубай", "ae", 3, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 4, rare),
|
||||
L("Япония", "Токио", "jp", 4, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 0, rare),
|
||||
L("Канада", "Торонто", "ca", 6, base),
|
||||
L("Австралия", "Сидней", "au", 0, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", 0, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 19, h: 15, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 15, t: 0, w: 10, h: 28, bg: "#FFFFFF" }, { l: 0, t: 9, w: 40, h: 10, bg: "#FFFFFF" }, { l: 17, t: 0, w: 5, h: 28, bg: "#C8102E" }, { l: 0, t: 11, w: 40, h: 5, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 11, t: 0, w: 7, h: 28, bg: "#003580" }, { l: 0, t: 10, w: 40, h: 7, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 11, t: 0, w: 5, h: 28, bg: "#FECC00" }, { l: 0, t: 11, w: 40, h: 5, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 10, t: 0, w: 9, h: 28, bg: "#FFFFFF" }, { l: 0, t: 9, w: 40, h: 9, bg: "#FFFFFF" }, { l: 13, t: 0, w: 3, h: 28, bg: "#00205B" }, { l: 0, t: 12, w: 40, h: 3, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 11, t: 0, w: 5, h: 28, bg: "#FFFFFF" }, { l: 0, t: 11, w: 40, h: 5, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 17, t: 7, w: 5, h: 14, bg: "#FFFFFF" }, { l: 13, t: 11, w: 14, h: 5, bg: "#FFFFFF" }] },
|
||||
at: { rows: ["#ED2939", "#FFFFFF", "#ED2939"] },
|
||||
es: { rows: ["#AA151B", "#F1BF00", "#AA151B"], sizes: "1fr 2fr 1fr" },
|
||||
it: { cols: ["#008C45", "#F4F5F0", "#CD212A"] },
|
||||
pl: { rows: ["#FFFFFF", "#DC143C"] },
|
||||
cz: { rows: ["#FFFFFF", "#D7141A"], marks: [{ l: 0, t: 0, w: 16, h: 28, bg: "#11457E", clip: "polygon(0 0, 100% 50%, 0 100%)" }] },
|
||||
ro: { cols: ["#002B7F", "#FCD116", "#CE1126"] },
|
||||
lt: { rows: ["#FDB913", "#006A44", "#C1272D"] },
|
||||
lv: { rows: ["#9E3039", "#FFFFFF", "#9E3039"], sizes: "2fr 1fr 2fr" },
|
||||
ee: { rows: ["#0072CE", "#000000", "#FFFFFF"] },
|
||||
tr: { bg: "#E30A17", marks: [{ l: 9, t: 7, w: 14, h: 14, r: "50%", bg: "#FFFFFF" }, { l: 13, t: 7, w: 14, h: 14, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 14, t: 7, w: 13, h: 13, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 17, t: 0, w: 5, h: 28, bg: "#FF0000" }, { l: 0, t: 11, w: 40, h: 5, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 10, h: 28, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 4, t: 3, w: 10, h: 10, r: "50%", bg: "#FFFFFF" }, { l: 8, t: 3, w: 10, h: 10, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 14, t: 8, w: 12, h: 12, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 14, t: 8, w: 12, h: 12, r: "50%", bg: "#CD2E3A" }, { l: 14, t: 8, w: 12, h: 12, r: "50%", bg: "#0047A0", clip: "polygon(0 50%, 100% 50%, 100% 100%, 0 100%)" }] },
|
||||
ca: { cols: ["#D80621", "#FFFFFF", "#D80621"], sizes: "1fr 2fr 1fr", marks: [{ l: 17, t: 8, w: 6, h: 12, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 8, t: 0, w: 3, h: 14, bg: "#FFFFFF" }, { l: 0, t: 5, w: 19, h: 3, bg: "#FFFFFF" }, { l: 26, t: 17, w: 6, h: 6, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 8, t: 3, w: 24, h: 22, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 15, t: 10, w: 9, h: 9, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = { width: "40px", height: "28px", borderRadius: "5px", overflow: "hidden", display: "grid", border: "1px solid var(--border)", position: "relative", flex: "none" };
|
||||
let bands = [];
|
||||
if (spec.rows) {
|
||||
bands = spec.rows;
|
||||
style.gridTemplateRows = spec.sizes || "repeat(" + bands.length + ", 1fr)";
|
||||
} else if (spec.cols) {
|
||||
bands = spec.cols;
|
||||
style.gridAutoFlow = "column";
|
||||
style.gridTemplateColumns = spec.sizes || "repeat(" + bands.length + ", 1fr)";
|
||||
} else {
|
||||
style.background = spec.bg || "var(--surface-2)";
|
||||
}
|
||||
const kids = bands.map((c, i) => h("span", { key: "b" + i, style: { background: c, display: "block" } }));
|
||||
(spec.marks || []).forEach((m, i) => kids.push(h("span", {
|
||||
key: "m" + i,
|
||||
style: { position: "absolute", left: m.l + "px", top: m.t + "px", width: m.w + "px", height: m.h + "px", borderRadius: m.r || 0, background: m.bg, clipPath: m.clip || "none", display: "block" }
|
||||
})));
|
||||
return h("span", { style: style }, kids);
|
||||
}
|
||||
|
||||
qrImage(seed) {
|
||||
const n = 21;
|
||||
let h = 0;
|
||||
for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
|
||||
const cells = [];
|
||||
const finder = (r, c) => (r < 7 && c < 7) || (r < 7 && c >= n - 7) || (r >= n - 7 && c < 7);
|
||||
for (let r = 0; r < n; r++) {
|
||||
for (let c = 0; c < n; c++) {
|
||||
let on;
|
||||
if (finder(r, c)) {
|
||||
const rr = r < 7 ? r : n - 1 - r;
|
||||
const cc = c < 7 ? c : n - 1 - c;
|
||||
on = Math.max(Math.abs(rr - 3), Math.abs(cc - 3)) !== 2;
|
||||
} else {
|
||||
h = (h * 1103515245 + 12345) >>> 0;
|
||||
on = ((h >>> 16) & 1) === 1;
|
||||
}
|
||||
cells.push(React.createElement("span", { key: r + "-" + c, style: { background: on ? "var(--ink)" : "transparent", display: "block" } }));
|
||||
}
|
||||
}
|
||||
return React.createElement("div", {
|
||||
style: { display: "grid", gridTemplateColumns: "repeat(" + n + ", 1fr)", width: "196px", height: "196px", padding: "10px", borderRadius: "14px", background: "var(--bg)", border: "1px solid var(--border)" }
|
||||
}, cells);
|
||||
}
|
||||
|
||||
plural(n) {
|
||||
const m10 = n % 10, m100 = n % 100;
|
||||
if (m10 === 1 && m100 !== 11) return "сервер";
|
||||
if (m10 >= 2 && m10 <= 4 && (m100 < 10 || m100 >= 20)) return "сервера";
|
||||
return "серверов";
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
const sel = "var(--accent-soft)";
|
||||
const theme = s.theme || this.props.defaultTheme || "light";
|
||||
const keyData = [
|
||||
{ title: "Нидерланды · Амстердам", ip: "185.72.14.203", until: s.lang === "en" ? "14 October" : "14 октября", badge: "Активен", warn: false, left: "Продлится автоматически через 31 день", xray: "vless://8f2a…@nl1.veilix.net:443?type=xhttp#Veilix-NL" },
|
||||
{ title: "США · Нью-Йорк", ip: "23.105.61.44", until: s.lang === "en" ? "21 September" : "21 сентября", badge: "Истекает", warn: true, left: "Осталось 8 дней", xray: "vless://c41d…@us1.veilix.net:443?type=xhttp#Veilix-US" }
|
||||
];
|
||||
const cur = keyData[s.key];
|
||||
const cfgNames = [["Ноутбук", "подключено"], ["Телефон", "подключено"], ["Планшет", "подключено"], ["Рабочий ноутбук", "не занят"], ["Свободный слот", "не занят"]];
|
||||
const faqData = [
|
||||
["Чем это отличается от обычного VPN?", "У вас личный IP-адрес, который не делят с другими. Его не банят за чужую активность, и сервисы видят вас как обычного жителя выбранной страны."],
|
||||
["Что входит в цену?", "Индивидуальный IP в выбранной стране, выделенный RU-адрес, до 5 устройств, WireGuard и Amnezia WG, а также Xray с VLESS, Hysteria2 и XHTTP."],
|
||||
["Сколько устройств можно подключить?", "До пяти. На каждое — своя конфигурация WireGuard, IP-адрес при этом один и тот же."],
|
||||
["Как быстро выдаётся доступ?", "Если в локации есть свободные серверы — сразу после оплаты. Если нет, поднимем сервер под вас в течение 24 часов."],
|
||||
["Что если страна перестала подходить?", "Напишите в поддержку: перенесём ключ в другую локацию, доплата только за разницу в тарифе."],
|
||||
["Как платить и как отменить?", "Карта привязана к аккаунту, можно держать баланс и пополнять его картой, через СБП или криптой. Отмена — в разделе «Счёт и платежи», ключ доработает оплаченный месяц."]
|
||||
];
|
||||
|
||||
return {
|
||||
locCount: s.lang === "en" ? "30 countries" : "30 стран",
|
||||
locations: this.data().map((d, i) => ({
|
||||
country: d.country,
|
||||
flag: this.flag(d.code),
|
||||
sub: d.servers > 0 ? d.city + " · " + d.servers + " " + this.plural(d.servers) + " свободно" : d.city + " · установим за 24 часа",
|
||||
statusInk: d.servers > 0 ? "var(--accent)" : "var(--ink-3)",
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
btn: d.servers > 0 ? "Купить" : "Заказать",
|
||||
btnBg: d.servers > 0 ? "var(--accent)" : "var(--surface-2)",
|
||||
btnInk: d.servers > 0 ? "var(--on-accent)" : "var(--ink-2)",
|
||||
buy: () => d.servers > 0 ? this.setState({ pay: d, paid: false }) : this.setState({ view: "lead" })
|
||||
})),
|
||||
faq: faqData.map((row, i) => ({
|
||||
q: row[0],
|
||||
a: row[1],
|
||||
off: s.faq !== i,
|
||||
rot: s.faq === i ? "rotate(-135deg) translateY(2px)" : "rotate(45deg) translateY(-2px)",
|
||||
toggle: () => this.setState(st => ({ faq: st.faq === i ? -1 : i }))
|
||||
})),
|
||||
keys: keyData.map((k, i) => ({
|
||||
title: k.title,
|
||||
ip: k.ip,
|
||||
untilLine: (s.lang === "en" ? "until " : "до ") + k.until,
|
||||
badge: k.badge,
|
||||
badgeBg: k.warn ? "color-mix(in oklab, var(--warn) 18%, transparent)" : sel,
|
||||
badgeInk: k.warn ? "var(--warn)" : "var(--accent)",
|
||||
open: () => this.setState({ view: "key", key: i })
|
||||
})),
|
||||
cur: {
|
||||
title: cur.title, ip: cur.ip, until: cur.until,
|
||||
left: cur.left, leftInk: cur.warn ? "var(--warn)" : "var(--ink-2)", xray: cur.xray
|
||||
},
|
||||
configs: cfgNames.map(c => ({
|
||||
name: c[0],
|
||||
state: c[1],
|
||||
showQr: () => this.setState({ qr: { title: "WireGuard · " + c[0], seed: cur.ip + c[0] } })
|
||||
})),
|
||||
goBuy: () => this.setState({ view: "buy" }),
|
||||
goFaq: () => this.setState({ view: "faq" }),
|
||||
goKeys: () => this.setState({ view: "keys" }),
|
||||
goMore: () => this.setState({ view: "more" }),
|
||||
goRef: () => this.setState({ view: "ref" }),
|
||||
goBilling: () => this.setState({ view: "billing" }),
|
||||
goLead: () => this.setState({ view: "lead" }),
|
||||
buyOff: s.view !== "buy",
|
||||
faqOff: s.view !== "faq",
|
||||
keysOff: s.view !== "keys",
|
||||
keyOff: s.view !== "key",
|
||||
moreOff: s.view !== "more",
|
||||
refOff: s.view !== "ref",
|
||||
billingOff: s.view !== "billing",
|
||||
leadOff: s.view !== "lead",
|
||||
tabBuyBg: s.view === "buy" ? sel : "transparent",
|
||||
tabFaqBg: s.view === "faq" ? sel : "transparent",
|
||||
tabKeysBg: (s.view === "keys" || s.view === "key") ? sel : "transparent",
|
||||
tabMoreBg: (s.view === "more" || s.view === "ref" || s.view === "billing" || s.view === "lead") ? sel : "transparent",
|
||||
tabBuyInk: s.view === "buy" ? "var(--accent)" : "var(--ink-2)",
|
||||
tabFaqInk: s.view === "faq" ? "var(--accent)" : "var(--ink-2)",
|
||||
tabKeysInk: (s.view === "keys" || s.view === "key") ? "var(--accent)" : "var(--ink-2)",
|
||||
tabMoreInk: (s.view === "more" || s.view === "ref" || s.view === "billing" || s.view === "lead") ? "var(--accent)" : "var(--ink-2)",
|
||||
openTopup: () => this.setState({ topup: true }),
|
||||
closeTopup: () => this.setState({ topup: false }),
|
||||
topupOff: !s.topup,
|
||||
sums: ["2 500 ₽", "5 000 ₽", "10 000 ₽"].map((label, i) => ({
|
||||
label: label,
|
||||
bg: s.sum === i ? sel : "transparent",
|
||||
border: s.sum === i ? "var(--accent)" : "var(--border)",
|
||||
pick: () => this.setState({ sum: i })
|
||||
})),
|
||||
methods: [
|
||||
{ label: "Карта ···· 4242", note: "мгновенно" },
|
||||
{ label: "СБП", note: "мгновенно" },
|
||||
{ label: "Криптовалюта", note: "USDT, BTC, ETH" }
|
||||
].map((m, i) => ({
|
||||
label: m.label,
|
||||
note: m.note,
|
||||
bg: s.method === i ? sel : "transparent",
|
||||
border: s.method === i ? "var(--accent)" : "var(--border)",
|
||||
pick: () => this.setState({ method: i })
|
||||
})),
|
||||
cryptoOff: s.method !== 2,
|
||||
sendTopup: () => this.setState({ topupSent: true }),
|
||||
topupButton: s.topupSent ? "Счёт выставлен" : "Пополнить",
|
||||
payments: [
|
||||
{ title: "Продление · Нидерланды", meta: "14 сентября · карта ···· 4242", amount: "−2 499 ₽", ink: "var(--ink)" },
|
||||
{ title: "Пополнение баланса", meta: "6 сентября · USDT (TRC-20)", amount: "+5 000 ₽", ink: "var(--accent)" },
|
||||
{ title: "Первый месяц · США", meta: "21 августа · карта ···· 4242", amount: "−4 900 ₽", ink: "var(--ink)" },
|
||||
{ title: "Продление · Нидерланды", meta: "14 августа · с баланса", amount: "−2 499 ₽", ink: "var(--ink)" },
|
||||
{ title: "Реферальный бонус", meta: "2 августа · месяц в подарок", amount: "+2 499 ₽", ink: "var(--accent)" }
|
||||
],
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadButton: s.leadSent ? "Заявка отправлена" : "Отправить заявку",
|
||||
refStats: s.lang === "en" ? "3 invited" : "3 друга",
|
||||
copyRef: () => this.setState({ refCopied: true }),
|
||||
refLabel: s.refCopied ? "Ссылка скопирована" : "Копировать ссылку",
|
||||
copyXray: () => this.setState({ copied: true }),
|
||||
copyLabel: s.copied ? "Скопировано" : "Копировать ключ",
|
||||
showXrayQr: () => this.setState({ qr: { title: "Xray · Happ", seed: cur.xray } }),
|
||||
payOff: !s.pay,
|
||||
payFlag: s.pay ? this.flag(s.pay.code) : null,
|
||||
payTitle: s.pay ? s.pay.country + " · " + s.pay.city : "",
|
||||
paySub: s.pay ? s.pay.servers + " " + this.plural(s.pay.servers) + " свободно · доступ сразу" : "",
|
||||
paySetup: s.pay ? s.pay.setup : "",
|
||||
payMonth: s.pay ? s.pay.month : "",
|
||||
payButton: s.paid ? "Оплата подтверждена" : (s.pay ? "Оплатить " + s.pay.setup : "Оплатить"),
|
||||
confirmPay: () => this.setState({ paid: true }),
|
||||
closePay: () => this.setState({ pay: null, paid: false }),
|
||||
stopPay: (e) => e.stopPropagation(),
|
||||
qrOff: !s.qr,
|
||||
qrTitle: s.qr ? s.qr.title : "",
|
||||
qrImage: s.qr ? this.qrImage(s.qr.seed) : null,
|
||||
closeQr: () => this.setState({ qr: null }),
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
ruBg: s.lang === "en" ? "transparent" : sel,
|
||||
enBg: s.lang === "en" ? sel : "transparent",
|
||||
ruInk: s.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: s.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent"
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,611 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<script src="https://unpkg.com/d3@7.9.0/dist/d3.min.js" integrity="sha384-CjloA8y00+1SDAUkjs099PVfnY2KmDC2BZnws9kh8D/lX1s46w6EPhpXdqMfjK6i" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/topojson-client@3.1.0/dist/topojson-client.min.js" integrity="sha384-Ukv1p/xTma6P4/2bY5KzWBw+ydSpXmhCMtyciIQVDJ1RmOxtCYNMF1uXT9T63H67" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-hover: oklch(38% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-1: oklch(86% 0.1 296);
|
||||
--glow-2: oklch(89% 0.09 266);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--land: oklch(93% 0.015 296);
|
||||
--land-line: oklch(87% 0.02 296);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 296 / 0.05), 0 12px 32px -12px oklch(24% 0.04 296 / 0.14);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-hover: oklch(80% 0.13 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-1: oklch(45% 0.16 296);
|
||||
--glow-2: oklch(40% 0.14 266);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--land: oklch(27% 0.03 296);
|
||||
--land-line: oklch(34% 0.035 296);
|
||||
--shadow: 0 1px 2px oklch(10% 0.02 296 / 0.4), 0 16px 40px -16px oklch(8% 0.02 296 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
@keyframes veilPulse{
|
||||
0%{ transform: scale(1); opacity: 0.55; }
|
||||
70%{ transform: scale(2.6); opacity: 0; }
|
||||
100%{ transform: scale(2.6); opacity: 0; }
|
||||
}
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<div style="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 88px; flex: none; white-space: nowrap;">Вейликс</span>
|
||||
</a>
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="#top" style="color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="App.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="Order.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="Price.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="#top" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="App.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="Order.dc.html" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(14px, 4vw, 24px) 80px;">
|
||||
|
||||
<section style="position: relative; isolation: isolate; margin-top: clamp(14px, 3vw, 40px); border-radius: 28px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; inset: 0; border-radius: 28px; overflow: hidden; pointer-events: none; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black);">
|
||||
<div style="position: absolute; right: min(-90px, calc(-150px + 11vw)); top: min(-130px, calc(-190px + 11vw)); width: clamp(190px, 40vw, 440px); height: clamp(190px, 40vw, 440px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-1) 0%, color-mix(in oklab, var(--glow-1) 35%, transparent) 55%, transparent 78%); filter: blur(80px); opacity: 0.45;"></div>
|
||||
<div style="position: absolute; left: min(-70px, calc(-130px + 11vw)); bottom: min(-150px, calc(-210px + 11vw)); width: clamp(160px, 34vw, 380px); height: clamp(160px, 34vw, 380px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-3) 0%, color-mix(in oklab, var(--glow-3) 35%, transparent) 55%, transparent 78%); filter: blur(80px); opacity: 0.35;"></div>
|
||||
</div>
|
||||
<div style="position: relative; padding: clamp(22px, 5vw, 48px) clamp(14px, 3.5vw, 40px) clamp(20px, 4vw, 36px);">
|
||||
<h1 style="margin: 0 0 12px; font-size: clamp(34px, 4.4vw, 52px); font-weight: 600; letter-spacing: -0.035em;">Локации</h1>
|
||||
<p style="margin: 0 0 28px; max-width: 46em; font-size: 17px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">30 стран на выбор и российский адрес в комплекте с любым тарифом.</p>
|
||||
<div id="veilix-map" style="width: 100%; min-height: 240px;"></div>
|
||||
<div style="margin-top: 10px; font-size: 13px; color: var(--ink-3);">Нажмите точку на карте — покажем страну и цену</div>
|
||||
<div hidden="{{ swipeOff }}" style="margin-top: 4px; font-size: 13px; color: var(--ink-3);">Карту можно прокрутить в сторону</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px 24px; margin-top: 20px; font-size: 14px; color: var(--ink-2);">
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
||||
<span style="width: 9px; height: 9px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
Серверы в наличии
|
||||
</span>
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
||||
<span style="width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--ink-3); display: block;"></span>
|
||||
Установим за 24 часа
|
||||
</span>
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
||||
<span style="width: 9px; height: 9px; border-radius: 2px; background: var(--ink-2); display: block;"></span>
|
||||
RU-адрес входит в каждый тариф
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding: 56px 0 0;">
|
||||
<h2 style="margin: 0 0 10px; font-size: clamp(26px, 2.8vw, 34px); font-weight: 600; letter-spacing: -0.03em;">Выберите страну</h2>
|
||||
<p style="margin: 0 0 24px; max-width: 52em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Базовая цена — 4 900 ₽ первый месяц и 2 499 ₽ далее. В редких локациях она выше и указана на карточке.</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;">
|
||||
<sc-for list="{{ items }}" as="loc" hint-placeholder-count="12">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: grid; align-content: start; gap: 14px; padding: 22px; border-radius: 22px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
{{ loc.flag }}
|
||||
<span style="display: flex; align-items: center; gap: 12px; flex: none; min-height: 32px;">
|
||||
<span onClick="{{ loc.buy }}" hidden="{{ loc.buyOff }}" style="display: flex; align-items: center; justify-content: center; height: 32px; padding: 0 16px; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 14px; font-weight: 500; cursor: pointer;">Купить</span>
|
||||
<span hidden="{{ loc.dotOff }}" style="width: 11px; height: 11px; border-radius: 50%; background: {{ loc.dot }}; display: block; flex: none;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span style="font-size: 19px; font-weight: 500; letter-spacing: -0.02em;">{{ loc.country }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: {{ loc.statusInk }};">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: {{ loc.statusDot }}; border: {{ loc.statusRing }}; display: block;"></span>
|
||||
{{ loc.status }}
|
||||
</span>
|
||||
<span style="display: grid; gap: 12px; padding-top: 14px; border-top: 1px solid var(--border);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
|
||||
<span style="font-size: 17px; font-weight: 500; color: var(--ink);">{{ loc.setup }}</span>
|
||||
первый месяц · далее
|
||||
<span style="font-weight: 500; color: var(--ink);">{{ loc.month }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding: 56px 0 0;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div style="padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em;">Российский адрес в каждом тарифе</h3>
|
||||
<p style="margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Он работает в обе стороны: из России банки и госуслуги видят обычное местное подключение, из другой страны те же сервисы открываются так, будто вы дома.</p>
|
||||
</div>
|
||||
<div style="padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 id="lead" style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em; scroll-margin-top: 90px;">Нужны индивидуальные условия</h3>
|
||||
<p style="margin: 0 0 18px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Нужной страны нет в списке, нужно много адресов или свой сценарий маршрутизации — напишите, скажем сроки и установим под вас.</p>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: inline-flex; align-items: center; height: 46px; padding: 0 20px; border-radius: 12px; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-soft);">Оставить заявку</button>
|
||||
<div hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 460px;">
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Персональные VPN-серверы с индивидуальным IP. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 Вейликс</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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, lang: "ru", langMenu: false, narrow: false, menu: false, loc: 0, lead: false, leadSent: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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();
|
||||
});
|
||||
this.drawMap();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
applyLang() {
|
||||
const lang = this.state.lang || "ru";
|
||||
if (window.VEILIX_APPLY_LANG) window.VEILIX_APPLY_LANG(document.body, lang);
|
||||
else setTimeout(() => this.applyLang(), 150);
|
||||
}
|
||||
|
||||
setTheme(t) {
|
||||
try { localStorage.setItem("veilix.theme", t); } catch (e) {}
|
||||
this.setState({ theme: t }, () => this.apply());
|
||||
}
|
||||
|
||||
setLang(l) {
|
||||
try { localStorage.setItem("veilix.lang", l); } catch (e) {}
|
||||
this.setState({ lang: l, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
async drawMap() {
|
||||
const host = document.getElementById("veilix-map");
|
||||
if (!host) return;
|
||||
if (!window.d3 || !window.topojson) { setTimeout(() => this.drawMap(), 200); return; }
|
||||
const d3 = window.d3;
|
||||
let topo;
|
||||
try {
|
||||
topo = await d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json");
|
||||
} catch (e) { return; }
|
||||
const land = window.topojson.feature(topo, topo.objects.countries);
|
||||
const W = 1200, H = 520;
|
||||
const projection = d3.geoNaturalEarth1().fitExtent([[10, 10], [W - 10, H - 10]], land);
|
||||
const path = d3.geoPath(projection);
|
||||
|
||||
host.innerHTML = "";
|
||||
const center = () => {
|
||||
const extra = host.scrollWidth - host.clientWidth;
|
||||
if (extra > 0) host.scrollLeft = extra / 2;
|
||||
};
|
||||
this.centerMap = center;
|
||||
if (!this.mapCentering) {
|
||||
this.mapCentering = true;
|
||||
window.addEventListener("resize", () => this.centerMap && this.centerMap());
|
||||
}
|
||||
host.style.overflowX = "auto";
|
||||
host.style.overflowY = "hidden";
|
||||
host.style.borderRadius = "18px";
|
||||
const svg = d3.select(host).append("svg")
|
||||
.attr("viewBox", "0 0 " + W + " " + H)
|
||||
.attr("width", "100%")
|
||||
.style("display", "block")
|
||||
.style("min-width", "820px")
|
||||
.style("overflow", "visible");
|
||||
|
||||
svg.append("g").selectAll("path").data(land.features).join("path")
|
||||
.attr("d", path)
|
||||
.attr("fill", "var(--land)")
|
||||
.attr("stroke", "var(--land-line)")
|
||||
.attr("stroke-width", 0.8);
|
||||
|
||||
const ru = projection([37.62, 55.75]);
|
||||
if (ru) {
|
||||
svg.append("rect")
|
||||
.attr("x", ru[0] - 5).attr("y", ru[1] - 5)
|
||||
.attr("width", 10).attr("height", 10).attr("rx", 2.5)
|
||||
.attr("fill", "var(--ink-2)");
|
||||
svg.append("text")
|
||||
.attr("x", ru[0] + 12).attr("y", ru[1] + 4)
|
||||
.attr("fill", "var(--ink-2)")
|
||||
.attr("font-size", 15)
|
||||
.attr("font-family", "Commissioner, system-ui, sans-serif")
|
||||
.text("RU");
|
||||
}
|
||||
|
||||
const FONT = "Commissioner, system-ui, sans-serif";
|
||||
const closePopup = () => { if (this.popupG) { this.popupG.remove(); this.popupG = null; } };
|
||||
this.closePopup = closePopup;
|
||||
svg.on("click", closePopup);
|
||||
|
||||
const popup = (d, p) => {
|
||||
closePopup();
|
||||
const pw = 268, ph = 138;
|
||||
const x = Math.max(8, Math.min(W - pw - 8, p[0] - pw / 2));
|
||||
let y = p[1] - ph - 16;
|
||||
if (y < 8) y = Math.min(H - ph - 8, p[1] + 18);
|
||||
const g = svg.append("g").attr("transform", "translate(" + x + "," + y + ")");
|
||||
this.popupG = g;
|
||||
g.on("click", (ev) => ev.stopPropagation());
|
||||
g.append("rect").attr("width", pw).attr("height", ph).attr("rx", 16)
|
||||
.attr("fill", "var(--surface)").attr("stroke", "var(--border)");
|
||||
g.append("text").attr("x", 20).attr("y", 33).attr("fill", "var(--ink)")
|
||||
.attr("font-size", 17).attr("font-weight", 500).attr("font-family", FONT).text(d.country);
|
||||
g.append("text").attr("x", 20).attr("y", 55).attr("fill", "var(--ink-3)")
|
||||
.attr("font-size", 13).attr("font-family", FONT).text(d.city);
|
||||
const price = g.append("text").attr("x", 20).attr("y", 83).attr("font-size", 13).attr("font-family", FONT);
|
||||
price.append("tspan").attr("fill", "var(--ink)").attr("font-weight", 500).text(d.setup);
|
||||
price.append("tspan").attr("dx", 6).attr("fill", "var(--ink-2)").text("первый месяц · далее");
|
||||
price.append("tspan").attr("dx", 6).attr("fill", "var(--ink)").attr("font-weight", 500).text(d.month);
|
||||
const link = g.append("a").attr("href", "Order.dc.html").style("cursor", "pointer");
|
||||
link.append("rect").attr("x", 20).attr("y", 97).attr("width", 108).attr("height", 30).attr("rx", 10).attr("fill", "var(--accent)");
|
||||
link.append("text").attr("x", 74).attr("y", 117).attr("text-anchor", "middle")
|
||||
.attr("fill", "var(--on-accent)").attr("font-size", 13).attr("font-weight", 500).attr("font-family", FONT).text("Купить");
|
||||
const close = g.append("g").style("cursor", "pointer").on("click", (ev) => { ev.stopPropagation(); closePopup(); });
|
||||
close.append("rect").attr("x", pw - 44).attr("y", 14).attr("width", 30).attr("height", 30).attr("rx", 9).attr("fill", "transparent");
|
||||
close.append("text").attr("x", pw - 29).attr("y", 35).attr("text-anchor", "middle")
|
||||
.attr("fill", "var(--ink-3)").attr("font-size", 18).attr("font-family", FONT).text("×");
|
||||
this.applyLang();
|
||||
};
|
||||
|
||||
this.data().forEach((d, i) => {
|
||||
const p = projection([d.lng, d.lat]);
|
||||
if (!p) return;
|
||||
const g = svg.append("g").attr("transform", "translate(" + p[0] + "," + p[1] + ")").style("cursor", "pointer");
|
||||
g.append("circle").attr("r", 12).attr("fill", "transparent");
|
||||
if (d.ready) {
|
||||
g.append("circle").attr("r", 6).attr("fill", "var(--accent)").attr("opacity", 0.32)
|
||||
.style("transform-box", "fill-box")
|
||||
.style("transform-origin", "center")
|
||||
.style("animation", "veilPulse 3.2s ease-out infinite");
|
||||
g.append("circle").attr("r", 5).attr("fill", "var(--accent)")
|
||||
.attr("stroke", "var(--surface)").attr("stroke-width", 1.4);
|
||||
} else {
|
||||
g.append("circle").attr("r", 5).attr("fill", "var(--bg)")
|
||||
.attr("stroke", "var(--ink-3)").attr("stroke-width", 2);
|
||||
}
|
||||
g.append("title").text(d.country + " · " + d.city);
|
||||
g.on("click", (ev) => { ev.stopPropagation(); this.setState({ loc: i }); popup(d, p); });
|
||||
});
|
||||
requestAnimationFrame(center);
|
||||
setTimeout(center, 120);
|
||||
}
|
||||
|
||||
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: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
const sel = "var(--accent-soft)";
|
||||
const theme = s.theme || this.props.defaultTheme || "light";
|
||||
const rows = this.data();
|
||||
const cur = rows[s.loc];
|
||||
return {
|
||||
items: rows.map((d, i) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
status: d.ready ? "Серверы в наличии" : "Установим за 24 часа",
|
||||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||||
statusDot: d.ready ? "var(--accent)" : "transparent",
|
||||
statusRing: d.ready ? "0" : "1.5px solid var(--ink-3)",
|
||||
bg: s.loc === i ? sel : "var(--surface)",
|
||||
border: s.loc === i ? "var(--accent)" : "var(--border)",
|
||||
dot: s.loc === i ? "var(--accent)" : "transparent",
|
||||
dotOff: s.loc === i,
|
||||
buy: (e) => { e.stopPropagation(); window.location.href = "Order.dc.html"; },
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
buyOff: s.loc !== i,
|
||||
pick: () => this.setState({ loc: i })
|
||||
})),
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: this.state.lead,
|
||||
leadClosed: !this.state.lead,
|
||||
leadButton: this.state.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
swipeOff: !s.narrow,
|
||||
navOff: s.narrow,
|
||||
burgerOff: !s.narrow,
|
||||
menuOff: !(s.narrow && s.menu),
|
||||
toggleMenu: () => this.setState(st => ({ menu: !st.menu, langMenu: false })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
toggleLangMenu: () => this.setState(st => ({ langMenu: !st.langMenu })),
|
||||
langMenuOff: !s.langMenu,
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
langLabel: s.lang === "en" ? "EN" : "RU",
|
||||
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)",
|
||||
ruCheck: s.lang === "en" ? "" : "✓",
|
||||
enCheck: s.lang === "en" ? "✓" : "",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,810 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script src="https://unpkg.com/d3@7.9.0/dist/d3.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/topojson-client@3.1.0/dist/topojson-client.min.js" crossorigin="anonymous"></script>
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-hover: oklch(38% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-1: oklch(86% 0.1 296);
|
||||
--glow-2: oklch(89% 0.09 266);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--land: oklch(95% 0.012 296);
|
||||
--land-line: oklch(90% 0.018 296);
|
||||
--ocean: oklch(97.5% 0.007 296);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 296 / 0.05), 0 12px 32px -12px oklch(24% 0.04 296 / 0.14);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-hover: oklch(80% 0.13 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-1: oklch(45% 0.16 296);
|
||||
--glow-2: oklch(40% 0.14 266);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--land: oklch(27% 0.028 296);
|
||||
--land-line: oklch(32% 0.032 296);
|
||||
--ocean: oklch(24% 0.025 296);
|
||||
--shadow: 0 1px 2px oklch(10% 0.02 296 / 0.4), 0 16px 40px -16px oklch(8% 0.02 296 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
summary{ list-style: none; }
|
||||
@keyframes veilPulse{
|
||||
0%{ transform: scale(1); opacity: 0.32; }
|
||||
70%{ transform: scale(2.6); opacity: 0; }
|
||||
100%{ transform: scale(2.6); opacity: 0; }
|
||||
}
|
||||
@keyframes veilDrift{
|
||||
0%{ transform: translate3d(0,0,0) scale(1); }
|
||||
50%{ transform: translate3d(2%,-3%,0) scale(1.06); }
|
||||
100%{ transform: translate3d(0,0,0) scale(1); }
|
||||
}
|
||||
@keyframes veilDrift2{
|
||||
0%{ transform: translate3d(0,0,0) scale(1.04); }
|
||||
50%{ transform: translate3d(-3%,2%,0) scale(1); }
|
||||
100%{ transform: translate3d(0,0,0) scale(1.04); }
|
||||
}
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
<div style="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh; transition: background 240ms ease, color 240ms ease;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
|
||||
<a href="#top" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span hidden="{{ mark1Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark2Off }}" style="position: absolute; inset: 0; overflow: hidden;">
|
||||
<span style="position: absolute; left: 2px; top: 2px; width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 50%, var(--glow-2))); display: block;"></span>
|
||||
<span style="position: absolute; left: -6px; top: 13px; width: 40px; height: 18px; border-radius: 10px 10px 4px 4px; background: color-mix(in oklab, var(--bg) 72%, transparent); backdrop-filter: blur(3px); transform: rotate(-14deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark3Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--accent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(-30deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 7px; top: 7px; width: 14px; height: 14px; border-radius: 50%; border: 2px solid color-mix(in oklab, var(--accent) 45%, transparent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(20deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark4Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 4px; top: 4px; width: 20px; height: 20px; border-radius: 6px; background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 45%, var(--glow-1))); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 9px; top: 9px; width: 10px; height: 10px; border-radius: 3px; background: color-mix(in oklab, var(--bg) 85%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 88px; flex: none; white-space: nowrap;">Вейликс</span>
|
||||
</a>
|
||||
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="App.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="#prices" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="Price.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="App.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#prices" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1200px; margin: 0 auto; padding: 0 24px;">
|
||||
|
||||
<section style="position: relative; min-height: 62vh; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(28px, 5vw, 56px); align-items: center; padding: clamp(32px, 7vw, 72px) 0 clamp(28px, 7vw, 84px);">
|
||||
<div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 28px;">
|
||||
<div style="display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px 6px 8px; border: 1px solid var(--border); border-radius: 999px; font-size: 13px; color: var(--ink-2);">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
Персональный сервер, не общий
|
||||
</div>
|
||||
<div style="display: inline-flex; align-items: center; padding: 6px 12px; border-radius: 999px; background: var(--accent-soft); color: var(--accent); font-size: 13px; font-weight: 500;">Работаем с 2022 года</div>
|
||||
</div>
|
||||
<h1 style="margin: 0 0 22px; font-size: clamp(40px, 5.2vw, 62px); line-height: 1.04; font-weight: 600; letter-spacing: -0.035em; text-wrap: balance;">Ваш личный VPN с индивидуальным IP</h1>
|
||||
<p style="margin: 0 0 34px; max-width: 30em; font-size: 18px; line-height: 1.55; color: var(--ink-2); font-weight: 300; text-wrap: pretty;">Адрес принадлежит только вам: один и тот же IP на всех устройствах, в любой стране. Без соседей по серверу и их репутации.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: clamp(8px, 2vw, 12px);">
|
||||
<a href="#prices" style="display: flex; align-items: center; justify-content: center; height: 50px; padding: 0 clamp(14px, 4vw, 24px); border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500; box-shadow: 0 10px 28px -12px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<a href="#locations" style="display: flex; align-items: center; justify-content: center; height: 50px; padding: 0 clamp(14px, 4vw, 22px); border-radius: 12px; border: 1px solid var(--border); color: var(--ink); font-size: 16px; white-space: nowrap;" style-hover="border-color: var(--ink-3); color: var(--ink);">Посмотреть локации</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position: relative; aspect-ratio: 1 / 1; max-width: 480px; width: 100%; justify-self: {{ heroAlign }}; border-radius: 28px; overflow: hidden; isolation: isolate; -webkit-transform: translateZ(0); transform: translateZ(0); background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="position: absolute; left: -12%; top: -8%; width: 78%; height: 78%; border-radius: 50%; background: var(--glow-1); filter: blur(46px); opacity: 0.85; animation: veilDrift 16s ease-in-out infinite;"></div>
|
||||
<div style="position: absolute; right: -14%; top: 14%; width: 70%; height: 70%; border-radius: 50%; background: var(--glow-3); filter: blur(52px); opacity: 0.75; animation: veilDrift2 19s ease-in-out infinite;"></div>
|
||||
<div style="position: absolute; left: 16%; bottom: -16%; width: 82%; height: 68%; border-radius: 50%; background: var(--glow-2); filter: blur(54px); opacity: 0.8; animation: veilDrift 23s ease-in-out infinite;"></div>
|
||||
<div id="veilix-globe" style="position: absolute; left: 8%; top: 8%; width: 84%; height: 84%;"></div>
|
||||
<div style="position: absolute; left: 8%; top: 30%; right: 8%; height: 26%; border-radius: 18px; background: color-mix(in oklab, var(--veil) 72%, transparent); border: 1px solid color-mix(in oklab, var(--ink) 10%, transparent); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); transform: rotate(-8deg);"></div>
|
||||
<div style="position: absolute; left: 12%; top: 46%; right: 4%; height: 24%; border-radius: 18px; background: color-mix(in oklab, var(--veil) 72%, transparent); border: 1px solid color-mix(in oklab, var(--ink) 8%, transparent); backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px); transform: rotate(6deg);"></div>
|
||||
<div style="position: absolute; left: 20px; right: 20px; bottom: 20px; display: grid; gap: 10px; padding: 14px 16px; border-radius: 16px; background: color-mix(in oklab, var(--surface) 84%, transparent); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--ink-2);">
|
||||
<span style="width: 7px; height: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
|
||||
Ваш IP закреплён только за вами
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px 16px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 13px; color: var(--ink-3); font-variant-numeric: tabular-nums;">
|
||||
<span>4 900 ₽ первый месяц, далее 2 499 ₽</span>
|
||||
<span>До 5 устройств</span>
|
||||
<span>RU-адрес в комплекте</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="advantages" style="padding: 16px 0 8px;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 24px; margin-bottom: 28px;">
|
||||
<h2 style="margin: 0; font-size: clamp(28px, 3vw, 38px); font-weight: 600; letter-spacing: -0.03em;">Что это даёт на практике</h2>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div style="position: relative; isolation: isolate; padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; inset: 0; border-radius: 22px; overflow: hidden; pointer-events: none; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black);">
|
||||
<div style="position: absolute; right: -80px; top: -80px; width: 260px; height: 260px; border-radius: 50%; background: var(--glow-1); filter: blur(60px); opacity: 0.5;"></div>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 18px;">
|
||||
<span style="position: relative; width: 34px; height: 34px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 11px; top: 3px; width: 12px; height: 12px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
<span style="position: absolute; left: 4px; top: 19px; width: 26px; height: 14px; border-radius: 13px 13px 4px 4px; background: var(--accent); opacity: 0.35; display: block;"></span>
|
||||
<span style="position: absolute; left: 23px; top: 18px; width: 11px; height: 11px; border-radius: 50%; border: 2px solid var(--accent); background: var(--surface); display: block;"></span>
|
||||
</span>
|
||||
<h3 style="margin: 0; font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">Индивидуальный IP</h3>
|
||||
</div>
|
||||
<p style="margin: 0 0 26px; max-width: 34em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Адрес выделен под вас одного. Сервисы видят обычного человека из выбранной страны, а не сотню людей за одним адресом.</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px 28px;">
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Западные AI-сервисы</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Риск блокировки ниже, чем у публичных VPN: их адреса уже давно в чёрных списках.</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Один адрес на всех устройствах</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Ноутбук, телефон, планшет выглядят одинаково. Вы всегда в одной локации со своим IP.</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Меньше проверок и капч</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">На массовых адресах срабатывает антибот-защита. На личном сервисы работают стабильнее.</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Трафик под шифрованием</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Дома, в отеле или в кафе соединение защищено в любой точке мира.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 16px; align-content: start;">
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; gap: 4px; margin-bottom: 16px;">
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: var(--accent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 75%, transparent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 55%, transparent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 18%, transparent); display: block;"></span>
|
||||
</div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em;">До 5 устройств в тарифе</h3>
|
||||
<p style="margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Нужно больше или другая страна — добавляете второй тариф со своим отдельным IP.</p>
|
||||
</div>
|
||||
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px;">
|
||||
<span style="width: 22px; height: 22px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
<span style="width: 26px; height: 2px; background: color-mix(in oklab, var(--accent) 40%, transparent); display: block;"></span>
|
||||
<span style="width: 22px; height: 22px; border-radius: 50%; border: 2px solid color-mix(in oklab, var(--accent) 55%, transparent); display: block;"></span>
|
||||
</div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em;">Плюс выделенный RU-адрес</h3>
|
||||
<p style="margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Идёт к любому тарифу. Работает и как прямое соединение, и как мост в обе стороны.</p>
|
||||
</div>
|
||||
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 16px;">
|
||||
<span style="width: 22px; height: 22px; border-radius: 7px; background: var(--accent); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="width: 22px; height: 22px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em;">Два типа подключения</h3>
|
||||
<p style="margin: 0 0 12px; font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Если один способ перестал работать, второй уже настроен. Список дополняется и обновляется.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px; font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums;">
|
||||
<span style="padding: 4px 9px; border-radius: 7px; background: var(--surface-2); border: 1px solid var(--border);">WireGuard / Amnezia WG</span>
|
||||
<span style="padding: 4px 9px; border-radius: 7px; background: var(--surface-2); border: 1px solid var(--border);">Xray: VLESS, Hysteria2, XHTTP</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 16px; padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 style="margin: 0 0 6px; font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">Трафик идёт коротким путём</h3>
|
||||
<p style="margin: 0 0 28px; max-width: 46em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Маршрут выбирается сам, по адресу сайта. Российские сервисы не ломаются, зарубежные открываются, переключать ничего не нужно.</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px;">
|
||||
<div style="padding: 22px; border-radius: 16px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 16px;">Вы в России</div>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">RU-сайты</span>
|
||||
<span style="flex: 1; height: 1px; background: linear-gradient(90deg, var(--accent), color-mix(in oklab, var(--accent) 25%, transparent)); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">напрямую через RU-сервер</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">Остальной мир</span>
|
||||
<span style="flex: 1; height: 1px; background: repeating-linear-gradient(90deg, var(--accent) 0 6px, transparent 6px 12px); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">туннелем в вашу локацию</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 16px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 16px;">Вы за границей</div>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">Мировые сайты</span>
|
||||
<span style="flex: 1; height: 1px; background: linear-gradient(90deg, var(--accent), color-mix(in oklab, var(--accent) 25%, transparent)); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">прямо из вашей локации</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">RU-сайты</span>
|
||||
<span style="flex: 1; height: 1px; background: repeating-linear-gradient(90deg, var(--accent) 0 6px, transparent 6px 12px); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">туннелем через RU-сервер</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 16px; font-size: 13px; color: var(--ink-3);">Для тех, кто работает с Россией из другой страны.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="prices" style="padding: 72px 0 8px;">
|
||||
<div style="position: relative; overflow: hidden; isolation: isolate; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black); -webkit-transform: translateZ(0); transform: translateZ(0); border-radius: 26px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; left: min(-60px, calc(-110px + 8vw)); bottom: min(-120px, calc(-170px + 10vw)); width: clamp(150px, 34vw, 340px); height: clamp(150px, 34vw, 340px); border-radius: 50%; background: radial-gradient(circle at 60% 40%, var(--glow-1) 0%, color-mix(in oklab, var(--glow-1) 35%, transparent) 55%, transparent 78%); filter: blur(70px); opacity: 0.45;"></div>
|
||||
<div style="position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: clamp(28px, 5vw, 40px); padding: clamp(20px, 5vw, 40px);">
|
||||
<div>
|
||||
<div style="display: flex; align-items: baseline; gap: 10px; white-space: nowrap;">
|
||||
<span style="font-size: clamp(34px, 8vw, 68px); font-weight: 600; letter-spacing: -0.04em; line-height: 1; font-variant-numeric: tabular-nums;">{{ curSetup }}</span>
|
||||
<span style="font-size: clamp(14px, 3.6vw, 16px); color: var(--ink-2);">первый месяц</span>
|
||||
</div>
|
||||
<div style="margin-top: 10px; font-size: clamp(15px, 3.8vw, 17px); color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums;">далее <span>{{ curMonth }}</span> в месяц</div>
|
||||
<div style="margin-top: 24px; display: grid; gap: 10px; font-size: 14px; color: var(--ink-2); font-weight: 300;">
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span>Индивидуальный IP в выбранной стране</div>
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span>Выделенный RU-адрес в комплекте</div>
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span>До 5 устройств</div>
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span><span style="display: grid; gap: 5px;"><span>2 типа подключения:</span><span style="display: flex; gap: 8px; color: var(--ink-2);"><span style="flex: none;">—</span><span>WireGuard / Amnezia WG</span></span><span style="display: flex; gap: 8px; color: var(--ink-2);"><span style="flex: none;">—</span><span>Xray: VLESS, Hysteria2, XHTTP</span></span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="locations">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 14px;">
|
||||
<div style="font-size: 15px; font-weight: 500;">Выберите локацию</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3);">{{ locHint }}</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 8px; max-height: 420px; overflow-y: auto; padding-right: 4px;">
|
||||
<sc-for list="{{ items }}" as="loc" hint-placeholder-count="8">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 12px 14px; border-radius: 14px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); font-size: 15px; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
{{ loc.flag }}
|
||||
<span style="flex: 1; min-width: 110px; display: grid; gap: 2px;">
|
||||
<span>{{ loc.country }}</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 2px; text-align: right; font-variant-numeric: tabular-nums;">
|
||||
<span style="font-size: 15px; font-weight: 500;">{{ loc.setup }}</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">далее <span>{{ loc.month }}</span></span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 20px;">
|
||||
<a href="Order.dc.html" style="display: flex; align-items: center; height: 48px; padding: 0 24px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500; box-shadow: 0 10px 28px -12px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Купить</a>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: flex; align-items: center; height: 48px; padding: 0 20px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;" style-hover="border-color: var(--ink-3);">Нужны индивидуальные условия</button>
|
||||
</div>
|
||||
<div id="lead" hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 460px; margin-top: 20px; scroll-margin-top: 90px;">
|
||||
<div style="font-size: 15px; font-weight: 500;">Нужны индивидуальные условия</div>
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="faq" style="padding: 72px 0 32px;">
|
||||
<h2 style="margin: 0 0 28px; font-size: clamp(28px, 3vw, 38px); font-weight: 600; letter-spacing: -0.03em;">Вопросы</h2>
|
||||
<div style="display: grid; gap: 10px; max-width: 860px;">
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq0 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Чем это отличается от обычного VPN за 300 рублей?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq0Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq0Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">В массовом VPN ваш адрес делят сотни людей. Если кто-то из них попал в чёрный список, ограничения касаются всех. У вас адрес один и только ваш, поэтому сервисы относятся к нему как к домашнему интернету.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq1 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Почему некоторые локации готовы только через 24 часа?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq1Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq1Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Мы не держим пул общих адресов. Если в нужной стране свободных серверов нет, мы устанавливаем сервер лично под вас и выдаём доступ в течение суток. Оплата за первый месяц начинается с момента выдачи.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq2 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Нужно ли что-то настраивать самому?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq2Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq2Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Нет. В личном кабинете вы скачиваете готовый файл или сканируете QR-код с телефона. Маршрутизация уже настроена: российские сайты идут напрямую, зарубежные — через ваш сервер.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq3 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Сколько устройств можно подключить?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq3Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq3Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">До пяти на один тариф, все с одним и тем же адресом. Нужно больше устройств или вторая страна — берёте дополнительный тариф с отдельным индивидуальным IP.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq4 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Зачем мне ещё и российский адрес?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq4Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq4Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Он работает в обе стороны. Из России — чтобы банки, госуслуги и стриминг видели обычное местное подключение. Из другой страны — чтобы те же сервисы открывались так, будто вы дома.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq5 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Что будет, если способ подключения перестанет работать?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq5Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq5Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">На вашем сервере одновременно работают два типа подключения. Если один способ начал блокироваться, вы переключаетесь на второй в том же кабинете. Набор протоколов мы дополняем и обновляем.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section hidden="{{ logoPickerOff }}" style="padding: 40px 0 24px; border-top: 1px dashed var(--border);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 20px; margin-bottom: 18px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.02em;">Варианты знака</h2>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Нажмите, чтобы примерить в шапке</span>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: 12px;">
|
||||
<button type="button" onClick="{{ pickMark1 }}" style="display: grid; gap: 14px; justify-items: start; padding: 22px; border-radius: 18px; border: 1px solid var(--border); background: {{ mark1Bg }}; color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 40px; height: 40px; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 4px; width: 30px; height: 30px; border-radius: 10px; 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: 4px; width: 30px; height: 30px; border-radius: 10px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(3px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-size: 14px; font-weight: 500;">Слои</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3); line-height: 1.4;">Одно скрыто за другим</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ pickMark2 }}" style="display: grid; gap: 14px; justify-items: start; padding: 22px; border-radius: 18px; border: 1px solid var(--border); background: {{ mark2Bg }}; color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 40px; height: 40px; display: block; overflow: hidden;">
|
||||
<span style="position: absolute; left: 3px; top: 3px; width: 34px; height: 34px; border-radius: 50%; background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 50%, var(--glow-2))); display: block;"></span>
|
||||
<span style="position: absolute; left: -8px; top: 19px; width: 56px; height: 26px; border-radius: 14px 14px 6px 6px; background: color-mix(in oklab, var(--bg) 72%, transparent); backdrop-filter: blur(4px); transform: rotate(-14deg); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-size: 14px; font-weight: 500;">Вуаль</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3); line-height: 1.4;">Полупрозрачная ткань поверх</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ pickMark3 }}" style="display: grid; gap: 14px; justify-items: start; padding: 22px; border-radius: 18px; border: 1px solid var(--border); background: {{ mark3Bg }}; color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 40px; height: 40px; display: block;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 38px; height: 38px; border-radius: 50%; border: 2.5px solid var(--accent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(-30deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 10px; top: 10px; width: 20px; height: 20px; border-radius: 50%; border: 2.5px solid color-mix(in oklab, var(--accent) 45%, transparent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(20deg); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-size: 14px; font-weight: 500;">Дуги</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3); line-height: 1.4;">Складка, лёгкий жест</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ pickMark4 }}" style="display: grid; gap: 14px; justify-items: start; padding: 22px; border-radius: 18px; border: 1px solid var(--border); background: {{ mark4Bg }}; color: var(--ink); font-family: inherit; text-align: left; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 40px; height: 40px; display: block;">
|
||||
<span style="position: absolute; left: 6px; top: 6px; width: 28px; height: 28px; border-radius: 8px; background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 45%, var(--glow-1))); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 13px; top: 13px; width: 14px; height: 14px; border-radius: 4px; background: color-mix(in oklab, var(--bg) 85%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-size: 14px; font-weight: 500;">Диафрагма</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3); line-height: 1.4;">Проём, через который видно</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="margin-top: 40px; border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<span style="position: relative; width: 24px; height: 24px; display: block; flex: none;">
|
||||
<span hidden="{{ mark1Off }}" style="position: absolute; inset: 0;">
|
||||
<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 hidden="{{ mark2Off }}" style="position: absolute; inset: 0; overflow: hidden;">
|
||||
<span style="position: absolute; left: 2px; top: 2px; width: 20px; height: 20px; border-radius: 50%; background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 50%, var(--glow-2))); display: block;"></span>
|
||||
<span style="position: absolute; left: -5px; top: 11px; width: 34px; height: 15px; border-radius: 9px 9px 4px 4px; background: color-mix(in oklab, var(--surface-2) 72%, transparent); backdrop-filter: blur(3px); transform: rotate(-14deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark3Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--accent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(-30deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 6px; top: 6px; width: 12px; height: 12px; border-radius: 50%; border: 2px solid color-mix(in oklab, var(--accent) 45%, transparent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(20deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark4Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 3px; top: 3px; width: 18px; height: 18px; border-radius: 5px; background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 45%, var(--glow-1))); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 8px; top: 8px; width: 8px; height: 8px; border-radius: 3px; background: color-mix(in oklab, var(--surface-2) 85%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Персональные VPN-серверы с индивидуальным IP. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 Вейликс</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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":"Тема"},"logoMark":{"editor":"enum","options":["layers","veil","arcs","aperture"],"default":"layers","tsType":"string","section":"Логотип"},"showLogoPicker":{"editor":"boolean","default":false,"tsType":"boolean","section":"Логотип"}}">
|
||||
class Component extends DCLogic {
|
||||
state = { theme: null, sysDark: false, loc: 0, faq: 0, mark: null, langMenu: false, lang: "ru", narrow: false, stacked: false, menu: false, lead: false, leadSent: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
async drawGlobe() {
|
||||
const host = document.getElementById("veilix-globe");
|
||||
if (!host) return;
|
||||
if (!window.d3 || !window.topojson) { setTimeout(() => this.drawGlobe(), 200); return; }
|
||||
const d3 = window.d3;
|
||||
let topo;
|
||||
try {
|
||||
topo = await d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json");
|
||||
} catch (e) { return; }
|
||||
const land = window.topojson.feature(topo, topo.objects.countries);
|
||||
const S = 440, r = S / 2 - 4;
|
||||
const projection = d3.geoOrthographic().rotate([-20, -22]).scale(r).translate([S / 2, S / 2]);
|
||||
const path = d3.geoPath(projection);
|
||||
host.innerHTML = "";
|
||||
const svg = d3.select(host).append("svg")
|
||||
.attr("viewBox", "0 0 " + S + " " + S)
|
||||
.attr("width", "100%").attr("height", "100%")
|
||||
.style("display", "block").style("overflow", "visible");
|
||||
svg.append("circle").attr("cx", S / 2).attr("cy", S / 2).attr("r", r)
|
||||
.attr("fill", "var(--ocean)").attr("stroke", "var(--border)");
|
||||
const graticule = d3.geoGraticule10 ? d3.geoGraticule10() : null;
|
||||
if (graticule) {
|
||||
svg.append("path").datum(graticule).attr("d", path)
|
||||
.attr("fill", "none").attr("stroke", "var(--land-line)")
|
||||
.attr("stroke-width", 0.5).attr("opacity", 0.5);
|
||||
}
|
||||
svg.append("g").selectAll("path").data(land.features).join("path")
|
||||
.attr("d", path).attr("fill", "var(--land)")
|
||||
.attr("stroke", "var(--land-line)").attr("stroke-width", 0.6);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.drawGlobe();
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
const st = window.innerWidth < 700;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
if (st !== this.state.stacked) this.setState({ stacked: st });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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.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, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const mark = this.state.mark || this.props.logoMark || "layers";
|
||||
const marks = ["layers", "veil", "arcs", "aperture"];
|
||||
const sel = "var(--accent-soft)";
|
||||
const v = {
|
||||
locHint: this.data()[this.state.loc].ready ? "Свободные серверы есть сейчас" : "Сервер установим под вас за 24 часа",
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: this.state.lead,
|
||||
leadClosed: !this.state.lead,
|
||||
leadButton: this.state.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
curSetup: this.data()[this.state.loc].setup,
|
||||
curMonth: this.data()[this.state.loc].month,
|
||||
items: this.data().map((d, n) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
bg: this.state.loc === n ? sel : "transparent",
|
||||
border: this.state.loc === n ? "var(--accent)" : "var(--border)",
|
||||
pick: () => this.setState({ loc: n })
|
||||
})),
|
||||
logoPickerOff: this.props.showLogoPicker !== true,
|
||||
langMenuOff: !this.state.langMenu,
|
||||
heroAlign: this.state.stacked ? "center" : "end",
|
||||
navOff: this.state.narrow,
|
||||
burgerOff: !this.state.narrow,
|
||||
menuOff: !(this.state.narrow && this.state.menu),
|
||||
toggleMenu: () => this.setState(s => ({ menu: !s.menu, langMenu: false })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
langLabel: this.state.lang === "en" ? "EN" : "RU",
|
||||
ruBg: this.state.lang === "en" ? "transparent" : sel,
|
||||
enBg: this.state.lang === "en" ? sel : "transparent",
|
||||
ruInk: this.state.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: this.state.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
ruCheck: this.state.lang === "en" ? "" : "✓",
|
||||
enCheck: this.state.lang === "en" ? "✓" : "",
|
||||
toggleLangMenu: () => this.setState(s => ({ langMenu: !s.langMenu })),
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
for (let i = 0; i < 5; i++) {
|
||||
v["loc" + i + "Bg"] = this.state.loc === i ? sel : "transparent";
|
||||
v["loc" + i + "Dot"] = this.state.loc === i ? "var(--accent)" : "var(--ink-3)";
|
||||
v["pickLoc" + i] = () => this.setState({ loc: i });
|
||||
}
|
||||
for (let i = 0; i < 6; i++) {
|
||||
v["faq" + i] = () => this.setState(s => ({ faq: s.faq === i ? -1 : i }));
|
||||
v["faq" + i + "Off"] = this.state.faq !== i;
|
||||
v["faq" + i + "Rot"] = this.state.faq === i ? "rotate(-135deg) translateY(2px)" : "rotate(45deg) translateY(-2px)";
|
||||
}
|
||||
marks.forEach((name, idx) => {
|
||||
const n = idx + 1;
|
||||
v["mark" + n + "Off"] = mark !== name;
|
||||
v["mark" + n + "Bg"] = mark === name ? sel : "transparent";
|
||||
v["pickMark" + n] = () => this.setState({ mark: name });
|
||||
});
|
||||
return v;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,984 @@
|
||||
<!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-hover: oklch(41% 0.1 178);
|
||||
--accent-soft: oklch(95.5% 0.032 180);
|
||||
--sel: oklch(96.5% 0.016 190);
|
||||
--on-accent: oklch(99% 0.01 180);
|
||||
--glow-1: oklch(86% 0.085 190);
|
||||
--glow-2: oklch(88% 0.075 205);
|
||||
--glow-3: oklch(89% 0.07 172);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--warn: oklch(62% 0.14 62);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 204 / 0.05), 0 12px 32px -12px oklch(24% 0.04 204 / 0.14);
|
||||
}
|
||||
: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-hover: oklch(83% 0.09 190);
|
||||
--accent-soft: oklch(31% 0.05 190);
|
||||
--sel: oklch(30% 0.03 195);
|
||||
--on-accent: oklch(20% 0.04 204);
|
||||
--glow-1: oklch(52% 0.09 200);
|
||||
--glow-2: oklch(47% 0.085 215);
|
||||
--glow-3: oklch(50% 0.08 180);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--warn: oklch(78% 0.13 62);
|
||||
--shadow: 0 1px 2px oklch(12% 0.03 204 / 0.4), 0 16px 40px -16px oklch(9% 0.03 204 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
input::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="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<div hidden="{{ appOff }}" style="display: grid; grid-template-rows: 1fr auto; min-height: 100vh;">
|
||||
<div style="display: flex; align-items: center; justify-content: center; padding: 24px;">
|
||||
<div style="width: 100%; max-width: 420px; padding: 36px; border-radius: 24px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 26px;">
|
||||
<span style="position: relative; width: 26px; height: 26px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 19px; height: 19px; 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: 19px; height: 19px; border-radius: 6px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 16px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</div>
|
||||
<h1 style="margin: 0 0 10px; font-size: 26px; font-weight: 600; letter-spacing: -0.03em;">Вход в кабинет</h1>
|
||||
<p style="margin: 0 0 22px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Введите почту, пришлём одноразовый код. Отдельной регистрации нет — кабинет создастся сам, если вы здесь впервые.</p>
|
||||
<div hidden="{{ otpSent }}" style="display: grid; gap: 10px;">
|
||||
<input type="email" placeholder="you@example.com" style="height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<button type="button" onClick="{{ sendOtp }}" style="height: 50px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Прислать код</button>
|
||||
</div>
|
||||
<div hidden="{{ otpNotSent }}" style="display: grid; gap: 10px;">
|
||||
<input type="text" inputmode="numeric" placeholder="6 цифр" style="height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 17px; letter-spacing: 0.28em; font-variant-numeric: tabular-nums; width: 100%;" />
|
||||
<button type="button" onClick="{{ signIn }}" style="height: 50px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Войти</button>
|
||||
<button type="button" onClick="{{ backToEmail }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 4px 0;">Другая почта</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; padding: 14px 24px; border-top: 1px solid var(--border); font-size: 13px; color: var(--ink-3);">
|
||||
<span>© 2026 ВейликсВПН</span>
|
||||
<a href="#legal" style="color: var(--ink-3);" style-hover="color: var(--ink);">Условия</a>
|
||||
<a href="#legal" style="color: var(--ink-3);" style-hover="color: var(--ink);">Конфиденциальность</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-3);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
<div style="margin-left: auto; display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; width: max-content; padding: 3px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 6px 11px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 6px 11px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
<div style="display: flex; width: max-content; padding: 3px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;">
|
||||
<span style="position: relative; width: 16px; height: 16px; display: block;">
|
||||
<span style="position: absolute; left: 3.5px; top: 3.5px; width: 9px; height: 9px; 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: 32px; height: 32px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; box-shadow: inset -5px -2px 0 0 currentColor; display: block;"></span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ loginOff }}" style="display: grid; grid-template-columns: {{ shellCols }}; min-height: 100vh;">
|
||||
|
||||
<div hidden="{{ topbarOff }}" style="position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: 10px; padding: 12px 16px; background: color-mix(in oklab, var(--bg) 85%, transparent); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); border-bottom: 1px solid var(--border);">
|
||||
<a href="MassMain.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink);">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<aside hidden="{{ asideOff }}" style="display: flex; flex-direction: column; gap: 24px; padding: 22px 16px; border-right: 1px solid var(--border); background: var(--surface-2); position: sticky; top: 0; height: 100vh;">
|
||||
<a href="MassMain.dc.html" style="display: flex; align-items: center; gap: 10px; padding: 4px 8px; color: var(--ink);">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</a>
|
||||
|
||||
<nav style="display: grid; gap: 4px;">
|
||||
<button type="button" onClick="{{ goHome }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ homeBg }}; color: {{ homeInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; bottom: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; bottom: 0; width: 8px; height: 8px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Главная
|
||||
</button>
|
||||
<button type="button" onClick="{{ goKeys }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ keysBg }}; color: {{ keysInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 2px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 8px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.6; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 14px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; opacity: 0.3; display: block;"></span>
|
||||
</span>
|
||||
Подключения
|
||||
</button>
|
||||
<button type="button" onClick="{{ goBuy }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ buyBg }}; color: {{ buyInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 7px; top: 0; width: 4px; height: 18px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 7px; width: 18px; height: 4px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Выбрать страну
|
||||
</button>
|
||||
<button type="button" onClick="{{ goBilling }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ billingBg }}; color: {{ billingInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 2px; width: 18px; height: 14px; border-radius: 4px; border: 2px solid currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 6px; width: 18px; height: 3px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Счёт и платежи
|
||||
</button>
|
||||
<button type="button" onClick="{{ goRef }}" style="display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 12px; border: 0; background: {{ refBg }}; color: {{ refInk }}; font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;" style-hover="background: var(--surface);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 0; top: 5px; width: 8px; height: 8px; border-radius: 50%; border: 2px solid currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 0; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 2px; bottom: 0; width: 8px; height: 8px; border-radius: 50%; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
</span>
|
||||
Реферальная программа
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div style="margin-top: auto; display: grid; gap: 14px; padding: 0 4px;">
|
||||
<a href="MassSupport.dc.html" style="display: flex; align-items: center; gap: 12px; padding: 11px 8px; border-radius: 12px; color: var(--ink-2); font-size: 15px;" style-hover="background: var(--surface); color: var(--ink);">
|
||||
<span style="position: relative; width: 18px; height: 18px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 16px; height: 16px; border-radius: 8px 8px 0 0; border: 2px solid currentColor; border-bottom: 0; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 8px; width: 5px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 8px; width: 5px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Поддержка
|
||||
</a>
|
||||
<div>
|
||||
<div style="font-size: 12px; color: var(--ink-3); margin-bottom: 6px; padding: 0 4px;">Тема</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px; width: max-content; max-width: 100%; align-self: start; padding: 3px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border: 0; border-radius: 9px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;">
|
||||
<span style="position: relative; width: 18px; height: 18px; display: block;">
|
||||
<span style="position: absolute; left: 4px; top: 4px; width: 10px; height: 10px; border-radius: 50%; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; top: 0; width: 18px; height: 18px; 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: 36px; height: 36px; border: 0; border-radius: 9px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;">
|
||||
<span style="width: 18px; height: 18px; border-radius: 50%; box-shadow: inset -6px -2px 0 0 currentColor; display: block;"></span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border: 0; border-radius: 9px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;">
|
||||
<span style="width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 12px; color: var(--ink-3); margin-bottom: 6px; padding: 0 4px;">Язык</div>
|
||||
<div style="display: flex; flex-wrap: wrap; width: max-content; max-width: 100%; align-self: start; padding: 3px; border-radius: 12px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 8px 14px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 13px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 8px 14px; border: 0; border-radius: 9px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 13px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ signOut }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 13px; cursor: pointer; padding: 4px 8px;">Выйти · anna@example.com</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main style="padding: {{ mainPad }}; max-width: 960px;">
|
||||
|
||||
<div hidden="{{ homeOff }}" style="display: grid; gap: 28px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Ваша подписка</h1>
|
||||
<p style="margin: 0; font-size: 15px; color: var(--ink-2); font-weight: 300;">Тридцать стран и российский адрес, до пяти устройств.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<sc-for list="{{ keys }}" as="key" hint-placeholder-count="2">
|
||||
<button type="button" onClick="{{ key.open }}" style="display: grid; gap: 16px; padding: 24px; border-radius: 20px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow); color: var(--ink); font-family: inherit; text-align: left; cursor: pointer; width: 100%;" style-hover="border-color: var(--accent);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px;">
|
||||
<span style="font-size: 19px; font-weight: 500; letter-spacing: -0.02em;">{{ key.title }}</span>
|
||||
<span style="padding: 4px 10px; border-radius: 8px; background: {{ key.badgeBg }}; color: {{ key.badgeInk }}; font-size: 12px;">{{ key.badge }}</span>
|
||||
<span style="margin-left: auto; font-size: 14px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ key.ip }}</span>
|
||||
</span>
|
||||
<span style="display: flex; flex-wrap: wrap; gap: 8px 28px; font-size: 14px; color: var(--ink-2);">
|
||||
<span>Действует до {{ key.until }}</span>
|
||||
<span>{{ key.devices }}</span>
|
||||
<span>{{ key.payment }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; padding: 24px; border-radius: 20px; background: var(--surface-2); border: 1px dashed var(--border);">
|
||||
<div style="display: grid; gap: 4px;">
|
||||
<div style="font-size: 17px; font-weight: 500;">Хотите другую страну</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2); font-weight: 300;">Все локации уже в подписке, переключение бесплатное.</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ goBuy }}" style="height: 46px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Сменить страну</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<h2 style="margin: 0 0 4px; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Короткие ответы</h2>
|
||||
<sc-for list="{{ faq }}" as="item" hint-placeholder-count="3">
|
||||
<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: 16px; width: 100%; padding: 17px 20px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 16px; font-weight: 500; text-align: left; cursor: pointer;">
|
||||
<span style="flex: 1;">{{ item.q }}</span>
|
||||
<span style="flex: none; width: 9px; height: 9px; 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 20px 20px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">{{ item.a }}</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ keysOff }}" style="display: grid; gap: 20px;">
|
||||
<h1 style="margin: 0; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Подключения</h1>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<sc-for list="{{ allKeys }}" as="key" hint-placeholder-count="3">
|
||||
<button type="button" onClick="{{ key.open }}" style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; padding: 20px 24px; border-radius: 18px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; text-align: left; cursor: pointer; width: 100%;" style-hover="border-color: var(--accent);">
|
||||
<span style="font-size: 17px; font-weight: 500;">{{ key.title }}</span>
|
||||
<span style="padding: 4px 10px; border-radius: 8px; background: {{ key.badgeBg }}; color: {{ key.badgeInk }}; font-size: 12px;">{{ key.badge }}</span>
|
||||
<span style="margin-left: auto; font-size: 14px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ key.ip }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-2); font-variant-numeric: tabular-nums;">{{ key.untilLine }}</span>
|
||||
</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ keyOff }}" style="display: grid; gap: 22px;">
|
||||
<button type="button" onClick="{{ goKeys }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">← Все подключения</button>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px 20px;">
|
||||
<h1 style="margin: 0; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">{{ cur.title }}</h1>
|
||||
<span style="font-size: 15px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ cur.ip }}</span>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;">
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Действует до</div>
|
||||
<div style="font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">{{ cur.until }}</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; color: {{ cur.leftInk }};">{{ cur.left }}</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Оплата</div>
|
||||
<div style="font-size: 18px; font-variant-numeric: tabular-nums;">{{ cur.card }}</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; color: var(--ink-2);">699 ₽ в месяц · карта аккаунта</div>
|
||||
<button type="button" onClick="{{ goBilling }}" style="margin-top: 14px; height: 40px; padding: 0 16px; border-radius: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="border-color: var(--ink-3);">Счёт и платежи</button>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Устройства</div>
|
||||
<div style="font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">{{ cur.devices }}</div>
|
||||
<div style="margin-top: 8px; font-size: 14px; color: var(--ink-2);">Каждое в своей стране</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 6px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Конфигурации XRAY</h2>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">5 конфигураций — по одной на устройство</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 18px; font-size: 14px; color: var(--ink-2); font-weight: 300; max-width: 60ch;">На компьютере скачайте файл и откройте его в клиенте XRAY. На телефоне — отсканируйте QR-код.</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px;">
|
||||
<sc-for list="{{ configs }}" as="cfg" hint-placeholder-count="5">
|
||||
<div style="display: grid; gap: 12px; padding: 16px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 15px; font-weight: 500;">{{ cfg.name }}</span>
|
||||
<span style="font-size: 12px; color: var(--ink-3);">{{ cfg.state }}</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button type="button" style="flex: 1; height: 38px; border-radius: 10px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="background: var(--accent-hover);">Скачать</button>
|
||||
<button type="button" onClick="{{ cfg.showQr }}" style="height: 38px; padding: 0 14px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="border-color: var(--ink-3);">QR</button>
|
||||
</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 6px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Ссылка подключения</h2>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Для Happ и других клиентов XRAY</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 18px; font-size: 14px; color: var(--ink-2); font-weight: 300; max-width: 60ch;">Быстрый способ подключить новое устройство. Скопируйте ссылку или отсканируйте код — настройки те же.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px; align-items: center;">
|
||||
<div style="flex: 1; min-width: 240px; padding: 14px 16px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border); font-size: 13px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums;">{{ cur.xray }}</div>
|
||||
<button type="button" onClick="{{ copyXray }}" style="height: 46px; padding: 0 20px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ copyLabel }}</button>
|
||||
<button type="button" onClick="{{ showXrayQr }}" style="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;" style-hover="border-color: var(--ink-3);">QR</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ buyOff }}" style="display: grid; gap: 22px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Выбрать страну</h1>
|
||||
<p style="margin: 0; font-size: 15px; color: var(--ink-2); font-weight: 300;">Все тридцать локаций входят в подписку. Переключение бесплатное и занимает пару секунд.</p>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px;">
|
||||
<sc-for list="{{ locations }}" as="loc" hint-placeholder-count="12">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: grid; align-content: start; gap: 14px; padding: 20px; border-radius: 18px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
{{ loc.flag }}
|
||||
<span style="display: flex; align-items: center; gap: 12px; flex: none; min-height: 32px;">
|
||||
<span hidden="{{ loc.buyOff }}" style="display: flex; align-items: center; justify-content: center; height: 32px; padding: 0 16px; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 14px; font-weight: 500; cursor: pointer;">Выбрать</span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span style="font-size: 17px; font-weight: 500; letter-spacing: -0.02em;">{{ loc.country }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
<div style="padding: 24px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<h2 id="lead" style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em; scroll-margin-top: 24px;">Нужны особые условия</h2>
|
||||
<p style="margin: 0 0 18px; max-width: 60ch; font-size: 14px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Подписки на команду, оплата по счёту или редкая страна. Оставьте заявку — ответим в течение дня.</p>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: inline-flex; align-items: center; height: 46px; padding: 0 20px; border-radius: 12px; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-soft);">Оставить заявку</button>
|
||||
<div hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 460px;">
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ billingOff }}" style="display: grid; gap: 22px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Счёт и платежи</h1>
|
||||
<p style="margin: 0; max-width: 62ch; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Карта привязана к аккаунту. Подписка списывается с неё — или с баланса, если на нём есть деньги.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px;">
|
||||
<div style="display: grid; align-content: start; gap: 8px; padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Баланс</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">1 250 ₽</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2);">Списывается в первую очередь</div>
|
||||
<button type="button" onClick="{{ openTopup }}" style="justify-self: start; margin-top: 6px; height: 40px; padding: 0 16px; border-radius: 10px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 14px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Пополнить</button>
|
||||
</div>
|
||||
<div style="display: grid; align-content: start; gap: 8px; padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Способ оплаты</div>
|
||||
<div style="font-size: 20px; font-weight: 500; font-variant-numeric: tabular-nums;">Карта ···· 4242</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2);">Основная для аккаунта</div>
|
||||
<button type="button" style="justify-self: start; margin-top: 6px; height: 40px; padding: 0 16px; border-radius: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="border-color: var(--ink-3);">Изменить карту</button>
|
||||
</div>
|
||||
<div style="display: grid; align-content: start; gap: 8px; padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Следующее списание</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">699 ₽</div>
|
||||
<div style="font-size: 14px; color: var(--ink-2);">14 октября · подписка на месяц</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Год выгоднее: 6 990 ₽ за 12 месяцев</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ topupOff }}" style="display: grid; gap: 16px; padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 12px;">
|
||||
<h2 style="margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Пополнить баланс</h2>
|
||||
<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: grid; gap: 8px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Сумма</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<sc-for list="{{ sums }}" as="sum" hint-placeholder-count="4">
|
||||
<button type="button" onClick="{{ sum.pick }}" style="height: 44px; padding: 0 18px; 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>
|
||||
<div style="display: grid; gap: 8px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">Способ</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<sc-for list="{{ methods }}" as="m" hint-placeholder-count="3">
|
||||
<button type="button" onClick="{{ m.pick }}" style="height: 44px; padding: 0 18px; border-radius: 12px; border: 1px solid {{ m.border }}; background: {{ m.bg }}; color: var(--ink); font-family: inherit; font-size: 15px; cursor: pointer;">{{ m.label }}</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
<div hidden="{{ cryptoOff }}" style="display: grid; gap: 10px; padding: 18px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 15px; font-weight: 500;">USDT (TRC-20), BTC, ETH</div>
|
||||
<p style="margin: 0; max-width: 60ch; font-size: 14px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Покажем адрес и сумму в выбранной монете. Курс фиксируется на 15 минут, баланс пополнится после одного подтверждения сети.</p>
|
||||
</div>
|
||||
<button type="button" onClick="{{ sendTopup }}" style="justify-self: start; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ topupButton }}</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<h2 style="margin: 0 0 4px; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">История платежей</h2>
|
||||
<sc-for list="{{ payments }}" as="p" hint-placeholder-count="5">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; padding: 18px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="width: 116px; flex: none; font-size: 14px; color: var(--ink-3); font-variant-numeric: tabular-nums;">{{ p.date }}</span>
|
||||
<span style="flex: 1; min-width: 180px; display: grid; gap: 3px;">
|
||||
<span style="font-size: 15px;">{{ p.title }}</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">{{ p.method }}</span>
|
||||
</span>
|
||||
<span style="font-size: 13px; color: var(--ink-3);">{{ p.status }}</span>
|
||||
<span style="width: 104px; flex: none; text-align: right; font-size: 16px; font-weight: 500; color: {{ p.ink }}; font-variant-numeric: tabular-nums;">{{ p.amount }}</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ refOff }}" style="display: grid; gap: 20px;">
|
||||
<div>
|
||||
<h1 style="margin: 0 0 6px; font-size: 30px; font-weight: 600; letter-spacing: -0.03em;">Реферальная программа</h1>
|
||||
<p style="margin: 0; max-width: 62ch; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Приведите друга — вы оба получите по месяцу в подарок.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;">
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Приглашено</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">3</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Месяцев начислено</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">2</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 18px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Ожидает подтверждения</div>
|
||||
<div style="font-size: 30px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">1</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 14px; padding: 26px; border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 16px; font-weight: 500;">Ваша ссылка</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px; align-items: center;">
|
||||
<div style="flex: 1; min-width: 220px; padding: 14px 16px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border); font-size: 14px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">veilix.com/r/anna-7f2c</div>
|
||||
<button type="button" onClick="{{ copyRef }}" style="height: 46px; padding: 0 20px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ refLabel }}</button>
|
||||
</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3); line-height: 1.5;">Месяц начисляется вам и другу после его первой оплаты и применяется к ближайшему списанию.</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<div style="font-size: 16px; font-weight: 500;">Приглашённые</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">m···@gmail.com</span>
|
||||
<span style="font-size: 14px; color: var(--accent);">Месяц начислен</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">d···@yandex.ru</span>
|
||||
<span style="font-size: 14px; color: var(--accent);">Месяц начислен</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-radius: 16px; background: var(--surface); border: 1px solid var(--border);">
|
||||
<span style="font-size: 15px;">k···@icloud.com</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">Ждём оплаты</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ moreOff }}" style="display: grid; gap: 16px;">
|
||||
<h1 style="margin: 0; font-size: 26px; font-weight: 600; letter-spacing: -0.03em;">Ещё</h1>
|
||||
<button type="button" onClick="{{ goBilling }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; font-size: 16px; text-align: left; cursor: pointer;">
|
||||
<span>Счёт и платежи</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ goRef }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-family: inherit; font-size: 16px; text-align: left; cursor: pointer;">
|
||||
<span>Реферальная программа</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</button>
|
||||
<a href="MassSupport.dc.html" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface); color: var(--ink); font-size: 16px;">
|
||||
<span>Поддержка</span>
|
||||
<span style="color: var(--ink-3);">›</span>
|
||||
</a>
|
||||
<div style="display: grid; gap: 12px; padding: 14px 18px; border-radius: 16px; border: 1px solid var(--border); background: var(--surface);">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
||||
<div style="font-size: 15px;">Тема</div>
|
||||
<div style="display: flex; padding: 3px; border-radius: 11px; border: 1px solid var(--border); flex: none;">
|
||||
<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>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 34px; border: 0; border-radius: 8px; background: {{ systemBg }}; color: var(--ink); cursor: pointer;">
|
||||
<span style="width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 12px; border-top: 1px solid var(--border);">
|
||||
<div style="font-size: 15px;">Язык</div>
|
||||
<div style="display: flex; padding: 3px; border-radius: 11px; border: 1px solid var(--border); flex: none;">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" onClick="{{ signOut }}" style="justify-self: start; min-height: 48px; padding: 0 16px; border-radius: 12px; border: 1px solid var(--border); background: transparent; color: var(--ink-2); font-family: inherit; font-size: 15px; cursor: pointer;">Выйти · anna@example.com</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ pillOff }}" style="position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 50; display: flex; gap: 4px; 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="{{ goHome }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; border: 0; border-radius: 999px; background: {{ tabHomeBg }}; color: {{ tabHomeInk }}; 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: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; top: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; left: 0; bottom: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; opacity: 0.45; display: block;"></span>
|
||||
<span style="position: absolute; right: 0; bottom: 0; width: 9px; height: 9px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</span>
|
||||
Главная
|
||||
</button>
|
||||
<button type="button" onClick="{{ goKeys }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; 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="{{ goBuy }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; 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="{{ goMore }}" style="display: grid; justify-items: center; gap: 4px; min-width: 76px; min-height: 52px; padding: 8px 12px; 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: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 24px; background: color-mix(in oklab, var(--ink) 45%, transparent); backdrop-filter: blur(6px);">
|
||||
<div style="display: grid; gap: 16px; justify-items: center; padding: 28px; border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 16px; font-weight: 500;">{{ qrTitle }}</div>
|
||||
{{ qrImage }}
|
||||
<div style="max-width: 260px; text-align: center; font-size: 13px; line-height: 1.5; color: var(--ink-3);">Откройте приложение на телефоне и наведите камеру на код.</div>
|
||||
</div>
|
||||
</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: "home", key: 0, faq: 0, loc: 0, lead: false, leadSent: false, qr: null, copied: false, signedIn: true, otp: false, lang: "ru", narrow: false, refCopied: false, topup: false, sum: 1, method: 0, topupSent: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, view: this.state.view === "more" && !n ? "home" : this.state.view });
|
||||
if (!n && this.state.view === "more") this.setState({ view: "home" });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
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;
|
||||
const d = Math.max(Math.abs(rr - 3), Math.abs(cc - 3));
|
||||
on = d !== 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: "220px",
|
||||
height: "220px",
|
||||
padding: "12px",
|
||||
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 keyData = [
|
||||
{ title: "Нидерланды · Амстердам", ip: "текущая локация", until: s.lang === "en" ? "14 October 2026" : "14 октября 2026", badge: "Активна", warn: false, devices: "3 из 5 устройств", payment: "Карта ···· 4242", card: "···· 4242", left: "Продлится автоматически через 31 день", xray: "vless://8f2a…@nl1.veilixvpn.net:443?type=xhttp#VeilixVPN-NL" },
|
||||
{ title: "США · Нью-Йорк", ip: "запасная локация", until: s.lang === "en" ? "14 October 2026" : "14 октября 2026", badge: "Готова", warn: false, devices: "1 из 5 устройств", payment: "Карта ···· 4242", card: "···· 4242", left: "Переключиться можно в любой момент", xray: "vless://c41d…@us1.veilixvpn.net:443?type=xhttp#VeilixVPN-US" }
|
||||
];
|
||||
const cur = keyData[s.key];
|
||||
const mk = (i) => ({
|
||||
title: keyData[i].title,
|
||||
ip: keyData[i].ip,
|
||||
until: keyData[i].until,
|
||||
untilLine: (s.lang === "en" ? "until " : "до ") + keyData[i].until,
|
||||
badge: keyData[i].badge,
|
||||
devices: keyData[i].devices,
|
||||
payment: keyData[i].payment,
|
||||
badgeBg: keyData[i].warn ? "color-mix(in oklab, var(--warn) 18%, transparent)" : sel,
|
||||
badgeInk: keyData[i].warn ? "var(--warn)" : "var(--accent)",
|
||||
open: () => this.setState({ view: "key", key: i })
|
||||
});
|
||||
const faqData = [
|
||||
["Как подключить новое устройство?", "Откройте подключение, скачайте любую свободную конфигурацию XRAY или отсканируйте её QR-код. Каждая конфигурация рассчитана на одно устройство."],
|
||||
["Что делать, если подключение перестало работать?", "Обновите конфигурацию в кабинете или смените локацию — это занимает пару секунд. Если не помогло, напишите в поддержку, ответим в течение часа."],
|
||||
["Как отменить продление?", "В разделе оплаты нажмите «Изменить карту» и отвяжите её. Подписка доработает оплаченный период."]
|
||||
];
|
||||
const locData = [
|
||||
["Нидерланды", "Амстердам", true],
|
||||
["Германия", "Франкфурт", true],
|
||||
["США", "Нью-Йорк", true],
|
||||
["Финляндия", "Хельсинки", false],
|
||||
["Турция", "Стамбул", false],
|
||||
["Япония", "Токио", false]
|
||||
];
|
||||
const cfgNames = [
|
||||
["Ноутбук", "подключено"],
|
||||
["Телефон", "подключено"],
|
||||
["Планшет", "подключено"],
|
||||
["Рабочий ноутбук", "не занят"],
|
||||
["Свободный слот", "не занят"]
|
||||
];
|
||||
|
||||
return {
|
||||
appOff: s.signedIn,
|
||||
loginOff: !s.signedIn,
|
||||
otpSent: s.otp,
|
||||
otpNotSent: !s.otp,
|
||||
sendOtp: () => this.setState({ otp: true }),
|
||||
signIn: () => this.setState({ signedIn: true, otp: false, view: "home" }),
|
||||
backToEmail: () => this.setState({ otp: false }),
|
||||
signOut: () => this.setState({ signedIn: false, otp: false }),
|
||||
|
||||
goHome: () => this.setState({ view: "home" }),
|
||||
goKeys: () => this.setState({ view: "keys" }),
|
||||
goBuy: () => this.setState({ view: "buy" }),
|
||||
goMore: () => this.setState({ view: "more" }),
|
||||
goBilling: () => this.setState({ view: "billing" }),
|
||||
billingOff: s.view !== "billing",
|
||||
billingBg: s.view === "billing" ? sel : "transparent",
|
||||
billingInk: 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: ["Карта ···· 4242", "СБП", "Криптовалюта"].map((label, i) => ({
|
||||
label: label,
|
||||
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: [
|
||||
{ date: "14 сентября", title: "Подписка на месяц", method: "Карта ···· 4242", status: "Оплачено", amount: "−699 ₽", ink: "var(--ink)" },
|
||||
{ date: "6 сентября", title: "Пополнение баланса", method: "USDT (TRC-20)", status: "Зачислено", amount: "+2 100 ₽", ink: "var(--accent)" },
|
||||
{ date: "14 августа", title: "Подписка на месяц", method: "С баланса", status: "Оплачено", amount: "−699 ₽", ink: "var(--ink)" },
|
||||
{ date: "14 июля", title: "Подписка на месяц", method: "Карта ···· 4242", status: "Оплачено", amount: "−699 ₽", ink: "var(--ink)" },
|
||||
{ date: "2 июля", title: "Реферальный бонус", method: "Месяц в подарок", status: "Начислено", amount: "+699 ₽", ink: "var(--accent)" }
|
||||
],
|
||||
goRef: () => this.setState({ view: "ref" }),
|
||||
refOff: s.view !== "ref",
|
||||
refBg: s.view === "ref" ? sel : "transparent",
|
||||
refInk: s.view === "ref" ? "var(--accent)" : "var(--ink-2)",
|
||||
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)",
|
||||
homeOff: s.view !== "home",
|
||||
keysOff: s.view !== "keys",
|
||||
keyOff: s.view !== "key",
|
||||
buyOff: s.view !== "buy",
|
||||
moreOff: s.view !== "more",
|
||||
shellCols: s.narrow ? "minmax(0, 1fr)" : "248px minmax(0, 1fr)",
|
||||
mainPad: s.narrow ? "20px 16px 110px" : "36px 40px 64px",
|
||||
asideOff: s.narrow,
|
||||
topbarOff: !s.narrow,
|
||||
pillOff: !s.narrow,
|
||||
tabHomeBg: s.view === "home" ? sel : "transparent",
|
||||
tabKeysBg: (s.view === "keys" || s.view === "key") ? sel : "transparent",
|
||||
tabBuyBg: s.view === "buy" ? sel : "transparent",
|
||||
tabMoreBg: s.view === "more" ? sel : "transparent",
|
||||
tabHomeInk: s.view === "home" ? "var(--accent)" : "var(--ink-2)",
|
||||
tabKeysInk: (s.view === "keys" || s.view === "key") ? "var(--accent)" : "var(--ink-2)",
|
||||
tabBuyInk: s.view === "buy" ? "var(--accent)" : "var(--ink-2)",
|
||||
tabMoreInk: s.view === "more" ? "var(--accent)" : "var(--ink-2)",
|
||||
homeBg: s.view === "home" ? sel : "transparent",
|
||||
keysBg: (s.view === "keys" || s.view === "key") ? sel : "transparent",
|
||||
buyBg: s.view === "buy" ? sel : "transparent",
|
||||
homeInk: s.view === "home" ? "var(--accent)" : "var(--ink-2)",
|
||||
keysInk: (s.view === "keys" || s.view === "key") ? "var(--accent)" : "var(--ink-2)",
|
||||
buyInk: s.view === "buy" ? "var(--accent)" : "var(--ink-2)",
|
||||
|
||||
keys: [mk(0), mk(1)],
|
||||
allKeys: [mk(0), mk(1)],
|
||||
cur: {
|
||||
title: cur.title,
|
||||
ip: cur.ip,
|
||||
until: cur.until,
|
||||
card: cur.card,
|
||||
devices: cur.devices,
|
||||
left: cur.left,
|
||||
leftInk: cur.warn ? "var(--warn)" : "var(--ink-2)",
|
||||
xray: cur.xray
|
||||
},
|
||||
configs: cfgNames.map((c, i) => ({
|
||||
name: c[0],
|
||||
state: c[1],
|
||||
showQr: () => this.setState({ qr: { title: "XRAY · " + c[0], seed: cur.ip + c[0] } })
|
||||
})),
|
||||
refStats: s.lang === "en" ? "Invited: 3 · Months earned: 2" : "Приглашено: 3 · Месяцев начислено: 2",
|
||||
copyRef: () => this.setState({ refCopied: true }),
|
||||
refLabel: s.refCopied ? "Ссылка скопирована" : "Копировать ссылку",
|
||||
copyXray: () => this.setState({ copied: true }),
|
||||
copyLabel: s.copied ? "Скопировано" : "Копировать",
|
||||
showXrayQr: () => this.setState({ qr: { title: "XRAY · Happ", seed: cur.xray } }),
|
||||
qrOff: !s.qr,
|
||||
qrTitle: s.qr ? s.qr.title : "",
|
||||
qrImage: s.qr ? this.qrImage(s.qr.seed) : null,
|
||||
closeQr: () => this.setState({ qr: null }),
|
||||
|
||||
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 }))
|
||||
})),
|
||||
|
||||
locations: this.data().map((d, i) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
status: d.ready ? "Доступно в подписке" : "Скоро",
|
||||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||||
bg: s.loc === i ? sel : "var(--surface)",
|
||||
border: s.loc === i ? "var(--accent)" : "var(--border)",
|
||||
dot: s.loc === i ? "var(--accent)" : "transparent",
|
||||
dotOff: s.loc === i,
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
buyOff: s.loc !== i,
|
||||
pick: () => this.setState({ loc: i })
|
||||
})),
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: s.lead,
|
||||
leadClosed: !s.lead,
|
||||
leadButton: s.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
pickedTitle: locData[s.loc][0] + " · " + locData[s.loc][1],
|
||||
pickedHint: "Локация доступна сразу, переключение бесплатное",
|
||||
|
||||
toggleLang: () => this.setLang(s.lang === "ru" ? "en" : "ru"),
|
||||
langTitle: s.lang === "ru" ? "Язык: русский" : "Язык: английский",
|
||||
langLabel: s.lang === "ru" ? "RU" : "EN",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,806 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,602 @@
|
||||
<!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>
|
||||
<script src="https://unpkg.com/d3@7.9.0/dist/d3.min.js" integrity="sha384-CjloA8y00+1SDAUkjs099PVfnY2KmDC2BZnws9kh8D/lX1s46w6EPhpXdqMfjK6i" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/topojson-client@3.1.0/dist/topojson-client.min.js" integrity="sha384-Ukv1p/xTma6P4/2bY5KzWBw+ydSpXmhCMtyciIQVDJ1RmOxtCYNMF1uXT9T63H67" crossorigin="anonymous"></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-hover: oklch(41% 0.1 178);
|
||||
--accent-soft: oklch(95.5% 0.032 180);
|
||||
--sel: oklch(96.5% 0.016 190);
|
||||
--on-accent: oklch(99% 0.01 180);
|
||||
--glow-1: oklch(86% 0.085 190);
|
||||
--glow-2: oklch(88% 0.075 205);
|
||||
--glow-3: oklch(89% 0.07 172);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--land: oklch(95% 0.012 196);
|
||||
--land-line: oklch(90% 0.018 196);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 204 / 0.05), 0 12px 32px -12px oklch(24% 0.04 204 / 0.14);
|
||||
}
|
||||
: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-hover: oklch(83% 0.09 190);
|
||||
--accent-soft: oklch(31% 0.05 190);
|
||||
--sel: oklch(30% 0.03 195);
|
||||
--on-accent: oklch(20% 0.04 204);
|
||||
--glow-1: oklch(52% 0.09 200);
|
||||
--glow-2: oklch(47% 0.085 215);
|
||||
--glow-3: oklch(50% 0.08 180);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--land: oklch(31% 0.035 204);
|
||||
--land-line: oklch(37% 0.04 204);
|
||||
--shadow: 0 1px 2px oklch(12% 0.03 204 / 0.4), 0 16px 40px -16px oklch(9% 0.03 204 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
@keyframes veilPulse{
|
||||
0%{ transform: scale(1); opacity: 0.55; }
|
||||
70%{ transform: scale(2.6); opacity: 0; }
|
||||
100%{ transform: scale(2.6); opacity: 0; }
|
||||
}
|
||||
</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="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
<a href="MassMain.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 132px; flex: none; white-space: nowrap;">ВейликсВПН</span>
|
||||
</a>
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="#top" style="color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="MassApp.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="MassOrder.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="MassPrice.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="#top" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="MassApp.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="MassOrder.dc.html" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(14px, 4vw, 24px) 80px;">
|
||||
|
||||
<section style="position: relative; isolation: isolate; margin-top: clamp(14px, 3vw, 40px); border-radius: 28px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; inset: 0; border-radius: 28px; overflow: hidden; pointer-events: none; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black);">
|
||||
<div style="position: absolute; right: min(-90px, calc(-150px + 11vw)); top: min(-130px, calc(-190px + 11vw)); width: clamp(190px, 40vw, 440px); height: clamp(190px, 40vw, 440px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-1) 0%, color-mix(in oklab, var(--glow-1) 35%, transparent) 55%, transparent 78%); filter: blur(80px); opacity: 0.45;"></div>
|
||||
<div style="position: absolute; left: min(-70px, calc(-130px + 11vw)); bottom: min(-150px, calc(-210px + 11vw)); width: clamp(160px, 34vw, 380px); height: clamp(160px, 34vw, 380px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-3) 0%, color-mix(in oklab, var(--glow-3) 35%, transparent) 55%, transparent 78%); filter: blur(80px); opacity: 0.35;"></div>
|
||||
</div>
|
||||
<div style="position: relative; padding: clamp(22px, 5vw, 48px) clamp(14px, 3.5vw, 40px) clamp(20px, 4vw, 36px);">
|
||||
<h1 style="margin: 0 0 12px; font-size: clamp(34px, 4.4vw, 52px); font-weight: 600; letter-spacing: -0.035em;">Локации</h1>
|
||||
<p style="margin: 0 0 28px; max-width: 46em; font-size: 17px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">30 стран и российский адрес входят в подписку.</p>
|
||||
<div id="veilix-map" style="width: 100%; min-height: 240px;"></div>
|
||||
<div style="margin-top: 10px; font-size: 13px; color: var(--ink-3);">Нажмите точку на карте — покажем страну и город</div>
|
||||
<div hidden="{{ swipeOff }}" style="margin-top: 4px; font-size: 13px; color: var(--ink-3);">Карту можно прокрутить в сторону</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px 24px; margin-top: 20px; font-size: 14px; color: var(--ink-2);">
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
||||
<span style="width: 9px; height: 9px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
Доступно в подписке
|
||||
</span>
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
||||
<span style="width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--ink-3); display: block;"></span>
|
||||
Скоро
|
||||
</span>
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
||||
<span style="width: 9px; height: 9px; border-radius: 2px; background: var(--ink-2); display: block;"></span>
|
||||
RU-адрес входит в подписку
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding: 56px 0 0;">
|
||||
<h2 style="margin: 0 0 10px; font-size: clamp(26px, 2.8vw, 34px); font-weight: 600; letter-spacing: -0.03em;">Все страны подписки</h2>
|
||||
<p style="margin: 0 0 24px; max-width: 52em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Одна цена за всё: 699 ₽ в месяц или 6 990 ₽ в год.</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;">
|
||||
<sc-for list="{{ items }}" as="loc" hint-placeholder-count="12">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: grid; align-content: start; gap: 14px; padding: 22px; border-radius: 22px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
{{ loc.flag }}
|
||||
<span style="display: flex; align-items: center; gap: 8px; flex: none; min-height: 32px; font-size: 13px; color: {{ loc.statusInk }};">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: {{ loc.statusDot }}; border: {{ loc.statusRing }}; display: block;"></span>
|
||||
{{ loc.badge }}
|
||||
</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span style="font-size: 19px; font-weight: 500; letter-spacing: -0.02em;">{{ loc.country }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding: 56px 0 0;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div style="padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em;">Российский адрес в подписке</h3>
|
||||
<p style="margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Он работает в обе стороны: из России банки и госуслуги видят обычное местное подключение, из другой страны те же сервисы открываются так, будто вы дома.</p>
|
||||
</div>
|
||||
<div style="padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 id="lead" style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em; scroll-margin-top: 90px;">Нужны особые условия</h3>
|
||||
<p style="margin: 0 0 18px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Нужной страны нет в списке или нужна подписка на команду — напишите, ответим в течение дня.</p>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: inline-flex; align-items: center; height: 46px; padding: 0 20px; border-radius: 12px; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-soft);">Оставить заявку</button>
|
||||
<div hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 460px;">
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">VPN, который не нужно отключать. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 ВейликсВПН</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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, lang: "ru", langMenu: false, narrow: false, menu: false, loc: 0, lead: false, leadSent: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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();
|
||||
});
|
||||
this.drawMap();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
applyLang() {
|
||||
const lang = this.state.lang || "ru";
|
||||
if (window.VEILIX_APPLY_LANG) window.VEILIX_APPLY_LANG(document.body, lang);
|
||||
else setTimeout(() => this.applyLang(), 150);
|
||||
}
|
||||
|
||||
setTheme(t) {
|
||||
try { localStorage.setItem("veilix.theme", t); } catch (e) {}
|
||||
this.setState({ theme: t }, () => this.apply());
|
||||
}
|
||||
|
||||
setLang(l) {
|
||||
try { localStorage.setItem("veilix.lang", l); } catch (e) {}
|
||||
this.setState({ lang: l, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
async drawMap() {
|
||||
const host = document.getElementById("veilix-map");
|
||||
if (!host) return;
|
||||
if (!window.d3 || !window.topojson) { setTimeout(() => this.drawMap(), 200); return; }
|
||||
const d3 = window.d3;
|
||||
let topo;
|
||||
try {
|
||||
topo = await d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json");
|
||||
} catch (e) { return; }
|
||||
const land = window.topojson.feature(topo, topo.objects.countries);
|
||||
const W = 1200, H = 520;
|
||||
const projection = d3.geoNaturalEarth1().fitExtent([[10, 10], [W - 10, H - 10]], land);
|
||||
const path = d3.geoPath(projection);
|
||||
|
||||
host.innerHTML = "";
|
||||
const center = () => {
|
||||
const extra = host.scrollWidth - host.clientWidth;
|
||||
if (extra > 0) host.scrollLeft = extra / 2;
|
||||
};
|
||||
this.centerMap = center;
|
||||
if (!this.mapCentering) {
|
||||
this.mapCentering = true;
|
||||
window.addEventListener("resize", () => this.centerMap && this.centerMap());
|
||||
}
|
||||
host.style.overflowX = "auto";
|
||||
host.style.overflowY = "hidden";
|
||||
host.style.borderRadius = "18px";
|
||||
const svg = d3.select(host).append("svg")
|
||||
.attr("viewBox", "0 0 " + W + " " + H)
|
||||
.attr("width", "100%")
|
||||
.style("display", "block")
|
||||
.style("min-width", "820px")
|
||||
.style("overflow", "visible");
|
||||
|
||||
svg.append("g").selectAll("path").data(land.features).join("path")
|
||||
.attr("d", path)
|
||||
.attr("fill", "var(--land)")
|
||||
.attr("stroke", "var(--land-line)")
|
||||
.attr("stroke-width", 0.8);
|
||||
|
||||
const ru = projection([37.62, 55.75]);
|
||||
if (ru) {
|
||||
svg.append("rect")
|
||||
.attr("x", ru[0] - 5).attr("y", ru[1] - 5)
|
||||
.attr("width", 10).attr("height", 10).attr("rx", 2.5)
|
||||
.attr("fill", "var(--ink-2)");
|
||||
svg.append("text")
|
||||
.attr("x", ru[0] + 12).attr("y", ru[1] + 4)
|
||||
.attr("fill", "var(--ink-2)")
|
||||
.attr("font-size", 15)
|
||||
.attr("font-family", "Commissioner, system-ui, sans-serif")
|
||||
.text("RU");
|
||||
}
|
||||
|
||||
const FONT = "Commissioner, system-ui, sans-serif";
|
||||
const closePopup = () => { if (this.popupG) { this.popupG.remove(); this.popupG = null; } };
|
||||
this.closePopup = closePopup;
|
||||
svg.on("click", closePopup);
|
||||
|
||||
const popup = (d, p) => {
|
||||
closePopup();
|
||||
const pw = 268, ph = 106;
|
||||
const x = Math.max(8, Math.min(W - pw - 8, p[0] - pw / 2));
|
||||
let y = p[1] - ph - 16;
|
||||
if (y < 8) y = Math.min(H - ph - 8, p[1] + 18);
|
||||
const g = svg.append("g").attr("transform", "translate(" + x + "," + y + ")");
|
||||
this.popupG = g;
|
||||
g.on("click", (ev) => ev.stopPropagation());
|
||||
g.append("rect").attr("width", pw).attr("height", ph).attr("rx", 16)
|
||||
.attr("fill", "var(--surface)").attr("stroke", "var(--border)");
|
||||
g.append("text").attr("x", 20).attr("y", 33).attr("fill", "var(--ink)")
|
||||
.attr("font-size", 17).attr("font-weight", 500).attr("font-family", FONT).text(d.country);
|
||||
g.append("text").attr("x", 20).attr("y", 55).attr("fill", "var(--ink-3)")
|
||||
.attr("font-size", 13).attr("font-family", FONT).text(d.city);
|
||||
const statusG = g.append("g").attr("transform", "translate(20, 78)");
|
||||
statusG.append("circle").attr("cx", 5).attr("cy", -4).attr("r", 4)
|
||||
.attr("fill", d.ready ? "var(--accent)" : "var(--bg)")
|
||||
.attr("stroke", d.ready ? "none" : "var(--ink-3)").attr("stroke-width", d.ready ? 0 : 1.5);
|
||||
statusG.append("text").attr("x", 17).attr("font-size", 13).attr("font-family", FONT)
|
||||
.attr("fill", d.ready ? "var(--accent)" : "var(--ink-3)")
|
||||
.text(d.ready ? "Доступно в подписке" : "Скоро");
|
||||
const close = g.append("g").style("cursor", "pointer").on("click", (ev) => { ev.stopPropagation(); closePopup(); });
|
||||
close.append("rect").attr("x", pw - 44).attr("y", 14).attr("width", 30).attr("height", 30).attr("rx", 9).attr("fill", "transparent");
|
||||
close.append("text").attr("x", pw - 29).attr("y", 35).attr("text-anchor", "middle")
|
||||
.attr("fill", "var(--ink-3)").attr("font-size", 18).attr("font-family", FONT).text("×");
|
||||
this.applyLang();
|
||||
};
|
||||
|
||||
this.data().forEach((d, i) => {
|
||||
const p = projection([d.lng, d.lat]);
|
||||
if (!p) return;
|
||||
const g = svg.append("g").attr("transform", "translate(" + p[0] + "," + p[1] + ")").style("cursor", "pointer");
|
||||
g.append("circle").attr("r", 12).attr("fill", "transparent");
|
||||
if (d.ready) {
|
||||
g.append("circle").attr("r", 6).attr("fill", "var(--accent)").attr("opacity", 0.32)
|
||||
.style("transform-box", "fill-box")
|
||||
.style("transform-origin", "center")
|
||||
.style("animation", "veilPulse 3.2s ease-out infinite");
|
||||
g.append("circle").attr("r", 5).attr("fill", "var(--accent)")
|
||||
.attr("stroke", "var(--surface)").attr("stroke-width", 1.4);
|
||||
} else {
|
||||
g.append("circle").attr("r", 5).attr("fill", "var(--bg)")
|
||||
.attr("stroke", "var(--ink-3)").attr("stroke-width", 2);
|
||||
}
|
||||
g.append("title").text(d.country + " · " + d.city);
|
||||
g.on("click", (ev) => { ev.stopPropagation(); this.setState({ loc: i }); popup(d, p); });
|
||||
});
|
||||
requestAnimationFrame(center);
|
||||
setTimeout(center, 120);
|
||||
}
|
||||
|
||||
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: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
const sel = "var(--sel)";
|
||||
const theme = s.theme || this.props.defaultTheme || "light";
|
||||
const rows = this.data();
|
||||
const cur = rows[s.loc];
|
||||
return {
|
||||
items: rows.map((d, i) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
status: d.ready ? "Доступно в подписке" : "Скоро",
|
||||
badge: d.ready ? "Включено" : "Скоро",
|
||||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||||
statusDot: d.ready ? "var(--accent)" : "transparent",
|
||||
statusRing: d.ready ? "0" : "1.5px solid var(--ink-3)",
|
||||
bg: s.loc === i ? sel : "var(--surface)",
|
||||
border: s.loc === i ? "var(--accent)" : "var(--border)",
|
||||
dot: s.loc === i ? "var(--accent)" : "transparent",
|
||||
dotOff: s.loc === i,
|
||||
buy: (e) => { e.stopPropagation(); window.location.href = "MassOrder.dc.html"; },
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
buyOff: s.loc !== i,
|
||||
pick: () => this.setState({ loc: i })
|
||||
})),
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: this.state.lead,
|
||||
leadClosed: !this.state.lead,
|
||||
leadButton: this.state.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
swipeOff: !s.narrow,
|
||||
navOff: s.narrow,
|
||||
burgerOff: !s.narrow,
|
||||
menuOff: !(s.narrow && s.menu),
|
||||
toggleMenu: () => this.setState(st => ({ menu: !st.menu, langMenu: false })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
toggleLangMenu: () => this.setState(st => ({ langMenu: !st.langMenu })),
|
||||
langMenuOff: !s.langMenu,
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
langLabel: s.lang === "en" ? "EN" : "RU",
|
||||
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)",
|
||||
ruCheck: s.lang === "en" ? "" : "✓",
|
||||
enCheck: s.lang === "en" ? "✓" : "",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,776 @@
|
||||
<!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 src="https://unpkg.com/d3@7.9.0/dist/d3.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/topojson-client@3.1.0/dist/topojson-client.min.js" crossorigin="anonymous"></script>
|
||||
<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-hover: oklch(41% 0.1 178);
|
||||
--accent-soft: oklch(95.5% 0.032 180);
|
||||
--sel: oklch(96.5% 0.016 190);
|
||||
--on-accent: oklch(99% 0.01 180);
|
||||
--glow-1: oklch(86% 0.085 190);
|
||||
--glow-2: oklch(88% 0.075 205);
|
||||
--glow-3: oklch(89% 0.07 172);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--land: oklch(95% 0.012 196);
|
||||
--land-line: oklch(90% 0.018 196);
|
||||
--ocean: oklch(97.5% 0.007 196);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 204 / 0.05), 0 12px 32px -12px oklch(24% 0.04 204 / 0.14);
|
||||
}
|
||||
: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-hover: oklch(83% 0.09 190);
|
||||
--accent-soft: oklch(31% 0.05 190);
|
||||
--sel: oklch(30% 0.03 195);
|
||||
--on-accent: oklch(20% 0.04 204);
|
||||
--glow-1: oklch(52% 0.09 200);
|
||||
--glow-2: oklch(47% 0.085 215);
|
||||
--glow-3: oklch(50% 0.08 180);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--land: oklch(31% 0.035 204);
|
||||
--land-line: oklch(37% 0.04 204);
|
||||
--ocean: oklch(27% 0.035 204);
|
||||
--shadow: 0 1px 2px oklch(12% 0.03 204 / 0.4), 0 16px 40px -16px oklch(9% 0.03 204 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
summary{ list-style: none; }
|
||||
@keyframes veilPulse{
|
||||
0%{ transform: scale(1); opacity: 0.32; }
|
||||
70%{ transform: scale(2.6); opacity: 0; }
|
||||
100%{ transform: scale(2.6); opacity: 0; }
|
||||
}
|
||||
@keyframes veilDrift{
|
||||
0%{ transform: translate3d(0,0,0) scale(1); }
|
||||
50%{ transform: translate3d(2%,-3%,0) scale(1.06); }
|
||||
100%{ transform: translate3d(0,0,0) scale(1); }
|
||||
}
|
||||
@keyframes veilDrift2{
|
||||
0%{ transform: translate3d(0,0,0) scale(1.04); }
|
||||
50%{ transform: translate3d(-3%,2%,0) scale(1); }
|
||||
100%{ transform: translate3d(0,0,0) scale(1.04); }
|
||||
}
|
||||
</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="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh; transition: background 240ms ease, color 240ms ease;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
|
||||
<a href="#top" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span hidden="{{ mark1Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark2Off }}" style="position: absolute; inset: 0; overflow: hidden;">
|
||||
<span style="position: absolute; left: 2px; top: 2px; width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 50%, var(--glow-2))); display: block;"></span>
|
||||
<span style="position: absolute; left: -6px; top: 13px; width: 40px; height: 18px; border-radius: 10px 10px 4px 4px; background: color-mix(in oklab, var(--bg) 72%, transparent); backdrop-filter: blur(3px); transform: rotate(-14deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark3Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--accent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(-30deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 7px; top: 7px; width: 14px; height: 14px; border-radius: 50%; border: 2px solid color-mix(in oklab, var(--accent) 45%, transparent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(20deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark4Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 4px; top: 4px; width: 20px; height: 20px; border-radius: 6px; background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 45%, var(--glow-1))); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 9px; top: 9px; width: 10px; height: 10px; border-radius: 3px; background: color-mix(in oklab, var(--bg) 85%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 132px; flex: none; white-space: nowrap;">ВейликсВПН</span>
|
||||
</a>
|
||||
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="MassApp.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="#prices" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="MassPrice.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="MassApp.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#prices" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1200px; margin: 0 auto; padding: 0 24px;">
|
||||
|
||||
<section style="position: relative; min-height: 62vh; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(28px, 5vw, 56px); align-items: center; padding: clamp(32px, 7vw, 72px) 0 clamp(28px, 7vw, 84px);">
|
||||
<div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 28px;">
|
||||
<div style="display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px 6px 8px; border: 1px solid var(--border); border-radius: 999px; font-size: 13px; color: var(--ink-2);">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
Все страны в одной подписке
|
||||
</div>
|
||||
<div style="display: inline-flex; align-items: center; padding: 6px 12px; border-radius: 999px; background: var(--accent-soft); color: var(--accent); font-size: 13px; font-weight: 500;">Работаем с 2022 года</div>
|
||||
</div>
|
||||
<h1 style="margin: 0 0 22px; font-size: clamp(40px, 5.2vw, 62px); line-height: 1.04; font-weight: 600; letter-spacing: -0.035em; text-wrap: balance;">VPN, который не нужно отключать</h1>
|
||||
<p style="margin: 0 0 34px; max-width: 30em; font-size: 18px; line-height: 1.55; color: var(--ink-2); font-weight: 300; text-wrap: pretty;">Тридцать локаций и российский адрес входят в подписку. Переключайтесь между странами сколько угодно, на пяти устройствах сразу.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: clamp(8px, 2vw, 12px);">
|
||||
<a href="#prices" style="display: flex; align-items: center; justify-content: center; height: 50px; padding: 0 clamp(14px, 4vw, 24px); border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500; box-shadow: 0 10px 28px -12px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<a href="#locations" style="display: flex; align-items: center; justify-content: center; height: 50px; padding: 0 clamp(14px, 4vw, 22px); border-radius: 12px; border: 1px solid var(--border); color: var(--ink); font-size: 16px; white-space: nowrap;" style-hover="border-color: var(--ink-3); color: var(--ink);">Посмотреть локации</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position: relative; aspect-ratio: 1 / 1; max-width: 480px; width: 100%; justify-self: {{ heroAlign }}; border-radius: 28px; overflow: hidden; isolation: isolate; -webkit-transform: translateZ(0); transform: translateZ(0); background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="position: absolute; left: -12%; top: -8%; width: 78%; height: 78%; border-radius: 50%; background: var(--glow-1); filter: blur(46px); opacity: 0.85; animation: veilDrift 16s ease-in-out infinite;"></div>
|
||||
<div style="position: absolute; right: -14%; top: 14%; width: 70%; height: 70%; border-radius: 50%; background: var(--glow-3); filter: blur(52px); opacity: 0.75; animation: veilDrift2 19s ease-in-out infinite;"></div>
|
||||
<div style="position: absolute; left: 16%; bottom: -16%; width: 82%; height: 68%; border-radius: 50%; background: var(--glow-2); filter: blur(54px); opacity: 0.8; animation: veilDrift 23s ease-in-out infinite;"></div>
|
||||
<div id="veilix-globe" style="position: absolute; left: 8%; top: 8%; width: 84%; height: 84%;"></div>
|
||||
<div style="position: absolute; left: 8%; top: 30%; right: 8%; height: 26%; border-radius: 18px; background: color-mix(in oklab, var(--veil) 72%, transparent); border: 1px solid color-mix(in oklab, var(--ink) 10%, transparent); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); transform: rotate(-8deg);"></div>
|
||||
<div style="position: absolute; left: 12%; top: 46%; right: 4%; height: 24%; border-radius: 18px; background: color-mix(in oklab, var(--veil) 72%, transparent); border: 1px solid color-mix(in oklab, var(--ink) 8%, transparent); backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px); transform: rotate(6deg);"></div>
|
||||
<div style="position: absolute; left: 20px; right: 20px; bottom: 20px; display: grid; gap: 10px; padding: 14px 16px; border-radius: 16px; background: color-mix(in oklab, var(--surface) 84%, transparent); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid var(--border);">
|
||||
<div style="display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--ink-2);">
|
||||
<span style="width: 7px; height: 7px; border-radius: 50%; background: var(--accent); display: block; flex: none;"></span>
|
||||
30 стран, переключение в один тап
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px 16px; padding-top: 10px; border-top: 1px solid var(--border); font-size: 13px; color: var(--ink-3); font-variant-numeric: tabular-nums;">
|
||||
<span>699 ₽ в месяц или 6 990 ₽ в год</span>
|
||||
<span>До 5 устройств</span>
|
||||
<span>RU-адрес включён</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="advantages" style="padding: 16px 0 8px;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 24px; margin-bottom: 28px;">
|
||||
<h2 style="margin: 0; font-size: clamp(28px, 3vw, 38px); font-weight: 600; letter-spacing: -0.03em;">Что это даёт на практике</h2>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div style="position: relative; isolation: isolate; padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; inset: 0; border-radius: 22px; overflow: hidden; pointer-events: none; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black);">
|
||||
<div style="position: absolute; right: -80px; top: -80px; width: 260px; height: 260px; border-radius: 50%; background: var(--glow-1); filter: blur(60px); opacity: 0.5;"></div>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 18px;">
|
||||
<span style="position: relative; width: 34px; height: 34px; flex: none; display: block;">
|
||||
<span style="position: absolute; left: 11px; top: 3px; width: 12px; height: 12px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
<span style="position: absolute; left: 4px; top: 19px; width: 26px; height: 14px; border-radius: 13px 13px 4px 4px; background: var(--accent); opacity: 0.35; display: block;"></span>
|
||||
<span style="position: absolute; left: 23px; top: 18px; width: 11px; height: 11px; border-radius: 50%; border: 2px solid var(--accent); background: var(--surface); display: block;"></span>
|
||||
</span>
|
||||
<h3 style="margin: 0; font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">Все локации в подписке</h3>
|
||||
</div>
|
||||
<p style="margin: 0 0 26px; max-width: 34em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Список стран открыт целиком. Выбираете страну в приложении, соединение перестраивается за пару секунд, доплачивать за переключение не нужно.</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px 28px;">
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Тридцать стран</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Европа, США, Азия, Ближний Восток и Южная Америка. Список пополняется.</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Пять устройств сразу</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Телефон, ноутбук, планшет и ещё два устройства работают одновременно.</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Российские сайты напрямую</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Банки и госуслуги работают как обычно, отключать VPN не нужно.</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 500; margin-bottom: 6px;">Трафик под шифрованием</div>
|
||||
<div style="font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Дома, в отеле или в кафе соединение защищено в любой точке мира.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 16px; align-content: start;">
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; gap: 4px; margin-bottom: 16px;">
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: var(--accent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 75%, transparent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 55%, transparent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
<span style="width: 11px; height: 22px; border-radius: 3px; background: color-mix(in oklab, var(--accent) 18%, transparent); display: block;"></span>
|
||||
</div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em;">Пять устройств в подписке</h3>
|
||||
<p style="margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Одна подписка на всю технику. Устройства работают одновременно и в разных странах.</p>
|
||||
</div>
|
||||
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px;">
|
||||
<span style="width: 22px; height: 22px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
<span style="width: 26px; height: 2px; background: color-mix(in oklab, var(--accent) 40%, transparent); display: block;"></span>
|
||||
<span style="width: 22px; height: 22px; border-radius: 50%; border: 2px solid color-mix(in oklab, var(--accent) 55%, transparent); display: block;"></span>
|
||||
</div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em;">Российский адрес включён</h3>
|
||||
<p style="margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Российская локация входит в подписку: банки, госуслуги и стриминг открываются как дома.</p>
|
||||
</div>
|
||||
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 16px;">
|
||||
<span style="width: 22px; height: 22px; border-radius: 7px; background: var(--accent); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="width: 22px; height: 22px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.02em;">Современный протокол XRAY</h3>
|
||||
<p style="margin: 0 0 12px; font-size: 14px; line-height: 1.5; color: var(--ink-2); font-weight: 300;">Трафик неотличим от обычного HTTPS, поэтому подключение держится там, где старые протоколы уже не работают.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px; font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums;">
|
||||
<span style="padding: 4px 9px; border-radius: 7px; background: var(--surface-2); border: 1px solid var(--border);">VLESS</span>
|
||||
<span style="padding: 4px 9px; border-radius: 7px; background: var(--surface-2); border: 1px solid var(--border);">Hysteria2</span>
|
||||
<span style="padding: 4px 9px; border-radius: 7px; background: var(--surface-2); border: 1px solid var(--border);">XHTTP</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 16px; padding: clamp(20px, 5vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 style="margin: 0 0 6px; font-size: 24px; font-weight: 600; letter-spacing: -0.025em;">Трафик идёт коротким путём</h3>
|
||||
<p style="margin: 0 0 28px; max-width: 46em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Маршрут выбирается сам, по адресу сайта. Российские сервисы не ломаются, зарубежные открываются, переключать ничего не нужно.</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px;">
|
||||
<div style="padding: 22px; border-radius: 16px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 16px;">Вы в России</div>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">RU-сайты</span>
|
||||
<span style="flex: 1; height: 1px; background: linear-gradient(90deg, var(--accent), color-mix(in oklab, var(--accent) 25%, transparent)); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">напрямую через RU-сервер</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">Остальной мир</span>
|
||||
<span style="flex: 1; height: 1px; background: repeating-linear-gradient(90deg, var(--accent) 0 6px, transparent 6px 12px); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">туннелем в выбранную локацию</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 22px; border-radius: 16px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 16px;">Вы за границей</div>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">Мировые сайты</span>
|
||||
<span style="flex: 1; height: 1px; background: linear-gradient(90deg, var(--accent), color-mix(in oklab, var(--accent) 25%, transparent)); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">прямо из выбранной локации</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 14px;">
|
||||
<span style="flex: none; width: 72px; color: var(--ink-2); font-weight: 500;">RU-сайты</span>
|
||||
<span style="flex: 1; height: 1px; background: repeating-linear-gradient(90deg, var(--accent) 0 6px, transparent 6px 12px); display: block;"></span>
|
||||
<span style="flex: none; color: var(--ink-2);">туннелем через RU-сервер</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 16px; font-size: 13px; color: var(--ink-3);">Для тех, кто работает с Россией из другой страны.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="prices" style="padding: 72px 0 8px;">
|
||||
<div style="position: relative; overflow: hidden; isolation: isolate; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black); -webkit-transform: translateZ(0); transform: translateZ(0); border-radius: 26px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; left: min(-60px, calc(-110px + 8vw)); bottom: min(-120px, calc(-170px + 10vw)); width: clamp(150px, 34vw, 340px); height: clamp(150px, 34vw, 340px); border-radius: 50%; background: radial-gradient(circle at 60% 40%, var(--glow-1) 0%, color-mix(in oklab, var(--glow-1) 35%, transparent) 55%, transparent 78%); filter: blur(70px); opacity: 0.45;"></div>
|
||||
<div style="position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: clamp(28px, 5vw, 40px); padding: clamp(20px, 5vw, 40px);">
|
||||
<div>
|
||||
<h2 style="margin: 0 0 10px; font-size: clamp(26px, 3vw, 34px); font-weight: 600; letter-spacing: -0.03em;">Одна подписка, все страны</h2>
|
||||
<p style="margin: 0 0 22px; max-width: 34em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Тридцать локаций, российский адрес и пять устройств.</p>
|
||||
<div style="display: grid; gap: 10px; font-size: 14px; color: var(--ink-2); font-weight: 300; margin-bottom: 24px;">
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span>Все 30 стран и российский адрес</div>
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span>До 5 устройств одновременно</div>
|
||||
<div style="display: flex; gap: 10px;"><span style="color: var(--accent);">—</span>Современный протокол XRAY</div>
|
||||
</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 10px;">Страны в подписке</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<sc-for list="{{ countries }}" as="c" hint-placeholder-count="14">
|
||||
<span style="padding: 5px 10px; border-radius: 8px; background: var(--surface-2); border: 1px solid var(--border); font-size: 13px; color: var(--ink-2);">{{ c.name }}</span>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 12px; align-content: start;">
|
||||
<div role="button" tabindex="0" onClick="{{ pickMonth }}" style="display: grid; gap: 6px; padding: 22px; border-radius: 18px; border: 1px solid {{ planMonthBorder }}; background: {{ planMonthBg }}; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="font-size: 16px; font-weight: 500;">Месяц</span>
|
||||
<span style="font-size: 26px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">699 ₽</span>
|
||||
</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3); font-weight: 300;">Списание раз в месяц, отмена в любой момент</span>
|
||||
</div>
|
||||
<div role="button" tabindex="0" onClick="{{ pickYear }}" style="display: grid; gap: 6px; padding: 22px; border-radius: 18px; border: 1px solid {{ planYearBorder }}; background: {{ planYearBg }}; cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 10px;">
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 500;">Год
|
||||
<span style="padding: 3px 8px; border-radius: 7px; background: var(--accent-soft); color: var(--accent); font-size: 12px; font-weight: 500;">выгоднее</span>
|
||||
</span>
|
||||
<span style="font-size: 26px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">6 990 ₽</span>
|
||||
</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3); font-weight: 300; font-variant-numeric: tabular-nums;">582 ₽ в месяц, экономия 1 398 ₽ за год</span>
|
||||
</div>
|
||||
<a href="MassOrder.dc.html" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 4px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500; box-shadow: 0 10px 28px -12px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Оформить подписку</a>
|
||||
<div style="font-size: 13px; color: var(--ink-3); line-height: 1.5;">Карты российских и зарубежных банков, криптовалюта. Доступ открывается сразу после оплаты.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding: 24px 0 0;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 18px; padding: clamp(20px, 5vw, 32px); border-radius: 22px; border: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div>
|
||||
<h3 style="margin: 0 0 8px; font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Нужен адрес, закреплённый только за вами</h3>
|
||||
<p style="margin: 0; max-width: 46em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Здесь сервер общий: он дешевле и этого достаточно большинству. Если сервисы требуют постоянный IP, посмотрите индивидуальные тарифы Вейликс.</p>
|
||||
</div>
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 1px solid var(--accent); color: var(--accent); font-size: 15px; font-weight: 500; white-space: nowrap;" style-hover="background: var(--accent-soft); color: var(--accent);">Индивидуальный IP</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="faq" style="padding: 72px 0 32px;">
|
||||
<h2 style="margin: 0 0 28px; font-size: clamp(28px, 3vw, 38px); font-weight: 600; letter-spacing: -0.03em;">Вопросы</h2>
|
||||
<div style="display: grid; gap: 10px; max-width: 860px;">
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq0 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Что входит в подписку?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq0Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq0Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Все тридцать стран, включая российскую локацию, и пять устройств. Отдельной платы за страну или устройство нет.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq1 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Можно менять страну сколько угодно?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq1Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq1Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Да. Переключение занимает пару секунд и ничего не стоит: список локаций открыт целиком на всё время подписки.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq2 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Нужно ли что-то настраивать самому?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq2Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq2Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Нет. В кабинете вы скачиваете готовый файл или сканируете QR-код с телефона. Российские сайты идут напрямую, зарубежные — через выбранную локацию.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq3 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">На каком протоколе работает подключение?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq3Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq3Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">На XRAY — это современный протокол с VLESS, Hysteria2 и XHTTP. Трафик выглядит как обычный HTTPS, поэтому соединение держится там, где старые протоколы блокируются.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq4 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Чем это отличается от индивидуального тарифа?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq4Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq4Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">Здесь адрес общий: на сервере несколько человек, поэтому подписка стоит заметно дешевле. Если нужен адрес, закреплённый только за вами, это отдельные тарифы с индивидуальным IP.</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid var(--border); border-radius: 16px; background: var(--surface); overflow: hidden;">
|
||||
<button type="button" onClick="{{ faq5 }}" style="display: flex; align-items: center; gap: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">Как оплачивается и продлевается?</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; border-right: 1.5px solid var(--ink-3); border-bottom: 1.5px solid var(--ink-3); transform: {{ faq5Rot }}; transition: transform 180ms ease; display: block;"></span>
|
||||
</button>
|
||||
<div hidden="{{ faq5Off }}" style="padding: 0 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">699 ₽ в месяц или 6 990 ₽ в год со списанием с привязанной карты. Годовая подписка дешевле помесячной на 1 398 ₽. Отменить продление можно в кабинете в любой момент.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer style="margin-top: 40px; border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<span style="position: relative; width: 24px; height: 24px; display: block; flex: none;">
|
||||
<span hidden="{{ mark1Off }}" style="position: absolute; inset: 0;">
|
||||
<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 hidden="{{ mark2Off }}" style="position: absolute; inset: 0; overflow: hidden;">
|
||||
<span style="position: absolute; left: 2px; top: 2px; width: 20px; height: 20px; border-radius: 50%; background: linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 50%, var(--glow-2))); display: block;"></span>
|
||||
<span style="position: absolute; left: -5px; top: 11px; width: 34px; height: 15px; border-radius: 9px 9px 4px 4px; background: color-mix(in oklab, var(--surface-2) 72%, transparent); backdrop-filter: blur(3px); transform: rotate(-14deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark3Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 1px; top: 1px; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--accent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(-30deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 6px; top: 6px; width: 12px; height: 12px; border-radius: 50%; border: 2px solid color-mix(in oklab, var(--accent) 45%, transparent); border-bottom-color: transparent; border-right-color: transparent; transform: rotate(20deg); display: block;"></span>
|
||||
</span>
|
||||
<span hidden="{{ mark4Off }}" style="position: absolute; inset: 0;">
|
||||
<span style="position: absolute; left: 3px; top: 3px; width: 18px; height: 18px; border-radius: 5px; background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 45%, var(--glow-1))); transform: rotate(45deg); display: block;"></span>
|
||||
<span style="position: absolute; left: 8px; top: 8px; width: 8px; height: 8px; border-radius: 3px; background: color-mix(in oklab, var(--surface-2) 85%, transparent); transform: rotate(45deg); display: block;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">VPN, который не нужно отключать. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 ВейликсВПН</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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":"Тема"},"logoMark":{"editor":"enum","options":["layers","veil","arcs","aperture"],"default":"layers","tsType":"string","section":"Логотип"},"showLogoPicker":{"editor":"boolean","default":false,"tsType":"boolean","section":"Логотип"}}">
|
||||
class Component extends DCLogic {
|
||||
state = { plan: "year", theme: null, sysDark: false, loc: 0, faq: 0, mark: null, langMenu: false, lang: "ru", narrow: false, stacked: false, menu: false, lead: false, leadSent: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
async drawGlobe() {
|
||||
const host = document.getElementById("veilix-globe");
|
||||
if (!host) return;
|
||||
if (!window.d3 || !window.topojson) { setTimeout(() => this.drawGlobe(), 200); return; }
|
||||
const d3 = window.d3;
|
||||
let topo;
|
||||
try {
|
||||
topo = await d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json");
|
||||
} catch (e) { return; }
|
||||
const land = window.topojson.feature(topo, topo.objects.countries);
|
||||
const S = 440, r = S / 2 - 4;
|
||||
const projection = d3.geoOrthographic().rotate([-20, -22]).scale(r).translate([S / 2, S / 2]);
|
||||
const path = d3.geoPath(projection);
|
||||
host.innerHTML = "";
|
||||
const svg = d3.select(host).append("svg")
|
||||
.attr("viewBox", "0 0 " + S + " " + S)
|
||||
.attr("width", "100%").attr("height", "100%")
|
||||
.style("display", "block").style("overflow", "visible");
|
||||
svg.append("circle").attr("cx", S / 2).attr("cy", S / 2).attr("r", r)
|
||||
.attr("fill", "var(--ocean)").attr("stroke", "var(--border)");
|
||||
const graticule = d3.geoGraticule10 ? d3.geoGraticule10() : null;
|
||||
if (graticule) {
|
||||
svg.append("path").datum(graticule).attr("d", path)
|
||||
.attr("fill", "none").attr("stroke", "var(--land-line)")
|
||||
.attr("stroke-width", 0.5).attr("opacity", 0.5);
|
||||
}
|
||||
svg.append("g").selectAll("path").data(land.features).join("path")
|
||||
.attr("d", path).attr("fill", "var(--land)")
|
||||
.attr("stroke", "var(--land-line)").attr("stroke-width", 0.6);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.drawGlobe();
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
const st = window.innerWidth < 700;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
if (st !== this.state.stacked) this.setState({ stacked: st });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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.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, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const mark = this.state.mark || this.props.logoMark || "layers";
|
||||
const marks = ["layers", "veil", "arcs", "aperture"];
|
||||
const sel = "var(--sel)";
|
||||
const v = {
|
||||
countries: this.data().map(d => ({ name: d.country })),
|
||||
pickMonth: () => this.setState({ plan: "month" }),
|
||||
pickYear: () => this.setState({ plan: "year" }),
|
||||
planMonthBg: this.state.plan === "month" ? sel : "transparent",
|
||||
planYearBg: this.state.plan === "year" ? sel : "transparent",
|
||||
planMonthBorder: this.state.plan === "month" ? "var(--accent)" : "var(--border)",
|
||||
planYearBorder: this.state.plan === "year" ? "var(--accent)" : "var(--border)",
|
||||
locHint: this.data()[this.state.loc].ready ? "Свободные серверы есть сейчас" : "Сервер установим под вас за 24 часа",
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: this.state.lead,
|
||||
leadClosed: !this.state.lead,
|
||||
leadButton: this.state.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
curSetup: this.data()[this.state.loc].setup,
|
||||
curMonth: this.data()[this.state.loc].month,
|
||||
items: this.data().map((d, n) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
bg: this.state.loc === n ? sel : "transparent",
|
||||
border: this.state.loc === n ? "var(--accent)" : "var(--border)",
|
||||
pick: () => this.setState({ loc: n })
|
||||
})),
|
||||
logoPickerOff: this.props.showLogoPicker !== true,
|
||||
langMenuOff: !this.state.langMenu,
|
||||
heroAlign: this.state.stacked ? "center" : "end",
|
||||
navOff: this.state.narrow,
|
||||
burgerOff: !this.state.narrow,
|
||||
menuOff: !(this.state.narrow && this.state.menu),
|
||||
toggleMenu: () => this.setState(s => ({ menu: !s.menu, langMenu: false })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
langLabel: this.state.lang === "en" ? "EN" : "RU",
|
||||
ruBg: this.state.lang === "en" ? "transparent" : sel,
|
||||
enBg: this.state.lang === "en" ? sel : "transparent",
|
||||
ruInk: this.state.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: this.state.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
ruCheck: this.state.lang === "en" ? "" : "✓",
|
||||
enCheck: this.state.lang === "en" ? "✓" : "",
|
||||
toggleLangMenu: () => this.setState(s => ({ langMenu: !s.langMenu })),
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
for (let i = 0; i < 5; i++) {
|
||||
v["loc" + i + "Bg"] = this.state.loc === i ? sel : "transparent";
|
||||
v["loc" + i + "Dot"] = this.state.loc === i ? "var(--accent)" : "var(--ink-3)";
|
||||
v["pickLoc" + i] = () => this.setState({ loc: i });
|
||||
}
|
||||
for (let i = 0; i < 6; i++) {
|
||||
v["faq" + i] = () => this.setState(s => ({ faq: s.faq === i ? -1 : i }));
|
||||
v["faq" + i + "Off"] = this.state.faq !== i;
|
||||
v["faq" + i + "Rot"] = this.state.faq === i ? "rotate(-135deg) translateY(2px)" : "rotate(45deg) translateY(-2px)";
|
||||
}
|
||||
marks.forEach((name, idx) => {
|
||||
const n = idx + 1;
|
||||
v["mark" + n + "Off"] = mark !== name;
|
||||
v["mark" + n + "Bg"] = mark === name ? sel : "transparent";
|
||||
v["pickMark" + n] = () => this.setState({ mark: name });
|
||||
});
|
||||
return v;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,296 @@
|
||||
<!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-hover: oklch(41% 0.1 178);
|
||||
--accent-soft: oklch(95.5% 0.032 180);
|
||||
--sel: oklch(96.5% 0.016 190);
|
||||
--on-accent: oklch(99% 0.01 180);
|
||||
--glow-1: oklch(86% 0.085 190);
|
||||
--glow-2: oklch(88% 0.075 205);
|
||||
--glow-3: oklch(89% 0.07 172);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 204 / 0.05), 0 12px 32px -12px oklch(24% 0.04 204 / 0.14);
|
||||
}
|
||||
: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-hover: oklch(83% 0.09 190);
|
||||
--accent-soft: oklch(31% 0.05 190);
|
||||
--sel: oklch(30% 0.03 195);
|
||||
--on-accent: oklch(20% 0.04 204);
|
||||
--glow-1: oklch(52% 0.09 200);
|
||||
--glow-2: oklch(47% 0.085 215);
|
||||
--glow-3: oklch(50% 0.08 180);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--shadow: 0 1px 2px oklch(12% 0.03 204 / 0.4), 0 16px 40px -16px oklch(9% 0.03 204 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
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="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="border-bottom: 1px solid var(--border); background: var(--bg);">
|
||||
<div style="max-width: 1080px; margin: 0 auto; padding: 16px 24px; display: flex; flex-wrap: wrap; align-items: center; gap: 14px 24px;">
|
||||
<a href="MassMain.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 26px; height: 26px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 19px; height: 19px; 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: 19px; height: 19px; border-radius: 6px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 16px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</a>
|
||||
<div style="margin-left: auto; display: flex; align-items: center; gap: 12px; font-size: 14px; color: var(--ink-3);">
|
||||
<button type="button" onClick="{{ toggleLang }}" title="{{ langTitle }}" style="display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; border-radius: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<span>Оформление заказа</span>
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Отменить</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main style="max-width: 1080px; margin: 0 auto; padding: 40px 24px 80px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; align-items: start;">
|
||||
|
||||
<div style="display: grid; gap: 16px;">
|
||||
|
||||
<div style="border: 1px solid {{ step1Border }}; border-radius: 20px; background: var(--surface); box-shadow: var(--shadow); overflow: hidden;">
|
||||
<div style="display: flex; align-items: center; gap: 14px; padding: 22px 24px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: {{ step1Dot }}; color: {{ step1DotInk }}; font-size: 13px; font-weight: 500; flex: none;">1</span>
|
||||
<span style="flex: 1; font-size: 18px; font-weight: 500; letter-spacing: -0.02em;">Почта</span>
|
||||
<span hidden="{{ emailNotDone }}" style="font-size: 14px; color: var(--ink-2);">{{ emailValue }}</span>
|
||||
<button type="button" hidden="{{ emailNotDone }}" onClick="{{ editEmail }}" style="border: 0; background: transparent; color: var(--accent); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">Изменить</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ step1Off }}" style="padding: 0 24px 24px;">
|
||||
<p style="margin: 0 0 16px; max-width: 46em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Введите почту — пришлём одноразовый код. Отдельной регистрации нет: если вы у нас уже были, попадёте в свой кабинет, если нет — он создастся сам.</p>
|
||||
<div hidden="{{ codeSent }}" style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<input type="email" value="{{ emailValue }}" onInput="{{ onEmail }}" placeholder="you@example.com" style="flex: 1; min-width: 220px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px;" />
|
||||
<button type="button" onClick="{{ sendCode }}" style="height: 50px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Прислать код</button>
|
||||
</div>
|
||||
<div hidden="{{ codeNotSent }}" style="display: grid; gap: 12px;">
|
||||
<div style="font-size: 14px; color: var(--ink-2);">{{ codeLine }}</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<input type="text" inputmode="numeric" placeholder="6 цифр" style="flex: 1; min-width: 180px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 17px; letter-spacing: 0.28em; font-variant-numeric: tabular-nums;" />
|
||||
<button type="button" onClick="{{ confirmCode }}" style="height: 50px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Продолжить</button>
|
||||
</div>
|
||||
<button type="button" onClick="{{ editEmail }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">Другая почта</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid {{ step2Border }}; border-radius: 20px; background: var(--surface); box-shadow: var(--shadow); overflow: hidden;">
|
||||
<div style="display: flex; align-items: center; gap: 14px; padding: 22px 24px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: {{ step2Dot }}; color: {{ step2DotInk }}; font-size: 13px; font-weight: 500; flex: none;">2</span>
|
||||
<span style="flex: 1; font-size: 18px; font-weight: 500; letter-spacing: -0.02em;">Оплата</span>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ step2Off }}" style="padding: 0 24px 24px; display: grid; gap: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<button type="button" onClick="{{ payCard }}" style="flex: 1; min-width: 140px; padding: 14px 16px; border-radius: 14px; border: 1px solid {{ cardBorder }}; background: {{ cardBg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">Карта</button>
|
||||
<button type="button" onClick="{{ payMir }}" style="flex: 1; min-width: 140px; padding: 14px 16px; border-radius: 14px; border: 1px solid {{ mirBorder }}; background: {{ mirBg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">Карта МИР</button>
|
||||
<button type="button" onClick="{{ payCrypto }}" style="flex: 1; min-width: 140px; padding: 14px 16px; border-radius: 14px; border: 1px solid {{ cryptoBorder }}; background: {{ cryptoBg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">Криптовалюта</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ cryptoChosen }}" style="display: grid; gap: 10px;">
|
||||
<input type="text" placeholder="Номер карты" style="height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%; font-variant-numeric: tabular-nums;" />
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<input type="text" placeholder="ММ / ГГ" style="flex: 1; min-width: 120px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; font-variant-numeric: tabular-nums;" />
|
||||
<input type="text" placeholder="CVC" style="flex: 1; min-width: 120px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; font-variant-numeric: tabular-nums;" />
|
||||
</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3); line-height: 1.5;">Карта сохранится для продления. Отменить или заменить её можно в кабинете в любой момент.</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ cardChosen }}" style="display: grid; gap: 10px; padding: 18px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 15px;">USDT, BTC или ETH</div>
|
||||
<div style="font-size: 14px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">После нажатия покажем адрес и сумму. При оплате криптой продление не автоматическое — пришлём напоминание за три дня до конца периода.</div>
|
||||
</div>
|
||||
|
||||
<label style="display: flex; align-items: flex-start; gap: 10px; font-size: 14px; color: var(--ink-2); font-weight: 300; line-height: 1.5; cursor: pointer;">
|
||||
<input type="checkbox" style="margin-top: 3px; width: 16px; height: 16px; accent-color: var(--accent);" />
|
||||
<span>Согласен с условиями использования и политикой конфиденциальности</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside style="position: sticky; top: 24px; display: grid; gap: 16px;">
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 16px;">Ваш заказ</div>
|
||||
<div style="display: grid; gap: 6px; margin-bottom: 20px;">
|
||||
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Подписка ВейликсВПН</div>
|
||||
<div style="font-size: 15px; color: var(--ink-2);">30 стран и российский адрес</div>
|
||||
<div style="display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--accent);">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
Доступ открывается сразу после оплаты
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 18px;">
|
||||
<button type="button" onClick="{{ pickMonth }}" style="flex: 1; padding: 12px 14px; border-radius: 12px; border: 1px solid {{ planMonthBorder }}; background: {{ planMonthBg }}; color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;">Месяц · 699 ₽</button>
|
||||
<button type="button" onClick="{{ pickYear }}" style="flex: 1; padding: 12px 14px; border-radius: 12px; border: 1px solid {{ planYearBorder }}; background: {{ planYearBg }}; color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;">Год · 6 990 ₽</button>
|
||||
</div>
|
||||
<div style="display: grid; gap: 10px; padding-top: 18px; border-top: 1px solid var(--border); font-size: 15px;">
|
||||
<div style="display: flex; justify-content: space-between; gap: 12px; color: var(--ink-2);"><span>Локации</span><span>все 30</span></div>
|
||||
<div style="display: flex; justify-content: space-between; gap: 12px; color: var(--ink-2);"><span>RU-адрес</span><span>включён</span></div>
|
||||
<div style="display: flex; justify-content: space-between; gap: 12px; color: var(--ink-2);"><span>Устройств</span><span>до 5</span></div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border);">
|
||||
<span style="font-size: 16px; font-weight: 500;">К оплате сегодня</span>
|
||||
<span style="font-size: 26px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">{{ payTotal }}</span>
|
||||
</div>
|
||||
<div style="margin-top: 8px; font-size: 13px; color: var(--ink-3);">{{ nextChargeLine }}</div>
|
||||
<button type="button" onClick="{{ pay }}" disabled="{{ payDisabled }}" style="margin-top: 20px; width: 100%; height: 52px; border-radius: 14px; border: 0; background: {{ payBg }}; color: {{ payInk }}; font-family: inherit; font-size: 16px; font-weight: 500; cursor: {{ payCursor }};">{{ payLabel }}</button>
|
||||
<div hidden="{{ paidOff }}" style="margin-top: 14px; padding: 14px; border-radius: 12px; background: var(--accent-soft); font-size: 14px; line-height: 1.5; color: var(--ink);">Оплачено. Подписка уже в кабинете — <a href="MassApp.dc.html">открыть</a>.</div>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</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 = { plan: "year", theme: null, sysDark: false, email: "", sent: false, authed: false, method: "card", paid: false, lang: "ru" };
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
const authed = s.authed;
|
||||
const nd = new Date();
|
||||
nd.setMonth(nd.getMonth() + 1);
|
||||
const months = ["января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"];
|
||||
const monthsEn = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
const en = s.lang === "en";
|
||||
const sel = "var(--sel)";
|
||||
const yearly = s.plan === "year";
|
||||
const total = yearly ? "6 990 ₽" : "699 ₽";
|
||||
return {
|
||||
pickMonth: () => this.setState({ plan: "month" }),
|
||||
pickYear: () => this.setState({ plan: "year" }),
|
||||
planMonthBg: yearly ? "transparent" : sel,
|
||||
planYearBg: yearly ? sel : "transparent",
|
||||
planMonthBorder: yearly ? "var(--border)" : "var(--accent)",
|
||||
planYearBorder: yearly ? "var(--accent)" : "var(--border)",
|
||||
payTotal: total,
|
||||
emailValue: s.email,
|
||||
toggleLang: () => this.setLang(s.lang === "en" ? "ru" : "en"),
|
||||
langLabel: s.lang === "en" ? "EN" : "RU",
|
||||
langTitle: s.lang === "en" ? "Язык: английский" : "Язык: русский",
|
||||
onEmail: (e) => this.setState({ email: e.target.value }),
|
||||
sendCode: () => this.setState({ sent: true }),
|
||||
confirmCode: () => this.setState({ authed: true }),
|
||||
editEmail: () => this.setState({ sent: false, authed: false }),
|
||||
codeSent: s.sent,
|
||||
codeNotSent: !s.sent,
|
||||
emailNotDone: !authed,
|
||||
step1Off: authed,
|
||||
step1Border: authed ? "var(--border)" : "var(--accent)",
|
||||
step1Dot: authed ? "var(--accent)" : "var(--accent-soft)",
|
||||
step1DotInk: authed ? "var(--on-accent)" : "var(--accent)",
|
||||
step2Off: !authed,
|
||||
step2Border: authed ? "var(--accent)" : "var(--border)",
|
||||
step2Dot: authed ? "var(--accent-soft)" : "var(--surface-2)",
|
||||
step2DotInk: authed ? "var(--accent)" : "var(--ink-3)",
|
||||
payCard: () => this.setState({ method: "card" }),
|
||||
payMir: () => this.setState({ method: "mir" }),
|
||||
payCrypto: () => this.setState({ method: "crypto" }),
|
||||
cardBg: s.method === "card" ? sel : "transparent",
|
||||
mirBg: s.method === "mir" ? sel : "transparent",
|
||||
cryptoBg: s.method === "crypto" ? sel : "transparent",
|
||||
cardBorder: s.method === "card" ? "var(--accent)" : "var(--border)",
|
||||
mirBorder: s.method === "mir" ? "var(--accent)" : "var(--border)",
|
||||
cryptoBorder: s.method === "crypto" ? "var(--accent)" : "var(--border)",
|
||||
cardChosen: s.method !== "crypto",
|
||||
cryptoChosen: s.method === "crypto",
|
||||
nextCharge: "первое списание " + nd.getDate() + " " + months[nd.getMonth()],
|
||||
nextChargeLine: yearly
|
||||
? (en ? "Renews yearly, next charge in 12 months" : "Продление раз в год, следующее списание через 12 месяцев")
|
||||
: (en ? "Renews monthly, next charge on " + nd.getDate() + " " + monthsEn[nd.getMonth()] : "Продление раз в месяц, следующее списание " + nd.getDate() + " " + months[nd.getMonth()]),
|
||||
codeLine: en
|
||||
? "The code has been sent to " + s.email + ". It is valid for 10 minutes."
|
||||
: "Код отправлен на " + s.email + ". Он действует 10 минут.",
|
||||
pay: () => { if (authed) this.setState({ paid: true }); },
|
||||
payDisabled: !authed,
|
||||
payBg: authed ? "var(--accent)" : "var(--surface-2)",
|
||||
payInk: authed ? "var(--on-accent)" : "var(--ink-3)",
|
||||
payCursor: authed ? "pointer" : "not-allowed",
|
||||
payLabel: s.paid ? "Оплачено" : (s.method === "crypto" ? "Показать адрес для оплаты" : "Оплатить " + total),
|
||||
paidOff: !s.paid
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,493 @@
|
||||
<!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-hover: oklch(41% 0.1 178);
|
||||
--accent-soft: oklch(95.5% 0.032 180);
|
||||
--sel: oklch(96.5% 0.016 190);
|
||||
--on-accent: oklch(99% 0.01 180);
|
||||
--glow-1: oklch(86% 0.085 190);
|
||||
--glow-2: oklch(88% 0.075 205);
|
||||
--glow-3: oklch(89% 0.07 172);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 204 / 0.05), 0 12px 32px -12px oklch(24% 0.04 204 / 0.14);
|
||||
}
|
||||
: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-hover: oklch(83% 0.09 190);
|
||||
--accent-soft: oklch(31% 0.05 190);
|
||||
--sel: oklch(30% 0.03 195);
|
||||
--on-accent: oklch(20% 0.04 204);
|
||||
--glow-1: oklch(52% 0.09 200);
|
||||
--glow-2: oklch(47% 0.085 215);
|
||||
--glow-3: oklch(50% 0.08 180);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--shadow: 0 1px 2px oklch(12% 0.03 204 / 0.4), 0 16px 40px -16px oklch(9% 0.03 204 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
@keyframes veilDrift{
|
||||
0%{ transform: translate3d(0,0,0) scale(1); }
|
||||
50%{ transform: translate3d(2%,-3%,0) scale(1.06); }
|
||||
100%{ transform: translate3d(0,0,0) scale(1); }
|
||||
}
|
||||
</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="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
<a href="MassMain.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 132px; flex: none; white-space: nowrap;">ВейликсВПН</span>
|
||||
</a>
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="#top" style="color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="MassApp.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="#locations" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="MassPrice.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="MassApp.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#locations" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1200px; margin: 0 auto; padding: 0 24px 80px;">
|
||||
|
||||
<section style="position: relative; overflow: hidden; isolation: isolate; -webkit-transform: translateZ(0); transform: translateZ(0); margin-top: 40px; border-radius: 28px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; right: min(-80px, calc(-140px + 11vw)); top: min(-120px, calc(-180px + 11vw)); width: clamp(180px, 38vw, 420px); height: clamp(180px, 38vw, 420px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-1) 0%, color-mix(in oklab, var(--glow-1) 35%, transparent) 55%, transparent 78%); filter: blur(70px); opacity: 0.5; animation: veilDrift 18s ease-in-out infinite;"></div>
|
||||
<div style="position: absolute; left: min(-60px, calc(-120px + 11vw)); bottom: min(-140px, calc(-200px + 11vw)); width: clamp(150px, 32vw, 360px); height: clamp(150px, 32vw, 360px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-3) 0%, color-mix(in oklab, var(--glow-3) 35%, transparent) 55%, transparent 78%); filter: blur(70px); opacity: 0.4;"></div>
|
||||
<div style="position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(28px, 5vw, 48px); padding: clamp(28px, 6vw, 56px) clamp(20px, 5vw, 48px);">
|
||||
<div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px;">
|
||||
<span style="font-size: clamp(56px, 8vw, 96px); font-weight: 600; letter-spacing: -0.045em; line-height: 0.95; font-variant-numeric: tabular-nums;">699 ₽</span>
|
||||
<span style="font-size: 18px; color: var(--ink-2);">в месяц</span>
|
||||
</div>
|
||||
<div style="margin-top: 16px; font-size: 20px; color: var(--ink-2); font-variant-numeric: tabular-nums;">или 6 990 ₽ в год — 582 ₽ в месяц</div>
|
||||
<p style="margin: 20px 0 0; max-width: 30em; font-size: 15px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Одна цена за всё. Отменить продление можно в кабинете.</p>
|
||||
<a href="MassOrder.dc.html" style="display: inline-flex; align-items: center; justify-content: center; height: 50px; margin-top: 24px; padding: 0 24px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500; box-shadow: 0 10px 28px -12px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Оформить подписку</a>
|
||||
</div>
|
||||
<div style="display: grid; gap: 14px; align-content: start;">
|
||||
<div style="font-size: 15px; font-weight: 500;">Что входит</div>
|
||||
<div style="display: grid; gap: 12px; font-size: 15px; color: var(--ink-2); font-weight: 300;">
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Все 30 стран, переключение в любой момент</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Российская локация: прямое соединение и мост в обе стороны</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>До 5 устройств одновременно</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Современный протокол XRAY</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="locations" style="padding: 72px 0 0;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 8px;">
|
||||
<h2 style="margin: 0; font-size: clamp(28px, 3vw, 38px); font-weight: 600; letter-spacing: -0.03em;">Страны в подписке</h2>
|
||||
</div>
|
||||
<p style="margin: 0 0 28px; max-width: 52em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Все локации доступны сразу после оплаты и входят в одну цену.</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;">
|
||||
<sc-for list="{{ items }}" as="loc" hint-placeholder-count="12">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: grid; align-content: start; gap: 14px; padding: 22px; border-radius: 22px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
{{ loc.flag }}
|
||||
<span style="display: flex; align-items: center; gap: 8px; flex: none; min-height: 32px; font-size: 13px; color: {{ loc.statusInk }};">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: {{ loc.statusDot }}; border: {{ loc.statusRing }}; display: block;"></span>
|
||||
{{ loc.badge }}
|
||||
</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span style="font-size: 19px; font-weight: 500; letter-spacing: -0.02em;">{{ loc.country }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section style="padding: 72px 0 0;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div style="padding: clamp(16px, 4vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em;">Нужен адрес, закреплённый только за вами</h3>
|
||||
<p style="margin: 0 0 20px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Здесь сервер общий: он дешевле и этого достаточно большинству. Если сервисы требуют постоянный IP, посмотрите индивидуальные тарифы Вейликс.</p>
|
||||
<a href="Main.dc.html" style="display: inline-flex; align-items: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 1px solid var(--accent); color: var(--accent); font-size: 15px; font-weight: 500;" style-hover="background: var(--accent-soft); color: var(--accent);">Индивидуальный IP</a>
|
||||
</div>
|
||||
<div style="position: relative; isolation: isolate; padding: clamp(16px, 4vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; inset: 0; border-radius: 22px; overflow: hidden; pointer-events: none; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black);">
|
||||
<div style="position: absolute; right: -70px; bottom: -90px; width: 260px; height: 260px; border-radius: 50%; background: var(--glow-2); filter: blur(60px); opacity: 0.5;"></div>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<h3 id="lead" style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em; scroll-margin-top: 90px;">Нужны особые условия</h3>
|
||||
<p style="margin: 0 0 20px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Подписки на команду, оплата по счёту или редкая страна. Оставьте заявку — ответим в течение дня.</p>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: inline-flex; align-items: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-soft);">Оставить заявку</button>
|
||||
<div hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 420px;">
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">VPN, который не нужно отключать. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 ВейликсВПН</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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, loc: 0, lead: false, leadSent: false, lang: "ru", narrow: false, menu: false, langMenu: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const sel = "var(--sel)";
|
||||
const names = ["Нидерланды · Амстердам", "Германия · Франкфурт", "США · Нью-Йорк", "Финляндия · Хельсинки", "Турция · Стамбул", "Япония · Токио"];
|
||||
const ready = [true, true, true, false, false, false];
|
||||
const i = this.state.loc;
|
||||
const v = {
|
||||
items: this.data().map((d, n) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
status: d.ready ? "Доступно в подписке" : "Скоро",
|
||||
badge: d.ready ? "Включено" : "Скоро",
|
||||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||||
statusDot: d.ready ? "var(--accent)" : "transparent",
|
||||
statusRing: d.ready ? "0" : "1.5px solid var(--ink-3)",
|
||||
bg: this.state.loc === n ? sel : "var(--surface)",
|
||||
border: this.state.loc === n ? "var(--accent)" : "var(--border)",
|
||||
dot: this.state.loc === n ? "var(--accent)" : "transparent",
|
||||
dotOff: this.state.loc === n,
|
||||
buy: (e) => { e.stopPropagation(); window.location.href = "MassOrder.dc.html"; },
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
buyOff: this.state.loc !== n,
|
||||
pick: () => this.setState({ loc: n })
|
||||
})),
|
||||
pickedTitle: names[i],
|
||||
pickedHint: ready[i] ? "Сервер свободен — доступ выдаём сразу после оплаты" : "Свободного сервера нет: установим лично под вас, доступ в течение 24 часов",
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
navOff: this.state.narrow,
|
||||
burgerOff: !this.state.narrow,
|
||||
menuOff: !(this.state.narrow && this.state.menu),
|
||||
toggleMenu: () => this.setState(st => ({ menu: !st.menu })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
toggleLangMenu: () => this.setState(st => ({ langMenu: !st.langMenu })),
|
||||
langMenuOff: !this.state.langMenu,
|
||||
langLabel: this.state.lang === "en" ? "EN" : "RU",
|
||||
ruCheck: this.state.lang === "en" ? "" : "✓",
|
||||
enCheck: this.state.lang === "en" ? "✓" : "",
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
ruBg: this.state.lang === "en" ? "transparent" : sel,
|
||||
enBg: this.state.lang === "en" ? sel : "transparent",
|
||||
ruInk: this.state.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: this.state.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: this.state.lead,
|
||||
leadClosed: !this.state.lead,
|
||||
leadButton: this.state.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
for (let n = 0; n < 6; n++) {
|
||||
v["loc" + n + "Bg"] = this.state.loc === n ? sel : "var(--surface)";
|
||||
v["loc" + n + "Border"] = this.state.loc === n ? "var(--accent)" : "var(--border)";
|
||||
v["loc" + n + "Dot"] = this.state.loc === n ? "var(--accent)" : "transparent";
|
||||
v["pickLoc" + n] = () => this.setState({ loc: n });
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,369 @@
|
||||
<!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-hover: oklch(41% 0.1 178);
|
||||
--accent-soft: oklch(95.5% 0.032 180);
|
||||
--sel: oklch(96.5% 0.016 190);
|
||||
--on-accent: oklch(99% 0.01 180);
|
||||
--glow-1: oklch(86% 0.085 190);
|
||||
--glow-2: oklch(88% 0.075 205);
|
||||
--glow-3: oklch(89% 0.07 172);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 204 / 0.05), 0 12px 32px -12px oklch(24% 0.04 204 / 0.14);
|
||||
}
|
||||
: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-hover: oklch(83% 0.09 190);
|
||||
--accent-soft: oklch(31% 0.05 190);
|
||||
--sel: oklch(30% 0.03 195);
|
||||
--on-accent: oklch(20% 0.04 204);
|
||||
--glow-1: oklch(52% 0.09 200);
|
||||
--glow-2: oklch(47% 0.085 215);
|
||||
--glow-3: oklch(50% 0.08 180);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--shadow: 0 1px 2px oklch(12% 0.03 204 / 0.4), 0 16px 40px -16px oklch(9% 0.03 204 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
</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="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
<a href="MassMain.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 132px; flex: none; white-space: nowrap;">ВейликсВПН</span>
|
||||
</a>
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="#top" style="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="MassApp.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="MassPrice.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="MassPrice.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="MassSupport.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="MassApp.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="MassPrice.dc.html" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1000px; margin: 0 auto; padding: 0 24px 80px;">
|
||||
|
||||
<section style="padding: 56px 0 8px;">
|
||||
<h1 style="margin: 0 0 14px; font-size: clamp(34px, 4.4vw, 52px); font-weight: 600; letter-spacing: -0.035em;">Поддержка</h1>
|
||||
<div style="height: 32px;"></div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
||||
<a href="#form" style="display: grid; gap: 10px; padding: clamp(20px, 4.8vw, 28px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow); color: var(--ink);" style-hover="border-color: var(--accent); color: var(--ink);">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Telegram</span>
|
||||
<span style="font-size: clamp(24px, 3vw, 32px); font-weight: 600; letter-spacing: -0.03em; color: var(--accent);">@veilix_support</span>
|
||||
<span style="font-size: 14px; color: var(--ink-2); font-weight: 300;">Быстрее всего. Можно писать голосом и скидывать скриншоты.</span>
|
||||
</a>
|
||||
<a href="#form" style="display: grid; gap: 10px; padding: clamp(20px, 4.8vw, 28px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow); color: var(--ink);" style-hover="border-color: var(--accent); color: var(--ink);">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Почта</span>
|
||||
<span style="font-size: clamp(22px, 2.6vw, 30px); font-weight: 600; letter-spacing: -0.03em; color: var(--accent); word-break: break-word;">help@veilixvpn.cc</span>
|
||||
<span style="font-size: 14px; color: var(--ink-2); font-weight: 300;">Для счетов, возвратов и всего, что нужно письменно.</span>
|
||||
</a>
|
||||
</div>
|
||||
<p style="margin: 16px 0 0; font-size: 14px; color: var(--ink-3);">Отвечаем с 10 до 20 в рабочие дни.</p>
|
||||
</section>
|
||||
|
||||
<section style="padding: 64px 0 0;">
|
||||
<h2 style="margin: 0 0 20px; font-size: clamp(26px, 2.8vw, 34px); font-weight: 600; letter-spacing: -0.03em;">Частые вопросы</h2>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px;">
|
||||
<button type="button" onClick="{{ tab0 }}" style="padding: 9px 16px; border-radius: 10px; border: 1px solid {{ tab0Border }}; background: {{ tab0Bg }}; color: {{ tab0Ink }}; font-family: inherit; font-size: 14px; cursor: pointer;">Подключение</button>
|
||||
<button type="button" onClick="{{ tab1 }}" style="padding: 9px 16px; border-radius: 10px; border: 1px solid {{ tab1Border }}; background: {{ tab1Bg }}; color: {{ tab1Ink }}; font-family: inherit; font-size: 14px; cursor: pointer;">Оплата и продление</button>
|
||||
<button type="button" onClick="{{ tab2 }}" style="padding: 9px 16px; border-radius: 10px; border: 1px solid {{ tab2Border }}; background: {{ tab2Bg }}; color: {{ tab2Ink }}; font-family: inherit; font-size: 14px; cursor: pointer;">Локации и подписка</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<sc-for list="{{ items }}" as="item" hint-placeholder-count="4">
|
||||
<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: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">{{ item.q }}</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; 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 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">{{ item.a }}</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="form" style="padding: 64px 0 0;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 40px; padding: 40px; border-radius: 24px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div>
|
||||
<h2 style="margin: 0 0 10px; font-size: clamp(24px, 2.6vw, 30px); font-weight: 600; letter-spacing: -0.03em;">Написать нам</h2>
|
||||
<p style="margin: 0; max-width: 26em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Если подписка уже оформлена, укажите почту, на которую она оформлена — так мы сразу увидим вашу конфигурацию.</p>
|
||||
</div>
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<input type="email" placeholder="Ваша почта" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<select style="height: 48px; padding: 0 12px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;">
|
||||
<option>Тема: подключение</option>
|
||||
<option>Тема: оплата и продление</option>
|
||||
<option>Тема: локации и подписка</option>
|
||||
<option>Тема: другое</option>
|
||||
</select>
|
||||
<textarea rows="5" placeholder="Что произошло" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ send }}" style="display: inline-flex; align-items: center; justify-content: center; height: 50px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ sendLabel }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">ВейликсВПН</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">VPN, который не нужно отключать. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="MassPrice.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="MassLocations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 ВейликсВПН</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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, tab: 0, open: 0, sent: false, lang: "ru", narrow: false, menu: false, langMenu: 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);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
faq() {
|
||||
return [
|
||||
[
|
||||
["Что делать сразу после оплаты?", "Зайдите в кабинет по почте, выберите страну и скачайте конфигурацию XRAY или отсканируйте QR-код с телефона. Маршрутизация уже настроена, вручную ничего вписывать не нужно."],
|
||||
["Сколько устройств можно подключить?", "До пяти на одну подписку. Страны у устройств могут быть разные: каждое подключается к своей локации."],
|
||||
["Перестал работать один способ подключения.", "Подключение работает на XRAY: откройте кабинет и обновите конфигурацию или смените локацию. Настройки протокола мы обновляем по мере блокировок."],
|
||||
["Нужно ли что-то настраивать вручную?", "Нет. В конфигурации уже прописано, что российские сайты идут напрямую, а зарубежные — через выбранную локацию. Переключать режимы во время работы не нужно."]
|
||||
],
|
||||
[
|
||||
["Сколько стоит подписка?", "699 ₽ в месяц или 6 990 ₽ в год. Годовая выходит по 582 ₽ в месяц и экономит 1 398 ₽. Платы за подключение и доплат за страну нет."],
|
||||
["Как продлевается подписка?", "Списание раз в месяц или раз в год с привязанной карты. Дату окончания и карту видно в кабинете, там же оплату можно поменять или остановить."],
|
||||
["Можно вернуть деньги?", "Если подписка не заработала по нашей вине, возвращаем полностью в течение первых семи дней. Если вы просто передумали — возвращаем остаток неиспользованного периода."],
|
||||
["Чем можно платить?", "Карты российских и зарубежных банков, а также криптовалюта. Все способы доступны на странице оформления заказа."]
|
||||
],
|
||||
[
|
||||
["Сколько стран входит в подписку?", "Тридцать, включая российскую локацию. Все они доступны сразу и не тарифицируются отдельно."],
|
||||
["Можно менять страну сколько угодно?", "Да. Переключение занимает пару секунд и ничего не стоит: список локаций открыт целиком на всё время подписки."],
|
||||
["Зачем в подписке российский адрес?", "Он работает в обе стороны. Из России — чтобы банки, госуслуги и стриминг видели обычное местное подключение. Из другой страны — чтобы те же сервисы открывались так, будто вы дома."],
|
||||
["Чем это отличается от индивидуального IP?", "Здесь адрес общий: на сервере несколько человек, поэтому подписка стоит заметно дешевле. Если нужен адрес, закреплённый только за вами, это отдельные тарифы Вейликс с индивидуальным IP."]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const sel = "var(--sel)";
|
||||
const list = this.faq()[this.state.tab];
|
||||
const v = {
|
||||
items: list.map((row, i) => ({
|
||||
q: row[0],
|
||||
a: row[1],
|
||||
off: this.state.open !== i,
|
||||
rot: this.state.open === i ? "rotate(-135deg) translateY(2px)" : "rotate(45deg) translateY(-2px)",
|
||||
toggle: () => this.setState(s => ({ open: s.open === i ? -1 : i }))
|
||||
})),
|
||||
send: () => this.setState({ sent: true }),
|
||||
navOff: this.state.narrow,
|
||||
burgerOff: !this.state.narrow,
|
||||
menuOff: !(this.state.narrow && this.state.menu),
|
||||
toggleMenu: () => this.setState(st => ({ menu: !st.menu })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
toggleLangMenu: () => this.setState(st => ({ langMenu: !st.langMenu })),
|
||||
langMenuOff: !this.state.langMenu,
|
||||
langLabel: this.state.lang === "en" ? "EN" : "RU",
|
||||
ruCheck: this.state.lang === "en" ? "" : "✓",
|
||||
enCheck: this.state.lang === "en" ? "✓" : "",
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
ruBg: this.state.lang === "en" ? "transparent" : sel,
|
||||
enBg: this.state.lang === "en" ? sel : "transparent",
|
||||
ruInk: this.state.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: this.state.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
sendLabel: this.state.sent ? "Отправлено, ответим на почту" : "Отправить",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
for (let i = 0; i < 3; i++) {
|
||||
v["tab" + i] = () => this.setState({ tab: i, open: 0 });
|
||||
v["tab" + i + "Bg"] = this.state.tab === i ? sel : "transparent";
|
||||
v["tab" + i + "Border"] = this.state.tab === i ? "var(--accent)" : "var(--border)";
|
||||
v["tab" + i + "Ink"] = this.state.tab === i ? "var(--ink)" : "var(--ink-2)";
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,281 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-hover: oklch(38% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-1: oklch(86% 0.1 296);
|
||||
--glow-2: oklch(89% 0.09 266);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 296 / 0.05), 0 12px 32px -12px oklch(24% 0.04 296 / 0.14);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-hover: oklch(80% 0.13 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-1: oklch(45% 0.16 296);
|
||||
--glow-2: oklch(40% 0.14 266);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--shadow: 0 1px 2px oklch(10% 0.02 296 / 0.4), 0 16px 40px -16px oklch(8% 0.02 296 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
input::placeholder, textarea::placeholder{ color: var(--ink-3); }
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
<div style="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="border-bottom: 1px solid var(--border); background: var(--bg);">
|
||||
<div style="max-width: 1080px; margin: 0 auto; padding: 16px 24px; display: flex; flex-wrap: wrap; align-items: center; gap: 14px 24px;">
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 26px; height: 26px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 19px; height: 19px; 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: 19px; height: 19px; border-radius: 6px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 16px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</a>
|
||||
<div style="margin-left: auto; display: flex; align-items: center; gap: 12px; font-size: 14px; color: var(--ink-3);">
|
||||
<button type="button" onClick="{{ toggleLang }}" title="{{ langTitle }}" style="display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; border-radius: 10px; border: 1px solid var(--border); background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<span>Оформление заказа</span>
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Отменить</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main style="max-width: 1080px; margin: 0 auto; padding: 40px 24px 80px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px; align-items: start;">
|
||||
|
||||
<div style="display: grid; gap: 16px;">
|
||||
|
||||
<div style="border: 1px solid {{ step1Border }}; border-radius: 20px; background: var(--surface); box-shadow: var(--shadow); overflow: hidden;">
|
||||
<div style="display: flex; align-items: center; gap: 14px; padding: 22px 24px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: {{ step1Dot }}; color: {{ step1DotInk }}; font-size: 13px; font-weight: 500; flex: none;">1</span>
|
||||
<span style="flex: 1; font-size: 18px; font-weight: 500; letter-spacing: -0.02em;">Почта</span>
|
||||
<span hidden="{{ emailNotDone }}" style="font-size: 14px; color: var(--ink-2);">{{ emailValue }}</span>
|
||||
<button type="button" hidden="{{ emailNotDone }}" onClick="{{ editEmail }}" style="border: 0; background: transparent; color: var(--accent); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">Изменить</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ step1Off }}" style="padding: 0 24px 24px;">
|
||||
<p style="margin: 0 0 16px; max-width: 46em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Введите почту — пришлём одноразовый код. Отдельной регистрации нет: если вы у нас уже были, попадёте в свой кабинет, если нет — он создастся сам.</p>
|
||||
<div hidden="{{ codeSent }}" style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<input type="email" value="{{ emailValue }}" onInput="{{ onEmail }}" placeholder="you@example.com" style="flex: 1; min-width: 220px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px;" />
|
||||
<button type="button" onClick="{{ sendCode }}" style="height: 50px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Прислать код</button>
|
||||
</div>
|
||||
<div hidden="{{ codeNotSent }}" style="display: grid; gap: 12px;">
|
||||
<div style="font-size: 14px; color: var(--ink-2);">{{ codeLine }}</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<input type="text" inputmode="numeric" placeholder="6 цифр" style="flex: 1; min-width: 180px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 17px; letter-spacing: 0.28em; font-variant-numeric: tabular-nums;" />
|
||||
<button type="button" onClick="{{ confirmCode }}" style="height: 50px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">Продолжить</button>
|
||||
</div>
|
||||
<button type="button" onClick="{{ editEmail }}" style="justify-self: start; border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 14px; cursor: pointer; padding: 0;">Другая почта</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid {{ step2Border }}; border-radius: 20px; background: var(--surface); box-shadow: var(--shadow); overflow: hidden;">
|
||||
<div style="display: flex; align-items: center; gap: 14px; padding: 22px 24px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: {{ step2Dot }}; color: {{ step2DotInk }}; font-size: 13px; font-weight: 500; flex: none;">2</span>
|
||||
<span style="flex: 1; font-size: 18px; font-weight: 500; letter-spacing: -0.02em;">Оплата</span>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ step2Off }}" style="padding: 0 24px 24px; display: grid; gap: 16px;">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
||||
<button type="button" onClick="{{ payCard }}" style="flex: 1; min-width: 140px; padding: 14px 16px; border-radius: 14px; border: 1px solid {{ cardBorder }}; background: {{ cardBg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">Карта</button>
|
||||
<button type="button" onClick="{{ payMir }}" style="flex: 1; min-width: 140px; padding: 14px 16px; border-radius: 14px; border: 1px solid {{ mirBorder }}; background: {{ mirBg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">Карта МИР</button>
|
||||
<button type="button" onClick="{{ payCrypto }}" style="flex: 1; min-width: 140px; padding: 14px 16px; border-radius: 14px; border: 1px solid {{ cryptoBorder }}; background: {{ cryptoBg }}; color: var(--ink); font-family: inherit; font-size: 15px; text-align: left; cursor: pointer;">Криптовалюта</button>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ cryptoChosen }}" style="display: grid; gap: 10px;">
|
||||
<input type="text" placeholder="Номер карты" style="height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%; font-variant-numeric: tabular-nums;" />
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
||||
<input type="text" placeholder="ММ / ГГ" style="flex: 1; min-width: 120px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; font-variant-numeric: tabular-nums;" />
|
||||
<input type="text" placeholder="CVC" style="flex: 1; min-width: 120px; height: 50px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; font-variant-numeric: tabular-nums;" />
|
||||
</div>
|
||||
<div style="font-size: 13px; color: var(--ink-3); line-height: 1.5;">Карта сохранится для продления. Отменить или заменить её можно в кабинете в любой момент.</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ cardChosen }}" style="display: grid; gap: 10px; padding: 18px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border);">
|
||||
<div style="font-size: 15px;">USDT, BTC или ETH</div>
|
||||
<div style="font-size: 14px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">После нажатия покажем адрес и сумму. При оплате криптой продление не автоматическое — пришлём напоминание за три дня до конца месяца.</div>
|
||||
</div>
|
||||
|
||||
<label style="display: flex; align-items: flex-start; gap: 10px; font-size: 14px; color: var(--ink-2); font-weight: 300; line-height: 1.5; cursor: pointer;">
|
||||
<input type="checkbox" style="margin-top: 3px; width: 16px; height: 16px; accent-color: var(--accent);" />
|
||||
<span>Согласен с условиями использования и политикой конфиденциальности</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside style="position: sticky; top: 24px; display: grid; gap: 16px;">
|
||||
<div style="padding: clamp(20px, 4.6vw, 26px); border-radius: 20px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 16px;">Ваш заказ</div>
|
||||
<div style="display: grid; gap: 6px; margin-bottom: 20px;">
|
||||
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.025em;">Персональный VPN</div>
|
||||
<div style="font-size: 15px; color: var(--ink-2);">Нидерланды · Амстердам</div>
|
||||
<div style="display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--accent);">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: block;"></span>
|
||||
Сервер свободен, доступ сразу после оплаты
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; gap: 10px; padding-top: 18px; border-top: 1px solid var(--border); font-size: 15px;">
|
||||
<div style="display: flex; justify-content: space-between; gap: 12px; color: var(--ink-2);"><span>Первый месяц</span><span style="font-variant-numeric: tabular-nums;">4 900 ₽</span></div>
|
||||
<div style="display: flex; justify-content: space-between; gap: 12px; color: var(--ink-2);"><span>RU-адрес</span><span>включён</span></div>
|
||||
<div style="display: flex; justify-content: space-between; gap: 12px; color: var(--ink-2);"><span>Устройств</span><span>до 5</span></div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border);">
|
||||
<span style="font-size: 16px; font-weight: 500;">К оплате сегодня</span>
|
||||
<span style="font-size: 26px; font-weight: 600; letter-spacing: -0.03em; font-variant-numeric: tabular-nums;">4 900 ₽</span>
|
||||
</div>
|
||||
<div style="margin-top: 8px; font-size: 13px; color: var(--ink-3);">{{ nextChargeLine }}</div>
|
||||
<button type="button" onClick="{{ pay }}" disabled="{{ payDisabled }}" style="margin-top: 20px; width: 100%; height: 52px; border-radius: 14px; border: 0; background: {{ payBg }}; color: {{ payInk }}; font-family: inherit; font-size: 16px; font-weight: 500; cursor: {{ payCursor }};">{{ payLabel }}</button>
|
||||
<div hidden="{{ paidOff }}" style="margin-top: 14px; padding: 14px; border-radius: 12px; background: var(--accent-soft); font-size: 14px; line-height: 1.5; color: var(--ink);">Оплачено. Ключ уже в кабинете — <a href="App.dc.html">открыть</a>.</div>
|
||||
</div>
|
||||
</aside>
|
||||
</main>
|
||||
</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, email: "", sent: false, authed: false, method: "card", paid: false, lang: "ru" };
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
const authed = s.authed;
|
||||
const nd = new Date();
|
||||
nd.setMonth(nd.getMonth() + 1);
|
||||
const months = ["января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"];
|
||||
const monthsEn = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
const en = s.lang === "en";
|
||||
const sel = "var(--accent-soft)";
|
||||
return {
|
||||
emailValue: s.email,
|
||||
toggleLang: () => this.setLang(s.lang === "en" ? "ru" : "en"),
|
||||
langLabel: s.lang === "en" ? "EN" : "RU",
|
||||
langTitle: s.lang === "en" ? "Язык: английский" : "Язык: русский",
|
||||
onEmail: (e) => this.setState({ email: e.target.value }),
|
||||
sendCode: () => this.setState({ sent: true }),
|
||||
confirmCode: () => this.setState({ authed: true }),
|
||||
editEmail: () => this.setState({ sent: false, authed: false }),
|
||||
codeSent: s.sent,
|
||||
codeNotSent: !s.sent,
|
||||
emailNotDone: !authed,
|
||||
step1Off: authed,
|
||||
step1Border: authed ? "var(--border)" : "var(--accent)",
|
||||
step1Dot: authed ? "var(--accent)" : "var(--accent-soft)",
|
||||
step1DotInk: authed ? "var(--on-accent)" : "var(--accent)",
|
||||
step2Off: !authed,
|
||||
step2Border: authed ? "var(--accent)" : "var(--border)",
|
||||
step2Dot: authed ? "var(--accent-soft)" : "var(--surface-2)",
|
||||
step2DotInk: authed ? "var(--accent)" : "var(--ink-3)",
|
||||
payCard: () => this.setState({ method: "card" }),
|
||||
payMir: () => this.setState({ method: "mir" }),
|
||||
payCrypto: () => this.setState({ method: "crypto" }),
|
||||
cardBg: s.method === "card" ? sel : "transparent",
|
||||
mirBg: s.method === "mir" ? sel : "transparent",
|
||||
cryptoBg: s.method === "crypto" ? sel : "transparent",
|
||||
cardBorder: s.method === "card" ? "var(--accent)" : "var(--border)",
|
||||
mirBorder: s.method === "mir" ? "var(--accent)" : "var(--border)",
|
||||
cryptoBorder: s.method === "crypto" ? "var(--accent)" : "var(--border)",
|
||||
cardChosen: s.method !== "crypto",
|
||||
cryptoChosen: s.method === "crypto",
|
||||
nextCharge: "первое списание " + nd.getDate() + " " + months[nd.getMonth()],
|
||||
nextChargeLine: en
|
||||
? "Then $24 per month, first charge on " + nd.getDate() + " " + monthsEn[nd.getMonth()]
|
||||
: "Далее 2 499 ₽ в месяц, первое списание " + nd.getDate() + " " + months[nd.getMonth()],
|
||||
codeLine: en
|
||||
? "The code has been sent to " + s.email + ". It is valid for 10 minutes."
|
||||
: "Код отправлен на " + s.email + ". Он действует 10 минут.",
|
||||
pay: () => { if (authed) this.setState({ paid: true }); },
|
||||
payDisabled: !authed,
|
||||
payBg: authed ? "var(--accent)" : "var(--surface-2)",
|
||||
payInk: authed ? "var(--on-accent)" : "var(--ink-3)",
|
||||
payCursor: authed ? "pointer" : "not-allowed",
|
||||
payLabel: s.paid ? "Оплачено" : (s.method === "crypto" ? "Показать адрес для оплаты" : "Оплатить 4 900 ₽"),
|
||||
paidOff: !s.paid
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,501 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-hover: oklch(38% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-1: oklch(86% 0.1 296);
|
||||
--glow-2: oklch(89% 0.09 266);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 296 / 0.05), 0 12px 32px -12px oklch(24% 0.04 296 / 0.14);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-hover: oklch(80% 0.13 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-1: oklch(45% 0.16 296);
|
||||
--glow-2: oklch(40% 0.14 266);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--shadow: 0 1px 2px oklch(10% 0.02 296 / 0.4), 0 16px 40px -16px oklch(8% 0.02 296 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
@keyframes veilDrift{
|
||||
0%{ transform: translate3d(0,0,0) scale(1); }
|
||||
50%{ transform: translate3d(2%,-3%,0) scale(1.06); }
|
||||
100%{ transform: translate3d(0,0,0) scale(1); }
|
||||
}
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
<div style="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 88px; flex: none; white-space: nowrap;">Вейликс</span>
|
||||
</a>
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="#top" style="color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="App.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="#locations" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="Price.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="App.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#locations" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1200px; margin: 0 auto; padding: 0 24px 80px;">
|
||||
|
||||
<section style="position: relative; overflow: hidden; isolation: isolate; -webkit-transform: translateZ(0); transform: translateZ(0); margin-top: 40px; border-radius: 28px; border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; right: min(-80px, calc(-140px + 11vw)); top: min(-120px, calc(-180px + 11vw)); width: clamp(180px, 38vw, 420px); height: clamp(180px, 38vw, 420px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-1) 0%, color-mix(in oklab, var(--glow-1) 35%, transparent) 55%, transparent 78%); filter: blur(70px); opacity: 0.5; animation: veilDrift 18s ease-in-out infinite;"></div>
|
||||
<div style="position: absolute; left: min(-60px, calc(-120px + 11vw)); bottom: min(-140px, calc(-200px + 11vw)); width: clamp(150px, 32vw, 360px); height: clamp(150px, 32vw, 360px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, var(--glow-3) 0%, color-mix(in oklab, var(--glow-3) 35%, transparent) 55%, transparent 78%); filter: blur(70px); opacity: 0.4;"></div>
|
||||
<div style="position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(28px, 5vw, 48px); padding: clamp(28px, 6vw, 56px) clamp(20px, 5vw, 48px);">
|
||||
<div>
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px;">
|
||||
<span style="font-size: clamp(56px, 8vw, 96px); font-weight: 600; letter-spacing: -0.045em; line-height: 0.95; font-variant-numeric: tabular-nums;">4 900 ₽</span>
|
||||
<span style="font-size: 18px; color: var(--ink-2);">первый месяц</span>
|
||||
</div>
|
||||
<div style="margin-top: 16px; font-size: 20px; color: var(--ink-2); font-variant-numeric: tabular-nums;">далее 2 499 ₽ в месяц</div>
|
||||
<p style="margin: 20px 0 0; max-width: 30em; font-size: 15px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">В первый месяц входит подготовка сервера под вас. Дальше — только абонентская плата, отменить можно в кабинете.</p>
|
||||
</div>
|
||||
<div style="display: grid; gap: 14px; align-content: start;">
|
||||
<div style="font-size: 15px; font-weight: 500;">Что входит</div>
|
||||
<div style="display: grid; gap: 12px; font-size: 15px; color: var(--ink-2); font-weight: 300;">
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Индивидуальный IP в выбранной стране, только ваш</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Выделенный RU-адрес: прямое соединение и мост в обе стороны</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>До 5 устройств с одним и тем же адресом</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Два типа подключения: WireGuard и Xray</span></div>
|
||||
<div style="display: flex; gap: 12px;"><span style="color: var(--accent);">—</span><span>Умная маршрутизация RU / Глобал без переключений</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="locations" style="padding: 72px 0 0;">
|
||||
<div style="display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 8px;">
|
||||
<h2 style="margin: 0; font-size: clamp(28px, 3vw, 38px); font-weight: 600; letter-spacing: -0.03em;">Выберите локацию</h2>
|
||||
</div>
|
||||
<p style="margin: 0 0 28px; max-width: 52em; font-size: 16px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Где-то сервер уже стоит и доступ выдаётся сразу. Если свободного нет, мы устанавливаем сервер лично под вас — на это уходит до 24 часов, и вы видите этот статус честно, до оплаты.</p>
|
||||
<p style="margin: -16px 0 28px; max-width: 52em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Базовая цена — 4 900 ₽ первый месяц и 2 499 ₽ далее. В редких локациях она выше и указана на карточке.</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;">
|
||||
<sc-for list="{{ items }}" as="loc" hint-placeholder-count="12">
|
||||
<div role="button" tabindex="0" onClick="{{ loc.pick }}" style="display: grid; align-content: start; gap: 14px; padding: 22px; border-radius: 22px; border: 1px solid {{ loc.border }}; background: {{ loc.bg }}; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="display: flex; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
{{ loc.flag }}
|
||||
<span style="display: flex; align-items: center; gap: 12px; flex: none; min-height: 32px;">
|
||||
<span onClick="{{ loc.buy }}" hidden="{{ loc.buyOff }}" style="display: flex; align-items: center; justify-content: center; height: 32px; padding: 0 16px; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 14px; font-weight: 500; cursor: pointer;">Купить</span>
|
||||
<span hidden="{{ loc.dotOff }}" style="width: 11px; height: 11px; border-radius: 50%; background: {{ loc.dot }}; display: block; flex: none;"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="display: grid; gap: 5px;">
|
||||
<span style="font-size: 19px; font-weight: 500; letter-spacing: -0.02em;">{{ loc.country }}</span>
|
||||
<span style="font-size: 14px; color: var(--ink-3);">{{ loc.city }}</span>
|
||||
</span>
|
||||
<span style="display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: {{ loc.statusInk }};">
|
||||
<span style="width: 6px; height: 6px; border-radius: 50%; background: {{ loc.statusDot }}; border: {{ loc.statusRing }}; display: block;"></span>
|
||||
{{ loc.status }}
|
||||
</span>
|
||||
<span style="display: grid; gap: 12px; padding-top: 14px; border-top: 1px solid var(--border);">
|
||||
<span style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;">
|
||||
<span style="font-size: 17px; font-weight: 500; color: var(--ink);">{{ loc.setup }}</span>
|
||||
первый месяц · далее
|
||||
<span style="font-weight: 500; color: var(--ink);">{{ loc.month }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section style="padding: 72px 0 0;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
||||
<div style="padding: clamp(16px, 4vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<h3 style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em;">Нужно несколько локаций</h3>
|
||||
<p style="margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Каждый тариф — отдельный индивидуальный IP. Берёте второй тариф в другой стране, оба ключа лежат в одном кабинете и продлеваются независимо.</p>
|
||||
</div>
|
||||
<div style="position: relative; isolation: isolate; padding: clamp(16px, 4vw, 32px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div style="position: absolute; inset: 0; border-radius: 22px; overflow: hidden; pointer-events: none; -webkit-mask-image: -webkit-radial-gradient(white, black); mask-image: radial-gradient(white, black);">
|
||||
<div style="position: absolute; right: -70px; bottom: -90px; width: 260px; height: 260px; border-radius: 50%; background: var(--glow-1); filter: blur(60px); opacity: 0.5;"></div>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<h3 id="lead" style="margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -0.025em; scroll-margin-top: 90px;">Нужны индивидуальные условия</h3>
|
||||
<p style="margin: 0 0 20px; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Команда, много адресов, редкая страна или свой сценарий маршрутизации. Оставьте заявку — ответим в течение дня.</p>
|
||||
<button type="button" onClick="{{ openLead }}" hidden="{{ leadOpen }}" style="display: inline-flex; align-items: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 1px solid var(--accent); background: transparent; color: var(--accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-soft);">Оставить заявку</button>
|
||||
<div hidden="{{ leadClosed }}" style="display: grid; gap: 10px; max-width: 420px;">
|
||||
<input type="text" placeholder="Имя" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="email" placeholder="Email" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;">
|
||||
<input type="text" placeholder="Telegram" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<input type="text" placeholder="WhatsApp" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
</div>
|
||||
<textarea rows="3" placeholder="Коротко о задаче" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ sendLead }}" style="display: inline-flex; align-items: center; justify-content: center; height: 48px; padding: 0 22px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ leadButton }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Персональные VPN-серверы с индивидуальным IP. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 Вейликс</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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, loc: 0, lead: false, leadSent: false, lang: "ru", narrow: false, menu: false, langMenu: false };
|
||||
|
||||
data() {
|
||||
const base = { setup: "4 900 ₽", month: "2 499 ₽" };
|
||||
const rare = { setup: "6 900 ₽", month: "3 499 ₽" };
|
||||
const far = { setup: "9 900 ₽", month: "4 999 ₽" };
|
||||
const L = (country, city, code, lng, lat, ready, tier) => ({ country, city, code, lng, lat, ready, setup: tier.setup, month: tier.month, rare: tier !== base });
|
||||
return [
|
||||
L("Нидерланды", "Амстердам", "nl", 4.9, 52.37, true, base),
|
||||
L("Германия", "Франкфурт", "de", 8.68, 50.11, true, base),
|
||||
L("США", "Нью-Йорк", "us", -74.01, 40.71, true, base),
|
||||
L("Великобритания", "Лондон", "gb", -0.13, 51.51, true, base),
|
||||
L("Франция", "Париж", "fr", 2.35, 48.86, true, base),
|
||||
L("Финляндия", "Хельсинки", "fi", 24.94, 60.17, true, base),
|
||||
L("Швеция", "Стокгольм", "se", 18.07, 59.33, true, base),
|
||||
L("Норвегия", "Осло", "no", 10.75, 59.91, false, base),
|
||||
L("Дания", "Копенгаген", "dk", 12.57, 55.68, false, base),
|
||||
L("Швейцария", "Цюрих", "ch", 8.54, 47.37, true, rare),
|
||||
L("Австрия", "Вена", "at", 16.37, 48.21, true, base),
|
||||
L("Испания", "Мадрид", "es", -3.7, 40.42, true, base),
|
||||
L("Италия", "Милан", "it", 9.19, 45.46, true, base),
|
||||
L("Польша", "Варшава", "pl", 21.01, 52.23, true, base),
|
||||
L("Чехия", "Прага", "cz", 14.44, 50.08, true, base),
|
||||
L("Румыния", "Бухарест", "ro", 26.1, 44.43, false, base),
|
||||
L("Литва", "Вильнюс", "lt", 25.28, 54.69, true, base),
|
||||
L("Латвия", "Рига", "lv", 24.11, 56.95, true, base),
|
||||
L("Эстония", "Таллин", "ee", 24.75, 59.44, false, base),
|
||||
L("Турция", "Стамбул", "tr", 28.98, 41.01, true, base),
|
||||
L("Казахстан", "Алматы", "kz", 76.89, 43.24, false, base),
|
||||
L("Армения", "Ереван", "am", 44.51, 40.18, false, base),
|
||||
L("Грузия", "Тбилиси", "ge", 44.8, 41.72, false, base),
|
||||
L("ОАЭ", "Дубай", "ae", 55.27, 25.2, true, rare),
|
||||
L("Сингапур", "Сингапур", "sg", 103.82, 1.35, true, rare),
|
||||
L("Япония", "Токио", "jp", 139.69, 35.69, true, rare),
|
||||
L("Южная Корея", "Сеул", "kr", 126.98, 37.57, false, rare),
|
||||
L("Канада", "Торонто", "ca", -79.38, 43.65, true, base),
|
||||
L("Австралия", "Сидней", "au", 151.21, -33.87, false, far),
|
||||
L("Бразилия", "Сан-Паулу", "br", -46.63, -23.55, false, far)
|
||||
];
|
||||
}
|
||||
|
||||
flagSpecs() {
|
||||
return {
|
||||
nl: { rows: ["#AE1C28", "#FFFFFF", "#21468B"] },
|
||||
de: { rows: ["#000000", "#DD0000", "#FFCE00"] },
|
||||
us: { rows: ["#B22234", "#FFFFFF", "#B22234", "#FFFFFF", "#B22234", "#FFFFFF"], marks: [{ l: 0, t: 0, w: 22, h: 17, bg: "#3C3B6E" }] },
|
||||
gb: { bg: "#012169", marks: [{ l: 17, t: 0, w: 12, h: 32, bg: "#FFFFFF" }, { l: 0, t: 10, w: 46, h: 12, bg: "#FFFFFF" }, { l: 20, t: 0, w: 6, h: 32, bg: "#C8102E" }, { l: 0, t: 13, w: 46, h: 6, bg: "#C8102E" }] },
|
||||
fr: { cols: ["#002395", "#FFFFFF", "#ED2939"] },
|
||||
fi: { bg: "#FFFFFF", marks: [{ l: 13, t: 0, w: 8, h: 32, bg: "#003580" }, { l: 0, t: 12, w: 46, h: 8, bg: "#003580" }] },
|
||||
se: { bg: "#006AA7", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FECC00" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FECC00" }] },
|
||||
no: { bg: "#BA0C2F", marks: [{ l: 12, t: 0, w: 10, h: 32, bg: "#FFFFFF" }, { l: 0, t: 11, w: 46, h: 10, bg: "#FFFFFF" }, { l: 15, t: 0, w: 4, h: 32, bg: "#00205B" }, { l: 0, t: 14, w: 46, h: 4, bg: "#00205B" }] },
|
||||
dk: { bg: "#C8102E", marks: [{ l: 13, t: 0, w: 6, h: 32, bg: "#FFFFFF" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FFFFFF" }] },
|
||||
ch: { bg: "#D52B1E", marks: [{ l: 20, t: 8, w: 6, h: 16, bg: "#FFFFFF" }, { l: 15, t: 13, w: 16, h: 6, 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: 18, h: 32, 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: 11, t: 8, w: 16, h: 16, r: "50%", bg: "#FFFFFF" }, { l: 16, t: 8, w: 16, h: 16, r: "50%", bg: "#E30A17" }] },
|
||||
kz: { bg: "#00AFCA", marks: [{ l: 16, t: 8, w: 15, h: 15, r: "50%", bg: "#FEC50C" }] },
|
||||
am: { rows: ["#D90012", "#0033A0", "#F2A800"] },
|
||||
ge: { bg: "#FFFFFF", marks: [{ l: 20, t: 0, w: 6, h: 32, bg: "#FF0000" }, { l: 0, t: 13, w: 46, h: 6, bg: "#FF0000" }] },
|
||||
ae: { rows: ["#00732F", "#FFFFFF", "#000000"], marks: [{ l: 0, t: 0, w: 11, h: 32, bg: "#FF0000" }] },
|
||||
sg: { rows: ["#EF3340", "#FFFFFF"], marks: [{ l: 5, t: 3, w: 11, h: 11, r: "50%", bg: "#FFFFFF" }, { l: 9, t: 3, w: 11, h: 11, r: "50%", bg: "#EF3340" }] },
|
||||
jp: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#BC002D" }] },
|
||||
kr: { bg: "#FFFFFF", marks: [{ l: 16, t: 9, w: 14, h: 14, r: "50%", bg: "#CD2E3A" }, { l: 16, t: 9, w: 14, h: 14, 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: 20, t: 9, w: 7, h: 13, bg: "#D80621", clip: "polygon(50% 0, 100% 100%, 0 100%)" }] },
|
||||
au: { bg: "#00247D", marks: [{ l: 9, t: 0, w: 4, h: 16, bg: "#FFFFFF" }, { l: 0, t: 6, w: 22, h: 4, bg: "#FFFFFF" }, { l: 30, t: 19, w: 7, h: 7, r: "50%", bg: "#FFFFFF" }] },
|
||||
br: { bg: "#009739", marks: [{ l: 9, t: 4, w: 28, h: 24, bg: "#FEDD00", clip: "polygon(50% 0, 100% 50%, 50% 100%, 0 50%)" }, { l: 18, t: 11, w: 10, h: 10, r: "50%", bg: "#012169" }] }
|
||||
};
|
||||
}
|
||||
|
||||
flag(code) {
|
||||
const h = React.createElement;
|
||||
const spec = this.flagSpecs()[code] || {};
|
||||
const style = {
|
||||
width: "46px", height: "32px", borderRadius: "6px", 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);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
this.mq = mq;
|
||||
this.onSys = () => this.setState({ sysDark: mq.matches });
|
||||
mq.addEventListener("change", this.onSys);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const sel = "var(--accent-soft)";
|
||||
const names = ["Нидерланды · Амстердам", "Германия · Франкфурт", "США · Нью-Йорк", "Финляндия · Хельсинки", "Турция · Стамбул", "Япония · Токио"];
|
||||
const ready = [true, true, true, false, false, false];
|
||||
const i = this.state.loc;
|
||||
const v = {
|
||||
items: this.data().map((d, n) => ({
|
||||
country: d.country,
|
||||
city: d.city,
|
||||
flag: this.flag(d.code),
|
||||
status: d.ready ? "Серверы в наличии" : "Установим за 24 часа",
|
||||
statusInk: d.ready ? "var(--accent)" : "var(--ink-3)",
|
||||
statusDot: d.ready ? "var(--accent)" : "transparent",
|
||||
statusRing: d.ready ? "0" : "1.5px solid var(--ink-3)",
|
||||
bg: this.state.loc === n ? sel : "var(--surface)",
|
||||
border: this.state.loc === n ? "var(--accent)" : "var(--border)",
|
||||
dot: this.state.loc === n ? "var(--accent)" : "transparent",
|
||||
dotOff: this.state.loc === n,
|
||||
buy: (e) => { e.stopPropagation(); window.location.href = "Order.dc.html"; },
|
||||
setup: d.setup,
|
||||
month: d.month,
|
||||
buyOff: this.state.loc !== n,
|
||||
pick: () => this.setState({ loc: n })
|
||||
})),
|
||||
pickedTitle: names[i],
|
||||
pickedHint: ready[i] ? "Сервер свободен — доступ выдаём сразу после оплаты" : "Свободного сервера нет: установим лично под вас, доступ в течение 24 часов",
|
||||
openLead: () => this.setState({ lead: true }),
|
||||
navOff: this.state.narrow,
|
||||
burgerOff: !this.state.narrow,
|
||||
menuOff: !(this.state.narrow && this.state.menu),
|
||||
toggleMenu: () => this.setState(st => ({ menu: !st.menu })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
toggleLangMenu: () => this.setState(st => ({ langMenu: !st.langMenu })),
|
||||
langMenuOff: !this.state.langMenu,
|
||||
langLabel: this.state.lang === "en" ? "EN" : "RU",
|
||||
ruCheck: this.state.lang === "en" ? "" : "✓",
|
||||
enCheck: this.state.lang === "en" ? "✓" : "",
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
ruBg: this.state.lang === "en" ? "transparent" : sel,
|
||||
enBg: this.state.lang === "en" ? sel : "transparent",
|
||||
ruInk: this.state.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: this.state.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
sendLead: () => this.setState({ leadSent: true }),
|
||||
leadOpen: this.state.lead,
|
||||
leadClosed: !this.state.lead,
|
||||
leadButton: this.state.leadSent ? "Заявка отправлена" : "Отправить",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
for (let n = 0; n < 6; n++) {
|
||||
v["loc" + n + "Bg"] = this.state.loc === n ? sel : "var(--surface)";
|
||||
v["loc" + n + "Border"] = this.state.loc === n ? "var(--accent)" : "var(--border)";
|
||||
v["loc" + n + "Dot"] = this.state.loc === n ? "var(--accent)" : "transparent";
|
||||
v["pickLoc" + n] = () => this.setState({ loc: n });
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,367 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600&family=Unbounded:wght@500;600&display=swap" rel="stylesheet" />
|
||||
<script>(function(){if(window.__veilixI18nSrc)return;window.__veilixI18nSrc="./i18n.js?v="+Date.now().toString(36)+Math.random().toString(36).slice(2,8);var s=document.createElement("script");s.src=window.__veilixI18nSrc;document.head.appendChild(s);})();</script>
|
||||
<style>
|
||||
:root{
|
||||
--bg: oklch(99% 0.005 296);
|
||||
--surface: oklch(100% 0 0);
|
||||
--surface-2: oklch(97.5% 0.01 296);
|
||||
--ink: oklch(24% 0.04 296);
|
||||
--ink-2: oklch(45% 0.03 296);
|
||||
--ink-3: oklch(60% 0.025 296);
|
||||
--border: oklch(92% 0.014 296);
|
||||
--accent: oklch(45% 0.17 296);
|
||||
--accent-hover: oklch(38% 0.17 296);
|
||||
--accent-soft: oklch(95.5% 0.028 296);
|
||||
--on-accent: oklch(99% 0.01 296);
|
||||
--glow-1: oklch(86% 0.1 296);
|
||||
--glow-2: oklch(89% 0.09 266);
|
||||
--glow-3: oklch(91% 0.08 332);
|
||||
--veil: oklch(100% 0 0 / 0.55);
|
||||
--shadow: 0 1px 2px oklch(24% 0.04 296 / 0.05), 0 12px 32px -12px oklch(24% 0.04 296 / 0.14);
|
||||
}
|
||||
:root[data-veilix-theme="dark"]{
|
||||
--bg: oklch(17% 0.022 296);
|
||||
--surface: oklch(21% 0.026 296);
|
||||
--surface-2: oklch(24% 0.03 296);
|
||||
--ink: oklch(96% 0.01 296);
|
||||
--ink-2: oklch(79% 0.02 296);
|
||||
--ink-3: oklch(63% 0.025 296);
|
||||
--border: oklch(31% 0.032 296);
|
||||
--accent: oklch(74% 0.15 296);
|
||||
--accent-hover: oklch(80% 0.13 296);
|
||||
--accent-soft: oklch(27% 0.05 296);
|
||||
--on-accent: oklch(17% 0.03 296);
|
||||
--glow-1: oklch(45% 0.16 296);
|
||||
--glow-2: oklch(40% 0.14 266);
|
||||
--glow-3: oklch(42% 0.13 332);
|
||||
--veil: oklch(100% 0 0 / 0.07);
|
||||
--shadow: 0 1px 2px oklch(10% 0.02 296 / 0.4), 0 16px 40px -16px oklch(8% 0.02 296 / 0.7);
|
||||
}
|
||||
*{ box-sizing: border-box; }
|
||||
input, textarea, select{ font-size: 16px; }
|
||||
[hidden]{ display: none !important; }
|
||||
body{ margin:0; background: var(--bg); color: var(--ink); font-family:"Commissioner", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
||||
a{ color: var(--accent); text-decoration: none; }
|
||||
a:hover{ color: var(--accent-hover); }
|
||||
</style>
|
||||
</helmet>
|
||||
|
||||
<template id="__bundler_thumbnail" data-bg-color="#efeaf8">
|
||||
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="480" y="290" width="150" height="150" rx="48" fill="#5b32c9" />
|
||||
<rect x="570" y="360" width="150" height="150" rx="48" fill="#ffffff" fill-opacity="0.62" stroke="#5b32c9" stroke-width="6" />
|
||||
</svg>
|
||||
</template>
|
||||
<div style="background: var(--bg); color: var(--ink); font-family: 'Commissioner', system-ui, sans-serif; font-weight: 400; letter-spacing: -0.01em; min-height: 100vh;">
|
||||
|
||||
<header style="position: sticky; top: 0; z-index: 40; background: color-mix(in oklab, var(--bg) 82%, transparent); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border-bottom: 1px solid var(--border);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 14px 24px; min-height: 66px; display: flex; flex-wrap: nowrap; align-items: center; gap: 14px 40px;">
|
||||
<a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none;">
|
||||
<span style="position: relative; width: 28px; height: 28px; display: block; flex: none;">
|
||||
<span style="position: absolute; left: 0; top: 3px; width: 21px; height: 21px; border-radius: 7px; 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: 21px; height: 21px; border-radius: 7px; background: var(--veil); border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent); backdrop-filter: blur(2px); display: block;"></span>
|
||||
</span>
|
||||
<span style="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 17px; letter-spacing: -0.02em; width: 88px; flex: none; white-space: nowrap;">Вейликс</span>
|
||||
</a>
|
||||
<nav hidden="{{ navOff }}" style="display: flex; flex-wrap: wrap; gap: 10px 28px; font-size: 15px; font-weight: 500; color: var(--ink-2);">
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="#top" style="color: var(--ink);">Поддержка</a>
|
||||
</nav>
|
||||
<div style="margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;">
|
||||
<div hidden="{{ navOff }}" style="position: relative;">
|
||||
<button type="button" onClick="{{ toggleLangMenu }}" title="Язык" style="display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 11px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-2); font-family: inherit; font-size: 14px; cursor: pointer;" style-hover="color: var(--ink); border-color: var(--ink-3);">
|
||||
<span style="position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block; flex: none;">
|
||||
<span style="position: absolute; left: -1.5px; top: 5.75px; width: 16px; height: 1.5px; background: currentColor; display: block;"></span>
|
||||
<span style="position: absolute; left: 3.25px; top: -1.5px; width: 8px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; display: block;"></span>
|
||||
</span>
|
||||
<span style="font-variant-numeric: tabular-nums;">{{ langLabel }}</span>
|
||||
</button>
|
||||
<div hidden="{{ langMenuOff }}" style="position: absolute; right: 0; top: 46px; width: 172px; padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); font-size: 14px;">
|
||||
<button type="button" onClick="{{ setRu }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>Русский</span>
|
||||
<span style="color: var(--accent);">{{ ruCheck }}</span>
|
||||
</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;">
|
||||
<span>English</span>
|
||||
<span style="color: var(--accent);">{{ enCheck }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="App.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 78px; flex: none; box-sizing: border-box; border-radius: 10px; color: var(--ink-2); font-size: 15px;" style-hover="color: var(--ink); background: var(--surface-2);">Войти</a>
|
||||
<a href="Price.dc.html" hidden="{{ navOff }}" style="display: flex; align-items: center; justify-content: center; height: 38px; width: 142px; flex: none; box-sizing: border-box; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-size: 15px; font-weight: 500; box-shadow: 0 6px 18px -8px var(--accent);" style-hover="background: var(--accent-hover); color: var(--on-accent);">Получить VPN</a>
|
||||
<button type="button" hidden="{{ burgerOff }}" onClick="{{ toggleMenu }}" title="Меню" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; width: 42px; height: 38px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer;" style-hover="border-color: var(--ink-3);">
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
<span style="width: 18px; height: 2px; border-radius: 2px; background: currentColor; display: block;"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden="{{ menuOff }}" style="position: absolute; left: 0; right: 0; top: 100%; z-index: 45; padding: 10px 20px 18px; background: var(--bg); border-bottom: 1px solid var(--border); box-shadow: var(--shadow); display: grid; gap: 2px;">
|
||||
<a href="Price.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Локации</a>
|
||||
<a href="Support.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Поддержка</a>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 6px; margin-top: 6px; border-top: 1px solid var(--border);">
|
||||
<a href="App.dc.html" onClick="{{ closeMenu }}" style="padding: 13px 6px; font-size: 17px; color: var(--ink);">Войти</a>
|
||||
<div style="display: flex; gap: 6px; flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ ruBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">Русский</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px; background: {{ enBg }}; color: var(--ink); font-family: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;">English</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="Price.dc.html" onClick="{{ closeMenu }}" style="display: flex; align-items: center; justify-content: center; height: 50px; margin-top: 12px; border-radius: 12px; background: var(--accent); color: var(--on-accent); font-size: 16px; font-weight: 500;">Получить VPN</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top" style="max-width: 1000px; margin: 0 auto; padding: 0 24px 80px;">
|
||||
|
||||
<section style="padding: 56px 0 8px;">
|
||||
<h1 style="margin: 0 0 14px; font-size: clamp(34px, 4.4vw, 52px); font-weight: 600; letter-spacing: -0.035em;">Поддержка</h1>
|
||||
<div style="height: 32px;"></div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
||||
<a href="#form" style="display: grid; gap: 10px; padding: clamp(20px, 4.8vw, 28px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow); color: var(--ink);" style-hover="border-color: var(--accent); color: var(--ink);">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Telegram</span>
|
||||
<span style="font-size: clamp(24px, 3vw, 32px); font-weight: 600; letter-spacing: -0.03em; color: var(--accent);">@veilix_support</span>
|
||||
<span style="font-size: 14px; color: var(--ink-2); font-weight: 300;">Быстрее всего. Можно писать голосом и скидывать скриншоты.</span>
|
||||
</a>
|
||||
<a href="#form" style="display: grid; gap: 10px; padding: clamp(20px, 4.8vw, 28px); border-radius: 22px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow); color: var(--ink);" style-hover="border-color: var(--accent); color: var(--ink);">
|
||||
<span style="font-size: 13px; color: var(--ink-3);">Почта</span>
|
||||
<span style="font-size: clamp(22px, 2.6vw, 30px); font-weight: 600; letter-spacing: -0.03em; color: var(--accent); word-break: break-word;">help@veilix.cc</span>
|
||||
<span style="font-size: 14px; color: var(--ink-2); font-weight: 300;">Для счетов, возвратов и всего, что нужно письменно.</span>
|
||||
</a>
|
||||
</div>
|
||||
<p style="margin: 16px 0 0; font-size: 14px; color: var(--ink-3);">Отвечаем с 10 до 20 в рабочие дни.</p>
|
||||
</section>
|
||||
|
||||
<section style="padding: 64px 0 0;">
|
||||
<h2 style="margin: 0 0 20px; font-size: clamp(26px, 2.8vw, 34px); font-weight: 600; letter-spacing: -0.03em;">Частые вопросы</h2>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px;">
|
||||
<button type="button" onClick="{{ tab0 }}" style="padding: 9px 16px; border-radius: 10px; border: 1px solid {{ tab0Border }}; background: {{ tab0Bg }}; color: {{ tab0Ink }}; font-family: inherit; font-size: 14px; cursor: pointer;">Подключение</button>
|
||||
<button type="button" onClick="{{ tab1 }}" style="padding: 9px 16px; border-radius: 10px; border: 1px solid {{ tab1Border }}; background: {{ tab1Bg }}; color: {{ tab1Ink }}; font-family: inherit; font-size: 14px; cursor: pointer;">Оплата и продление</button>
|
||||
<button type="button" onClick="{{ tab2 }}" style="padding: 9px 16px; border-radius: 10px; border: 1px solid {{ tab2Border }}; background: {{ tab2Bg }}; color: {{ tab2Ink }}; font-family: inherit; font-size: 14px; cursor: pointer;">Локации и IP</button>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<sc-for list="{{ items }}" as="item" hint-placeholder-count="4">
|
||||
<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: 16px; width: 100%; padding: 20px 22px; background: transparent; border: 0; color: var(--ink); font-family: inherit; font-size: 17px; font-weight: 500; text-align: left; cursor: pointer; letter-spacing: -0.015em;">
|
||||
<span style="flex: 1;">{{ item.q }}</span>
|
||||
<span style="flex: none; width: 10px; height: 10px; 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 22px 22px; font-size: 15px; line-height: 1.6; color: var(--ink-2); font-weight: 300; max-width: 62ch;">{{ item.a }}</div>
|
||||
</div>
|
||||
</sc-for>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="form" style="padding: 64px 0 0;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 40px; padding: 40px; border-radius: 24px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow);">
|
||||
<div>
|
||||
<h2 style="margin: 0 0 10px; font-size: clamp(24px, 2.6vw, 30px); font-weight: 600; letter-spacing: -0.03em;">Написать нам</h2>
|
||||
<p style="margin: 0; max-width: 26em; font-size: 15px; line-height: 1.55; color: var(--ink-2); font-weight: 300;">Если уже купили ключ, укажите почту, на которую он оформлен — так мы сразу увидим вашу конфигурацию.</p>
|
||||
</div>
|
||||
<div style="display: grid; gap: 10px;">
|
||||
<input type="email" placeholder="Ваша почта" style="height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;" />
|
||||
<select style="height: 48px; padding: 0 12px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; width: 100%;">
|
||||
<option>Тема: подключение</option>
|
||||
<option>Тема: оплата и продление</option>
|
||||
<option>Тема: локации и IP</option>
|
||||
<option>Тема: другое</option>
|
||||
</select>
|
||||
<textarea rows="5" placeholder="Что произошло" style="padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--bg); color: var(--ink); font-family: inherit; font-size: 15px; resize: vertical; width: 100%;"></textarea>
|
||||
<button type="button" onClick="{{ send }}" style="display: inline-flex; align-items: center; justify-content: center; height: 50px; border-radius: 12px; border: 0; background: var(--accent); color: var(--on-accent); font-family: inherit; font-size: 16px; font-weight: 500; cursor: pointer;" style-hover="background: var(--accent-hover);">{{ sendLabel }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style="border-top: 1px solid var(--border); background: var(--surface-2);">
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 48px clamp(16px, 4vw, 24px) 32px; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 32px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 14px;">
|
||||
<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="font-family: 'Unbounded', 'Commissioner', sans-serif; font-weight: 600; font-size: 15px; letter-spacing: -0.02em;">Вейликс</span>
|
||||
</div>
|
||||
<p style="margin: 0 0 20px; max-width: 26em; font-size: 14px; line-height: 1.55; color: var(--ink-3); font-weight: 300;">Персональные VPN-серверы с индивидуальным IP. Работаем с 2022 года.</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px;">
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">VISA</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">MC</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">МИР</span>
|
||||
<span style="display: flex; align-items: center; justify-content: center; width: 60px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-size: 11px; letter-spacing: 0.06em; color: var(--ink-3);">CRYPTO</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 24px 32px; align-content: start;">
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Сервис</div>
|
||||
<a href="Price.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Цены</a>
|
||||
<a href="Locations.dc.html" style="color: var(--ink-2);" style-hover="color: var(--ink);">Локации</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Поддержка</a>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; font-size: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3); margin-bottom: 2px;">Документы</div>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Условия использования</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Политика конфиденциальности</a>
|
||||
<a href="#top" style="color: var(--ink-2);" style-hover="color: var(--ink);">Возврат средств</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 1200px; margin: 0 auto; padding: 0 clamp(16px, 4vw, 24px) 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px;">
|
||||
<div style="font-size: 13px; color: var(--ink-3);">© 2026 Вейликс</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setLight }}" title="Светлая тема" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ lightBg }}; color: {{ lightInk }}; cursor: pointer;"><span style="position: relative; width: 14px; height: 14px; display: block;"><span style="position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: 50%; background: currentColor; display: block;"></span><span style="position: absolute; left: 0; top: 0; width: 14px; height: 14px; 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: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ darkBg }}; color: {{ darkInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; box-shadow: inset -5px -1.5px 0 0 currentColor; display: block;"></span></button>
|
||||
<button type="button" onClick="{{ setSystem }}" title="Как в системе" style="display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 7px; background: {{ systemBg }}; color: {{ systemInk }}; cursor: pointer;"><span style="width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); display: block;"></span></button>
|
||||
</div>
|
||||
<div style="display: flex; padding: 2px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); flex: none;">
|
||||
<button type="button" onClick="{{ setRu }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ ruBg }}; color: {{ ruInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">RU</button>
|
||||
<button type="button" onClick="{{ setEn }}" style="padding: 0 10px; height: 28px; border: 0; border-radius: 7px; background: {{ enBg }}; color: {{ enInk }}; font-family: inherit; font-size: 12px; cursor: pointer;">EN</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</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, tab: 0, open: 0, sent: false, lang: "ru", narrow: false, menu: false, langMenu: 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);
|
||||
this.onResize = () => {
|
||||
const n = window.innerWidth < 900;
|
||||
if (n !== this.state.narrow) this.setState({ narrow: n, menu: false });
|
||||
};
|
||||
window.addEventListener("resize", this.onResize);
|
||||
this.onResize();
|
||||
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, langMenu: false }, () => this.applyLang());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.mq) this.mq.removeEventListener("change", this.onSys);
|
||||
if (this.onResize) window.removeEventListener("resize", this.onResize);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
faq() {
|
||||
return [
|
||||
[
|
||||
["Что делать сразу после оплаты?", "Зайдите в кабинет по почте, откройте свой ключ и скачайте конфигурацию или отсканируйте QR-код с телефона. Маршрутизация уже настроена, вручную ничего вписывать не нужно."],
|
||||
["Сколько устройств можно подключить?", "До пяти на один ключ, все с одним и тем же адресом. Если нужно больше устройств или вторая страна — берёте дополнительный тариф с отдельным индивидуальным IP."],
|
||||
["Перестал работать один способ подключения.", "На вашем сервере всегда настроены оба типа: WireGuard и Xray. Откройте кабинет и подключитесь по второй конфигурации, ничего запрашивать не нужно. Набор протоколов мы дополняем и обновляем."],
|
||||
["Нужно ли что-то настраивать вручную?", "Нет. В конфигурации уже прописано, что российские сайты идут напрямую, а зарубежные — через ваш сервер. Переключать режимы во время работы не нужно."]
|
||||
],
|
||||
[
|
||||
["Почему первый месяц дороже?", "В 4 900 ₽ входит подготовка сервера: выделение адреса, настройка обоих протоколов и маршрутизации. Дальше вы платите только абонентскую плату 2 499 ₽ в месяц."],
|
||||
["Как продлевается ключ?", "Списание раз в месяц с привязанной карты. Дату окончания и карту видно на странице ключа в кабинете, там же оплату можно поменять или остановить."],
|
||||
["Можно вернуть деньги?", "Если сервер не заработал по нашей вине, возвращаем полностью в течение первых семи дней. Если вы просто передумали — возвращаем остаток неиспользованного месяца."],
|
||||
["Чем можно платить?", "Карты российских и зарубежных банков, а также криптовалюта. Все способы доступны на странице оформления заказа."]
|
||||
],
|
||||
[
|
||||
["Чем индивидуальный IP отличается от обычного VPN?", "В массовом VPN один адрес делят сотни людей, и ограничения, которые получил любой из них, касаются всех. Ваш адрес принадлежит только вам, поэтому сервисы относятся к нему как к домашнему интернету."],
|
||||
["Почему часть локаций готова только через 24 часа?", "Мы не держим пул общих адресов. Если в нужной стране свободного сервера нет, мы устанавливаем его лично под вас. Оплата первого месяца начинается с момента выдачи доступа."],
|
||||
["Зачем к тарифу российский адрес?", "Он работает в обе стороны. Из России — чтобы банки, госуслуги и стриминг видели обычное местное подключение. Из другой страны — чтобы те же сервисы открывались так, будто вы дома."],
|
||||
["Можно сменить страну позже?", "Ключ привязан к своему серверу, поэтому страну у него не меняем. Если нужна другая локация, возьмите второй ключ: он появится в том же кабинете и будет продлеваться отдельно. Первый можно оставить или отключить в любой момент."]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const theme = this.state.theme || this.props.defaultTheme || "light";
|
||||
const sel = "var(--accent-soft)";
|
||||
const list = this.faq()[this.state.tab];
|
||||
const v = {
|
||||
items: list.map((row, i) => ({
|
||||
q: row[0],
|
||||
a: row[1],
|
||||
off: this.state.open !== i,
|
||||
rot: this.state.open === i ? "rotate(-135deg) translateY(2px)" : "rotate(45deg) translateY(-2px)",
|
||||
toggle: () => this.setState(s => ({ open: s.open === i ? -1 : i }))
|
||||
})),
|
||||
send: () => this.setState({ sent: true }),
|
||||
navOff: this.state.narrow,
|
||||
burgerOff: !this.state.narrow,
|
||||
menuOff: !(this.state.narrow && this.state.menu),
|
||||
toggleMenu: () => this.setState(st => ({ menu: !st.menu })),
|
||||
closeMenu: () => this.setState({ menu: false }),
|
||||
toggleLangMenu: () => this.setState(st => ({ langMenu: !st.langMenu })),
|
||||
langMenuOff: !this.state.langMenu,
|
||||
langLabel: this.state.lang === "en" ? "EN" : "RU",
|
||||
ruCheck: this.state.lang === "en" ? "" : "✓",
|
||||
enCheck: this.state.lang === "en" ? "✓" : "",
|
||||
setRu: () => this.setLang("ru"),
|
||||
setEn: () => this.setLang("en"),
|
||||
ruBg: this.state.lang === "en" ? "transparent" : sel,
|
||||
enBg: this.state.lang === "en" ? sel : "transparent",
|
||||
ruInk: this.state.lang === "en" ? "var(--ink-3)" : "var(--ink)",
|
||||
enInk: this.state.lang === "en" ? "var(--ink)" : "var(--ink-3)",
|
||||
sendLabel: this.state.sent ? "Отправлено, ответим на почту" : "Отправить",
|
||||
setLight: () => this.setTheme("light"),
|
||||
setDark: () => this.setTheme("dark"),
|
||||
setSystem: () => this.setTheme("system"),
|
||||
lightBg: theme === "light" ? sel : "transparent",
|
||||
darkBg: theme === "dark" ? sel : "transparent",
|
||||
systemBg: theme === "system" ? sel : "transparent",
|
||||
lightInk: theme === "light" ? "var(--ink)" : "var(--ink-3)",
|
||||
darkInk: theme === "dark" ? "var(--ink)" : "var(--ink-3)",
|
||||
systemInk: theme === "system" ? "var(--ink)" : "var(--ink-3)"
|
||||
};
|
||||
for (let i = 0; i < 3; i++) {
|
||||
v["tab" + i] = () => this.setState({ tab: i, open: 0 });
|
||||
v["tab" + i + "Bg"] = this.state.tab === i ? sel : "transparent";
|
||||
v["tab" + i + "Border"] = this.state.tab === i ? "var(--accent)" : "var(--border)";
|
||||
v["tab" + i + "Ink"] = this.state.tab === i ? "var(--ink)" : "var(--ink-2)";
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpkYjhkMjAzOC0wZTdmLTQ5ZDEtYmIyZi02MjU4ZDI5YjFjOGQAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaM919rkJOu+r56vDmbDSuogAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDphNDg3ZjVlYS0yNGMwLTRmMWMtYjU3Mi1jZTk0ZmNhOWRlMzVscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNou3TLKnEXX9d3aOK364c3gAAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggh2uOqYcsUCfLLJlcsUZW5XDb53hqcmG3yNeaOfhXo82kZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaMSFmG4KKe0hfDzBQkq0FWAAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCClLIpUxJ//UabRp4m2GHMZs3IWyl6ZrhiOD7Ks5Zus2GRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBh7Zmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmRiOGQyMDM4LTBlN2YtNDlkMS1iYjJmLTYyNThkMjliMWM4ZC9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjI1OTJhMmM5LTM5ZjgtNDA2My05ZGVjLTc3MTYzNWRkYzM0ZXJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCCHa46phyxQJ8ssmVyxRlblcNvneGpyYbfI15o5+FejzaJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggMi0M7Zd5/U7IYIcq59ZD8cEknO87z1xU61ChlcYW6FaiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggsul0+GJ+xq2xeDU2smw5zoclumj5sx2mCDioTMkW60Z0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
|
||||
<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#00705e"></stop><stop offset="1" stop-color="#419883"></stop>
|
||||
</linearGradient></defs>
|
||||
<rect x="1" y="1" width="24" height="24" rx="8" fill="url(#g)"></rect>
|
||||
<rect x="7" y="7" width="24" height="24" rx="8" fill="#a9dfd2"></rect>
|
||||
<rect x="7.7" y="7.7" width="22.6" height="22.6" rx="7.3" fill="none" stroke="#00705e" stroke-opacity="0.55" stroke-width="1.4"></rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYToyYmQ5ZmU0Yy1lYTYwLTQyZTgtYTYwMC1kM2YxN2ZlMWZmNWQAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaP7mE7KwatMQ9m/EB5bQf0sAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDoxODIyYmQwYy01MDA2LTQxNGMtOGI3Yi0xMDlhZTA2OTQ5N2ZscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNoYTB+WI41NX8SM923oLhJ1wAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFgg3E108VpPF0A89PCiPkJvU0xFRCjEsYXd9w6fJa/3hpSkZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaP0IC7BR054ns2mFgoQ6hCcAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCC92+BR8qFvAXAhWyDvg9hz/npVNqNBZTe8aAHtqKCqXmRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBh7Zmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOjJiZDlmZTRjLWVhNjAtNDJlOC1hNjAwLWQzZjE3ZmUxZmY1ZC9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjA3MzMyY2RhLTljZWMtNGExZC1hYWJmLWU2OWQ0NTI5MDgwZHJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCDcTXTxWk8XQDz08KI+Qm9TTEVEKMSxhd33Dp8lr/eGlKJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggAOYiBKaqhWClbcPbmxFMjwITDHZ752RjpbEazyMlcrmiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFgge2AxpUBp9srweE9dMa1k1kbdgI4m5KetgQH8+8GInxB0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALine truncated
|
||||
<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#6137a6"></stop><stop offset="1" stop-color="#9c66b6"></stop>
|
||||
</linearGradient></defs>
|
||||
<rect x="1" y="1" width="24" height="24" rx="8" fill="url(#g)"></rect>
|
||||
<rect x="7" y="7" width="24" height="24" rx="8" fill="#d9c4ea"></rect>
|
||||
<rect x="7.7" y="7.7" width="22.6" height="22.6" rx="7.3" fill="none" stroke="#6137a6" stroke-opacity="0.55" stroke-width="1.4"></rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,597 @@
|
||||
// Veilix — словарь RU → EN. Источник перевода для фронтенда.
|
||||
// Ключ — строка как она написана в разметке, значение — английский вариант.
|
||||
window.VEILIX_I18N = {
|
||||
// Навигация и общее
|
||||
"Цены": "Pricing",
|
||||
"Локации": "Locations",
|
||||
"Поддержка": "Support",
|
||||
"Войти": "Log in",
|
||||
"Получить VPN": "Get a VPN",
|
||||
"Купить": "Buy",
|
||||
"Отменить": "Cancel",
|
||||
"Шесть стран на выбор и российский адрес в комплекте с любым тарифом.": "Six countries to choose from, with a Russian address included in every plan.",
|
||||
"RU-адрес входит в каждый тариф": "A RU address comes with every plan",
|
||||
"Выберите страну": "Choose a country",
|
||||
"Российский адрес в каждом тарифе": "A Russian address in every plan",
|
||||
"Он работает в обе стороны: из России банки и госуслуги видят обычное местное подключение, из другой страны те же сервисы открываются так, будто вы дома.": "It works both ways: from Russia, banks and government services see an ordinary local connection; from abroad, those same services open as if you were at home.",
|
||||
"Нужной страны нет в списке": "Your country is not on the list",
|
||||
"Мы устанавливаем серверы и в других странах. Напишите, какая нужна, — скажем сроки и установим под вас.": "We set up servers in other countries too. Tell us which one you need and we will give you the timing and build it for you.",
|
||||
"Условия": "Terms",
|
||||
"Конфиденциальность": "Privacy",
|
||||
"Реферальная программа": "Referral program",
|
||||
"Ваша ссылка": "Your link",
|
||||
"Ожидает подтверждения": "Awaiting confirmation",
|
||||
"Месяц начисляется вам и другу после его первой оплаты и применяется к ближайшему списанию.": "The month is credited after your friend's first payment and applies to the next charge.",
|
||||
"Приглашённые": "Invitees",
|
||||
"Месяц начислен": "Month credited",
|
||||
"Ждём оплаты": "Awaiting payment",
|
||||
"Реферальная ссылка": "Referral link",
|
||||
"Приведите друга — вы оба получите по месяцу в подарок.": "Invite a friend and you both get a free month.",
|
||||
"Приглашено": "Invited",
|
||||
"Месяцев начислено": "Months earned",
|
||||
"Поделиться": "Share",
|
||||
"Ссылка скопирована": "Link copied",
|
||||
"Копировать ссылку": "Copy link",
|
||||
"Сервис": "Service",
|
||||
"Документы": "Legal",
|
||||
"Тема": "Theme",
|
||||
"Язык": "Language",
|
||||
"Условия использования": "Terms of use",
|
||||
"Политика конфиденциальности": "Privacy policy",
|
||||
"Возврат средств": "Refunds",
|
||||
"Вейликс": "Veilix",
|
||||
"Адрес принадлежит только вам: один и тот же IP на всех устройствах, в любой стране. Без соседей по серверу и их репутации.": "The address belongs to you alone: the same IP on every device, in any country. No neighbours on the server and no reputation to inherit.",
|
||||
"Российский адрес в подписке": "Russian address in the subscription",
|
||||
"Нужной страны нет в списке или нужна подписка на команду — напишите, ответим в течение дня.": "If the country you need is missing or you want a team subscription, write to us and we will reply within a day.",
|
||||
"Скоро": "Coming soon",
|
||||
"Доступно в подписке": "Included in the subscription",
|
||||
"VPN, который не нужно отключать. Работаем с 2022 года.": "A VPN you never have to switch off. Running since 2022.",
|
||||
"30 стран и российский адрес входят в подписку.": "Thirty countries and a Russian address come with the subscription.",
|
||||
"Одна цена за всё: 699 ₽ в месяц или 6 990 ₽ в год.": "One price for everything: 699 ₽ a month or 6,990 ₽ a year.",
|
||||
"Одна цена за всё. Отменить продление можно в кабинете.": "One price for everything. You can cancel renewal in your account.",
|
||||
"Все локации доступны сразу после оплаты и входят в одну цену.": "Every location is available right after payment and included in one price.",
|
||||
"Тридцать локаций и российский адрес входят в подписку. Переключайтесь между странами сколько угодно, на пяти устройствах сразу.": "Thirty locations and a Russian address come with the subscription. Switch countries as often as you like, on five devices at once.",
|
||||
"VPN, который не нужно отключать": "A VPN you never have to switch off",
|
||||
"Российские сайты напрямую": "Russian sites go direct",
|
||||
"Банки и госуслуги работают как обычно, отключать VPN не нужно.": "Banks and government services keep working as usual, with no need to switch the VPN off.",
|
||||
"Современный протокол XRAY": "Modern XRAY protocol",
|
||||
"Трафик неотличим от обычного HTTPS, поэтому подключение держится там, где старые протоколы уже не работают.": "The traffic is indistinguishable from ordinary HTTPS, so the connection holds where older protocols no longer work.",
|
||||
"На каком протоколе работает подключение?": "Which protocol does the connection use?",
|
||||
"На XRAY — это современный протокол с VLESS, Hysteria2 и XHTTP. Трафик выглядит как обычный HTTPS, поэтому соединение держится там, где старые протоколы блокируются.": "XRAY — a modern protocol with VLESS, Hysteria2 and XHTTP. The traffic looks like ordinary HTTPS, so the connection holds where older protocols get blocked.",
|
||||
"Все тридцать стран, включая российскую локацию, и пять устройств. Отдельной платы за страну или устройство нет.": "All thirty countries including the Russian location, and five devices. There is no separate charge per country or device.",
|
||||
"Конфигурации XRAY": "XRAY configurations",
|
||||
"Ссылка подключения": "Connection link",
|
||||
"Для Happ и других клиентов XRAY": "For Happ and other XRAY clients",
|
||||
"Быстрый способ подключить новое устройство. Скопируйте ссылку или отсканируйте код — настройки те же.": "A quick way to add a new device. Copy the link or scan the code — the settings are the same.",
|
||||
"На компьютере скачайте файл и откройте его в клиенте XRAY. На телефоне — отсканируйте QR-код.": "On a computer download the file and open it in an XRAY client. On a phone, scan the QR code.",
|
||||
"Откройте подключение, скачайте любую свободную конфигурацию XRAY или отсканируйте её QR-код. Каждая конфигурация рассчитана на одно устройство.": "Open the connection, download any free XRAY configuration or scan its QR code. Each configuration covers one device.",
|
||||
"Обновите конфигурацию в кабинете или смените локацию — это занимает пару секунд. Если не помогло, напишите в поддержку, ответим в течение часа.": "Refresh the configuration in your account or change location — it takes a couple of seconds. If that doesn't help, write to support and we will reply within an hour.",
|
||||
"Подключение работает на XRAY: откройте кабинет и обновите конфигурацию или смените локацию. Настройки протокола мы обновляем по мере блокировок.": "The connection runs on XRAY: open your account and refresh the configuration or change location. We update the protocol settings as blocks appear.",
|
||||
"Зайдите в кабинет по почте, выберите страну и скачайте конфигурацию XRAY или отсканируйте QR-код с телефона. Маршрутизация уже настроена, вручную ничего вписывать не нужно.": "Sign in with your email, choose a country and download the XRAY configuration or scan the QR code with your phone. Routing is already set up, nothing to enter by hand.",
|
||||
"Наведите камеру телефона на код в приложении Happ или другом клиенте XRAY.": "Point your phone camera at the code in Happ or another XRAY client.",
|
||||
"Откройте подключение, скачайте любую свободную конфигурацию WireGuard или отсканируйте её QR-код. Каждая конфигурация рассчитана на одно устройство.": "Open the connection, download any free WireGuard configuration or scan its QR code. Each configuration covers one device.",
|
||||
"Переключитесь на Xray в той же локации — он настроен заранее. Если не помогло, напишите в поддержку, ответим в течение часа.": "Switch to Xray in the same location — it is set up in advance. If that doesn't help, write to support and we will reply within an hour.",
|
||||
"В разделе оплаты нажмите «Изменить карту» и отвяжите её. Подписка доработает оплаченный период.": "In the payment section tap “Change card” and unlink it. The subscription runs to the end of the paid period.",
|
||||
"VPN со всеми странами в одной подписке. Работаем с 2022 года.": "A VPN with every country in one subscription. Running since 2022.",
|
||||
"или 6 990 ₽ в год — 582 ₽ в месяц": "or 6,990 ₽ a year — 582 ₽ per month",
|
||||
"Одна цена за всё: страны не тарифицируются отдельно, платы за подключение нет. Отменить продление можно в кабинете.": "One price for everything: no per-country charges and no setup fee. You can cancel renewal in your account.",
|
||||
"Все 30 стран, переключение в любой момент": "All 30 countries, switch any time",
|
||||
"Российская локация: прямое соединение и мост в обе стороны": "Russian location: direct connection and a bridge both ways",
|
||||
"Все локации доступны сразу после оплаты и входят в одну цену. Переключаться между ними можно сколько угодно раз.": "Every location is available right after payment and included in one price. Switch between them as often as you like.",
|
||||
"Включено": "Included",
|
||||
"30 стран и российский адрес входят в подписку. Переключайтесь между ними сколько угодно раз.": "Thirty countries and a Russian address come with the subscription. Switch between them as often as you like.",
|
||||
"Нажмите точку на карте — покажем страну и город": "Tap a dot on the map to see the country and city",
|
||||
"Доступно сразу": "Available now",
|
||||
"RU-адрес входит в подписку": "Russian address included",
|
||||
"Все страны подписки": "Every country in the subscription",
|
||||
"Одна цена за всё: 699 ₽ в месяц или 6 990 ₽ в год. Страны не тарифицируются отдельно.": "One price for everything: 699 ₽ a month or 6,990 ₽ a year. Countries are not charged separately.",
|
||||
"Локации и подписка": "Locations and subscription",
|
||||
"Тема: локации и подписка": "Topic: locations and subscription",
|
||||
"Если подписка уже оформлена, укажите почту, на которую она оформлена — так мы сразу увидим вашу конфигурацию.": "If you already have a subscription, give the email it is registered to and we will see your configuration right away.",
|
||||
"Нужны особые условия": "Need special terms",
|
||||
"Подписки на команду, оплата по счёту или редкая страна. Оставьте заявку — ответим в течение дня.": "Team subscriptions, invoice payment or a rare country. Leave a request and we will reply within a day.",
|
||||
"Подписка ВейликсВПН": "VeilixVPN subscription",
|
||||
"30 стран и российский адрес": "30 countries and a Russian address",
|
||||
"Доступ открывается сразу после оплаты": "Access opens right after payment",
|
||||
"Месяц · 699 ₽": "Monthly · 699 ₽",
|
||||
"Год · 6 990 ₽": "Yearly · 6,990 ₽",
|
||||
"все 30": "all 30",
|
||||
"Ваша подписка": "Your subscription",
|
||||
"Тридцать стран и российский адрес, до пяти устройств.": "Thirty countries and a Russian address, up to five devices.",
|
||||
"Хотите другую страну": "Want a different country",
|
||||
"Все локации уже в подписке, переключение бесплатное.": "Every location is already in the subscription and switching is free.",
|
||||
"Сменить страну": "Change country",
|
||||
"Подключения": "Connections",
|
||||
"← Все подключения": "← All connections",
|
||||
"699 ₽ в месяц": "699 ₽ per month",
|
||||
"Каждое в своей стране": "Each in its own country",
|
||||
"Выбрать страну": "Choose a country",
|
||||
"Все тридцать локаций входят в подписку. Переключение бесплатное и занимает пару секунд.": "All thirty locations come with the subscription. Switching is free and takes a couple of seconds.",
|
||||
"Выбрать": "Choose",
|
||||
"Все страны входят в подписку, переключение бесплатное": "Every country is included, switching is free",
|
||||
"Переключиться": "Switch",
|
||||
"Смена локации применяется сразу, платить ничего не нужно.": "The new location applies immediately and costs nothing.",
|
||||
"Подписка": "Subscription",
|
||||
"Страны": "Countries",
|
||||
"Активна": "Active",
|
||||
"Готова": "Ready",
|
||||
"Оплачено. Подписка уже в кабинете — ": "Paid. Your subscription is in the account — ",
|
||||
"Сколько стоит подписка?": "How much does the subscription cost?",
|
||||
"699 ₽ в месяц или 6 990 ₽ в год. Годовая выходит по 582 ₽ в месяц и экономит 1 398 ₽. Платы за подключение и доплат за страну нет.": "699 ₽ a month or 6,990 ₽ a year. The yearly plan works out at 582 ₽ a month and saves 1,398 ₽. There is no setup fee and no per-country charge.",
|
||||
"Как продлевается подписка?": "How is the subscription renewed?",
|
||||
"Списание раз в месяц или раз в год с привязанной карты. Дату окончания и карту видно в кабинете, там же оплату можно поменять или остановить.": "Charged monthly or yearly to the linked card. The end date and the card are shown in your account, where you can change or stop payment.",
|
||||
"Сколько стран входит в подписку?": "How many countries are included?",
|
||||
"Тридцать, включая российскую локацию. Все они доступны сразу и не тарифицируются отдельно.": "Thirty, including the Russian location. All of them are available immediately and are not charged separately.",
|
||||
"Чем это отличается от индивидуального IP?": "How is this different from a dedicated IP?",
|
||||
"Здесь адрес общий: на сервере несколько человек, поэтому подписка стоит заметно дешевле. Если нужен адрес, закреплённый только за вами, это отдельные тарифы Вейликс с индивидуальным IP.": "The address here is shared: several people use one server, so the subscription costs much less. If you need an address reserved for you alone, Veilix has separate dedicated-IP plans.",
|
||||
"Зачем в подписке российский адрес?": "Why does the subscription include a Russian address?",
|
||||
"До пяти на одну подписку. Страны у устройств могут быть разные: каждое подключается к своей локации.": "Up to five per subscription. Devices can use different countries: each connects to its own location.",
|
||||
"Локация доступна сразу, переключение бесплатное": "The location is available now and switching is free",
|
||||
"ВейликсВПН": "VeilixVPN",
|
||||
"© 2026 ВейликсВПН": "© 2026 VeilixVPN",
|
||||
"Все страны в одной подписке": "Every country in one subscription",
|
||||
"Быстрый VPN со всеми странами сразу": "A fast VPN with every country included",
|
||||
"Тридцать локаций и российский адрес входят в подписку. Переключайтесь между странами сколько угодно, на пяти устройствах сразу, без лимитов трафика и скорости.": "Thirty locations and a Russian address come with the subscription. Switch countries as often as you like, on five devices at once, with no traffic or speed limits.",
|
||||
"699 ₽ в месяц или 6 990 ₽ в год": "699 ₽ a month or 6,990 ₽ a year",
|
||||
"RU-адрес включён": "Russian address included",
|
||||
"30 стран, переключение в один тап": "30 countries, one tap to switch",
|
||||
"Все локации в подписке": "Every location in the subscription",
|
||||
"Список стран открыт целиком. Выбираете страну в приложении, соединение перестраивается за пару секунд, доплачивать за переключение не нужно.": "The whole country list is open. Pick a country in the app, the connection rebuilds in a couple of seconds, and switching costs nothing extra.",
|
||||
"Тридцать стран": "Thirty countries",
|
||||
"Европа, США, Азия, Ближний Восток и Южная Америка. Список пополняется.": "Europe, the US, Asia, the Middle East and South America. The list keeps growing.",
|
||||
"Пять устройств сразу": "Five devices at once",
|
||||
"Телефон, ноутбук, планшет и ещё два устройства работают одновременно.": "Phone, laptop, tablet and two more devices all run at the same time.",
|
||||
"Без лимитов трафика": "No traffic limits",
|
||||
"Скорость не режем, объём не считаем, ограничений по времени нет.": "We don't throttle speed, count traffic or limit time.",
|
||||
"Пять устройств в подписке": "Five devices per subscription",
|
||||
"Одна подписка на всю технику. Устройства работают одновременно и в разных странах.": "One subscription for all your devices. They run at the same time and in different countries.",
|
||||
"Российский адрес включён": "Russian address included",
|
||||
"Российская локация входит в подписку: банки, госуслуги и стриминг открываются как дома.": "The Russian location is part of the subscription: banks, government services and streaming open as if you were home.",
|
||||
"туннелем в выбранную локацию": "tunnelled to the chosen location",
|
||||
"прямо из выбранной локации": "straight from the chosen location",
|
||||
"Одна подписка, все страны": "One subscription, every country",
|
||||
"Тридцать локаций, российский адрес и пять устройств.": "Thirty locations, a Russian address and five devices.",
|
||||
"Все 30 стран и российский адрес": "All 30 countries and a Russian address",
|
||||
"До 5 устройств одновременно": "Up to 5 devices at once",
|
||||
"Без лимитов трафика и скорости": "No traffic or speed limits",
|
||||
"Оба типа подключения на выбор": "Both connection types available",
|
||||
"Страны в подписке": "Countries included",
|
||||
"Месяц": "Monthly",
|
||||
"699 ₽": "699 ₽",
|
||||
"Списание раз в месяц, отмена в любой момент": "Charged monthly, cancel any time",
|
||||
"Год": "Yearly",
|
||||
"выгоднее": "better value",
|
||||
"6 990 ₽": "6,990 ₽",
|
||||
"582 ₽ в месяц, экономия 1 398 ₽ за год": "582 ₽ a month, saving 1,398 ₽ a year",
|
||||
"Оформить подписку": "Get the subscription",
|
||||
"Карты российских и зарубежных банков, криптовалюта. Доступ открывается сразу после оплаты.": "Russian and international cards, plus crypto. Access opens right after payment.",
|
||||
"Нужен адрес, закреплённый только за вами": "Need an address that is yours alone",
|
||||
"Здесь сервер общий: он дешевле и этого достаточно большинству. Если сервисы требуют постоянный IP, посмотрите индивидуальные тарифы Вейликс.": "The server here is shared: it costs less and is enough for most people. If a service needs a permanent IP, look at Veilix dedicated plans.",
|
||||
"Что входит в подписку?": "What is included in the subscription?",
|
||||
"Все тридцать стран, включая российскую локацию, пять устройств и оба типа подключения. Отдельной платы за страну или устройство нет.": "All thirty countries including the Russian location, five devices and both connection types. There is no separate charge per country or device.",
|
||||
"Можно менять страну сколько угодно?": "Can I change the country as often as I want?",
|
||||
"Да. Переключение занимает пару секунд и ничего не стоит: список локаций открыт целиком на всё время подписки.": "Yes. Switching takes a couple of seconds and costs nothing: the full location list stays open for the whole subscription.",
|
||||
"Нет. В кабинете вы скачиваете готовый файл или сканируете QR-код с телефона. Российские сайты идут напрямую, зарубежные — через выбранную локацию.": "No. In your account you download a ready file or scan a QR code with your phone. Russian sites go direct, the rest travel through the chosen location.",
|
||||
"Есть ограничения по скорости и трафику?": "Are there speed or traffic limits?",
|
||||
"Нет. Объём не считаем и скорость не режем: каналов хватает на видео в высоком качестве и звонки.": "No. We don't count traffic or throttle speed: the channels handle high-quality video and calls.",
|
||||
"Чем это отличается от индивидуального тарифа?": "How is this different from a dedicated plan?",
|
||||
"Здесь адрес общий: на сервере несколько человек, поэтому подписка стоит заметно дешевле. Если нужен адрес, закреплённый только за вами, это отдельные тарифы с индивидуальным IP.": "The address here is shared: several people use one server, which makes the subscription much cheaper. If you need an address reserved for you alone, that is the dedicated IP plan.",
|
||||
"Как оплачивается и продлевается?": "How is it paid and renewed?",
|
||||
"699 ₽ в месяц или 6 990 ₽ в год со списанием с привязанной карты. Годовая подписка дешевле помесячной на 1 398 ₽. Отменить продление можно в кабинете в любой момент.": "699 ₽ a month or 6,990 ₽ a year, charged to the linked card. The yearly plan is 1,398 ₽ cheaper than paying monthly. You can cancel renewal in your account at any time.",
|
||||
"© 2026 Вейликс": "© 2026 Veilix",
|
||||
"Персональные VPN-серверы с индивидуальным IP. Работаем с 2022 года.": "Personal VPN servers with a dedicated IP. Running since 2022.",
|
||||
"Русский": "Russian",
|
||||
"English": "English",
|
||||
"скоро": "soon",
|
||||
|
||||
// Главная — hero
|
||||
"Персональный сервер, не общий": "A personal server, not a shared one",
|
||||
"Работаем с 2022 года": "Running since 2022",
|
||||
"Ваш личный VPN с индивидуальным IP": "Your private VPN with a dedicated IP",
|
||||
"Адрес принадлежит только вам: один и тот же IP на всех устройствах, в любой стране. Без соседей по серверу, без общих подсетей и их репутации.": "The address belongs to you alone: the same IP on every device, in any country. No neighbours on the server, no shared subnets and their reputation.",
|
||||
"Посмотреть локации": "See locations",
|
||||
"4 900 ₽ первый месяц, далее 2 499 ₽": "$49 first month, then $24",
|
||||
"4 900 ₽": "$49",
|
||||
"2 900 ₽": "$24",
|
||||
"2 499 ₽": "$24",
|
||||
"490 ₽": "$5",
|
||||
"До 5 устройств": "Up to 5 devices",
|
||||
"RU-адрес в комплекте": "RU address included",
|
||||
"Ваш IP закреплён только за вами": "Your IP is reserved for you alone",
|
||||
|
||||
// Главная — преимущества
|
||||
"Что это даёт на практике": "What this means in practice",
|
||||
"Индивидуальный IP": "Dedicated IP",
|
||||
"Адрес выделен под вас одного. Сервисы видят обычного человека из выбранной страны, а не сотню людей за одним адресом.": "The address is assigned to you alone. Services see one ordinary person from the country you chose, not a hundred people behind a single address.",
|
||||
"Западные AI-сервисы": "Western AI services",
|
||||
"Риск блокировки ниже, чем у публичных VPN: их адреса уже давно в чёрных списках.": "Lower risk of being blocked than with public VPNs, whose addresses have long been blacklisted.",
|
||||
"Один адрес на всех устройствах": "One address across all devices",
|
||||
"Ноутбук, телефон, планшет выглядят одинаково. Вы всегда в одной локации со своим IP.": "Laptop, phone and tablet all look the same. You always stay in one location with your own IP.",
|
||||
"Меньше проверок и капч": "Fewer checks and captchas",
|
||||
"На массовых адресах срабатывает антибот-защита. На личном сервисы работают стабильнее.": "Mass addresses trigger anti-bot protection. On a personal one, services behave more predictably.",
|
||||
"Трафик под шифрованием": "Encrypted traffic",
|
||||
"Дома, в отеле или в кафе соединение защищено в любой точке мира.": "At home, in a hotel or in a café, the connection is protected anywhere in the world.",
|
||||
"До 5 устройств в тарифе": "Up to 5 devices per plan",
|
||||
"Нужно больше или другая страна — добавляете второй тариф со своим отдельным IP.": "Need more, or another country — add a second plan with its own separate IP.",
|
||||
"Плюс выделенный RU-адрес": "Plus a dedicated RU address",
|
||||
"Идёт к любому тарифу. Работает и как прямое соединение, и как мост в обе стороны.": "Comes with every plan. Works both as a direct connection and as a two-way bridge.",
|
||||
"Два типа подключения": "Two connection types",
|
||||
"Если один способ перестал работать, второй уже настроен. Список дополняется и обновляется.": "If one method stops working, the second is already set up. The list is extended and updated over time.",
|
||||
"Трафик идёт коротким путём": "Traffic takes the short route",
|
||||
"Маршрут выбирается сам, по адресу сайта. Российские сервисы не ломаются, зарубежные открываются, переключать ничего не нужно.": "The route is chosen automatically by the site address. Russian services keep working, foreign ones open, and you never switch anything.",
|
||||
"Вы в России": "You are in Russia",
|
||||
"Вы за границей": "You are abroad",
|
||||
"RU-сайты": "RU sites",
|
||||
"Остальной мир": "The rest of the world",
|
||||
"Мировые сайты": "Global sites",
|
||||
"напрямую через RU-сервер": "directly through the RU server",
|
||||
"туннелем в вашу локацию": "tunnelled to your location",
|
||||
"прямо из вашей локации": "straight from your location",
|
||||
"туннелем через RU-сервер": "tunnelled through the RU server",
|
||||
"Для тех, кто работает с Россией из другой страны.": "For people who work with Russia from another country.",
|
||||
|
||||
// Главная — цены и вопросы
|
||||
"Один тариф, без уровней": "One plan, no tiers",
|
||||
"первый месяц": "first month",
|
||||
"далее 2 499 ₽ в месяц": "then $24 per month",
|
||||
"Индивидуальный IP в выбранной стране": "Dedicated IP in the country you choose",
|
||||
"Выделенный RU-адрес в комплекте": "Dedicated RU address included",
|
||||
"До 5 устройств": "Up to 5 devices",
|
||||
"2 типа подключения:": "2 connection types:",
|
||||
"WireGuard / Amnezia WG": "WireGuard / Amnezia WG",
|
||||
"Xray: VLESS, Hysteria2, XHTTP": "Xray: VLESS, Hysteria2, XHTTP",
|
||||
"Выберите локацию": "Choose a location",
|
||||
"Свободные серверы есть сейчас": "Servers are available right now",
|
||||
"Сервер установим под вас за 24 часа": "We will set up a server for you within 24 hours",
|
||||
"Нидерланды · Амстердам": "Netherlands · Amsterdam",
|
||||
"Германия · Франкфурт": "Germany · Frankfurt",
|
||||
"США · Нью-Йорк": "United States · New York",
|
||||
"Финляндия · Хельсинки": "Finland · Helsinki",
|
||||
"Турция · Стамбул": "Türkiye · Istanbul",
|
||||
"Япония · Токио": "Japan · Tokyo",
|
||||
"Нидерланды": "Netherlands",
|
||||
"Германия": "Germany",
|
||||
"США": "United States",
|
||||
"Финляндия": "Finland",
|
||||
"Турция": "Türkiye",
|
||||
"Япония": "Japan",
|
||||
"Амстердам": "Amsterdam",
|
||||
"Франкфурт": "Frankfurt",
|
||||
"Нью-Йорк": "New York",
|
||||
"Хельсинки": "Helsinki",
|
||||
"Стамбул": "Istanbul",
|
||||
"Токио": "Tokyo",
|
||||
"3 сервера в наличии": "3 servers available",
|
||||
"2 сервера в наличии": "2 servers available",
|
||||
"1 сервер в наличии": "1 server available",
|
||||
"готово за 24 часа": "ready within 24 hours",
|
||||
"Установим за 24 часа": "Ready within 24 hours",
|
||||
"Серверы в наличии": "Servers available",
|
||||
"Нужны индивидуальные условия": "Need custom terms",
|
||||
"Вопросы": "Questions",
|
||||
"Чем это отличается от обычного VPN за 300 рублей?": "How is this different from an ordinary 300-rouble VPN?",
|
||||
"В массовом VPN ваш адрес делят сотни людей. Если кто-то из них попал в чёрный список, ограничения касаются всех. У вас адрес один и только ваш, поэтому сервисы относятся к нему как к домашнему интернету.": "In a mass-market VPN hundreds of people share your address. If any of them ends up blacklisted, the restrictions apply to everyone. Here the address is yours alone, so services treat it like a home connection.",
|
||||
"Почему некоторые локации готовы только через 24 часа?": "Why are some locations only ready in 24 hours?",
|
||||
"Мы не держим пул общих адресов. Если в нужной стране свободных серверов нет, мы устанавливаем сервер лично под вас и выдаём доступ в течение суток. Оплата за первый месяц начинается с момента выдачи.": "We do not keep a pool of shared addresses. If there is no free server in the country you need, we set one up for you personally and hand over access within a day. The first month starts when access is issued.",
|
||||
"Нужно ли что-то настраивать самому?": "Do I have to configure anything myself?",
|
||||
"Нет. В личном кабинете вы скачиваете готовый файл или сканируете QR-код с телефона. Маршрутизация уже настроена: российские сайты идут напрямую, зарубежные — через ваш сервер.": "No. In your account you download a ready-made file or scan a QR code with your phone. Routing is already set up: Russian sites go direct, foreign ones through your server.",
|
||||
"Сколько устройств можно подключить?": "How many devices can I connect?",
|
||||
"До пяти на один тариф, все с одним и тем же адресом. Нужно больше устройств или вторая страна — берёте дополнительный тариф с отдельным индивидуальным IP.": "Up to five per plan, all sharing the same address. If you need more devices or a second country, take an additional plan with its own dedicated IP.",
|
||||
"Зачем мне ещё и российский адрес?": "Why do I also need a Russian address?",
|
||||
"Он работает в обе стороны. Из России — чтобы банки, госуслуги и стриминг видели обычное местное подключение. Из другой страны — чтобы те же сервисы открывались так, будто вы дома.": "It works both ways. From Russia, so that banks, government services and streaming see an ordinary local connection. From abroad, so the same services open as if you were at home.",
|
||||
"Что будет, если способ подключения перестанет работать?": "What if a connection method stops working?",
|
||||
"На вашем сервере одновременно работают два типа подключения. Если один способ начал блокироваться, вы переключаетесь на второй в том же кабинете. Набор протоколов мы дополняем и обновляем.": "Your server runs both connection types at once. If one starts getting blocked, you switch to the other in the same account. We keep extending and updating the set of protocols.",
|
||||
|
||||
// Цены
|
||||
"Одна цена, без уровней и апсейлов": "One price, no tiers and no upsells",
|
||||
"В первый месяц входит подготовка сервера под вас. Дальше — только абонентская плата, отменить можно в кабинете.": "The first month covers preparing the server for you. After that it is only the subscription fee, and you can cancel in your account.",
|
||||
"Что входит": "What is included",
|
||||
"Индивидуальный IP в выбранной стране, только ваш": "A dedicated IP in the country you choose, yours alone",
|
||||
"Выделенный RU-адрес: прямое соединение и мост в обе стороны": "A dedicated RU address: direct connection and a two-way bridge",
|
||||
"До 5 устройств с одним и тем же адресом": "Up to 5 devices sharing the same address",
|
||||
"Два типа подключения: WireGuard и Xray": "Two connection types: WireGuard and Xray",
|
||||
"Умная маршрутизация RU / Глобал без переключений": "Smart RU / global routing with no switching",
|
||||
"Где-то сервер уже стоит и доступ выдаётся сразу. Если свободного нет, мы устанавливаем сервер лично под вас — на это уходит до 24 часов, и вы видите этот статус честно, до оплаты.": "In some countries a server is already running and access is issued immediately. If none is free, we set one up for you personally — that takes up to 24 hours, and you see this status honestly, before paying.",
|
||||
"Сервер свободен — доступ выдаём сразу после оплаты": "A server is free — access is issued right after payment",
|
||||
"Свободного сервера нет: установим лично под вас, доступ в течение 24 часов": "No free server: we will set one up for you personally, access within 24 hours",
|
||||
"4 900 ₽ · далее 2 499 ₽": "$49 · then $24",
|
||||
"Нужно несколько локаций": "Need several locations",
|
||||
"Каждый тариф — отдельный индивидуальный IP. Берёте второй тариф в другой стране, оба ключа лежат в одном кабинете и продлеваются независимо.": "Each plan is a separate dedicated IP. Take a second plan in another country and both keys live in one account, renewing independently.",
|
||||
"Команда, много адресов, редкая страна или свой сценарий маршрутизации. Оставьте заявку — ответим в течение дня.": "A team, many addresses, a rare country or your own routing scenario. Leave a request and we will reply within a day.",
|
||||
"Оставить заявку": "Send a request",
|
||||
"Почта или @username в Telegram": "Email or @username on Telegram",
|
||||
"Коротко о задаче": "Briefly about your case",
|
||||
"Отправить": "Send",
|
||||
"Заявка отправлена": "Request sent",
|
||||
"Общий сервер": "Shared server",
|
||||
"Персональный IP": "Dedicated IP",
|
||||
"Общий сервер, адрес делится с другими пользователями": "A shared server: the address is shared with other users",
|
||||
"в месяц": "per month",
|
||||
"или 4 900 ₽ за год": "or $49 per year",
|
||||
"Подключение сразу после оплаты: сервер уже работает, вы просто получаете доступ. Отменить можно в кабинете.": "You connect right after payment: the server is already running, you simply get access. You can cancel in your account.",
|
||||
"Общий IP в выбранной стране": "Shared IP in the country you choose",
|
||||
"Доступ сразу после оплаты": "Access right after payment",
|
||||
"До 5 устройств": "Up to 5 devices",
|
||||
"Общие серверы уже работают во всех странах из списка — доступ выдаём сразу после оплаты.": "Shared servers are already running in every country on the list — access is issued right after payment.",
|
||||
"490 ₽ в месяц": "$5 per month",
|
||||
"Чем персональный IP лучше общего": "Why a dedicated IP beats a shared one",
|
||||
"На общем сервере адрес делят другие пользователи, и ограничения, которые получил любой из них, касаются всех. Персональный IP принадлежит только вам — сервисы относятся к нему как к домашнему интернету.": "On a shared server the address is used by other people, and any restriction one of them earns applies to everyone. A dedicated IP belongs to you alone — services treat it like a home connection.",
|
||||
|
||||
// Поддержка
|
||||
"Быстрее всего. Можно писать голосом и скидывать скриншоты.": "The fastest way. Voice messages and screenshots welcome.",
|
||||
"Почта": "Email",
|
||||
"Для счетов, возвратов и всего, что нужно письменно.": "For invoices, refunds and anything that needs to be in writing.",
|
||||
"Частые вопросы": "Frequent questions",
|
||||
"Подключение": "Connecting",
|
||||
"Оплата и продление": "Payment and renewal",
|
||||
"Локации и IP": "Locations and IP",
|
||||
"Что делать сразу после оплаты?": "What do I do right after paying?",
|
||||
"Зайдите в кабинет по почте, откройте свой ключ и скачайте конфигурацию или отсканируйте QR-код с телефона. Маршрутизация уже настроена, вручную ничего вписывать не нужно.": "Sign in to your account by email, open your key and download the configuration or scan the QR code with your phone. Routing is already configured, nothing to type in by hand.",
|
||||
"До пяти на один ключ, все с одним и тем же адресом. Если нужно больше устройств или вторая страна — берёте дополнительный тариф с отдельным индивидуальным IP.": "Up to five per key, all sharing the same address. If you need more devices or a second country, take an additional plan with its own dedicated IP.",
|
||||
"Перестал работать один способ подключения.": "One connection method stopped working.",
|
||||
"На вашем сервере всегда настроены оба типа: WireGuard и Xray. Откройте кабинет и подключитесь по второй конфигурации, ничего запрашивать не нужно. Набор протоколов мы дополняем и обновляем.": "Both types are always configured on your server: WireGuard and Xray. Open your account and connect with the second configuration, no request needed. We keep extending and updating the set of protocols.",
|
||||
"Нужно ли что-то настраивать вручную?": "Do I need to configure anything manually?",
|
||||
"Нет. В конфигурации уже прописано, что российские сайты идут напрямую, а зарубежные — через ваш сервер. Переключать режимы во время работы не нужно.": "No. The configuration already states that Russian sites go direct and foreign ones go through your server. There are no modes to switch while you work.",
|
||||
"Почему первый месяц дороже?": "Why is the first month more expensive?",
|
||||
"В 4 900 ₽ входит подготовка сервера: выделение адреса, настройка обоих протоколов и маршрутизации. Дальше вы платите только абонентскую плату 2 499 ₽ в месяц.": "The $49 covers preparing the server: assigning the address and configuring both protocols and the routing. After that you only pay the $24 monthly fee.",
|
||||
"Как продлевается ключ?": "How does renewal work?",
|
||||
"Списание раз в месяц с привязанной карты. Дату окончания и карту видно на странице ключа в кабинете, там же оплату можно поменять или остановить.": "A monthly charge to the card on file. The expiry date and the card are shown on the key page in your account, where you can change or stop the payment.",
|
||||
"Можно вернуть деньги?": "Can I get a refund?",
|
||||
"Если сервер не заработал по нашей вине, возвращаем полностью в течение первых семи дней. Если вы просто передумали — возвращаем остаток неиспользованного месяца.": "If the server failed to work through our fault, we refund in full within the first seven days. If you simply changed your mind, we refund the unused part of the month.",
|
||||
"Чем можно платить?": "What can I pay with?",
|
||||
"Карты российских и зарубежных банков, а также криптовалюта. Все способы доступны на странице оформления заказа.": "Cards from Russian and foreign banks, as well as cryptocurrency. All methods are available at checkout.",
|
||||
"Чем индивидуальный IP отличается от обычного VPN?": "How does a dedicated IP differ from an ordinary VPN?",
|
||||
"В массовом VPN один адрес делят сотни людей, и ограничения, которые получил любой из них, касаются всех. Ваш адрес принадлежит только вам, поэтому сервисы относятся к нему как к домашнему интернету.": "In a mass-market VPN hundreds of people share one address, and any restriction one of them earns applies to everyone. Your address belongs to you alone, so services treat it like a home connection.",
|
||||
"Почему часть локаций готова только через 24 часа?": "Why are some locations only ready in 24 hours?",
|
||||
"Мы не держим пул общих адресов. Если в нужной стране свободного сервера нет, мы устанавливаем его лично под вас. Оплата первого месяца начинается с момента выдачи доступа.": "We do not keep a pool of shared addresses. If there is no free server in the country you need, we set one up for you personally. The first month starts when access is issued.",
|
||||
"Зачем к тарифу российский адрес?": "Why does the plan include a Russian address?",
|
||||
"Можно сменить страну позже?": "Can I change the country later?",
|
||||
"Ключ привязан к своему серверу, поэтому страну у него не меняем. Если нужна другая локация, возьмите второй ключ: он появится в том же кабинете и будет продлеваться отдельно. Первый можно оставить или отключить в любой момент.": "A key is tied to its own server, so we don't change its country. If you need another location, take a second key: it appears in the same account and renews separately. Keep the first one or cancel it whenever you like.",
|
||||
"Написать нам": "Write to us",
|
||||
"Если уже купили ключ, укажите почту, на которую он оформлен — так мы сразу увидим вашу конфигурацию.": "If you already bought a key, use the email it is registered to so we can see your configuration straight away.",
|
||||
"Ваша почта": "Your email",
|
||||
"Тема: подключение": "Topic: connecting",
|
||||
"Тема: оплата и продление": "Topic: payment and renewal",
|
||||
"Тема: локации и IP": "Topic: locations and IP",
|
||||
"Тема: другое": "Topic: something else",
|
||||
"Что произошло": "What happened",
|
||||
"Отправлено, ответим на почту": "Sent, we will reply by email",
|
||||
"Выберите удобный способ связи.": "Choose whichever way suits you.",
|
||||
|
||||
// Оформление заказа
|
||||
"Оформление заказа": "Checkout",
|
||||
"Оплата": "Payment",
|
||||
"Изменить": "Change",
|
||||
"Введите почту — пришлём одноразовый код. Отдельной регистрации нет: если вы у нас уже были, попадёте в свой кабинет, если нет — он создастся сам.": "Enter your email and we will send a one-time code. There is no separate sign-up: if you have been here before you land in your account, if not it is created for you.",
|
||||
"Прислать код": "Send the code",
|
||||
"6 цифр": "6 digits",
|
||||
"Продолжить": "Continue",
|
||||
"Другая почта": "Use another email",
|
||||
"Карта": "Card",
|
||||
"Карта МИР": "MIR card",
|
||||
"Криптовалюта": "Cryptocurrency",
|
||||
"Номер карты": "Card number",
|
||||
"ММ / ГГ": "MM / YY",
|
||||
"Карта сохранится для продления. Отменить или заменить её можно в кабинете в любой момент.": "The card is saved for renewals. You can cancel or replace it in your account at any time.",
|
||||
"USDT, BTC или ETH": "USDT, BTC or ETH",
|
||||
"После нажатия покажем адрес и сумму. При оплате криптой продление не автоматическое — пришлём напоминание за три дня до конца месяца.": "We will show the address and amount after you press the button. With crypto, renewal is not automatic — we send a reminder three days before the month ends.",
|
||||
"Согласен с условиями использования и политикой конфиденциальности": "I agree to the terms of use and the privacy policy",
|
||||
"Ваш заказ": "Your order",
|
||||
"Персональный VPN": "Personal VPN",
|
||||
"Сервер свободен, доступ сразу после оплаты": "A server is free, access right after payment",
|
||||
"Первый месяц": "First month",
|
||||
"RU-адрес": "RU address",
|
||||
"включён": "included",
|
||||
"Устройств": "Devices",
|
||||
"до 5": "up to 5",
|
||||
"К оплате сегодня": "Due today",
|
||||
"Оплатить 4 900 ₽": "Pay $49",
|
||||
"Показать адрес для оплаты": "Show the payment address",
|
||||
"Оплачено": "Paid",
|
||||
"Оплачено. Ключ уже в кабинете —": "Paid. The key is already in your account —",
|
||||
"открыть": "open it",
|
||||
"Возврат в течение семи дней, если сервер не заработал по нашей вине. Вопросы —": "A refund within seven days if the server failed through our fault. Questions —",
|
||||
"поддержка": "support",
|
||||
|
||||
// Кабинет
|
||||
"Вход в кабинет": "Sign in",
|
||||
"Введите почту, пришлём одноразовый код. Отдельной регистрации нет — кабинет создастся сам, если вы здесь впервые.": "Enter your email and we will send a one-time code. There is no separate sign-up — the account is created for you if this is your first visit.",
|
||||
"Главная": "Overview",
|
||||
"Список ключей": "All keys",
|
||||
"Купить ключ": "Buy a key",
|
||||
"Ваши ключи": "Your keys",
|
||||
"Два активных тарифа, каждый со своим индивидуальным IP.": "Two active plans, each with its own dedicated IP.",
|
||||
"Действует до": "Active until",
|
||||
"Активен": "Active",
|
||||
"Истекает": "Expiring",
|
||||
"3 из 5 устройств": "3 of 5 devices",
|
||||
"1 из 5 устройств": "1 of 5 devices",
|
||||
"Карта ···· 4242": "Card ···· 4242",
|
||||
"Карта ···· 8801": "Card ···· 8801",
|
||||
"Нужен ещё один IP или другая страна": "Need another IP or a different country",
|
||||
"Новый ключ появится здесь же, оплата отдельная.": "The new key appears right here, billed separately.",
|
||||
"Короткие ответы": "Quick answers",
|
||||
"Как подключить новое устройство?": "How do I connect a new device?",
|
||||
"Откройте ключ, скачайте любую свободную конфигурацию WireGuard или отсканируйте её QR-код. Каждая конфигурация рассчитана на одно устройство.": "Open the key, download any free WireGuard configuration or scan its QR code. Each configuration is meant for one device.",
|
||||
"Что делать, если подключение перестало работать?": "What if the connection stops working?",
|
||||
"Переключитесь на Xray в том же ключе — он настроен заранее. Если не помогло, напишите в поддержку, ответим в течение часа.": "Switch to Xray within the same key — it is configured in advance. If that does not help, write to support and we will reply within an hour.",
|
||||
"Как отменить продление?": "How do I cancel renewal?",
|
||||
"На странице ключа нажмите «Изменить карту» и отвяжите её. Ключ доработает оплаченный месяц.": "On the key page press “Change card” and detach it. The key keeps working until the paid month ends.",
|
||||
"← Все ключи": "← All keys",
|
||||
"Изменить карту": "Change card",
|
||||
"2 499 ₽ в месяц": "$24 per month",
|
||||
"Устройства": "Devices",
|
||||
"Один и тот же IP на всех": "The same IP on all of them",
|
||||
"Продлится автоматически через 31 день": "Renews automatically in 31 days",
|
||||
"Осталось 8 дней": "8 days left",
|
||||
"5 конфигураций — по одной на устройство": "5 configurations — one per device",
|
||||
"На компьютере скачайте файл и откройте его в приложении. На телефоне — отсканируйте QR-код.": "On a computer, download the file and open it in the app. On a phone, scan the QR code.",
|
||||
"Ноутбук": "Laptop",
|
||||
"Телефон": "Phone",
|
||||
"Планшет": "Tablet",
|
||||
"Рабочий ноутбук": "Work laptop",
|
||||
"Свободный слот": "Free slot",
|
||||
"подключено": "connected",
|
||||
"не занят": "unused",
|
||||
"Скачать": "Download",
|
||||
"Для Happ и других клиентов": "For Happ and other clients",
|
||||
"Запасной способ подключения. Скопируйте ссылку или отсканируйте код — настройки те же.": "A backup way to connect. Copy the link or scan the code — the settings are the same.",
|
||||
"Копировать": "Copy",
|
||||
"Копировать ключ": "Copy key",
|
||||
"Скопировано": "Copied",
|
||||
"Цена одна для любой страны: 4 900 ₽ первый месяц, далее 2 499 ₽.": "One price for every country: $49 the first month, then $24.",
|
||||
"Доступ выдадим сразу после оплаты": "Access is issued right after payment",
|
||||
"Установим сервер под вас, доступ в течение 24 часов": "We will set up a server for you, access within 24 hours",
|
||||
"Откройте приложение на телефоне и наведите камеру на код.": "Open the app on your phone and point the camera at the code.",
|
||||
"Выйти · anna@example.com": "Sign out · anna@example.com",
|
||||
"Светлая тема": "Light theme",
|
||||
"Тёмная тема": "Dark theme",
|
||||
"Как в системе": "Match the system",
|
||||
"Язык: русский": "Language: Russian",
|
||||
"Язык: английский": "Language: English",
|
||||
|
||||
// MiniApp
|
||||
"мини-приложение": "mini app",
|
||||
"Закрыть": "Close",
|
||||
"Нажмите, чтобы открыть подключение": "Tap to open the connection",
|
||||
"Купить ещё один ключ": "Buy another key",
|
||||
"← Ключи": "← Keys",
|
||||
"Ключи": "Keys",
|
||||
"Ещё": "More",
|
||||
"5 устройств": "5 devices",
|
||||
"Файл": "File",
|
||||
"Happ и другие": "Happ and others",
|
||||
"4 900 ₽ первый месяц, далее 2 499 ₽": "$49 first month, then $24",
|
||||
"Оплата пройдёт внутри Telegram, ключ появится в списке.": "Payment happens inside Telegram, the key appears in the list.",
|
||||
"Написать в поддержку": "Message support",
|
||||
"Почта аккаунта": "Account email",
|
||||
"По умолчанию подстраивается под тему Telegram.": "By default it follows the Telegram theme.",
|
||||
"Наведите камеру телефона на код в приложении WireGuard или Happ.": "Point your phone camera at the code in WireGuard or Happ.",
|
||||
"Мини-приложение в Telegram": "Telegram mini app",
|
||||
"Тот же кабинет в парадигме телефона: список ключей, страница ключа с QR-кодами и покупка без выхода в браузер. Вход не нужен — Telegram уже знает, кто вы.": "The same account in phone form: the list of keys, a key page with QR codes and buying without leaving Telegram. No sign-in needed — Telegram already knows who you are.",
|
||||
"Нижние вкладки повторяют сайдбар веб-кабинета: Ключи, Купить, Ещё.": "The bottom tabs mirror the web sidebar: Keys, Buy, More.",
|
||||
|
||||
// Локации — 30 направлений
|
||||
"Великобритания": "United Kingdom",
|
||||
"Лондон": "London",
|
||||
"Франция": "France",
|
||||
"Париж": "Paris",
|
||||
"Швеция": "Sweden",
|
||||
"Стокгольм": "Stockholm",
|
||||
"Норвегия": "Norway",
|
||||
"Осло": "Oslo",
|
||||
"Дания": "Denmark",
|
||||
"Копенгаген": "Copenhagen",
|
||||
"Швейцария": "Switzerland",
|
||||
"Цюрих": "Zurich",
|
||||
"Австрия": "Austria",
|
||||
"Вена": "Vienna",
|
||||
"Испания": "Spain",
|
||||
"Мадрид": "Madrid",
|
||||
"Италия": "Italy",
|
||||
"Милан": "Milan",
|
||||
"Польша": "Poland",
|
||||
"Варшава": "Warsaw",
|
||||
"Чехия": "Czechia",
|
||||
"Прага": "Prague",
|
||||
"Румыния": "Romania",
|
||||
"Бухарест": "Bucharest",
|
||||
"Литва": "Lithuania",
|
||||
"Вильнюс": "Vilnius",
|
||||
"Латвия": "Latvia",
|
||||
"Рига": "Riga",
|
||||
"Эстония": "Estonia",
|
||||
"Таллин": "Tallinn",
|
||||
"Казахстан": "Kazakhstan",
|
||||
"Алматы": "Almaty",
|
||||
"Армения": "Armenia",
|
||||
"Ереван": "Yerevan",
|
||||
"Грузия": "Georgia",
|
||||
"Тбилиси": "Tbilisi",
|
||||
"ОАЭ": "UAE",
|
||||
"Дубай": "Dubai",
|
||||
"Сингапур": "Singapore",
|
||||
"Южная Корея": "South Korea",
|
||||
"Сеул": "Seoul",
|
||||
"Канада": "Canada",
|
||||
"Торонто": "Toronto",
|
||||
"Австралия": "Australia",
|
||||
"Сидней": "Sydney",
|
||||
"Бразилия": "Brazil",
|
||||
"Сан-Паулу": "São Paulo",
|
||||
|
||||
// Цены локаций
|
||||
"6 900 ₽": "$69",
|
||||
"3 499 ₽": "$34",
|
||||
"9 900 ₽": "$99",
|
||||
"4 999 ₽": "$49",
|
||||
"первый месяц · далее": "first month · then",
|
||||
"далее": "then",
|
||||
"в месяц": "per month",
|
||||
"Персональный сервер с индивидуальным IP": "A personal server with a dedicated IP",
|
||||
"30 стран на выбор и российский адрес в комплекте с любым тарифом.": "30 countries to choose from, with a Russian address included in every plan.",
|
||||
"Базовая цена — 4 900 ₽ первый месяц и 2 499 ₽ далее. В редких локациях она выше и указана на карточке.": "The base price is $49 for the first month and $24 after that. Rare locations cost more, and the price is shown on the card.",
|
||||
"Нажмите точку на карте — покажем страну и цену": "Tap a dot on the map to see the country and its price",
|
||||
"Базовая цена: 4 900 ₽ первый месяц, далее 2 499 ₽. В редких локациях — выше.": "Base price: $49 the first month, then $24. Rare locations cost more.",
|
||||
|
||||
"Имя": "Name",
|
||||
"Нужны индивидуальные условия": "Need custom terms",
|
||||
|
||||
"Отвечаем с 10 до 20 в рабочие дни.": "We reply from 10:00 to 20:00 on business days.",
|
||||
|
||||
"Нужной страны нет в списке, нужно много адресов или свой сценарий маршрутизации — напишите, скажем сроки и установим под вас.": "If your country is missing, you need many addresses, or you have a custom routing setup — write to us and we will give you timing and set it up.",
|
||||
|
||||
// Даты и единицы, встречающиеся в демо-данных
|
||||
"14 октября 2026": "14 October 2026",
|
||||
"21 сентября 2026": "21 September 2026",
|
||||
"14 октября": "14 October",
|
||||
"21 сентября": "21 September"
|
||||
};
|
||||
|
||||
// Переключение языка: подменяет текстовые узлы и подписи внутри корня.
|
||||
window.VEILIX_APPLY_LANG = function (root, lang) {
|
||||
const dict = window.VEILIX_I18N || {};
|
||||
const el = root || document.body;
|
||||
if (!el) return;
|
||||
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);
|
||||
const nodes = [];
|
||||
while (walker.nextNode()) nodes.push(walker.currentNode);
|
||||
nodes.forEach(node => {
|
||||
const parent = node.parentNode;
|
||||
if (parent && (parent.nodeName === "SCRIPT" || parent.nodeName === "STYLE")) return;
|
||||
// Кэш валиден, только если узел с тех пор не менялся снаружи (React мог перерисовать текст).
|
||||
const cached = node.__veilixOut != null && node.nodeValue === node.__veilixOut;
|
||||
if (lang === "en") {
|
||||
const raw = cached ? node.__veilixRu : node.nodeValue;
|
||||
const key = raw.trim();
|
||||
if (!key || !dict[key]) { node.__veilixRu = null; node.__veilixOut = null; return; }
|
||||
node.__veilixRu = raw;
|
||||
node.nodeValue = raw.replace(key, dict[key]);
|
||||
node.__veilixOut = node.nodeValue;
|
||||
} else if (cached && node.__veilixRu != null) {
|
||||
node.nodeValue = node.__veilixRu;
|
||||
node.__veilixRu = null;
|
||||
node.__veilixOut = null;
|
||||
}
|
||||
});
|
||||
["placeholder", "title", "aria-label"].forEach(attr => {
|
||||
el.querySelectorAll("[" + attr + "]").forEach(target => {
|
||||
const store = "__veilix_" + attr;
|
||||
const out = "__veilixOut_" + attr;
|
||||
const current = target.getAttribute(attr);
|
||||
const cached = target[out] != null && current === target[out];
|
||||
if (lang === "en") {
|
||||
const raw = cached ? target[store] : current;
|
||||
const key = (raw || "").trim();
|
||||
if (!key || !dict[key]) { target[store] = null; target[out] = null; return; }
|
||||
target[store] = raw;
|
||||
target.setAttribute(attr, dict[key]);
|
||||
target[out] = dict[key];
|
||||
} else if (cached && target[store] != null) {
|
||||
target.setAttribute(attr, target[store]);
|
||||
target[store] = null;
|
||||
target[out] = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,352 @@
|
||||
// @ds-adherence-ignore -- omelette starter scaffold (raw elements/hex/px by design)
|
||||
// Copied omelette starter. Re-running copy_starter_component with this kind overwrites this file with the latest version (page content is unaffected).
|
||||
|
||||
/* BEGIN USAGE */
|
||||
// iOS.jsx — Simplified iOS 26 (Liquid Glass) device frame
|
||||
// Based on the iOS 26 UI Kit + Figma status bar spec. No assets, no deps.
|
||||
// Exports (to window): IOSDevice, IOSStatusBar, IOSNavBar, IOSGlassPill, IOSList, IOSListRow, IOSKeyboard
|
||||
//
|
||||
// Usage — wrap your screen content in <IOSDevice> to get the bezel, status bar
|
||||
// and home indicator (props: title, dark, keyboard):
|
||||
//
|
||||
// <IOSDevice title="Settings">
|
||||
// ...your screen content...
|
||||
// </IOSDevice>
|
||||
// <IOSDevice dark title="Search" keyboard>…</IOSDevice>
|
||||
/* END USAGE */
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Status bar
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSStatusBar({ dark = false, time = '9:41' }) {
|
||||
const c = dark ? '#fff' : '#000';
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', gap: 154, alignItems: 'center', justifyContent: 'center',
|
||||
padding: '21px 24px 19px', boxSizing: 'border-box',
|
||||
position: 'relative', zIndex: 20, width: '100%',
|
||||
}}>
|
||||
<div style={{ flex: 1, height: 22, display: 'flex', alignItems: 'center', justifyContent: 'center', paddingTop: 1.5 }}>
|
||||
<span style={{
|
||||
fontFamily: '-apple-system, "SF Pro", system-ui', fontWeight: 590,
|
||||
fontSize: 17, lineHeight: '22px', color: c,
|
||||
}}>{time}</span>
|
||||
</div>
|
||||
<div style={{ flex: 1, height: 22, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7, paddingTop: 1, paddingRight: 1 }}>
|
||||
<svg width="19" height="12" viewBox="0 0 19 12">
|
||||
<rect x="0" y="7.5" width="3.2" height="4.5" rx="0.7" fill={c}/>
|
||||
<rect x="4.8" y="5" width="3.2" height="7" rx="0.7" fill={c}/>
|
||||
<rect x="9.6" y="2.5" width="3.2" height="9.5" rx="0.7" fill={c}/>
|
||||
<rect x="14.4" y="0" width="3.2" height="12" rx="0.7" fill={c}/>
|
||||
</svg>
|
||||
<svg width="17" height="12" viewBox="0 0 17 12">
|
||||
<path d="M8.5 3.2C10.8 3.2 12.9 4.1 14.4 5.6L15.5 4.5C13.7 2.7 11.2 1.5 8.5 1.5C5.8 1.5 3.3 2.7 1.5 4.5L2.6 5.6C4.1 4.1 6.2 3.2 8.5 3.2Z" fill={c}/>
|
||||
<path d="M8.5 6.8C9.9 6.8 11.1 7.3 12 8.2L13.1 7.1C11.8 5.9 10.2 5.1 8.5 5.1C6.8 5.1 5.2 5.9 3.9 7.1L5 8.2C5.9 7.3 7.1 6.8 8.5 6.8Z" fill={c}/>
|
||||
<circle cx="8.5" cy="10.5" r="1.5" fill={c}/>
|
||||
</svg>
|
||||
<svg width="27" height="13" viewBox="0 0 27 13">
|
||||
<rect x="0.5" y="0.5" width="23" height="12" rx="3.5" stroke={c} strokeOpacity="0.35" fill="none"/>
|
||||
<rect x="2" y="2" width="20" height="9" rx="2" fill={c}/>
|
||||
<path d="M25 4.5V8.5C25.8 8.2 26.5 7.2 26.5 6.5C26.5 5.8 25.8 4.8 25 4.5Z" fill={c} fillOpacity="0.4"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Liquid glass pill — blur + tint + shine
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSGlassPill({ children, dark = false, style = {} }) {
|
||||
return (
|
||||
<div style={{
|
||||
height: 44, minWidth: 44, borderRadius: 9999,
|
||||
position: 'relative', overflow: 'hidden',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: dark
|
||||
? '0 2px 6px rgba(0,0,0,0.35), 0 6px 16px rgba(0,0,0,0.2)'
|
||||
: '0 1px 3px rgba(0,0,0,0.07), 0 3px 10px rgba(0,0,0,0.06)',
|
||||
...style,
|
||||
}}>
|
||||
{/* blur + tint */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 9999,
|
||||
backdropFilter: 'blur(12px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||||
background: dark ? 'rgba(120,120,128,0.28)' : 'rgba(255,255,255,0.5)',
|
||||
}} />
|
||||
{/* shine */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 9999,
|
||||
boxShadow: dark
|
||||
? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.15), inset -1px -1px 1px rgba(255,255,255,0.08)'
|
||||
: 'inset 1.5px 1.5px 1px rgba(255,255,255,0.7), inset -1px -1px 1px rgba(255,255,255,0.4)',
|
||||
border: dark ? '0.5px solid rgba(255,255,255,0.15)' : '0.5px solid rgba(0,0,0,0.06)',
|
||||
}} />
|
||||
<div style={{ position: 'relative', zIndex: 1, display: 'flex', alignItems: 'center', padding: '0 4px' }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Navigation bar — glass pills + large title
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSNavBar({ title = 'Title', dark = false, trailingIcon = true }) {
|
||||
const muted = dark ? 'rgba(255,255,255,0.6)' : '#404040';
|
||||
const text = dark ? '#fff' : '#000';
|
||||
const pillIcon = (content) => (
|
||||
<IOSGlassPill dark={dark}>
|
||||
<div style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
{content}
|
||||
</div>
|
||||
</IOSGlassPill>
|
||||
);
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 10,
|
||||
paddingTop: 62, paddingBottom: 10, position: 'relative', zIndex: 5,
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
padding: '0 16px',
|
||||
}}>
|
||||
{/* back chevron */}
|
||||
{pillIcon(
|
||||
<svg width="12" height="20" viewBox="0 0 12 20" fill="none" style={{ marginLeft: -1 }}>
|
||||
<path d="M10 2L2 10l8 8" stroke={muted} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
)}
|
||||
{/* trailing ellipsis */}
|
||||
{trailingIcon && pillIcon(
|
||||
<svg width="22" height="6" viewBox="0 0 22 6">
|
||||
<circle cx="3" cy="3" r="2.5" fill={muted}/>
|
||||
<circle cx="11" cy="3" r="2.5" fill={muted}/>
|
||||
<circle cx="19" cy="3" r="2.5" fill={muted}/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
{/* large title */}
|
||||
<div style={{
|
||||
padding: '0 16px',
|
||||
fontFamily: '-apple-system, system-ui',
|
||||
fontSize: 34, fontWeight: 700, lineHeight: '41px',
|
||||
color: text, letterSpacing: 0.4,
|
||||
}}>{title}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Grouped list (inset card, r:26) + row (52px)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSListRow({ title, detail, icon, chevron = true, isLast = false, dark = false }) {
|
||||
const text = dark ? '#fff' : '#000';
|
||||
const sec = dark ? 'rgba(235,235,245,0.6)' : 'rgba(60,60,67,0.6)';
|
||||
const ter = dark ? 'rgba(235,235,245,0.3)' : 'rgba(60,60,67,0.3)';
|
||||
const sep = dark ? 'rgba(84,84,88,0.65)' : 'rgba(60,60,67,0.12)';
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', minHeight: 52,
|
||||
padding: '0 16px', position: 'relative',
|
||||
fontFamily: '-apple-system, system-ui', fontSize: 17,
|
||||
letterSpacing: -0.43,
|
||||
}}>
|
||||
{icon && (
|
||||
<div style={{
|
||||
width: 30, height: 30, borderRadius: 7, background: icon,
|
||||
marginRight: 12, flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
<div style={{ flex: 1, color: text }}>{title}</div>
|
||||
{detail && <span style={{ color: sec, marginRight: 6 }}>{detail}</span>}
|
||||
{chevron && (
|
||||
<svg width="8" height="14" viewBox="0 0 8 14" style={{ flexShrink: 0 }}>
|
||||
<path d="M1 1l6 6-6 6" stroke={ter} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
)}
|
||||
{!isLast && (
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 0, right: 0,
|
||||
left: icon ? 58 : 16, height: 0.5, background: sep,
|
||||
}} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IOSList({ header, children, dark = false }) {
|
||||
const hc = dark ? 'rgba(235,235,245,0.6)' : 'rgba(60,60,67,0.6)';
|
||||
const bg = dark ? '#1C1C1E' : '#fff';
|
||||
return (
|
||||
<div>
|
||||
{header && (
|
||||
<div style={{
|
||||
fontFamily: '-apple-system, system-ui', fontSize: 13,
|
||||
color: hc, textTransform: 'uppercase',
|
||||
padding: '8px 36px 6px', letterSpacing: -0.08,
|
||||
}}>{header}</div>
|
||||
)}
|
||||
<div style={{
|
||||
background: bg, borderRadius: 26,
|
||||
margin: '0 16px', overflow: 'hidden',
|
||||
}}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Device frame
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSDevice({
|
||||
children, width = 402, height = 874, dark = false,
|
||||
title, keyboard = false,
|
||||
}) {
|
||||
return (
|
||||
// data-om-starter: inert presence marker — Claude Design's starter-usage
|
||||
// probe reads it; it renders nothing. Keep it on this root element.
|
||||
<div data-om-starter="ios-frame" style={{
|
||||
width, height, borderRadius: 48, overflow: 'hidden',
|
||||
position: 'relative', background: dark ? '#000' : '#F2F2F7',
|
||||
boxShadow: '0 40px 80px rgba(0,0,0,0.18), 0 0 0 1px rgba(0,0,0,0.12)',
|
||||
fontFamily: '-apple-system, system-ui, sans-serif',
|
||||
WebkitFontSmoothing: 'antialiased',
|
||||
}}>
|
||||
{/* dynamic island */}
|
||||
<div style={{
|
||||
position: 'absolute', top: 11, left: '50%', transform: 'translateX(-50%)',
|
||||
width: 126, height: 37, borderRadius: 24, background: '#000', zIndex: 50,
|
||||
}} />
|
||||
{/* status bar (absolute) */}
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, zIndex: 10 }}>
|
||||
<IOSStatusBar dark={dark} />
|
||||
</div>
|
||||
{/* nav + content */}
|
||||
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
{title !== undefined && <IOSNavBar title={title} dark={dark} />}
|
||||
<div style={{ flex: 1, overflow: 'auto' }}>{children}</div>
|
||||
{keyboard && <IOSKeyboard dark={dark} />}
|
||||
</div>
|
||||
{/* home indicator — always on top */}
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 60,
|
||||
height: 34, display: 'flex', justifyContent: 'center', alignItems: 'flex-end',
|
||||
paddingBottom: 8, pointerEvents: 'none',
|
||||
}}>
|
||||
<div style={{
|
||||
width: 139, height: 5, borderRadius: 100,
|
||||
background: dark ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.25)',
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Keyboard — iOS 26 liquid glass
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSKeyboard({ dark = false }) {
|
||||
const glyph = dark ? 'rgba(255,255,255,0.7)' : '#595959';
|
||||
const sugg = dark ? 'rgba(255,255,255,0.6)' : '#333';
|
||||
const keyBg = dark ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.85)';
|
||||
|
||||
// special-key icons
|
||||
const icons = {
|
||||
shift: <svg width="19" height="17" viewBox="0 0 19 17"><path d="M9.5 1L1 9.5h4.5V16h8V9.5H18L9.5 1z" fill={glyph}/></svg>,
|
||||
del: <svg width="23" height="17" viewBox="0 0 23 17"><path d="M7 1h13a2 2 0 012 2v11a2 2 0 01-2 2H7l-6-7.5L7 1z" fill="none" stroke={glyph} strokeWidth="1.6" strokeLinejoin="round"/><path d="M10 5l7 7M17 5l-7 7" stroke={glyph} strokeWidth="1.6" strokeLinecap="round"/></svg>,
|
||||
ret: <svg width="20" height="14" viewBox="0 0 20 14"><path d="M18 1v6H4m0 0l4-4M4 7l4 4" fill="none" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
|
||||
};
|
||||
|
||||
const key = (content, { w, flex, ret, fs = 25, k } = {}) => (
|
||||
<div key={k} style={{
|
||||
height: 42, borderRadius: 8.5,
|
||||
flex: flex ? 1 : undefined, width: w, minWidth: 0,
|
||||
background: ret ? '#08f' : keyBg,
|
||||
boxShadow: '0 1px 0 rgba(0,0,0,0.075)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontFamily: '-apple-system, "SF Compact", system-ui',
|
||||
fontSize: fs, fontWeight: 458, color: ret ? '#fff' : glyph,
|
||||
}}>{content}</div>
|
||||
);
|
||||
|
||||
const row = (keys, pad = 0) => (
|
||||
<div style={{ display: 'flex', gap: 6.5, justifyContent: 'center', padding: `0 ${pad}px` }}>
|
||||
{keys.map(l => key(l, { flex: true, k: l }))}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'relative', zIndex: 15, borderRadius: 27, overflow: 'hidden',
|
||||
padding: '11px 0 2px',
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
||||
boxShadow: dark
|
||||
? '0 -2px 20px rgba(0,0,0,0.09)'
|
||||
: '0 -1px 6px rgba(0,0,0,0.018), 0 -3px 20px rgba(0,0,0,0.012)',
|
||||
}}>
|
||||
{/* liquid glass bg — same recipe as nav pills */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 27,
|
||||
backdropFilter: 'blur(12px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||||
background: dark ? 'rgba(120,120,128,0.14)' : 'rgba(255,255,255,0.25)',
|
||||
}} />
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 27,
|
||||
boxShadow: dark
|
||||
? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.15)'
|
||||
: 'inset 1.5px 1.5px 1px rgba(255,255,255,0.7), inset -1px -1px 1px rgba(255,255,255,0.4)',
|
||||
border: dark ? '0.5px solid rgba(255,255,255,0.15)' : '0.5px solid rgba(0,0,0,0.06)',
|
||||
pointerEvents: 'none',
|
||||
}} />
|
||||
|
||||
{/* autocorrect bar */}
|
||||
<div style={{
|
||||
display: 'flex', gap: 20, alignItems: 'center',
|
||||
padding: '8px 22px 13px', width: '100%', boxSizing: 'border-box',
|
||||
position: 'relative',
|
||||
}}>
|
||||
{['"The"', 'the', 'to'].map((w, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{i > 0 && <div style={{ width: 1, height: 25, background: '#ccc', opacity: 0.3 }} />}
|
||||
<div style={{
|
||||
flex: 1, textAlign: 'center',
|
||||
fontFamily: '-apple-system, system-ui', fontSize: 17,
|
||||
color: sugg, letterSpacing: -0.43, lineHeight: '22px',
|
||||
}}>{w}</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* key layout */}
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 13,
|
||||
padding: '0 6.5px', width: '100%', boxSizing: 'border-box',
|
||||
position: 'relative',
|
||||
}}>
|
||||
{row(['q','w','e','r','t','y','u','i','o','p'])}
|
||||
{row(['a','s','d','f','g','h','j','k','l'], 20)}
|
||||
<div style={{ display: 'flex', gap: 14.25, alignItems: 'center' }}>
|
||||
{key(icons.shift, { w: 45, k: 'shift' })}
|
||||
<div style={{ display: 'flex', gap: 6.5, flex: 1 }}>
|
||||
{['z','x','c','v','b','n','m'].map(l => key(l, { flex: true, k: l }))}
|
||||
</div>
|
||||
{key(icons.del, { w: 45, k: 'del' })}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
||||
{key('ABC', { w: 92.25, fs: 18, k: 'abc' })}
|
||||
{key('', { flex: true, k: 'space' })}
|
||||
{key(icons.ret, { w: 92.25, ret: true, k: 'ret' })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* bottom spacer (emoji+mic area, icons omitted) */}
|
||||
<div style={{ height: 56, width: '100%', position: 'relative' }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(window, {
|
||||
IOSDevice, IOSStatusBar, IOSNavBar, IOSGlassPill, IOSList, IOSListRow, IOSKeyboard,
|
||||
});
|
||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,197 @@
|
||||
# Дизайн VEILIX
|
||||
|
||||
|
||||
## Общая задача
|
||||
|
||||
Нужен дизайн сайта по продаже персональных VPN. Не классический VPN провайдер, когда много клиентов на 1 сервере, а когда мы делаем каждому клиенту индивидуальный IP.
|
||||
|
||||
Быстрый современный сайт на tailwind с максимальной скоростью под Google Page Speed. Стек который будем дальше использовать: payloadcms + Next.
|
||||
|
||||
|
||||
## Дизайн
|
||||
|
||||
"Чистый" современный дизайн без лишней маркетинговых бессмысленных фраз, которые есть у всех крупных. Нам нужно выделиться, чётким предложениям видным и понятным, по делу, а не придумывать того чего нет. Прям минимум, тк нет лишней информации.
|
||||
|
||||
Если сравнить карты Apple vs Карты Google. Apple в своих картах явно пошёл не биться в лоб с Google, тк это бессмысленно, а сделан совсем минимальный интерфейс, при том что у Google в целом, тоже понятно, но за счёт функционала интерфейс перегружен в любом случае. Этот же пример и про Apple Music.
|
||||
|
||||
### Основные тезисы:
|
||||
|
||||
- "Чистая" страница с минимум информации
|
||||
- Современный шрифт, не технический
|
||||
- Это сайт не для гиков, а для обычных людей. Графика, иконки, шрифты должны располагать и быть понятны человеку не компьютерному. Есть когда сайты выглядят больше как для гиков и не только по описанию и текстам, а потому какие шрифты, графика, иконки.
|
||||
- Не нужны банальные иконки всяких замков, защит и прочего
|
||||
- Ночная тема
|
||||
|
||||
|
||||
## Верхнее меню
|
||||
|
||||
1. Логотип (слева)
|
||||
2. далее отдельно немного от логотипа правее
|
||||
- Цены
|
||||
- Локации
|
||||
- Поддержка
|
||||
3. далее прижато справа:
|
||||
- Переключение языков (иконка)
|
||||
- Кнопка Login
|
||||
- Кнопка Get VPN
|
||||
|
||||
Название Veilix - без слова VPN для EN. Для русского Вейликс.
|
||||
|
||||
Висит всегда на экране, стандартно.
|
||||
|
||||
|
||||
## Нижнее меню
|
||||
|
||||
Меню, теже пункты что и вверху
|
||||
Дополнительно Legal страницы
|
||||
Оплата карточками
|
||||
Переключение темы 3 режима
|
||||
Переключение языка
|
||||
|
||||
|
||||
## Логотип
|
||||
|
||||
Придумать логотип лёгкий, тк Veilix, от слова Вуаль - Veil, когда она легко скрывает за собой что-то, нужен и логотип такой же. Те нужно чтобы это были какие-то замки и вот это всё банальное. У Nord на лого какие-то горы, возможно что-то с севером ассоциировано.
|
||||
|
||||
|
||||
|
||||
## Цвета
|
||||
|
||||
Сайту нужен светлый, например у Nord hero экран тёмный - так не нужно. У blancvpn тоже. При этом акцентные цвета - тёмно фиолетовый, глубокий и пастельный.
|
||||
|
||||
У Firefox режим инкогнито когда включается там набор градиентный фиолетовых цветов.
|
||||
|
||||
У Protonvpn в сторону Фиолетового неплохие цвета и светлое всё, нет тёмных.
|
||||
|
||||
|
||||
## Языки
|
||||
|
||||
Сделать всё на 2х языках RU и EN.
|
||||
|
||||
|
||||
|
||||
## Главная страница
|
||||
|
||||
### Hero экран
|
||||
|
||||
Ваш личный VPN с индивидуальным IP. Но не нужно его делать на весь экран по высоте, достаточно 2/3, чтобы далее контент просматривался ниже.
|
||||
|
||||
|
||||
### Преимущества
|
||||
|
||||
C иконками и расшифровкой, что это значит. Нужно написать красиво, то что перечислил ниже, можно разложить даже как-то иначе. Художественно, чтобы понятно было потребителю не разбирающемуся в технологиях, и руководителям.
|
||||
|
||||
|
||||
1. Индивидуальный ip
|
||||
- Работа с западными AI снижается риск блокировки по сравнению с публичными VPN
|
||||
- Безопасность вашего трафика в любой точке мира где вы будете пользоваться
|
||||
- Одинаковый IP на всех ваших устройствах где-бы вы не находились, не отличить. Вы всегда в одной локации с вашим IP.
|
||||
- Любые сервисы работают стабильнее с личными IP, тк при использовании массовых часто срабатывает защита и ботов
|
||||
|
||||
2. Тариф включает до 4-х устройств
|
||||
- Можно приобретать дополнительные тарифы в этой или других локациях с дополнительными индивидуальными IP
|
||||
|
||||
3. К выбранному тарифу дополнительно выделенный RU IP адрес
|
||||
- Используется и как прямое соединение и для организации моста в обе стороны
|
||||
|
||||
4. Два типа протоколов WireGuard (Amnezia WG) и Xray (Vless + Histeria2 + xhttp)
|
||||
- Дополняются и обновляются
|
||||
|
||||
5. Умная маршрутиризация трафика из Ру к Глобал
|
||||
- К RU сайтам через RU сервер напрямую
|
||||
- К Глобальным трафик через туннель прокидывается через локацию в вашем тарифе
|
||||
|
||||
6. При использовании из Глобала обратная маршрутиризация
|
||||
- К мировым сайтам прямой коннект из вашей локации
|
||||
- К RU сайтам трафик через туннель идёт через RU сервер
|
||||
- Для работающих с Россией
|
||||
|
||||
7. О нас
|
||||
- Работаем с 2022 года
|
||||
|
||||
|
||||
### FAQ
|
||||
|
||||
Стандартная история, несколько вопросов с ответами раскрывающимися. Придумай для моей тематике исходя из описанного в задаче.
|
||||
|
||||
|
||||
|
||||
## Страницы Цены
|
||||
|
||||
Сразу крупно обыграть блок с ценой на первом экране, одна цена
|
||||
|
||||
4900 - первый месяц
|
||||
2900 далее
|
||||
|
||||
Должен быть выбор локации, и при этом какие-то локации показывают, есть несколько серверов в наличии, а какие-то показывают будет готово через 24 часа.
|
||||
|
||||
Готово через 24 часа это даже фича, что покупатель реально видит что под него делается.
|
||||
|
||||
Выбор локации и Купить. Переход на страницу оформления заказа.
|
||||
|
||||
Кнопка собирать лиды, кому требуется индивидуальные условия - оставьте заявку.
|
||||
|
||||
|
||||
|
||||
|
||||
## Страница Поддержка
|
||||
|
||||
Крупно блок с контактный email и tg, дальше в FAQ разделённый по разделам и ниже форма связи.
|
||||
|
||||
|
||||
|
||||
|
||||
## Страница Оплаты и Оформления заказа
|
||||
|
||||
Страница оплаты заказа при нажатии на Купить.
|
||||
|
||||
Стандартно как у Nord и Blanc, с предложением Авторизоваться по почте.
|
||||
|
||||
|
||||
|
||||
|
||||
## Личный кабинет на Вебе
|
||||
|
||||
Максимально простой, как у Nord и Blanc. Страница и слева сайдбар. Для личного tailwind если используют, то на нём, если что-то другое применяют, то предлагай.
|
||||
|
||||
Регистрация и авторизация только по почте по одноразовому паролю, при этом не разделять авторизацию и регистрацию.
|
||||
|
||||
В сайбаре Главная, Купить Ключ, Список ключей и внизу Поддержка, Язык и Тема.
|
||||
|
||||
C Иконками пункты меню.
|
||||
|
||||
В отличии от Nord и Blanc у нас ключей может быть несколько, чтобы сразу в нужный попадать и видеть срок истечения.
|
||||
|
||||
### Главная страница
|
||||
- Список ключей
|
||||
- Купить Ключ
|
||||
- FAQ
|
||||
|
||||
### Страница Ключа
|
||||
|
||||
- Дата окончания
|
||||
- Изменить Оплату (привязанную карточку)
|
||||
- Скачать 4 конфигурации для WG или отсканировать QR
|
||||
- И ключ для Happ, crypto. Копировать и плюс QR.
|
||||
|
||||
### Страница Купить
|
||||
|
||||
Внутри личного кабинета без перехода на сайт, весь функционал как в разделе Купить с выбором локации
|
||||
|
||||
|
||||
|
||||
## Личный кабинет в Telegram
|
||||
|
||||
Нужен дизайн для MiniApp, и чтобы это выглядело в стиле телефона, например у самого Telegram, есть нативное приложение Wallet, и там похоже на интерфейс телефона.
|
||||
|
||||
Обыграть всё что я написал про Личный на Вебе, в парадигме MiniApp и интерфейса как у телефона.
|
||||
|
||||
|
||||
|
||||
## Дополнительный вариант Дизайна
|
||||
|
||||
Будет 2 одинаковых сайта, но на 1 будет продаваться только индивидуальные IP, на втором чтобы была возможно и Массовые тарифы купить и также индивидуальный. Всё одинаково, за исключением 2х тарифов, Базовый и Премиум. Везде нужно это обыграть, где представлены тарифы.
|
||||
|
||||
Но нужно максимально унифицированно.
|
||||
|
||||
Чтобы в итоге это обрабатывали одни скрипты под разными доменами будет разное видно. В тч в Личном кабинете.
|
||||