- Landing hero texts (headline, description, badge) from settings table - Landing categories from /api/tools (dynamic from DB) - Landing advantages from /api/content/advantages - Dashboard about block from /api/content/dashboard - Static HTML remains as fallback if API fails - All editable via AdminJS admin panel
962 lines
38 KiB
HTML
962 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru" class="dark">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>WA Dev Tools — 13 бесплатных инструментов для веб-разработчика</title>
|
||
<meta name="description" content="Сжатие картинок, форматирование кода, HTTP-клиент и ещё 10 инструментов для веб-разработчиков — всё в браузере, бесплатно." />
|
||
<script src="/vendor/tailwind.js"></script>
|
||
<script>
|
||
tailwind.config = {
|
||
darkMode: 'class',
|
||
theme: { extend: {
|
||
fontFamily: { mono: ['JetBrains Mono', 'monospace'], sans: ['Manrope', 'sans-serif'] },
|
||
colors: { surface: { 900: '#060c18', 800: '#091020', 700: '#0d1828', 600: '#162040' }, accent: { DEFAULT: '#0054e6', dim: '#0043b8', bright: '#6b9fff' } }
|
||
}}
|
||
};
|
||
</script>
|
||
<link href="/vendor/fonts.css" rel="stylesheet">
|
||
<link href="/shared.css" rel="stylesheet">
|
||
<style>
|
||
/* ── Resets & base ── */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
html { scroll-behavior: smooth; }
|
||
|
||
/* ── Animations ── */
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
@keyframes slideUp {
|
||
from { opacity: 0; transform: translateY(28px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
@keyframes glow-pulse {
|
||
0%, 100% { opacity: 0.45; }
|
||
50% { opacity: 0.70; }
|
||
}
|
||
@keyframes float {
|
||
0%, 100% { transform: translateY(0px); }
|
||
50% { transform: translateY(-8px); }
|
||
}
|
||
|
||
.animate-fade-in { animation: fadeIn 0.7s ease both; }
|
||
.animate-slide-up { animation: slideUp 0.7s ease both; }
|
||
.delay-100 { animation-delay: 0.10s; }
|
||
.delay-200 { animation-delay: 0.20s; }
|
||
.delay-300 { animation-delay: 0.30s; }
|
||
.delay-400 { animation-delay: 0.40s; }
|
||
.delay-500 { animation-delay: 0.50s; }
|
||
.delay-600 { animation-delay: 0.60s; }
|
||
|
||
/* Intersection-observer driven reveal */
|
||
.reveal {
|
||
opacity: 0;
|
||
transform: translateY(24px);
|
||
transition: opacity 0.6s ease, transform 0.6s ease;
|
||
}
|
||
.reveal.visible {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
.reveal-delay-1 { transition-delay: 0.10s; }
|
||
.reveal-delay-2 { transition-delay: 0.20s; }
|
||
.reveal-delay-3 { transition-delay: 0.30s; }
|
||
.reveal-delay-4 { transition-delay: 0.40s; }
|
||
|
||
/* ── Hero ── */
|
||
.hero {
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
padding: 80px 20px 60px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Glow blobs */
|
||
.hero-glow-1 {
|
||
position: absolute;
|
||
top: -10%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: min(700px, 120vw);
|
||
height: min(700px, 120vw);
|
||
background: radial-gradient(ellipse at center, rgba(0, 84, 230, 0.18) 0%, transparent 65%);
|
||
pointer-events: none;
|
||
animation: glow-pulse 5s ease-in-out infinite;
|
||
}
|
||
.hero-glow-2 {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: -15%;
|
||
width: 420px;
|
||
height: 420px;
|
||
background: radial-gradient(ellipse at center, rgba(107, 159, 255, 0.08) 0%, transparent 60%);
|
||
pointer-events: none;
|
||
animation: glow-pulse 7s ease-in-out infinite reverse;
|
||
}
|
||
.hero-glow-3 {
|
||
position: absolute;
|
||
bottom: 10%;
|
||
left: -10%;
|
||
width: 360px;
|
||
height: 360px;
|
||
background: radial-gradient(ellipse at center, rgba(0, 67, 184, 0.10) 0%, transparent 60%);
|
||
pointer-events: none;
|
||
animation: glow-pulse 6s ease-in-out infinite 1s;
|
||
}
|
||
|
||
/* Grid lines overlay */
|
||
.hero-grid {
|
||
position: absolute;
|
||
inset: 0;
|
||
background-image:
|
||
linear-gradient(rgba(0, 84, 230, 0.04) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(0, 84, 230, 0.04) 1px, transparent 1px);
|
||
background-size: 60px 60px;
|
||
mask-image: radial-gradient(ellipse at center, black 20%, transparent 75%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.hero-logo {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: clamp(11px, 1.5vw, 13px);
|
||
font-weight: 700;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--text-muted);
|
||
margin-bottom: 28px;
|
||
}
|
||
.hero-logo span {
|
||
color: var(--accent-bright);
|
||
}
|
||
|
||
.hero-headline {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: clamp(2rem, 5.5vw, 3.75rem);
|
||
font-weight: 800;
|
||
line-height: 1.15;
|
||
letter-spacing: -0.03em;
|
||
color: var(--text-primary);
|
||
max-width: 820px;
|
||
margin: 0 auto 20px;
|
||
}
|
||
.hero-headline .accent { color: var(--accent); }
|
||
|
||
.hero-sub {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: clamp(1rem, 2vw, 1.2rem);
|
||
line-height: 1.65;
|
||
color: var(--text-secondary);
|
||
max-width: 600px;
|
||
margin: 0 auto 44px;
|
||
}
|
||
|
||
.hero-cta-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
@media (min-width: 480px) {
|
||
.hero-cta-group { flex-direction: row; justify-content: center; }
|
||
}
|
||
|
||
.btn-primary {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 15px 32px;
|
||
background: var(--accent);
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 12px;
|
||
font-family: 'Manrope', sans-serif;
|
||
font-weight: 700;
|
||
font-size: 16px;
|
||
text-decoration: none;
|
||
cursor: pointer;
|
||
transition: background 0.2s, transform 0.2s, box-shadow 0.2s;
|
||
box-shadow: 0 0 0 0 rgba(0, 84, 230, 0);
|
||
}
|
||
.btn-primary:hover {
|
||
background: var(--accent-dim);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 32px rgba(0, 84, 230, 0.35);
|
||
}
|
||
.btn-primary:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.btn-secondary {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 15px;
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
transition: color 0.2s;
|
||
}
|
||
.btn-secondary:hover { color: var(--text-primary); }
|
||
.btn-secondary .link-text {
|
||
border-bottom: 1px solid var(--surface-600);
|
||
padding-bottom: 1px;
|
||
}
|
||
|
||
/* Count badge in hero */
|
||
.hero-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
background: var(--surface-700);
|
||
border: 1px solid var(--surface-600);
|
||
border-radius: 999px;
|
||
padding: 6px 16px 6px 10px;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
margin-bottom: 28px;
|
||
}
|
||
.hero-badge .dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--accent-bright);
|
||
box-shadow: 0 0 6px var(--accent-bright);
|
||
animation: glow-pulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
/* ── Scroll hint ── */
|
||
.scroll-hint {
|
||
position: absolute;
|
||
bottom: 28px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 6px;
|
||
color: var(--text-muted);
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 10px;
|
||
letter-spacing: 0.15em;
|
||
text-transform: uppercase;
|
||
animation: float 2.5s ease-in-out infinite;
|
||
}
|
||
|
||
/* ── Section common ── */
|
||
.section {
|
||
padding: 80px 20px;
|
||
}
|
||
.section-inner {
|
||
max-width: 1120px;
|
||
margin: 0 auto;
|
||
}
|
||
.section-label {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--accent-bright);
|
||
margin-bottom: 12px;
|
||
}
|
||
.section-title {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: clamp(1.6rem, 4vw, 2.4rem);
|
||
font-weight: 800;
|
||
letter-spacing: -0.025em;
|
||
color: var(--text-primary);
|
||
margin: 0 0 48px;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
/* ── Category cards ── */
|
||
.cat-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
}
|
||
@media (min-width: 640px) {
|
||
.nav-link { display:inline-flex; }
|
||
.cat-grid { grid-template-columns: repeat(2, 1fr); }
|
||
}
|
||
@media (min-width: 1024px) {
|
||
.cat-grid { grid-template-columns: repeat(4, 1fr); }
|
||
}
|
||
|
||
.cat-card {
|
||
background: var(--surface-700);
|
||
border: 1px solid var(--surface-600);
|
||
border-radius: 16px;
|
||
padding: 28px 24px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
transition: border-color 0.25s, transform 0.25s, box-shadow 0.25s;
|
||
}
|
||
.cat-card:hover {
|
||
border-color: rgba(0, 84, 230, 0.5);
|
||
transform: translateY(-4px);
|
||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(0, 84, 230, 0.15);
|
||
}
|
||
|
||
.cat-icon-wrap {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 12px;
|
||
background: var(--accent-bg);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--accent-bright);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.cat-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
|
||
.cat-title {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.cat-badge {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
color: var(--accent-bright);
|
||
background: rgba(107, 159, 255, 0.12);
|
||
border: 1px solid rgba(107, 159, 255, 0.2);
|
||
border-radius: 999px;
|
||
padding: 2px 10px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.cat-desc {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: var(--text-secondary);
|
||
flex: 1;
|
||
}
|
||
|
||
.cat-tools {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
padding-top: 4px;
|
||
border-top: 1px solid var(--surface-600);
|
||
}
|
||
|
||
.cat-tool-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
}
|
||
.cat-tool-item::before {
|
||
content: '';
|
||
width: 4px;
|
||
height: 4px;
|
||
border-radius: 50%;
|
||
background: var(--accent-bright);
|
||
opacity: 0.5;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── Advantages ── */
|
||
.adv-section {
|
||
background: var(--surface-700);
|
||
border-top: 1px solid var(--surface-600);
|
||
border-bottom: 1px solid var(--surface-600);
|
||
}
|
||
|
||
.adv-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 0;
|
||
}
|
||
@media (min-width: 640px) {
|
||
.adv-grid { grid-template-columns: repeat(2, 1fr); }
|
||
}
|
||
@media (min-width: 1024px) {
|
||
.adv-grid { grid-template-columns: repeat(4, 1fr); }
|
||
}
|
||
|
||
.adv-card {
|
||
padding: 36px 28px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
border-right: 1px solid var(--surface-600);
|
||
border-bottom: 1px solid var(--surface-600);
|
||
}
|
||
.adv-card:last-child { border-right: none; }
|
||
@media (min-width: 640px) {
|
||
.adv-card:nth-child(2) { border-right: none; }
|
||
.adv-card:nth-child(3) { border-bottom: none; }
|
||
.adv-card:nth-child(4) { border-right: none; border-bottom: none; }
|
||
}
|
||
@media (min-width: 1024px) {
|
||
.adv-card:nth-child(2) { border-right: 1px solid var(--surface-600); }
|
||
.adv-card:nth-child(3) { border-bottom: 1px solid var(--surface-600); }
|
||
.adv-card:last-child { border-right: none; border-bottom: none; }
|
||
/* last row has no bottom border */
|
||
.adv-card { border-bottom: none; }
|
||
}
|
||
|
||
.adv-icon-wrap {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 10px;
|
||
background: var(--accent-bg);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--accent-bright);
|
||
}
|
||
|
||
.adv-title {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.adv-desc {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 14px;
|
||
line-height: 1.65;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
/* ── Final CTA ── */
|
||
.cta-section {
|
||
text-align: center;
|
||
padding: 100px 20px;
|
||
}
|
||
|
||
.cta-glow {
|
||
position: relative;
|
||
display: inline-block;
|
||
}
|
||
.cta-glow::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -40px -80px;
|
||
background: radial-gradient(ellipse at center, rgba(0, 84, 230, 0.12) 0%, transparent 65%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.cta-headline {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: clamp(1.8rem, 4vw, 2.8rem);
|
||
font-weight: 800;
|
||
letter-spacing: -0.025em;
|
||
color: var(--text-primary);
|
||
margin: 0 0 16px;
|
||
}
|
||
|
||
.cta-sub {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: clamp(0.95rem, 1.8vw, 1.1rem);
|
||
line-height: 1.65;
|
||
color: var(--text-secondary);
|
||
margin: 0 auto 40px;
|
||
max-width: 480px;
|
||
}
|
||
|
||
/* ── Nav ── */
|
||
.landing-nav {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16px 32px;
|
||
background: rgba(6, 12, 24, 0.7); backdrop-filter: blur(12px);
|
||
border-bottom: 1px solid transparent;
|
||
transition: background 0.3s, border-color 0.3s, backdrop-filter 0.3s;
|
||
}
|
||
.landing-nav.scrolled {
|
||
background: rgba(6, 12, 24, 0.85);
|
||
border-color: var(--surface-600);
|
||
backdrop-filter: blur(12px);
|
||
}
|
||
.nav-logo {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 18px;
|
||
font-weight: 800;
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
.nav-logo span { color: var(--accent); }
|
||
.nav-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.nav-link {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
transition: color 0.2s;
|
||
padding: 8px 4px;
|
||
}
|
||
.nav-link:hover { color: var(--text-primary); }
|
||
.nav-link.nav-page { display: none; }
|
||
@media (min-width: 768px) {
|
||
.nav-link.nav-page { display: inline-flex; }
|
||
}
|
||
.nav-btn {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
background: var(--accent);
|
||
text-decoration: none;
|
||
padding: 9px 20px;
|
||
border-radius: 8px;
|
||
transition: background 0.2s;
|
||
}
|
||
.nav-btn:hover { background: var(--accent-dim); }
|
||
|
||
/* ── Footer ── */
|
||
.landing-footer {
|
||
padding: 28px 32px;
|
||
border-top: 1px solid var(--surface-600);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
.footer-copy {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
}
|
||
.footer-links {
|
||
display: flex;
|
||
gap: 20px;
|
||
}
|
||
.footer-link {
|
||
font-family: 'Manrope', sans-serif;
|
||
font-size: 13px;
|
||
color: var(--text-muted);
|
||
text-decoration: none;
|
||
transition: color 0.2s;
|
||
}
|
||
.footer-link:hover { color: var(--text-secondary); }
|
||
|
||
/* ── Divider line with accent ── */
|
||
.section-divider {
|
||
width: 40px;
|
||
height: 3px;
|
||
background: var(--accent);
|
||
border-radius: 2px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ════════════════════════════════════════════
|
||
NAV
|
||
════════════════════════════════════════════ -->
|
||
<nav class="landing-nav" id="mainNav">
|
||
<a href="/" class="nav-logo">WA Dev <span>Tools</span></a>
|
||
<div class="nav-actions">
|
||
<a href="#tools" class="nav-link nav-page">Инструменты</a>
|
||
<a href="#features" class="nav-link nav-page">Возможности</a>
|
||
<a href="/auth/login" class="nav-link">Войти</a>
|
||
<a href="/auth/register" class="nav-btn">Регистрация</a>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- ════════════════════════════════════════════
|
||
HERO
|
||
════════════════════════════════════════════ -->
|
||
<section class="hero">
|
||
<div class="hero-glow-1"></div>
|
||
<div class="hero-glow-2"></div>
|
||
<div class="hero-glow-3"></div>
|
||
<div class="hero-grid"></div>
|
||
|
||
<div style="position:relative;z-index:1;width:100%;">
|
||
<div class="hero-badge animate-fade-in">
|
||
<span class="dot"></span>
|
||
<span id="hero-badge-text">13 инструментов — бесплатно навсегда</span>
|
||
</div>
|
||
|
||
<div class="hero-logo animate-slide-up delay-100">
|
||
WA Dev <span>Tools</span>
|
||
</div>
|
||
|
||
<h1 class="hero-headline animate-slide-up delay-200" id="hero-headline">
|
||
Инструменты для<br>
|
||
<span class="accent">веб-разработчика</span>
|
||
</h1>
|
||
|
||
<p class="hero-sub animate-slide-up delay-300" id="hero-sub">
|
||
Сжатие картинок, форматирование кода, HTTP-клиент и ещё 10 инструментов —
|
||
всё в браузере, бесплатно и без регистрации карты.
|
||
</p>
|
||
|
||
<div class="hero-cta-group animate-slide-up delay-400">
|
||
<a href="/auth/register" class="btn-primary">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M13 5H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6"/>
|
||
<path d="M17 3h4v4"/>
|
||
<path d="M10 14 21 3"/>
|
||
</svg>
|
||
Начать бесплатно
|
||
</a>
|
||
<a href="/auth/login" class="btn-secondary">
|
||
<span class="link-text">Уже есть аккаунт? Войти</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<a href="#tools" class="scroll-hint" aria-label="Прокрутить вниз">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="m6 9 6 6 6-6"/>
|
||
</svg>
|
||
<span>Подробнее</span>
|
||
</a>
|
||
</section>
|
||
|
||
<!-- ════════════════════════════════════════════
|
||
CATEGORIES
|
||
════════════════════════════════════════════ -->
|
||
<section class="section" id="tools">
|
||
<div class="section-inner">
|
||
<div class="reveal">
|
||
<p class="section-label">Что внутри</p>
|
||
<div class="section-divider"></div>
|
||
<h2 class="section-title">Инструменты</h2>
|
||
</div>
|
||
|
||
<div class="cat-grid">
|
||
|
||
<!-- Изображения -->
|
||
<div class="cat-card reveal reveal-delay-1">
|
||
<div class="cat-icon-wrap">
|
||
<!-- Heroicons: photo -->
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="3" y="3" width="18" height="18" rx="3"/>
|
||
<path d="M3 9h18"/>
|
||
<circle cx="9" cy="6" r="1" fill="currentColor" stroke="none"/>
|
||
<path d="M3 15l4-4 4 4 3-3 5 5"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="cat-header">
|
||
<span class="cat-title">Изображения</span>
|
||
<span class="cat-badge">4 инструмента</span>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="cat-desc">Сжатие, редактирование, SVG и placeholder-генератор</p>
|
||
|
||
<ul class="cat-tools">
|
||
<li class="cat-tool-item">Сжатие картинок</li>
|
||
<li class="cat-tool-item">Placeholder-генератор</li>
|
||
<li class="cat-tool-item">SVG-редактор</li>
|
||
<li class="cat-tool-item">Фоторедактор</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Код -->
|
||
<div class="cat-card reveal reveal-delay-2">
|
||
<div class="cat-icon-wrap">
|
||
<!-- Heroicons: code-bracket -->
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="m8 6-4 6 4 6"/>
|
||
<path d="m16 6 4 6-4 6"/>
|
||
<path d="m14 4-4 16"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="cat-header">
|
||
<span class="cat-title">Код</span>
|
||
<span class="cat-badge">3 инструмента</span>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="cat-desc">Форматирование, очистка и конвертация кода между форматами</p>
|
||
|
||
<ul class="cat-tools">
|
||
<li class="cat-tool-item">Форматирование кода</li>
|
||
<li class="cat-tool-item">HTML Sanitizer</li>
|
||
<li class="cat-tool-item">Конвертер форматов</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Веб -->
|
||
<div class="cat-card reveal reveal-delay-3">
|
||
<div class="cat-icon-wrap">
|
||
<!-- Heroicons: globe-alt -->
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"/>
|
||
<path d="M3.6 9h16.8M3.6 15h16.8"/>
|
||
<path d="M12 3a14.4 14.4 0 0 1 0 18M12 3a14.4 14.4 0 0 0 0 18"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="cat-header">
|
||
<span class="cat-title">Веб</span>
|
||
<span class="cat-badge">3 инструмента</span>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="cat-desc">HTTP-клиент, парсер страниц и анализ редиректов</p>
|
||
|
||
<ul class="cat-tools">
|
||
<li class="cat-tool-item">HTTP-клиент</li>
|
||
<li class="cat-tool-item">Парсер статей</li>
|
||
<li class="cat-tool-item">Redirect-анализатор</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Утилиты -->
|
||
<div class="cat-card reveal reveal-delay-4">
|
||
<div class="cat-icon-wrap">
|
||
<!-- Heroicons: wrench-screwdriver -->
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M11.42 15.17 17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877"/>
|
||
<path d="M11.42 15.17l-2.496-2.496a3.5 3.5 0 0 1-4.95-4.95l2.496 2.496"/>
|
||
<path d="m6.496 10.22 4.258-4.257a3.5 3.5 0 0 1 4.95 4.95l-4.258 4.257"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="cat-header">
|
||
<span class="cat-title">Утилиты</span>
|
||
<span class="cat-badge">2 инструмента</span>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="cat-desc">Генератор паролей и Markdown-просмотрщик с превью</p>
|
||
|
||
<ul class="cat-tools">
|
||
<li class="cat-tool-item">Генератор паролей</li>
|
||
<li class="cat-tool-item">Markdown Viewer</li>
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════════════════════════════════════════
|
||
ADVANTAGES
|
||
════════════════════════════════════════════ -->
|
||
<section class="adv-section" id="features">
|
||
<div style="max-width:1120px;margin:0 auto;">
|
||
<div class="adv-grid">
|
||
|
||
<div class="adv-card reveal">
|
||
<div class="adv-icon-wrap">
|
||
<!-- Heroicons: gift -->
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M20 12v10H4V12"/>
|
||
<path d="M22 7H2v5h20V7z"/>
|
||
<path d="M12 22V7"/>
|
||
<path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"/>
|
||
<path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"/>
|
||
</svg>
|
||
</div>
|
||
<div class="adv-title">Бесплатно навсегда</div>
|
||
<p class="adv-desc">Никаких подписок и скрытых платежей. Все 13 инструментов доступны сразу после регистрации.</p>
|
||
</div>
|
||
|
||
<div class="adv-card reveal reveal-delay-1">
|
||
<div class="adv-icon-wrap">
|
||
<!-- Heroicons: computer-desktop -->
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="2" y="3" width="20" height="14" rx="2"/>
|
||
<path d="M8 21h8M12 17v4"/>
|
||
</svg>
|
||
</div>
|
||
<div class="adv-title">В браузере</div>
|
||
<p class="adv-desc">Ничего устанавливать не нужно. Работает на любом устройстве с современным браузером.</p>
|
||
</div>
|
||
|
||
<div class="adv-card reveal reveal-delay-2">
|
||
<div class="adv-icon-wrap">
|
||
<!-- Heroicons: shield-check -->
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||
<path d="m9 12 2 2 4-4"/>
|
||
</svg>
|
||
</div>
|
||
<div class="adv-title">Безопасно</div>
|
||
<p class="adv-desc">Ваши файлы обрабатываются локально или транзитно и не сохраняются на нашем сервере.</p>
|
||
</div>
|
||
|
||
<div class="adv-card reveal reveal-delay-3">
|
||
<div class="adv-icon-wrap">
|
||
<!-- Heroicons: bolt -->
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>
|
||
</svg>
|
||
</div>
|
||
<div class="adv-title">Быстро</div>
|
||
<p class="adv-desc">Лёгкий интерфейс, мгновенная обработка без очередей и ожидания серверного ответа.</p>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════════════════════════════════════════
|
||
FINAL CTA
|
||
════════════════════════════════════════════ -->
|
||
<section class="cta-section">
|
||
<div class="cta-glow reveal">
|
||
<p class="section-label" style="margin-bottom:16px;">Начните прямо сейчас</p>
|
||
<h2 class="cta-headline">Готовы начать?</h2>
|
||
<p class="cta-sub">
|
||
Зарегистрируйтесь бесплатно и получите доступ ко всем инструментам — без кредитной карты.
|
||
</p>
|
||
<a href="/auth/register" class="btn-primary" style="font-size:17px;padding:17px 40px;">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
|
||
<circle cx="9" cy="7" r="4"/>
|
||
<line x1="19" y1="8" x2="19" y2="14"/>
|
||
<line x1="22" y1="11" x2="16" y2="11"/>
|
||
</svg>
|
||
Создать аккаунт
|
||
</a>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ════════════════════════════════════════════
|
||
FOOTER
|
||
════════════════════════════════════════════ -->
|
||
<footer class="landing-footer">
|
||
<span class="footer-copy">© 2024–2026 WA Dev Tools</span>
|
||
<nav class="footer-links">
|
||
<a href="/auth/login" class="footer-link">Войти</a>
|
||
<a href="/auth/register" class="footer-link">Регистрация</a>
|
||
</nav>
|
||
</footer>
|
||
|
||
<script>
|
||
/* ── Sticky nav ── */
|
||
const nav = document.getElementById('mainNav');
|
||
const onScroll = () => {
|
||
nav.classList.toggle('scrolled', window.scrollY > 40);
|
||
};
|
||
window.addEventListener('scroll', onScroll, { passive: true });
|
||
|
||
/* ── Intersection observer for reveal animations ── */
|
||
const revealEls = document.querySelectorAll('.reveal');
|
||
if ('IntersectionObserver' in window) {
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach((entry) => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('visible');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
|
||
|
||
revealEls.forEach((el) => observer.observe(el));
|
||
} else {
|
||
// Fallback for old browsers
|
||
revealEls.forEach((el) => el.classList.add('visible'));
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
|
||
<script>
|
||
/* ── Dynamic content from API ── */
|
||
(async function() {
|
||
try {
|
||
const [settingsRes, toolsRes, advRes] = await Promise.all([
|
||
fetch('/api/settings'),
|
||
fetch('/api/tools'),
|
||
fetch('/api/content/advantages'),
|
||
]);
|
||
|
||
if (settingsRes.ok) {
|
||
const s = await settingsRes.json();
|
||
const headline = document.getElementById('hero-headline');
|
||
if (headline && s.hero_headline) {
|
||
const parts = s.hero_headline.split(' ');
|
||
const last = parts.pop();
|
||
headline.innerHTML = parts.join(' ') + '<br><span class="accent">' + last + '</span>';
|
||
}
|
||
const sub = document.getElementById('hero-sub');
|
||
if (sub && s.hero_description) sub.textContent = s.hero_description;
|
||
|
||
const badge = document.getElementById('hero-badge-text');
|
||
if (badge && s.site_tagline) badge.textContent = s.site_tagline;
|
||
}
|
||
|
||
if (toolsRes.ok) {
|
||
const cats = await toolsRes.json();
|
||
const grid = document.querySelector('.cat-grid');
|
||
if (grid && cats.length) {
|
||
grid.innerHTML = cats.map((cat, i) => {
|
||
const toolsList = cat.tools.map(t => '<li class="cat-tool-item">' + t.title + '</li>').join('');
|
||
return '<div class="cat-card reveal visible reveal-delay-' + (i+1) + '">' +
|
||
'<div class="cat-icon-wrap" style="color:' + (cat.color || '#0054e6') + '">' +
|
||
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="3" y="3" width="18" height="18" rx="3"/></svg>' +
|
||
'</div><div><div class="cat-header"><span class="cat-title">' + cat.title + '</span>' +
|
||
'<span class="cat-badge">' + cat.tools.length + ' инстр.</span></div></div>' +
|
||
'<p class="cat-desc">' + (cat.description || '') + '</p>' +
|
||
'<ul class="cat-tools">' + toolsList + '</ul></div>';
|
||
}).join('');
|
||
}
|
||
}
|
||
|
||
if (advRes.ok) {
|
||
const blocks = await advRes.json();
|
||
const advGrid = document.querySelector('.adv-grid');
|
||
if (advGrid && blocks.length) {
|
||
advGrid.innerHTML = blocks.map((b, i) =>
|
||
'<div class="adv-card reveal visible reveal-delay-' + i + '">' +
|
||
'<div class="adv-icon-wrap"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z"/></svg></div>' +
|
||
'<div class="adv-title">' + b.title + '</div>' +
|
||
'<p class="adv-desc">' + b.body + '</p></div>'
|
||
).join('');
|
||
}
|
||
}
|
||
} catch(e) { /* silent — static content stays as fallback */ }
|
||
})();
|
||
</script>
|