Загрузка данных
Теперь полный код есть, и причина найдена: две карточки действительно оказались за пределами контейнера .features. Поэтому первые восемь карточек образуют сетку 2 × 4, а «VPN наоборот» и «Ищем партнёров» выпадают из неё.
Исправим это прямо в /opt/smuf-landing/site/index.html:
- уберём точки после четырёх фраз;
- переместим обе карточки внутрь общей сетки — получится 10 карточек, по две в ряд на компьютере;
- усилим синее свечение, подсветку границ и эффект подъёма при наведении;
- на телефонах сохраним одну колонку, как сейчас.
Другие тексты, кнопки, ссылки, блок предложения и настройки Caddy не меняем.
Внеси изменения одной командой
Скопируй блок целиком в терминал VPS. Скрипт проверит исходные фрагменты, изменит только нужные места и не создаст резервных копий.
python3 - <<'PY'
from pathlib import Path
p = Path("/opt/smuf-landing/site/index.html")
s = p.read_text(encoding="utf-8")
original = s
# Убираем точки в четырёх заголовках
replacements = {
"Интернет без границ.<br><span class=\"gradient-text\">Всё под контролем.</span>":
"Интернет без границ<br><span class=\"gradient-text\">Всё под контролем</span>",
"Больше возможностей.<br>Меньше ограничений.":
"Больше возможностей<br>Меньше ограничений",
}
for old, new in replacements.items():
if s.count(old) != 1:
raise SystemExit(f"ОШИБКА: не найден однозначно фрагмент: {old}")
s = s.replace(old, new, 1)
# Возвращаем две карточки внутрь общей сетки .features
old = ' </div>\n </section>\n\n <article class="feature-card">'
if s.count(old) != 1:
raise SystemExit("ОШИБКА: не удалось однозначно определить место карточек")
s = s.replace(old, ' <article class="feature-card">', 1)
# Усиливаем свечение карточек при наведении
old = """ background: radial-gradient(circle at 100% 0%, rgba(94, 161, 255, .12), transparent 52%);
content: "";
opacity: .6;"""
new = """ background:
radial-gradient(circle at 100% 0%, rgba(65, 142, 255, .32), transparent 55%),
radial-gradient(circle at 0% 100%, rgba(92, 169, 255, .16), transparent 52%);
content: "";
opacity: .72;"""
if s.count(old) != 1:
raise SystemExit("ОШИБКА: не найден исходный градиент карточек")
s = s.replace(old, new, 1)
old = """ .feature-card:hover {
transform: translateY(-5px);
border-color: rgba(83, 151, 247, .5);
background: rgba(255, 255, 255, .94);
box-shadow: 0 20px 48px rgba(44, 108, 196, .15);
}"""
new = """ .feature-card:hover {
transform: translateY(-7px);
border-color: rgba(55, 132, 255, .9);
background: rgba(255, 255, 255, .98);
box-shadow:
0 24px 60px rgba(44, 108, 196, .24),
0 0 0 1px rgba(62, 133, 232, .28),
0 0 34px rgba(45, 125, 255, .30);
}"""
if s.count(old) != 1:
raise SystemExit("ОШИБКА: не найден исходный стиль наведения")
s = s.replace(old, new, 1)
if s == original:
raise SystemExit("Изменений нет")
p.write_text(s, encoding="utf-8")
print("Готово: заголовки, сетка карточек и свечение обновлены.")
print("Карточек:", s.count('<article class="feature-card">'))
PY
Проверка
После выполнения проверь структуру HTML и настройки CSS:
grep -n -E 'Интернет без границ|Всё под контролем|Больше возможностей|Меньше ограничений|VPN наоборот|Ищем партнёров|grid-template-columns|0 0 34px' /opt/smuf-landing/site/index.html
Caddy перезагружать не нужно: он уже раздаёт файлы лендинга из каталога. Открой https://smufvpn.com/ и нажми Ctrl + F5.
На широком экране карточки будут располагаться по две в ряд, а на мобильном останутся в одной колонке. Если всё применилось, но карточки визуально стоят неправильно, пришли вывод проверки — продолжим точечно.
полностью убираем этот текст
SMUF VPN · НОВЫЙ УРОВЕНЬ СВОБОДЫ
вместо него ставим лого а снизу лого текст:
Smuf VPN
удаляем лого и тескст слева вверху
делаем вторичный текст в карточках темнее, и чуть больше
делаем плавный анимированный задний фон, будто еле еле видные плавающие волны линии чет в таком духе
Сделаем следующий этап оформления Smuf VPN в том же светлом стиле с синими акцентами.
- Верхний логотип и надпись Smuf VPN уберём из левой части шапки.
- В центре первого экрана удалим строку SMUF VPN · НОВЫЙ УРОВЕНЬ СВОБОДЫ, поставим логотип, а непосредственно под ним — Smuf VPN.
- Текст описаний внутри карточек сделаем темнее и немного крупнее, чтобы он легче читался.
- Добавим на фон медленно движущиеся полупрозрачные волны и тонкие линии с голубым свечением. Анимация будет ненавязчивой, без резких вспышек; на мобильных устройствах тоже сохранится аккуратный вид.
Применяем правки
Выполни команду целиком. Она изменит только index.html, без резервных копий и без перезапуска контейнеров.
python3 - <<'PY'
from pathlib import Path
p = Path("/opt/smuf-landing/site/index.html")
s = p.read_text(encoding="utf-8")
original = s
def replace_once(old, new, label):
global s
count = s.count(old)
if count != 1:
raise SystemExit(
f"ОШИБКА: {label}: найдено совпадений {count}, ожидалось 1. Файл не изменён."
)
s = s.replace(old, new, 1)
# 1. Убираем логотип и название из левой части шапки.
replace_once(
''' <a class="brand" href="/" aria-label="Smuf VPN — главная">
<span class="brand-mark"><span class="brand-fallback">S</span><img src="/logo.png" alt="" onerror="this.style.display='none'"></span>
<span>Smuf VPN</span>
</a>
''',
"",
"Логотип в шапке"
)
# Оставляем ссылку на личный кабинет справа.
replace_once(
" justify-content: space-between;\n padding: 25px 0;",
" justify-content: flex-end;\n padding: 25px 0;",
"Выравнивание шапки"
)
# 2. Вместо верхней строки — логотип и название под ним.
replace_once(
''' <p class="eyebrow"><span class="eyebrow-dot"></span> SMUF VPN · НОВЫЙ УРОВЕНЬ СВОБОДЫ</p>''',
''' <div class="hero-brand">
<img src="/logo.png" alt="Логотип Smuf VPN">
<span>Smuf VPN</span>
</div>''',
"Брендинг первого экрана"
)
# 3. Добавляем оформление логотипа над заголовком.
replace_once(
''' .hero {
position: relative;
padding: 83px 0 85px;
text-align: center;
}
''',
''' .hero {
position: relative;
padding: 65px 0 85px;
text-align: center;
}
.hero-brand {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
margin: 0 auto 28px;
}
.hero-brand img {
display: block;
width: 88px;
height: 88px;
object-fit: contain;
border-radius: 22px;
box-shadow: 0 12px 34px rgba(40, 105, 226, .2);
}
.hero-brand span {
color: var(--ink);
font-size: 23px;
font-weight: 780;
letter-spacing: -.045em;
}
''',
"Стили логотипа"
)
# 4. Делаем описания карточек крупнее и темнее.
replace_once(
''' .feature-card p {
max-width: 470px;
margin: 0;
color: var(--muted);
font-size: 14px;
line-height: 1.8;
}''',
''' .feature-card p {
max-width: 470px;
margin: 0;
color: #4b5d75;
font-size: 15.5px;
line-height: 1.75;
}''',
"Текст карточек"
)
# 5. Добавляем медленно движущиеся светлые волны на фон.
replace_once(
''' a { color: inherit; }
''',
''' body::before,
body::after {
content: "";
position: fixed;
inset: -25%;
z-index: 0;
pointer-events: none;
will-change: transform;
}
body::before {
background:
radial-gradient(ellipse at 18% 28%, rgba(93, 166, 255, .17), transparent 38%),
radial-gradient(ellipse at 82% 66%, rgba(133, 157, 255, .13), transparent 36%),
repeating-linear-gradient(
118deg,
transparent 0 115px,
rgba(56, 126, 229, .035) 116px 117px,
transparent 118px 225px
);
animation: ambient-drift 38s ease-in-out infinite alternate;
}
body::after {
inset: -35%;
opacity: .55;
background: repeating-radial-gradient(
ellipse at 68% 48%,
transparent 0 115px,
rgba(66, 139, 239, .055) 116px 117px,
transparent 118px 205px
);
animation: ambient-flow 52s ease-in-out infinite alternate;
}
@keyframes ambient-drift {
from { transform: translate3d(-1.2%, -1%, 0) scale(1); }
to { transform: translate3d(1.2%, 1%, 0) scale(1.04); }
}
@keyframes ambient-flow {
from { transform: rotate(-7deg) translate3d(-1%, 0, 0); }
to { transform: rotate(-3deg) translate3d(1%, -1%, 0); }
}
a { color: inherit; }
''',
"Анимированный фон"
)
# Содержимое страницы должно располагаться над декоративным фоном.
replace_once(
''' .page {
width: min(1120px, calc(100% - 40px));
margin: 0 auto;
}''',
''' .page {
position: relative;
z-index: 1;
width: min(1120px, calc(100% - 40px));
margin: 0 auto;
}''',
"Слой содержимого"
)
if s == original:
raise SystemExit("Изменений нет.")
p.write_text(s, encoding="utf-8")
print("Готово: все 6 изменений применены.")
print("Размер HTML:", len(s.encode("utf-8")), "байт")
PY
Проверяем результат
grep -n -E 'hero-brand|SMUF VPN · НОВЫЙ УРОВЕНЬ СВОБОДЫ|feature-card p|ambient-drift|ambient-flow|class="brand"' /opt/smuf-landing/site/index.html
После этого открой https://smufvpn.com/ и нажми Ctrl + F5.
Анимация будет работать на фоне всей страницы, а содержимое останется поверх неё. При включённой в системе настройке уменьшения движения уже предусмотренное правило prefers-reduced-motion отключит анимации.
root@1984996:~# python3 - <<'PY'
from pathlib import Path
p = Path("/opt/smuf-landing/site/index.html")
s = p.read_text(encoding="utf-8")
original = s
def replace_once(old, new, label):
global s
count = s.count(old)
if count != 1:
raise SystemExit(
f"ОШИБКА: {label}: найдено совпадений {count}, ожидалось 1. Файл не изменён."
)
s = s.replace(old, new, 1)
# 1. Убираем логотип и название из левой части шапки.
replace_once(
''' <a class="brand" href="/" aria-label="Smuf VPN — главная">
<span class="brand-mark"><span class="brand-fallback">S</span><img src="/logo.png" alt="" onerror="this.style.display='none'"></span>
<span>Smuf VPN</span>
</a>
''',
"",
"Логотип в шапке"
)
# Оставляем ссылку на личный кабинет справа.
replace_once(
" justify-content: space-between;\n padding: 25px 0;",
" justify-content: flex-end;\n padding: 25px 0;",
"Выравнивание шапки"
)
# 2. Вместо верхней строки — логотип и название под ним.
replace_once(
''' <p class="eyebrow"><span class="eyebrow-dot"></span> SMUF VPN · НОВЫЙ УРОВЕНЬ СВОБОДЫ</p>''',
''' <div class="hero-brand">
<img src="/logo.png" alt="Логотип Smuf VPN">
PYint("Размер HTML:", len(s.encode("utf-8")), "байт")тивным фоном.3), transparent 36%),
ОШИБКА: Логотип в шапке: найдено совпадений 2, ожидалось 1. Файл не изменён.
root@1984996:~#
Ошибка понятна: блок с логотипом встречается в HTML дважды, поэтому скрипт остановился до записи изменений. Файл остался без изменений. Ничего страшного — просто уточним, где находится шапка.
Выполни эту команду. Она только покажет оба места с логотипом и ничего не изменит:
grep -n -B 3 -A 5 'class="brand" href="/" aria-label="Smuf VPN' /opt/smuf-landing/site/index.html
Пришли вывод. Я подстрою команду так, чтобы убрать логотип и текст только слева вверху, поставить логотип с надписью Smuf VPN в центре первого экрана, сделать текст карточек темнее и немного крупнее, а фон — с плавными едва заметными анимированными волнами. Остальные элементы сайта трогать не будем.
root@1984996:~# grep -n -B 3 -A 5 'class="brand" href="/" aria-label="Smuf VPN' /opt/smuf-landing/site/index.html
512-<body>
513- <div class="page">
514- <header class="topbar">
515: <a class="brand" href="/" aria-label="Smuf VPN — главная">
516- <span class="brand-mark"><span class="brand-fallback">S</span><img src="/logo.png" alt="" onerror="this.style.display='none'"></span>
517- <span>Smuf VPN</span>
518- </a>
519- <a class="top-link" href="/?open=app">Личный кабинет ↗</a>
520- </header>
--
686- </main>
687-
688- <footer>
689: <a class="brand" href="/" aria-label="Smuf VPN — главная">
690- <span class="brand-mark"><span class="brand-fallback">S</span><img src="/logo.png" alt="" onerror="this.style.display='none'"></span>
691- <span>Smuf VPN</span>
692- </a>
693- <span>Свобода в сети начинается с Smuf.</span>
694- <a href="/?open=app">Подключиться ↗</a>
root@1984996:~#
Отлично, теперь причина ясна: логотип есть и в шапке, и в подвале. Уберём его только из шапки, а подвал оставим без изменений.
Выполни команду целиком. Она проверит все совпадения до записи и применит изменения только при однозначном результате.
python3 - <<'PY'
from pathlib import Path
p = Path("/opt/smuf-landing/site/index.html")
s = p.read_text(encoding="utf-8")
def replace_once(old, new, label):
global s
n = s.count(old)
if n != 1:
raise SystemExit(f"ОШИБКА: {label}: найдено {n} совпадений. Файл не изменён.")
s = s.replace(old, new, 1)
# Убираем бренд только из верхней шапки.
replace_once(
''' <header class="topbar">
<a class="brand" href="/" aria-label="Smuf VPN — главная">
<span class="brand-mark"><span class="brand-fallback">S</span><img src="/logo.png" alt="" onerror="this.style.display='none'"></span>
<span>Smuf VPN</span>
</a>
<a class="top-link" href="/?open=app">Личный кабинет ↗</a>
</header>''',
''' <header class="topbar">
<a class="top-link" href="/?open=app">Личный кабинет ↗</a>
</header>''',
"Шапка сайта"
)
# Меняем слоган над заголовком на логотип с названием.
replace_once(
''' <p class="eyebrow"><span class="eyebrow-dot"></span> SMUF VPN · НОВЫЙ УРОВЕНЬ СВОБОДЫ</p>''',
''' <div class="hero-brand">
<img src="/logo.png" alt="Логотип Smuf VPN">
<span>Smuf VPN</span>
</div>''',
"Брендинг первого экрана"
)
# Добавляем оформление логотипа, не затрагивая остальные стили hero.
replace_once(
''' .hero {
position: relative;
padding: 83px 0 85px;
text-align: center;
}''',
''' .hero {
position: relative;
padding: 65px 0 85px;
text-align: center;
}
.hero-brand {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
margin: 0 auto 28px;
}
.hero-brand img {
display: block;
width: 88px;
height: 88px;
object-fit: contain;
border-radius: 22px;
box-shadow: 0 12px 34px rgba(40, 105, 226, .2);
}
.hero-brand span {
color: var(--ink);
font-size: 23px;
font-weight: 780;
letter-spacing: -.045em;
}''',
"Стили логотипа"
)
# Делаем описание карточек темнее и немного крупнее.
replace_once(
''' .feature-card p {
max-width: 470px;
margin: 0;
color: var(--muted);
font-size: 14px;
line-height: 1.8;
}''',
''' .feature-card p {
max-width: 470px;
margin: 0;
color: #4b5d75;
font-size: 15.5px;
line-height: 1.75;
}''',
"Текст карточек"
)
# Добавляем ненавязчивые плавающие световые волны на задний план.
replace_once(
''' a { color: inherit; }''',
''' body::before,
body::after {
content: "";
position: fixed;
inset: -25%;
z-index: 0;
pointer-events: none;
will-change: transform;
}
body::before {
background:
radial-gradient(ellipse at 18% 28%, rgba(93, 166, 255, .17), transparent 38%),
radial-gradient(ellipse at 82% 66%, rgba(133, 157, 255, .13), transparent 36%),
repeating-linear-gradient(
118deg,
transparent 0 115px,
rgba(56, 126, 229, .035) 116px 117px,
transparent 118px 225px
);
animation: ambient-drift 38s ease-in-out infinite alternate;
}
body::after {
inset: -35%;
opacity: .55;
background: repeating-radial-gradient(
ellipse at 68% 48%,
transparent 0 115px,
rgba(66, 139, 239, .055) 116px 117px,
transparent 118px 205px
);
animation: ambient-flow 52s ease-in-out infinite alternate;
}
@keyframes ambient-drift {
from { transform: translate3d(-1.2%, -1%, 0) scale(1); }
to { transform: translate3d(1.2%, 1%, 0) scale(1.04); }
}
@keyframes ambient-flow {
from { transform: rotate(-7deg) translate3d(-1%, 0, 0); }
to { transform: rotate(-3deg) translate3d(1%, -1%, 0); }
}
@media (prefers-reduced-motion: reduce) {
body::before,
body::after {
animation: none !important;
}
}
a { color: inherit; }''',
"Анимированный фон"
)
# Поднимаем содержимое над декоративным фоном.
replace_once(
''' .page {
width: min(1120px, calc(100% - 40px));
margin: 0 auto;
}''',
''' .page {
position: relative;
z-index: 1;
width: min(1120px, calc(100% - 40px));
margin: 0 auto;
}''',
"Слой содержимого"
)
p.write_text(s, encoding="utf-8")
print("Готово: изменения применены.")
print("Размер HTML:", len(s.encode("utf-8")), "байт")
PY
Если увидишь Готово: изменения применены., обнови сайт https://smufvpn.com/ сочетанием Ctrl + F5. Перезапуск Caddy не требуется.
Если скрипт сообщит об ошибке совпадений, пришли её текст — файл при этом не будет перезаписан.
вот это еще просто на текст наверное поменяй - Почему Smuf VPN , а то выглядит как кнопка
Да, сделаем «Почему Smuf VPN» обычным заголовком, без оформления кнопки.
Сначала найди этот элемент:
grep -n -B 4 -A 5 'Почему Smuf VPN' /opt/smuf-landing/site/index.html
Пришли вывод — подскажу точечную замену, чтобы убрать только вид кнопки и не задеть остальные элементы.
root@1984996:~# grep -n -B 4 -A 5 'Почему Smuf VPN' /opt/smuf-landing/site/index.html
611- </section>
612-
613- <section aria-labelledby="features-title">
614- <div class="section-heading">
615: <p class="section-kicker">Почему Smuf VPN</p>
616- <h2 id="features-title">Больше возможностей<br>Меньше ограничений</h2>
617- <p class="section-description">
618- Всё необходимое для комфортного использования интернета — в одном месте.
619- </p>
620- </div>
root@1984996:~#
Нашли. Это <p class="section-kicker"> — скорее всего, именно CSS-класс section-kicker придаёт тексту вид кнопки.
Выполни команду — заменим его на обычный текстовый класс, не меняя сам текст и заголовок ниже:
python3 - <<'PY'
from pathlib import Path
p = Path("/opt/smuf-landing/site/index.html")
s = p.read_text(encoding="utf-8")
old = '<p class="section-kicker">Почему Smuf VPN</p>'
new = '<p class="section-label">Почему Smuf VPN</p>'
if s.count(old) != 1:
raise SystemExit("Ожидаемый элемент не найден ровно один раз. Файл не изменён.")
s = s.replace(old, new, 1)
# Добавляем спокойный стиль обычной подписи.
marker = ' .section-kicker'
if marker in s:
insert = ''' .section-label {
margin: 0 0 14px;
color: #3976d8;
font-size: 13px;
font-weight: 650;
letter-spacing: .08em;
}
'''
# Вставляем рядом с существующими стилями kicker.
pos = s.find(marker)
s = s[:pos] + insert + s[pos:]
else:
raise SystemExit("CSS-класс section-kicker не найден. HTML не записан.")
p.write_text(s, encoding="utf-8")
print("Готово: «Почему Smuf VPN» теперь обычная текстовая подпись.")
PY
После этого обнови страницу через Ctrl + F5. Перезапуск Caddy не нужен.
=== Файлы в архиве ===
01-security.webp 14636 байт
02-speed.webp 14948 байт
03-connection.webp 13530 байт
04-no-ads.webp 15176 байт
05-global-access.webp 15896 байт
06-privacy.webp 12162 байт
07-global-servers.webp 15230 байт
08-devices.webp 14490 байт
09-special-offer.webp 14056 байт
10-easy-setup.webp 10572 байт
README.txt 381 байт
=== HTML первой и остальных карточек ===
<section aria-labelledby="features-title">
<div class="section-heading">
<p class="section-label">Почему Smuf VPN</p>
<h2 id="features-title">Больше возможностей<br>Меньше ограничений</h2>
<p class="section-description">
Всё необходимое для комфортного использования интернета — в одном месте.
</p>
</div>
<div class="features">
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><path d="M12 3 4.5 7v5.5c0 4.3 3.2 7.3 7.5 8.5 4.3-1.2 7.5-4.2 7.5-8.5V7L12 3Z"/><path d="m8.5 12 2.2 2.2 4.8-5"/></svg>
</span>
</div>
<h3>Современные протоколы</h3>
<p>Используем передовые VPN-протоколы, такие как VLESS, для высокой скорости, стабильности и безопасности соединения в самых разных условиях.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18"/></svg>
</span>
</div>
<h3>Доступ к ресурсам</h3>
<p>Пользуйтесь сайтами, социальными сетями и стриминговыми платформами в поездках и повседневной жизни — где бы вы ни находились.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><path d="M4 5h16v12H8l-4 3V5Z"/><path d="M8 9h8M8 13h5"/></svg>
</span>
</div>
<h3>Круглосуточная поддержка</h3>
<p>Общайтесь с поддержкой в чате приложения — в привычном формате мессенджера, без лишних сложностей.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><path d="M3 17 9 11l4 4 8-9"/><path d="M15 6h6v6"/></svg>
</span>
</div>
<h3>Высокая скорость</h3>
<p>Серверы оптимизированы для производительности: смотрите видео, играйте, работайте и общайтесь с комфортом.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3M12 14v3"/></svg>
</span>
</div>
<h3>Защита данных</h3>
<p>Шифрование соединения помогает защитить передаваемые данные от перехвата в сети и постороннего доступа.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><path d="M3 7h18v13H3z"/><path d="M3 11h18M7 4v6M17 4v6"/><path d="m8 16 2 2 5-5"/></svg>
</span>
</div>
<h3>Экономия на онлайн-покупках</h3>
<p>Открывайте для себя региональные предложения на авиабилеты, отели, товары и подписки. Цены и доступность могут различаться в зависимости от страны.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-11V5l-8-3-8 3v6c0 7 8 11 8 11Z"/><rect x="7" y="8" width="10" height="7" rx="1.5"/><path d="M9 11h6M9 13h3M8 16 16 8"/></svg>
</span>
</div>
<h3>Забудьте о рекламе</h3>
<p>Встроенный блокировщик рекламы помогает убрать отвлекающие баннеры и сделать просмотр страниц комфортнее. Доступность функции зависит от конфигурации сервиса.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M19 8v6M16 11h6"/></svg>
</span>
</div>
<h3>Рефералы и бонусы</h3>
<p>Приглашайте друзей в Smuf VPN и получайте приятные бонусы.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18"/></svg>
</span>
</div>
<h3>VPN наоборот</h3>
<p>Доступ к российским сервисам из-за границы? Легко! Наш сервер в России открывает вам российский интернет.</p>
</article>
<article class="feature-card">
<div class="feature-top">
<span class="feature-icon">
<svg viewBox="0 0 24 24"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20v-1.5A4.5 4.5 0 0 1 7 14h4a4.5 4.5 0 0 1 4.5 4.5V20M16 5.5a3.5 3.5 0 0 1 0 6.8M18 14.5a4.5 4.5 0 0 1 3.5 4.3V20"/></svg>
</span>
</div>
<h3>Ищем партнёров</h3>
<p>Делитесь с миром быстрым и надёжным VPN.</p>
<a class="button partner-button" href="/?open=app">СТАТЬ ПАРТНЁРОМ <span class="button-arrow" aria-hidden="true">→</span></a>
</article>
</div>
</section>
<section class="offer" aria-labelledby="offer-title">
<div class="offer-main">
<span class="offer-label">Специальное предложение</span>
<h2 id="offer-title">Уже купили другой VPN, но он перестал работать?</h2>
<p class="offer-intro">
Если у вас есть активная платная подписка на другой VPN, которая
вас больше не устраивает, мы подготовили для вас специальное предложение.
<br><br>
Отправьте в чат поддержки скриншот чека, письма или личного кабинета
=== Стили карточек ===
line-height: 1.75;
}
.features {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 19px;
padding-bottom: 88px;
}
.feature-card {
position: relative;
min-height: 245px;
overflow: hidden;
padding: 29px 30px 31px;
border: 1px solid rgba(255, 255, 255, .95);
border-radius: 25px;
background: var(--surface);
box-shadow: var(--shadow);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
transition:
transform .28s ease,
border-color .28s ease,
box-shadow .28s ease,
background .28s ease;
animation: rise-in .65s ease both;
}
.feature-card::before {
position: absolute;
inset: 0;
z-index: 0;
background:
radial-gradient(circle at 100% 0%, rgba(65, 142, 255, .32), transparent 55%),
radial-gradient(circle at 0% 100%, rgba(92, 169, 255, .16), transparent 52%);
content: "";
opacity: .72;
transition: opacity .28s ease;
pointer-events: none;
}
.feature-card:hover {
transform: translateY(-7px);
border-color: rgba(55, 132, 255, .9);
background: rgba(255, 255, 255, .98);
box-shadow:
0 24px 60px rgba(44, 108, 196, .24),
0 0 0 1px rgba(62, 133, 232, .28),
0 0 34px rgba(45, 125, 255, .30);
}
.feature-card:hover::before { opacity: 1; }
.feature-card > * { position: relative; z-index: 1; }
.feature-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 25px;
}
.feature-icon {
display: grid;
width: 49px;
height: 49px;
place-items: center;
border: 1px solid rgba(88, 148, 231, .15);
border-radius: 16px;
color: #347ee9;
background: linear-gradient(145deg, #f4f9ff, #e6f0ff);
transition: transform .28s ease, background .28s ease;
}
root@1984996:~#