Initial commit: konesaliturku.fi website

Colocation/datacenter service website with:
- One-page main site (hero, services, pricing, contact form)
- Technical specs page (power, cooling, connectivity, security)
- Dark blue technical theme, fully responsive
- PHP backend for contact form with rate limiting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 14:33:15 +02:00
commit 1ae342c1f5
7 changed files with 2073 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.DS_Store
*.swp
*.swo
.env

126
api.php Normal file
View File

@@ -0,0 +1,126 @@
<?php
header('Content-Type: application/json; charset=utf-8');
// Rate limiting (simple file-based)
function checkRateLimit($ip, $maxRequests = 5, $windowSeconds = 300) {
$file = __DIR__ . '/data/rate_limits.json';
$limits = [];
if (file_exists($file)) {
$limits = json_decode(file_get_contents($file), true) ?: [];
}
$now = time();
// Clean old entries
foreach ($limits as $key => $entries) {
$limits[$key] = array_filter($entries, fn($t) => $now - $t < $windowSeconds);
if (empty($limits[$key])) {
unset($limits[$key]);
}
}
$count = count($limits[$ip] ?? []);
if ($count >= $maxRequests) {
return false;
}
$limits[$ip][] = $now;
$dir = dirname($file);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
file_put_contents($file, json_encode($limits));
return true;
}
$action = $_GET['action'] ?? '';
switch ($action) {
case 'contact':
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
// Honeypot
if (!empty($_POST['website'])) {
echo json_encode(['success' => true]);
exit;
}
// Rate limit
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
if (!checkRateLimit($ip)) {
http_response_code(429);
echo json_encode(['error' => 'Liian monta viestiä. Yritä myöhemmin uudelleen.']);
exit;
}
// Validate
$name = trim($_POST['name'] ?? '');
$company = trim($_POST['company'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$message = trim($_POST['message'] ?? '');
if (!$name || !$email || !$message) {
echo json_encode(['error' => 'Täytä kaikki pakolliset kentät.']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode(['error' => 'Tarkista sähköpostiosoite.']);
exit;
}
// Save to file
$contactFile = __DIR__ . '/data/contacts.json';
$contacts = [];
if (file_exists($contactFile)) {
$contacts = json_decode(file_get_contents($contactFile), true) ?: [];
}
$contacts[] = [
'id' => uniqid(),
'name' => $name,
'company' => $company,
'email' => $email,
'phone' => $phone,
'message' => $message,
'ip' => $ip,
'created_at' => date('Y-m-d H:i:s')
];
$dir = dirname($contactFile);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
file_put_contents($contactFile, json_encode($contacts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
// Send email
$to = 'info@konesaliturku.fi';
$subject = 'Yhteydenotto: ' . $name . ($company ? " ($company)" : '');
$body = "Uusi yhteydenotto konesaliturku.fi:n kautta\n\n";
$body .= "Nimi: $name\n";
if ($company) $body .= "Yritys: $company\n";
$body .= "Sähköposti: $email\n";
if ($phone) $body .= "Puhelin: $phone\n";
$body .= "\nViesti:\n$message\n";
$headers = "From: noreply@konesaliturku.fi\r\n";
$headers .= "Reply-To: $email\r\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
@mail($to, $subject, $body, $headers);
echo json_encode(['success' => true]);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Unknown action']);
break;
}

450
index.html Normal file
View File

@@ -0,0 +1,450 @@
<!DOCTYPE html>
<html lang="fi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Konesali Turku — Colocation &amp; konesalipalvelut Turussa</title>
<meta name="description" content="Luotettavat konesalipalvelut Turussa. Colocation-laitepaikkoja 1U:sta kokokaappiin. Redundantti infrastruktuuri, henkilökohtainen palvelu.">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Header -->
<header class="header" id="header">
<div class="container header-inner">
<a href="/" class="logo">
<svg class="logo-icon" viewBox="0 0 32 32" width="32" height="32" fill="none">
<rect x="2" y="6" width="28" height="20" rx="3" stroke="currentColor" stroke-width="2"/>
<rect x="6" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="6" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="18" width="4" height="4" rx="1" fill="currentColor"/>
</svg>
<span>Konesali<strong>Turku</strong></span>
</a>
<nav class="nav" id="nav">
<a href="#palvelut">Palvelut</a>
<a href="konesali.html">Konesali</a>
<a href="#hinnat">Hinnat</a>
<a href="#yhteystiedot">Yhteystiedot</a>
</nav>
<button class="nav-toggle" id="nav-toggle" aria-label="Avaa valikko">
<span></span>
<span></span>
<span></span>
</button>
</div>
</header>
<!-- Hero -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1>Luotettavat konesalipalvelut <span class="highlight">Turussa</span></h1>
<p class="hero-subtitle">Colocation-laitepaikkoja ammattimaisessa laitetilassa. Redundantti sähkönsyöttö, nopeat tietoliikenneyhteydet ja henkilökohtainen palvelu.</p>
<div class="hero-actions">
<a href="#yhteystiedot" class="btn btn-primary">Pyydä tarjous</a>
<a href="#hinnat" class="btn btn-secondary">Katso hinnat</a>
</div>
<div class="hero-stats">
<div class="stat">
<span class="stat-value">99,9%</span>
<span class="stat-label">Käytettävyys SLA</span>
</div>
<div class="stat">
<span class="stat-value">24/7</span>
<span class="stat-label">Valvonta</span>
</div>
<div class="stat">
<span class="stat-value">10 Gbit/s</span>
<span class="stat-label">Yhteysnopeus</span>
</div>
</div>
</div>
</div>
</section>
<!-- Palvelut -->
<section class="section" id="palvelut">
<div class="container">
<h2 class="section-title">Palvelut</h2>
<p class="section-subtitle">Tarjoamme colocation-laitepaikkoja kaikenkokoisille tarpeille</p>
<div class="services-grid">
<div class="service-card">
<div class="service-icon">
<svg viewBox="0 0 48 48" width="48" height="48" fill="none">
<rect x="14" y="8" width="20" height="32" rx="3" stroke="currentColor" stroke-width="2"/>
<circle cx="24" cy="16" r="2" fill="currentColor"/>
<line x1="18" y1="24" x2="30" y2="24" stroke="currentColor" stroke-width="2"/>
</svg>
</div>
<h3>1U Laitepaikka</h3>
<p>Yksittäinen palvelinpaikka 42U kaapissa. Sopii pienille palvelimille ja verkkolaitteille.</p>
<ul class="service-features">
<li>1 rack unit (1U)</li>
<li>Jaettu 1 Gbit/s yhteys</li>
<li>1x 230V sähkösyöttö</li>
<li>Rajoittamaton liikenne</li>
</ul>
</div>
<div class="service-card featured">
<div class="service-badge">Suosituin</div>
<div class="service-icon">
<svg viewBox="0 0 48 48" width="48" height="48" fill="none">
<rect x="8" y="4" width="32" height="40" rx="3" stroke="currentColor" stroke-width="2"/>
<circle cx="24" cy="12" r="2" fill="currentColor"/>
<circle cx="24" cy="20" r="2" fill="currentColor"/>
<line x1="14" y1="28" x2="34" y2="28" stroke="currentColor" stroke-width="2"/>
<line x1="14" y1="34" x2="34" y2="34" stroke="currentColor" stroke-width="2"/>
</svg>
</div>
<h3>Puolikaappi (21U)</h3>
<p>Puolet 42U kaapista omassa lukitussa tilassa. Ihanteellinen kasvavalle yritykselle.</p>
<ul class="service-features">
<li>21 rack unitia (21U)</li>
<li>Oma 1 Gbit/s yhteys</li>
<li>2x 230V / 16A sähkösyöttö</li>
<li>Rajoittamaton liikenne</li>
</ul>
</div>
<div class="service-card">
<div class="service-icon">
<svg viewBox="0 0 48 48" width="48" height="48" fill="none">
<rect x="6" y="2" width="36" height="44" rx="3" stroke="currentColor" stroke-width="2"/>
<circle cx="24" cy="10" r="2" fill="currentColor"/>
<circle cx="24" cy="18" r="2" fill="currentColor"/>
<circle cx="24" cy="26" r="2" fill="currentColor"/>
<line x1="12" y1="34" x2="36" y2="34" stroke="currentColor" stroke-width="2"/>
<line x1="12" y1="40" x2="36" y2="40" stroke="currentColor" stroke-width="2"/>
</svg>
</div>
<h3>Kokokaappi (42U)</h3>
<p>Kokonainen 42U kaappi omassa lukitussa tilassa. Täysi hallinta ja maksimikapasiteetti.</p>
<ul class="service-features">
<li>42 rack unitia (42U)</li>
<li>Oma 10 Gbit/s yhteys</li>
<li>2x 230V / 32A A/B-syöttö</li>
<li>Rajoittamaton liikenne</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Miksi me -->
<section class="section section-dark" id="miksi">
<div class="container">
<h2 class="section-title">Miksi Konesali Turku?</h2>
<p class="section-subtitle">Pienen konesalin edut isoihin ketjuihin verrattuna</p>
<div class="features-grid">
<div class="feature">
<div class="feature-icon">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/>
<path d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</div>
<h3>Paikallinen palvelu</h3>
<p>Laitteesi ovat lähellä. Voit käydä konesalilla milloin vain ja tapaat aina tutun henkilökunnan.</p>
</div>
<div class="feature">
<div class="feature-icon">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
</svg>
</div>
<h3>Redundantti infrastruktuuri</h3>
<p>Kahdennettu sähkönsyöttö, UPS-järjestelmä ja varavoimageneraattori takaavat katkeamattoman toiminnan.</p>
</div>
<div class="feature">
<div class="feature-icon">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
</div>
<h3>Nopeat yhteydet</h3>
<p>Moniliittymäinen tietoliikenne usealta operaattorilta. 110 Gbit/s porttinopeudet.</p>
</div>
<div class="feature">
<div class="feature-icon">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg>
</div>
<h3>24/7 tuki</h3>
<p>Remote hands -palvelu ja tekninen tuki ympäri vuorokauden. Ongelmatilanteissa reagoimme nopeasti.</p>
</div>
<div class="feature">
<div class="feature-icon">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
</svg>
</div>
<h3>Turvallisuus</h3>
<p>Kulunvalvonta, kameravalvonta ja lukitut kaapit. Laitteesi ovat turvassa ympäri vuorokauden.</p>
</div>
<div class="feature">
<div class="feature-icon">
<svg viewBox="0 0 24 24" width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<h3>Selkeä hinnoittelu</h3>
<p>Ei piilokustannuksia. Kuukausihintaan sisältyy laitepaikka, yhteys ja peruspalvelut.</p>
</div>
</div>
</div>
</section>
<!-- Hinnat -->
<section class="section" id="hinnat">
<div class="container">
<h2 class="section-title">Hinnat</h2>
<p class="section-subtitle">Kaikki hinnat alv 0%. Sähkö laskutetaan erikseen kulutuksen mukaan.</p>
<div class="pricing-grid">
<div class="pricing-card">
<div class="pricing-header">
<h3>1U Laitepaikka</h3>
<div class="price">
<span class="price-amount">&euro;49</span>
<span class="price-period">/kk</span>
</div>
</div>
<ul class="pricing-features">
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
1 rack unit (1U)
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Jaettu 1 Gbit/s
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
1x 230V sähkösyöttö
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Rajoittamaton liikenne
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
1x IPv4-osoite
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
99,9% SLA
</li>
</ul>
<a href="#yhteystiedot" class="btn btn-outline">Pyydä tarjous</a>
</div>
<div class="pricing-card pricing-featured">
<div class="pricing-badge">Suosituin</div>
<div class="pricing-header">
<h3>Puolikaappi</h3>
<div class="price">
<span class="price-amount">&euro;249</span>
<span class="price-period">/kk</span>
</div>
</div>
<ul class="pricing-features">
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
21 rack unitia (21U)
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Oma 1 Gbit/s
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
2x 230V / 16A sähkösyöttö
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Rajoittamaton liikenne
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
4x IPv4-osoitetta
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
IPv6 /64 -verkko
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
99,9% SLA
</li>
</ul>
<a href="#yhteystiedot" class="btn btn-primary">Pyydä tarjous</a>
</div>
<div class="pricing-card">
<div class="pricing-header">
<h3>Kokokaappi</h3>
<div class="price">
<span class="price-amount">&euro;490</span>
<span class="price-period">/kk</span>
</div>
</div>
<ul class="pricing-features">
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
42 rack unitia (42U)
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Oma 10 Gbit/s
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
2x 230V / 32A A/B-syöttö
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Rajoittamaton liikenne
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
8x IPv4-osoitetta
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
IPv6 /48 -verkko
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
Remote hands sisältyy
</li>
<li>
<svg viewBox="0 0 20 20" width="16" height="16" fill="currentColor"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
99,9% SLA
</li>
</ul>
<a href="#yhteystiedot" class="btn btn-outline">Pyydä tarjous</a>
</div>
</div>
<p class="pricing-note">Sähkönkulutus laskutetaan erikseen toteutuneen kulutuksen mukaan hintaan 0,25 &euro;/kWh (alv 0%). Kaikki hinnat ovat kuukausihintoja, alv 0%. Sopimuksen minimikesto 1 kuukausi.</p>
</div>
</section>
<!-- Yhteystiedot -->
<section class="section section-dark" id="yhteystiedot">
<div class="container">
<h2 class="section-title">Ota yhteyttä</h2>
<p class="section-subtitle">Kerro tarpeistasi, niin teemme sinulle tarjouksen</p>
<div class="contact-grid">
<form class="contact-form" id="contact-form">
<div class="form-group">
<label for="name">Nimi *</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="company">Yritys</label>
<input type="text" id="company" name="company">
</div>
<div class="form-group">
<label for="email">Sähköposti *</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone">Puhelinnumero</label>
<input type="tel" id="phone" name="phone">
</div>
<div class="form-group form-group-full">
<label for="message">Viesti *</label>
<textarea id="message" name="message" rows="5" required placeholder="Kerro meille tarpeistasi: montako laitepaikkaa tarvitset, millaisia laitteita, tehovaatimukset..."></textarea>
</div>
<!-- Honeypot -->
<div style="display:none" aria-hidden="true">
<input type="text" name="website" tabindex="-1" autocomplete="off">
</div>
<button type="submit" class="btn btn-primary btn-submit">Lähetä viesti</button>
<div class="form-status" id="form-status"></div>
</form>
<div class="contact-info">
<div class="contact-item">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/>
<path d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
<div>
<strong>Osoite</strong>
<p>Esimerkkikatu 1<br>20100 Turku</p>
</div>
</div>
<div class="contact-item">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
<div>
<strong>Sähköposti</strong>
<p>info@konesaliturku.fi</p>
</div>
</div>
<div class="contact-item">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>
</svg>
<div>
<strong>Puhelin</strong>
<p>+358 2 123 4567</p>
</div>
</div>
<div class="contact-item">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<div>
<strong>Aukioloajat</strong>
<p>Ma-Pe 8:00-17:00<br>Konesali 24/7</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container footer-inner">
<div class="footer-brand">
<a href="/" class="logo">
<svg class="logo-icon" viewBox="0 0 32 32" width="24" height="24" fill="none">
<rect x="2" y="6" width="28" height="20" rx="3" stroke="currentColor" stroke-width="2"/>
<rect x="6" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="6" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="18" width="4" height="4" rx="1" fill="currentColor"/>
</svg>
<span>Konesali<strong>Turku</strong></span>
</a>
<p>Luotettavat konesalipalvelut Turussa.</p>
</div>
<div class="footer-links">
<h4>Sivusto</h4>
<a href="#palvelut">Palvelut</a>
<a href="konesali.html">Konesali</a>
<a href="#hinnat">Hinnat</a>
<a href="#yhteystiedot">Yhteystiedot</a>
</div>
<div class="footer-links">
<h4>Palvelut</h4>
<a href="#hinnat">1U Laitepaikka</a>
<a href="#hinnat">Puolikaappi</a>
<a href="#hinnat">Kokokaappi</a>
</div>
</div>
<div class="footer-bottom">
<div class="container">
<p>&copy; 2026 Konesali Turku. Kaikki oikeudet pidätetään.</p>
</div>
</div>
</footer>
<script src="script.js"></script>
</body>
</html>

382
konesali.html Normal file
View File

@@ -0,0 +1,382 @@
<!DOCTYPE html>
<html lang="fi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Konesali — Tekniset tiedot | Konesali Turku</title>
<meta name="description" content="Konesali Turun tekniset tiedot: sähköjärjestelmät, jäähdytys, tietoliikenneyhteydet, turvallisuus ja tilat.">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Header -->
<header class="header" id="header">
<div class="container header-inner">
<a href="/" class="logo">
<svg class="logo-icon" viewBox="0 0 32 32" width="32" height="32" fill="none">
<rect x="2" y="6" width="28" height="20" rx="3" stroke="currentColor" stroke-width="2"/>
<rect x="6" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="6" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="18" width="4" height="4" rx="1" fill="currentColor"/>
</svg>
<span>Konesali<strong>Turku</strong></span>
</a>
<nav class="nav" id="nav">
<a href="index.html#palvelut">Palvelut</a>
<a href="konesali.html" class="active">Konesali</a>
<a href="index.html#hinnat">Hinnat</a>
<a href="index.html#yhteystiedot">Yhteystiedot</a>
</nav>
<button class="nav-toggle" id="nav-toggle" aria-label="Avaa valikko">
<span></span>
<span></span>
<span></span>
</button>
</div>
</header>
<!-- Page Hero -->
<section class="page-hero">
<div class="container">
<h1>Konesalin tekniset tiedot</h1>
<p>Ammattimaisesti suunniteltu ja ylläpidetty laitetila Turussa</p>
</div>
</section>
<!-- Overview -->
<section class="section">
<div class="container">
<div class="specs-overview">
<div class="spec-highlight">
<div class="spec-highlight-value">99,9%</div>
<div class="spec-highlight-label">Käytettävyys (SLA)</div>
</div>
<div class="spec-highlight">
<div class="spec-highlight-value">N+1</div>
<div class="spec-highlight-label">Redundanssi</div>
</div>
<div class="spec-highlight">
<div class="spec-highlight-value">24/7</div>
<div class="spec-highlight-label">Valvonta</div>
</div>
<div class="spec-highlight">
<div class="spec-highlight-value">10 Gbit/s</div>
<div class="spec-highlight-label">Yhteysnopeus</div>
</div>
</div>
</div>
</section>
<!-- Sähkö -->
<section class="section section-dark" id="sahko">
<div class="container">
<div class="spec-section">
<div class="spec-section-header">
<div class="spec-section-icon">
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
</div>
<div>
<h2>Sähköjärjestelmät</h2>
<p>Kahdennettu sähkönsyöttö varmistaa katkeamattoman toiminnan</p>
</div>
</div>
<div class="spec-table">
<table>
<tbody>
<tr>
<td class="spec-label">Sähkönsyöttö</td>
<td>Kahdennettu A/B-syöttö kahdelta eri muuntajalta</td>
</tr>
<tr>
<td class="spec-label">UPS-järjestelmä</td>
<td>Online double-conversion UPS, N+1 redundantti</td>
</tr>
<tr>
<td class="spec-label">Akuston kapasiteetti</td>
<td>Vähintään 15 minuuttia täydellä kuormalla</td>
</tr>
<tr>
<td class="spec-label">Varavoimageneraattori</td>
<td>Dieselgeneraattori, automaattinen käynnistys sähkökatkossa</td>
</tr>
<tr>
<td class="spec-label">Teho per kaappi</td>
<td>28 kW (räätälöitävissä tarpeen mukaan)</td>
</tr>
<tr>
<td class="spec-label">Liitännät</td>
<td>230V / 16A tai 32A, C13/C19 -pistokkeet</td>
</tr>
<tr>
<td class="spec-label">PDU</td>
<td>Monitoroidut PDU:t (virrankulutuksen seuranta)</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Jäähdytys -->
<section class="section" id="jaahdytys">
<div class="container">
<div class="spec-section">
<div class="spec-section-header">
<div class="spec-section-icon">
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 3v18m0-18l-3 3m3-3l3 3m-3 15l-3-3m3 3l3-3M3 12h18M3 12l3-3M3 12l3 3m15-3l-3-3m3 3l-3 3"/>
</svg>
</div>
<div>
<h2>Jäähdytys</h2>
<p>Tehokas ja energiaoptimoitu jäähdytysjärjestelmä</p>
</div>
</div>
<div class="spec-table">
<table>
<tbody>
<tr>
<td class="spec-label">Jäähdytystyyppi</td>
<td>Precision air cooling, Cold Aisle Containment</td>
</tr>
<tr>
<td class="spec-label">Redundanssi</td>
<td>N+1 jäähdytyskapasiteetti</td>
</tr>
<tr>
<td class="spec-label">Lämpötila</td>
<td>Tavoite 2024 °C (ASHRAE A1 -suositus)</td>
</tr>
<tr>
<td class="spec-label">Ilmankosteus</td>
<td>4060% suhteellinen kosteus</td>
</tr>
<tr>
<td class="spec-label">Valvonta</td>
<td>Jatkuva lämpötila- ja kosteusseuranta, hälytykset</td>
</tr>
<tr>
<td class="spec-label">Free cooling</td>
<td>Ulkoilman hyödyntäminen Suomen ilmaston ansiosta</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Tietoliikenne -->
<section class="section section-dark" id="tietoliikenne">
<div class="container">
<div class="spec-section">
<div class="spec-section-header">
<div class="spec-section-icon">
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/>
</svg>
</div>
<div>
<h2>Tietoliikenne</h2>
<p>Moniliittymäinen verkko usealta operaattorilta</p>
</div>
</div>
<div class="spec-table">
<table>
<tbody>
<tr>
<td class="spec-label">Porttinopeudet</td>
<td>1 Gbit/s 10 Gbit/s (päivitettävissä 100 Gbit/s)</td>
</tr>
<tr>
<td class="spec-label">Operaattorit</td>
<td>Moniliittymäinen (useampi operaattori redundanssia varten)</td>
</tr>
<tr>
<td class="spec-label">Liikenne</td>
<td>Rajoittamaton sisäänpäin/ulospäin</td>
</tr>
<tr>
<td class="spec-label">IPv4</td>
<td>18 osoitetta paketista riippuen (lisäosoitteet saatavilla)</td>
</tr>
<tr>
<td class="spec-label">IPv6</td>
<td>/64 /48 -verkot saatavilla</td>
</tr>
<tr>
<td class="spec-label">Reititys</td>
<td>BGP-reititys, oma AS-numero saatavilla</td>
</tr>
<tr>
<td class="spec-label">Cross-connect</td>
<td>Suorat yhteydet muihin operaattoreihin ja asiakkaisiin</td>
</tr>
<tr>
<td class="spec-label">SLA</td>
<td>99,9% verkon käytettävyys</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Turvallisuus -->
<section class="section" id="turvallisuus">
<div class="container">
<div class="spec-section">
<div class="spec-section-header">
<div class="spec-section-icon">
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
</svg>
</div>
<div>
<h2>Turvallisuus</h2>
<p>Monikerroksiset turvallisuusjärjestelmät suojaavat laitteita</p>
</div>
</div>
<div class="spec-table">
<table>
<tbody>
<tr>
<td class="spec-label">Kulunvalvonta</td>
<td>Sähköinen kulunvalvonta, avainkortti + PIN-koodi</td>
</tr>
<tr>
<td class="spec-label">Kameravalvonta</td>
<td>24/7 kameravalvonta sisä- ja ulkotiloissa, tallentava</td>
</tr>
<tr>
<td class="spec-label">Palontorjunta</td>
<td>Automaattinen palonilmaisinjärjestelmä, inerttikaasu-sammutusjärjestelmä</td>
</tr>
<tr>
<td class="spec-label">Kaappien lukitus</td>
<td>Lukittavat 42U-kaapit, asiakaskohtaiset avaimet</td>
</tr>
<tr>
<td class="spec-label">Hälytysjärjestelmä</td>
<td>Reaaliaikaiset hälytykset: lämpötila, kosteus, sähkö, murto</td>
</tr>
<tr>
<td class="spec-label">Pääsyloki</td>
<td>Kaikki käynnit kirjataan ja aikaleimotaan</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Tilat -->
<section class="section section-dark" id="tilat">
<div class="container">
<div class="spec-section">
<div class="spec-section-header">
<div class="spec-section-icon">
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
</svg>
</div>
<div>
<h2>Tilat ja kaapit</h2>
<p>Ammattitason laitetila suunniteltu vaativaan käyttöön</p>
</div>
</div>
<div class="spec-table">
<table>
<tbody>
<tr>
<td class="spec-label">Sijainti</td>
<td>Turku, Suomi</td>
</tr>
<tr>
<td class="spec-label">Kaapit</td>
<td>Standardi 42U / 600mm x 1000mm serverikaapit</td>
</tr>
<tr>
<td class="spec-label">Ilmavirtaus</td>
<td>Hot Aisle / Cold Aisle -erottelu</td>
</tr>
<tr>
<td class="spec-label">Korotettu lattia</td>
<td>Korotettu lattia kaapelointia ja ilmavirtausta varten</td>
</tr>
<tr>
<td class="spec-label">Kantavuus</td>
<td>Riittävä raskaimmillekin palvelinratkaisuille</td>
</tr>
<tr>
<td class="spec-label">Pääsy</td>
<td>24/7 pääsy konesalille etukäteisilmoituksella</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- CTA -->
<section class="section">
<div class="container" style="text-align: center;">
<h2 class="section-title">Kiinnostuitko?</h2>
<p class="section-subtitle">Kerro tarpeistasi ja teemme sinulle räätälöidyn tarjouksen</p>
<div style="display: flex; gap: 16px; justify-content: center; flex-wrap: wrap;">
<a href="index.html#yhteystiedot" class="btn btn-primary">Pyydä tarjous</a>
<a href="index.html#hinnat" class="btn btn-secondary">Katso hinnat</a>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container footer-inner">
<div class="footer-brand">
<a href="/" class="logo">
<svg class="logo-icon" viewBox="0 0 32 32" width="24" height="24" fill="none">
<rect x="2" y="6" width="28" height="20" rx="3" stroke="currentColor" stroke-width="2"/>
<rect x="6" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="6" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="14" y="18" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="10" width="4" height="4" rx="1" fill="currentColor"/>
<rect x="22" y="18" width="4" height="4" rx="1" fill="currentColor"/>
</svg>
<span>Konesali<strong>Turku</strong></span>
</a>
<p>Luotettavat konesalipalvelut Turussa.</p>
</div>
<div class="footer-links">
<h4>Sivusto</h4>
<a href="index.html#palvelut">Palvelut</a>
<a href="konesali.html">Konesali</a>
<a href="index.html#hinnat">Hinnat</a>
<a href="index.html#yhteystiedot">Yhteystiedot</a>
</div>
<div class="footer-links">
<h4>Palvelut</h4>
<a href="index.html#hinnat">1U Laitepaikka</a>
<a href="index.html#hinnat">Puolikaappi</a>
<a href="index.html#hinnat">Kokokaappi</a>
</div>
</div>
<div class="footer-bottom">
<div class="container">
<p>&copy; 2026 Konesali Turku. Kaikki oikeudet pidätetään.</p>
</div>
</div>
</footer>
<script src="script.js"></script>
</body>
</html>

126
script.js Normal file
View File

@@ -0,0 +1,126 @@
document.addEventListener('DOMContentLoaded', () => {
// === Mobile nav toggle ===
const navToggle = document.getElementById('nav-toggle');
const nav = document.getElementById('nav');
if (navToggle && nav) {
navToggle.addEventListener('click', () => {
nav.classList.toggle('open');
navToggle.classList.toggle('open');
});
// Close nav on link click
nav.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
nav.classList.remove('open');
navToggle.classList.remove('open');
});
});
}
// === Sticky header ===
const header = document.getElementById('header');
if (header) {
window.addEventListener('scroll', () => {
header.classList.toggle('scrolled', window.scrollY > 50);
}, { passive: true });
}
// === Scroll animations ===
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Add fade-in to sections
document.querySelectorAll('.service-card, .feature, .pricing-card, .contact-form, .contact-info').forEach(el => {
el.classList.add('fade-in');
observer.observe(el);
});
// === Contact form ===
const form = document.getElementById('contact-form');
const formStatus = document.getElementById('form-status');
if (form) {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const submitBtn = form.querySelector('.btn-submit');
const originalText = submitBtn.textContent;
submitBtn.textContent = 'Lähetetään...';
submitBtn.disabled = true;
// Honeypot check
const honeypot = form.querySelector('input[name="website"]');
if (honeypot && honeypot.value) {
formStatus.textContent = 'Kiitos viestistäsi!';
formStatus.className = 'form-status success';
form.reset();
submitBtn.textContent = originalText;
submitBtn.disabled = false;
return;
}
const formData = new FormData(form);
try {
const response = await fetch('api.php?action=contact', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
formStatus.textContent = 'Kiitos viestistäsi! Otamme yhteyttä pian.';
formStatus.className = 'form-status success';
form.reset();
} else {
formStatus.textContent = data.error || 'Viestin lähetys epäonnistui. Yritä uudelleen.';
formStatus.className = 'form-status error';
}
} catch {
formStatus.textContent = 'Viestin lähetys epäonnistui. Yritä uudelleen.';
formStatus.className = 'form-status error';
}
submitBtn.textContent = originalText;
submitBtn.disabled = false;
});
}
// === Active nav link on scroll ===
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav a[href^="#"]');
if (sections.length && navLinks.length) {
window.addEventListener('scroll', () => {
let current = '';
sections.forEach(section => {
const top = section.offsetTop - 100;
if (window.scrollY >= top) {
current = section.getAttribute('id');
}
});
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + current) {
link.classList.add('active');
}
});
}, { passive: true });
}
});

15
server.py Normal file
View File

@@ -0,0 +1,15 @@
import subprocess
import sys
def main():
try:
subprocess.run(
["php", "-S", "localhost:3001"],
check=True
)
except KeyboardInterrupt:
print("\nServer stopped.")
sys.exit(0)
if __name__ == "__main__":
main()

970
style.css Normal file
View File

@@ -0,0 +1,970 @@
/* === Reset & Base === */
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--color-bg: #0a1628;
--color-bg-light: #0f1f3d;
--color-bg-card: #132244;
--color-primary: #1a73e8;
--color-primary-light: #4a9af5;
--color-primary-dark: #1557b0;
--color-accent: #00d4aa;
--color-text: #e2e8f0;
--color-text-muted: #94a3b8;
--color-text-heading: #f1f5f9;
--color-border: #1e3a5f;
--color-surface: #162a4a;
--color-white: #ffffff;
--font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
--max-width: 1200px;
--header-height: 72px;
--radius: 12px;
--radius-sm: 8px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 8px 48px rgba(0, 0, 0, 0.4);
--transition: 0.3s ease;
}
html {
scroll-behavior: smooth;
scroll-padding-top: var(--header-height);
}
body {
font-family: var(--font-family);
background: var(--color-bg);
color: var(--color-text);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
a {
color: var(--color-primary-light);
text-decoration: none;
transition: color var(--transition);
}
a:hover {
color: var(--color-accent);
}
.container {
max-width: var(--max-width);
margin: 0 auto;
padding: 0 24px;
}
/* === Header === */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--header-height);
background: rgba(10, 22, 40, 0.9);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid var(--color-border);
z-index: 1000;
transition: background var(--transition);
}
.header.scrolled {
background: rgba(10, 22, 40, 0.98);
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.3);
}
.header-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
}
.logo {
display: flex;
align-items: center;
gap: 10px;
color: var(--color-white);
font-size: 1.25rem;
font-weight: 400;
letter-spacing: -0.01em;
}
.logo:hover {
color: var(--color-white);
}
.logo strong {
font-weight: 700;
}
.logo-icon {
color: var(--color-primary-light);
}
.nav {
display: flex;
gap: 32px;
}
.nav a {
color: var(--color-text-muted);
font-size: 0.95rem;
font-weight: 500;
transition: color var(--transition);
position: relative;
}
.nav a:hover,
.nav a.active {
color: var(--color-white);
}
.nav a::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 0;
height: 2px;
background: var(--color-primary);
transition: width var(--transition);
}
.nav a:hover::after {
width: 100%;
}
.nav-toggle {
display: none;
flex-direction: column;
gap: 5px;
background: none;
border: none;
cursor: pointer;
padding: 8px;
}
.nav-toggle span {
display: block;
width: 24px;
height: 2px;
background: var(--color-white);
transition: all var(--transition);
}
/* === Hero === */
.hero {
padding: 160px 0 100px;
background: linear-gradient(135deg, var(--color-bg) 0%, var(--color-bg-light) 50%, var(--color-bg) 100%);
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(ellipse 800px 600px at 20% 50%, rgba(26, 115, 232, 0.08) 0%, transparent 70%),
radial-gradient(ellipse 600px 400px at 80% 30%, rgba(0, 212, 170, 0.05) 0%, transparent 70%);
pointer-events: none;
}
.hero-content {
position: relative;
max-width: 720px;
}
.hero h1 {
font-size: clamp(2.2rem, 5vw, 3.5rem);
font-weight: 800;
line-height: 1.15;
color: var(--color-white);
margin-bottom: 20px;
letter-spacing: -0.02em;
}
.highlight {
color: var(--color-primary-light);
}
.hero-subtitle {
font-size: 1.2rem;
color: var(--color-text-muted);
line-height: 1.7;
margin-bottom: 36px;
max-width: 560px;
}
.hero-actions {
display: flex;
gap: 16px;
margin-bottom: 56px;
}
.hero-stats {
display: flex;
gap: 48px;
}
.stat {
display: flex;
flex-direction: column;
}
.stat-value {
font-size: 1.5rem;
font-weight: 800;
color: var(--color-white);
}
.stat-label {
font-size: 0.85rem;
color: var(--color-text-muted);
margin-top: 4px;
}
/* === Buttons === */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 14px 28px;
border-radius: var(--radius-sm);
font-size: 1rem;
font-weight: 600;
border: none;
cursor: pointer;
transition: all var(--transition);
text-decoration: none;
}
.btn-primary {
background: var(--color-primary);
color: var(--color-white);
}
.btn-primary:hover {
background: var(--color-primary-dark);
color: var(--color-white);
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(26, 115, 232, 0.4);
}
.btn-secondary {
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background: var(--color-bg-card);
color: var(--color-white);
border-color: var(--color-primary);
}
.btn-outline {
background: transparent;
color: var(--color-primary-light);
border: 2px solid var(--color-primary);
}
.btn-outline:hover {
background: var(--color-primary);
color: var(--color-white);
transform: translateY(-2px);
}
/* === Sections === */
.section {
padding: 100px 0;
}
.section-dark {
background: var(--color-bg-light);
}
.section-title {
font-size: 2.2rem;
font-weight: 800;
color: var(--color-text-heading);
text-align: center;
margin-bottom: 12px;
letter-spacing: -0.02em;
}
.section-subtitle {
font-size: 1.1rem;
color: var(--color-text-muted);
text-align: center;
margin-bottom: 56px;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
/* === Services === */
.services-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.service-card {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 36px 28px;
transition: all var(--transition);
position: relative;
}
.service-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
border-color: var(--color-primary);
}
.service-card.featured {
border-color: var(--color-primary);
background: linear-gradient(180deg, rgba(26, 115, 232, 0.08) 0%, var(--color-bg-card) 100%);
}
.service-badge {
position: absolute;
top: -12px;
right: 20px;
background: var(--color-primary);
color: var(--color-white);
font-size: 0.8rem;
font-weight: 700;
padding: 4px 14px;
border-radius: 20px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.service-icon {
color: var(--color-primary-light);
margin-bottom: 20px;
}
.service-card h3 {
font-size: 1.35rem;
font-weight: 700;
color: var(--color-text-heading);
margin-bottom: 12px;
}
.service-card p {
color: var(--color-text-muted);
margin-bottom: 20px;
font-size: 0.95rem;
}
.service-features {
list-style: none;
}
.service-features li {
padding: 6px 0;
color: var(--color-text);
font-size: 0.9rem;
border-bottom: 1px solid rgba(30, 58, 95, 0.5);
}
.service-features li:last-child {
border-bottom: none;
}
/* === Features (Miksi me) === */
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 32px;
}
.feature {
text-align: center;
padding: 24px;
}
.feature-icon {
color: var(--color-primary-light);
margin-bottom: 16px;
display: flex;
justify-content: center;
}
.feature h3 {
font-size: 1.15rem;
font-weight: 700;
color: var(--color-text-heading);
margin-bottom: 10px;
}
.feature p {
color: var(--color-text-muted);
font-size: 0.95rem;
line-height: 1.6;
}
/* === Pricing === */
.pricing-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
align-items: start;
}
.pricing-card {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 36px 28px;
text-align: center;
transition: all var(--transition);
position: relative;
}
.pricing-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
}
.pricing-featured {
border-color: var(--color-primary);
background: linear-gradient(180deg, rgba(26, 115, 232, 0.1) 0%, var(--color-bg-card) 100%);
transform: scale(1.04);
}
.pricing-featured:hover {
transform: scale(1.04) translateY(-4px);
}
.pricing-badge {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: var(--color-primary);
color: var(--color-white);
font-size: 0.8rem;
font-weight: 700;
padding: 4px 18px;
border-radius: 20px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.pricing-header h3 {
font-size: 1.3rem;
font-weight: 700;
color: var(--color-text-heading);
margin-bottom: 16px;
}
.price {
margin-bottom: 28px;
}
.price-amount {
font-size: 3rem;
font-weight: 800;
color: var(--color-white);
line-height: 1;
}
.price-period {
font-size: 1.1rem;
color: var(--color-text-muted);
font-weight: 400;
}
.pricing-features {
list-style: none;
text-align: left;
margin-bottom: 28px;
}
.pricing-features li {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
font-size: 0.95rem;
color: var(--color-text);
border-bottom: 1px solid rgba(30, 58, 95, 0.4);
}
.pricing-features li:last-child {
border-bottom: none;
}
.pricing-features svg {
color: var(--color-accent);
flex-shrink: 0;
}
.pricing-note {
text-align: center;
color: var(--color-text-muted);
font-size: 0.9rem;
margin-top: 40px;
max-width: 700px;
margin-left: auto;
margin-right: auto;
}
/* === Contact === */
.contact-grid {
display: grid;
grid-template-columns: 1.2fr 0.8fr;
gap: 48px;
align-items: start;
}
.contact-form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-group-full {
grid-column: 1 / -1;
}
.form-group label {
font-size: 0.9rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 6px;
}
.form-group input,
.form-group textarea {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: 12px 16px;
color: var(--color-text);
font-family: inherit;
font-size: 1rem;
transition: border-color var(--transition);
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.15);
}
.form-group textarea {
resize: vertical;
min-height: 120px;
}
.btn-submit {
grid-column: 1 / -1;
margin-top: 4px;
}
.form-status {
grid-column: 1 / -1;
font-size: 0.95rem;
padding: 8px 0;
}
.form-status.success {
color: var(--color-accent);
}
.form-status.error {
color: #f87171;
}
.contact-info {
display: flex;
flex-direction: column;
gap: 28px;
}
.contact-item {
display: flex;
gap: 16px;
align-items: flex-start;
}
.contact-item svg {
color: var(--color-primary-light);
flex-shrink: 0;
margin-top: 2px;
}
.contact-item strong {
display: block;
color: var(--color-text-heading);
font-size: 0.95rem;
margin-bottom: 4px;
}
.contact-item p {
color: var(--color-text-muted);
font-size: 0.9rem;
line-height: 1.5;
}
/* === Footer === */
.footer {
background: var(--color-bg);
border-top: 1px solid var(--color-border);
padding-top: 56px;
}
.footer-inner {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
gap: 48px;
padding-bottom: 40px;
}
.footer-brand .logo {
margin-bottom: 12px;
}
.footer-brand p {
color: var(--color-text-muted);
font-size: 0.95rem;
}
.footer-links h4 {
color: var(--color-text-heading);
font-size: 0.95rem;
font-weight: 700;
margin-bottom: 16px;
}
.footer-links a {
display: block;
color: var(--color-text-muted);
font-size: 0.9rem;
padding: 4px 0;
}
.footer-links a:hover {
color: var(--color-primary-light);
}
.footer-bottom {
border-top: 1px solid var(--color-border);
padding: 20px 0;
}
.footer-bottom p {
color: var(--color-text-muted);
font-size: 0.85rem;
text-align: center;
}
/* === Konesali Page === */
.page-hero {
padding: 140px 0 60px;
background: linear-gradient(135deg, var(--color-bg) 0%, var(--color-bg-light) 100%);
text-align: center;
}
.page-hero h1 {
font-size: clamp(1.8rem, 4vw, 2.8rem);
font-weight: 800;
color: var(--color-white);
margin-bottom: 12px;
letter-spacing: -0.02em;
}
.page-hero p {
font-size: 1.15rem;
color: var(--color-text-muted);
}
.specs-overview {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
text-align: center;
}
.spec-highlight {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 32px 20px;
}
.spec-highlight-value {
font-size: 2rem;
font-weight: 800;
color: var(--color-primary-light);
margin-bottom: 8px;
}
.spec-highlight-label {
font-size: 0.9rem;
color: var(--color-text-muted);
}
.spec-section {
max-width: 800px;
margin: 0 auto;
}
.spec-section-header {
display: flex;
gap: 20px;
align-items: flex-start;
margin-bottom: 32px;
}
.spec-section-icon {
color: var(--color-primary-light);
flex-shrink: 0;
}
.spec-section-header h2 {
font-size: 1.8rem;
font-weight: 800;
color: var(--color-text-heading);
margin-bottom: 8px;
}
.spec-section-header p {
color: var(--color-text-muted);
font-size: 1rem;
}
.spec-table {
background: var(--color-bg-card);
border: 1px solid var(--color-border);
border-radius: var(--radius);
overflow: hidden;
}
.spec-table table {
width: 100%;
border-collapse: collapse;
}
.spec-table tr {
border-bottom: 1px solid var(--color-border);
}
.spec-table tr:last-child {
border-bottom: none;
}
.spec-table td {
padding: 16px 20px;
font-size: 0.95rem;
color: var(--color-text);
vertical-align: top;
}
.spec-label {
font-weight: 600;
color: var(--color-text-heading);
width: 200px;
white-space: nowrap;
}
@media (max-width: 768px) {
.specs-overview {
grid-template-columns: repeat(2, 1fr);
}
.spec-section-header {
flex-direction: column;
align-items: center;
text-align: center;
}
.spec-label {
width: auto;
white-space: normal;
display: block;
padding-bottom: 4px;
}
.spec-table td {
display: block;
padding: 8px 16px;
}
.spec-table tr {
padding: 8px 0;
}
.spec-table td:first-child {
padding-bottom: 0;
}
}
@media (max-width: 480px) {
.specs-overview {
grid-template-columns: 1fr 1fr;
}
}
/* === Animations === */
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
/* === Responsive === */
@media (max-width: 1024px) {
.services-grid,
.pricing-grid,
.features-grid {
grid-template-columns: repeat(2, 1fr);
}
.pricing-featured {
transform: none;
}
.pricing-featured:hover {
transform: translateY(-4px);
}
}
@media (max-width: 768px) {
.nav {
display: none;
position: fixed;
top: var(--header-height);
left: 0;
right: 0;
background: var(--color-bg);
flex-direction: column;
padding: 24px;
gap: 0;
border-bottom: 1px solid var(--color-border);
box-shadow: var(--shadow);
}
.nav.open {
display: flex;
}
.nav a {
padding: 14px 0;
border-bottom: 1px solid var(--color-border);
font-size: 1.05rem;
}
.nav a:last-child {
border-bottom: none;
}
.nav a::after {
display: none;
}
.nav-toggle {
display: flex;
}
.nav-toggle.open span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.nav-toggle.open span:nth-child(2) {
opacity: 0;
}
.nav-toggle.open span:nth-child(3) {
transform: rotate(-45deg) translate(5px, -5px);
}
.hero {
padding: 120px 0 60px;
}
.hero h1 {
font-size: 2rem;
}
.hero-subtitle {
font-size: 1.05rem;
}
.hero-actions {
flex-direction: column;
gap: 12px;
}
.hero-stats {
gap: 24px;
}
.services-grid,
.pricing-grid,
.features-grid {
grid-template-columns: 1fr;
}
.section {
padding: 64px 0;
}
.section-title {
font-size: 1.75rem;
}
.contact-grid {
grid-template-columns: 1fr;
gap: 40px;
}
.contact-form {
grid-template-columns: 1fr;
}
.footer-inner {
grid-template-columns: 1fr;
gap: 32px;
}
}
@media (max-width: 480px) {
.hero-stats {
flex-direction: column;
gap: 16px;
}
.stat {
flex-direction: row;
gap: 12px;
align-items: center;
}
}