Add service worker for push notifications, create calendar layout, and implement WLAN QR code page

- Implemented a service worker (sw.js) to handle push notifications with dynamic options and notification click events.
- Created a calendar layout in test.html with a grid system for displaying events across days and times.
- Developed a visually engaging WLAN QR code page (wlan.html) with animated backgrounds, particle effects, and tips for connecting to the network.
This commit is contained in:
2026-02-22 00:50:22 +01:00
parent 6b96cd2012
commit 038910e9f0
26 changed files with 32980 additions and 5 deletions

971
public/networktester.html Normal file
View File

@@ -0,0 +1,971 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>NETZWERKTESTER™ — Das beste Gadget der Welt</title>
<meta name="description" content="Der übertriebenste Netzwerktester-Hype aller Zeiten. Animiert. Neon. Absolut unnötig — und genau deshalb perfekt." />
<style>
:root{
--bg:#060611;
--bg2:#0b0b1f;
--neon:#7CFF6B;
--neon2:#7afcff;
--hot:#ff4fd8;
--warn:#ffd34d;
--text:#f2f6ff;
--muted:rgba(242,246,255,.72);
--card:rgba(255,255,255,.06);
--stroke:rgba(255,255,255,.12);
--shadow: 0 22px 80px rgba(0,0,0,.65);
--radius: 22px;
--max: 1180px;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
background: radial-gradient(1200px 800px at 20% 10%, rgba(122,252,255,.14), transparent 55%),
radial-gradient(1100px 900px at 80% 20%, rgba(255,79,216,.12), transparent 55%),
radial-gradient(900px 700px at 50% 80%, rgba(124,255,107,.10), transparent 55%),
linear-gradient(180deg, var(--bg), var(--bg2));
color:var(--text);
overflow-x:hidden;
}
/* ======= Animated background grid + lasers ======= */
.bg-grid{
position:fixed; inset:0;
pointer-events:none;
opacity:.55;
background:
linear-gradient(rgba(255,255,255,.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.06) 1px, transparent 1px);
background-size: 42px 42px;
transform: perspective(900px) rotateX(60deg) translateY(-18vh);
filter: drop-shadow(0 0 18px rgba(122,252,255,.08));
animation: gridFloat 8s ease-in-out infinite;
}
@keyframes gridFloat{
0%,100%{transform: perspective(900px) rotateX(60deg) translateY(-18vh) translateX(0)}
50%{transform: perspective(900px) rotateX(60deg) translateY(-16vh) translateX(12px)}
}
.lasers{
position:fixed; inset:-30vh -30vw;
pointer-events:none;
mix-blend-mode: screen;
opacity:.65;
filter: blur(.2px);
background:
conic-gradient(from 180deg at 50% 50%,
rgba(122,252,255,.0),
rgba(122,252,255,.25),
rgba(255,79,216,.18),
rgba(124,255,107,.18),
rgba(255,211,77,.12),
rgba(122,252,255,.0));
animation: spin 14s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}
/* ======= Layout ======= */
.wrap{max-width:var(--max); margin:0 auto; padding: 22px 18px 90px;}
header{
position:sticky; top:0; z-index:50;
backdrop-filter: blur(12px);
background: linear-gradient(180deg, rgba(6,6,17,.78), rgba(6,6,17,.35));
border-bottom: 1px solid rgba(255,255,255,.08);
}
.nav{
max-width:var(--max); margin:0 auto;
padding:12px 18px;
display:flex; align-items:center; justify-content:space-between; gap:10px;
}
.brand{
display:flex; align-items:center; gap:10px;
font-weight:900;
letter-spacing:.5px;
text-transform:uppercase;
user-select:none;
}
.brand .dot{
width:12px;height:12px;border-radius:99px;
background: radial-gradient(circle at 30% 30%, #fff, var(--neon2));
box-shadow: 0 0 18px rgba(122,252,255,.55), 0 0 34px rgba(255,79,216,.18);
animation: pulse 1.2s ease-in-out infinite;
}
@keyframes pulse{
0%,100%{transform:scale(1); opacity:1}
50%{transform:scale(1.35); opacity:.85}
}
.nav a{
color:var(--muted);
text-decoration:none;
font-weight:700;
padding:10px 12px;
border-radius:12px;
transition:.2s;
display:none;
}
.nav a:hover{background:rgba(255,255,255,.06); color:var(--text)}
@media (min-width:860px){
.nav a{display:inline-block}
}
.cta{
display:flex; align-items:center; gap:10px;
}
.btn{
border:1px solid rgba(255,255,255,.14);
background: linear-gradient(180deg, rgba(255,255,255,.12), rgba(255,255,255,.06));
color:var(--text);
border-radius:14px;
padding:10px 14px;
font-weight:850;
text-decoration:none;
box-shadow: 0 10px 40px rgba(0,0,0,.35);
transition: transform .15s ease, filter .15s ease;
position:relative;
overflow:hidden;
user-select:none;
}
.btn:hover{transform: translateY(-2px); filter:brightness(1.08)}
.btn.primary{
border-color: rgba(122,252,255,.25);
background: linear-gradient(135deg, rgba(122,252,255,.25), rgba(255,79,216,.16));
box-shadow: 0 18px 60px rgba(122,252,255,.14), 0 18px 70px rgba(255,79,216,.10);
}
.btn .shine{
content:"";
position:absolute; inset:-30%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.45), transparent);
transform: rotate(20deg) translateX(-120%);
animation: shine 2.8s ease-in-out infinite;
opacity:.8;
}
@keyframes shine{
0%,55%{transform: rotate(20deg) translateX(-140%)}
85%,100%{transform: rotate(20deg) translateX(140%)}
}
/* ======= Hero ======= */
.hero{
padding: 46px 0 16px;
display:grid;
grid-template-columns: 1fr;
gap:18px;
position:relative;
}
@media (min-width:980px){
.hero{
grid-template-columns: 1.05fr .95fr;
align-items:center;
padding: 64px 0 22px;
}
}
.kicker{
display:inline-flex; align-items:center; gap:10px;
padding:8px 12px;
border-radius:999px;
border:1px solid rgba(255,255,255,.14);
background: rgba(255,255,255,.06);
color:var(--muted);
font-weight:800;
width:fit-content;
box-shadow: 0 10px 40px rgba(0,0,0,.35);
}
.badge-live{
display:inline-flex; align-items:center; gap:7px;
padding:4px 10px;
border-radius:999px;
font-size:12px;
font-weight:900;
letter-spacing:.6px;
color:#071208;
background: linear-gradient(135deg, var(--neon), var(--neon2));
box-shadow: 0 0 18px rgba(124,255,107,.22), 0 0 28px rgba(122,252,255,.18);
text-transform:uppercase;
}
.badge-live i{
width:8px;height:8px;border-radius:99px;background:#071208;
box-shadow: 0 0 0 6px rgba(7,18,8,.15);
animation: ping 1.1s ease-in-out infinite;
}
@keyframes ping{
0%,100%{transform:scale(1); opacity:1}
50%{transform:scale(1.35); opacity:.8}
}
h1{
margin: 14px 0 12px;
font-size: clamp(36px, 5vw, 62px);
line-height: 1.02;
letter-spacing: -1.2px;
}
.glitch{
position:relative;
display:inline-block;
text-shadow:
0 0 14px rgba(122,252,255,.18),
0 0 22px rgba(255,79,216,.14);
}
.glitch::before,.glitch::after{
content: attr(data-text);
position:absolute; left:0; top:0;
opacity:.8;
mix-blend-mode: screen;
clip-path: inset(0 0 0 0);
animation: glitch 2.4s infinite linear alternate-reverse;
}
.glitch::before{
transform: translate(2px,-2px);
color: var(--neon2);
filter: blur(.2px);
}
.glitch::after{
transform: translate(-2px,2px);
color: var(--hot);
animation-duration: 2.9s;
filter: blur(.25px);
}
@keyframes glitch{
0%{clip-path: inset(0 0 85% 0)}
10%{clip-path: inset(12% 0 60% 0)}
20%{clip-path: inset(65% 0 10% 0)}
30%{clip-path: inset(40% 0 40% 0)}
40%{clip-path: inset(80% 0 5% 0)}
50%{clip-path: inset(10% 0 75% 0)}
60%{clip-path: inset(35% 0 40% 0)}
70%{clip-path: inset(5% 0 85% 0)}
80%{clip-path: inset(55% 0 20% 0)}
90%{clip-path: inset(25% 0 55% 0)}
100%{clip-path: inset(70% 0 15% 0)}
}
.sub{
color:var(--muted);
font-size: clamp(16px, 2.1vw, 20px);
line-height:1.5;
margin: 0 0 18px;
max-width: 58ch;
}
.hero-actions{
display:flex; flex-wrap:wrap; gap:10px;
align-items:center;
margin: 18px 0 12px;
}
.stats{
display:grid;
grid-template-columns: repeat(2, minmax(0,1fr));
gap:10px;
margin-top: 14px;
max-width: 520px;
}
@media(min-width:520px){
.stats{grid-template-columns: repeat(3, minmax(0,1fr));}
}
.stat{
border:1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.06);
border-radius: 18px;
padding: 12px 12px;
box-shadow: 0 12px 55px rgba(0,0,0,.35);
position:relative;
overflow:hidden;
}
.stat b{
display:block;
font-size: 22px;
letter-spacing:-.3px;
}
.stat span{color:var(--muted); font-weight:700; font-size:13px}
.stat::after{
content:"";
position:absolute; inset:-40%;
background: radial-gradient(circle at 30% 30%, rgba(122,252,255,.18), transparent 55%),
radial-gradient(circle at 80% 70%, rgba(255,79,216,.16), transparent 55%);
animation: drift 6s ease-in-out infinite;
opacity:.8;
}
@keyframes drift{
0%,100%{transform: translate(0,0) rotate(0deg)}
50%{transform: translate(18px,-12px) rotate(8deg)}
}
.stat > *{position:relative; z-index:1}
/* ======= Right side device card ======= */
.device{
border:1px solid rgba(255,255,255,.14);
background: linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,.04));
border-radius: var(--radius);
padding: 14px;
box-shadow: var(--shadow);
position:relative;
overflow:hidden;
min-height: 420px;
}
.device::before{
content:"";
position:absolute; inset:-40%;
background: conic-gradient(from 180deg at 50% 50%,
rgba(122,252,255,.0),
rgba(122,252,255,.22),
rgba(255,79,216,.18),
rgba(124,255,107,.16),
rgba(255,211,77,.10),
rgba(122,252,255,.0));
animation: spin 10s linear infinite;
opacity:.55;
}
.device-inner{
position:relative; z-index:1;
height:100%;
border-radius: calc(var(--radius) - 10px);
border:1px solid rgba(255,255,255,.12);
background: rgba(0,0,0,.22);
overflow:hidden;
display:grid;
grid-template-rows: auto 1fr auto;
}
.device-top{
display:flex; align-items:center; justify-content:space-between;
padding:12px 12px 10px;
gap:10px;
border-bottom:1px solid rgba(255,255,255,.10);
}
.dots{display:flex; gap:7px}
.dots i{
width:10px;height:10px;border-radius:99px;
background: rgba(255,255,255,.16);
box-shadow: 0 0 0 6px rgba(255,255,255,.03);
}
.device-top .mode{
font-size:12px; font-weight:900; letter-spacing:.7px;
text-transform:uppercase;
color:var(--muted);
}
.screen{
position:relative;
padding: 14px;
display:grid;
place-items:center;
}
.screen img{
width:100%;
height: 280px;
object-fit:cover;
border-radius: 18px;
border:1px solid rgba(255,255,255,.14);
filter: saturate(1.15) contrast(1.05);
transform: translateZ(0);
box-shadow: 0 18px 70px rgba(0,0,0,.55);
}
.floaters{
position:absolute; inset:0;
pointer-events:none;
overflow:hidden;
}
.floater{
position:absolute;
font-size: clamp(16px, 2.6vw, 26px);
filter: drop-shadow(0 10px 18px rgba(0,0,0,.55));
opacity:.95;
animation: floatUp linear infinite;
}
@keyframes floatUp{
from{transform: translateY(40px) rotate(0deg); opacity:0}
15%{opacity:1}
to{transform: translateY(-380px) rotate(22deg); opacity:0}
}
.device-bottom{
padding: 12px;
border-top:1px solid rgba(255,255,255,.10);
display:flex; gap:10px; align-items:center; justify-content:space-between;
flex-wrap:wrap;
}
.meter{
flex:1;
min-width: 210px;
border:1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.05);
border-radius: 999px;
height: 14px;
overflow:hidden;
position:relative;
}
.meter span{
display:block; height:100%;
width: 20%;
background: linear-gradient(90deg, var(--neon2), var(--hot), var(--neon));
border-radius: 999px;
animation: fill 3.2s ease-in-out infinite;
filter: saturate(1.2);
}
@keyframes fill{
0%{width:18%}
50%{width:98%}
100%{width:30%}
}
.meter-label{
font-weight:900;
color:var(--muted);
font-size:12px;
letter-spacing:.6px;
text-transform:uppercase;
display:flex; align-items:center; gap:8px;
user-select:none;
}
/* ======= Sections ======= */
section{padding: 26px 0}
.grid{
display:grid;
grid-template-columns: 1fr;
gap:14px;
}
@media(min-width:980px){
.grid{grid-template-columns: 1fr 1fr}
}
.card{
border:1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.06);
border-radius: var(--radius);
padding: 16px;
box-shadow: 0 18px 70px rgba(0,0,0,.35);
position:relative;
overflow:hidden;
}
.card::before{
content:"";
position:absolute; inset:-30%;
background: radial-gradient(circle at 20% 20%, rgba(122,252,255,.14), transparent 55%),
radial-gradient(circle at 70% 70%, rgba(255,79,216,.14), transparent 55%);
animation: drift 8s ease-in-out infinite;
opacity:.7;
}
.card > *{position:relative; z-index:1}
.card h2{
margin: 2px 0 10px;
font-size: 22px;
letter-spacing:-.4px;
}
.card p{margin: 0; color: var(--muted); line-height:1.55}
.bullets{
margin: 12px 0 0;
padding-left: 0;
list-style:none;
display:grid;
gap:10px;
}
.bullets li{
display:flex; gap:10px;
align-items:flex-start;
padding: 10px 10px;
border-radius: 16px;
border:1px solid rgba(255,255,255,.10);
background: rgba(0,0,0,.18);
}
.emoji{
width: 34px; height: 34px;
display:grid; place-items:center;
border-radius: 14px;
background: rgba(255,255,255,.07);
border:1px solid rgba(255,255,255,.12);
flex:0 0 auto;
}
.bullets b{display:block; margin-bottom:2px}
.bullets span{color: var(--muted); font-weight:650}
/* ======= Old tester comparison ======= */
.compare{
display:grid;
grid-template-columns: 1fr;
gap: 14px;
align-items:stretch;
}
@media(min-width:980px){
.compare{grid-template-columns: 1fr 1fr}
}
.img-card{
border:1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.05);
border-radius: var(--radius);
overflow:hidden;
box-shadow: var(--shadow);
position:relative;
}
.img-card img{
width:100%;
height: 320px;
object-fit: cover;
display:block;
filter: contrast(1.05) saturate(1.12);
}
.img-card .cap{
padding: 14px 14px 16px;
border-top:1px solid rgba(255,255,255,.10);
}
.tag{
display:inline-flex; align-items:center; gap:8px;
font-weight:900;
letter-spacing:.5px;
text-transform:uppercase;
font-size:12px;
padding:6px 10px;
border-radius:999px;
border:1px solid rgba(255,255,255,.14);
background: rgba(0,0,0,.22);
color:var(--muted);
}
/* ======= Testimonials ======= */
.quotes{
display:grid;
grid-template-columns: 1fr;
gap: 12px;
margin-top: 10px;
}
@media(min-width:860px){
.quotes{grid-template-columns: repeat(3, 1fr)}
}
blockquote{
margin:0;
padding: 14px;
border-radius: var(--radius);
border:1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.06);
box-shadow: 0 18px 60px rgba(0,0,0,.28);
position:relative;
overflow:hidden;
}
blockquote::before{
content:"“";
position:absolute;
top:-24px; left:10px;
font-size: 88px;
opacity:.16;
}
blockquote p{margin:0 0 10px; color:var(--muted); line-height:1.5}
blockquote footer{font-weight:900; color:var(--text)}
blockquote small{display:block; color:var(--muted); font-weight:750; margin-top:3px}
/* ======= Footer ======= */
footer{
margin-top: 26px;
padding-top: 18px;
border-top: 1px solid rgba(255,255,255,.10);
color: var(--muted);
font-weight:700;
display:flex; flex-direction:column; gap:8px;
}
/* ======= Scroll reveal ======= */
.reveal{
opacity:0;
transform: translateY(14px) scale(.98);
transition: opacity .7s ease, transform .7s ease;
}
.reveal.on{
opacity:1;
transform: translateY(0) scale(1);
}
/* ======= Confetti canvas ======= */
canvas#confetti{
position:fixed; inset:0;
pointer-events:none;
z-index:999;
}
/* ======= Reduced motion ======= */
@media (prefers-reduced-motion: reduce){
*{animation:none!important; transition:none!important; scroll-behavior:auto!important}
.bg-grid,.lasers{display:none}
}
</style>
</head>
<body>
<div class="bg-grid" aria-hidden="true"></div>
<div class="lasers" aria-hidden="true"></div>
<canvas id="confetti"></canvas>
<header>
<div class="nav">
<div class="brand">
<span class="dot" aria-hidden="true"></span>
<span>NETZWERKTESTER™</span>
</div>
<nav>
<a href="#features">Features</a>
<a href="#compare">Vergleich</a>
<a href="#testimonials">Stimmen</a>
</nav>
<div class="cta">
<a class="btn" href="#compare">👀 Showdown</a>
<a class="btn primary" id="boostBtn" href="#features">🚀 BOOST <span class="shine" aria-hidden="true"></span></a>
</div>
</div>
</header>
<main class="wrap">
<section class="hero">
<div>
<div class="kicker">
<span class="badge-live"><i></i> Live Hype</span>
<span>Übertrieben. Animiert. Absolut notwendig. 😎⚡</span>
</div>
<h1>
<span class="glitch" data-text="Der Netzwerktester, den wirklich ALLE brauchen.">Der Netzwerktester, den wirklich ALLE brauchen.</span>
<span aria-hidden="true"> 🧪🔌✨</span>
</h1>
<p class="sub">
Schluss mit „Warum gehts nicht?!“ — unser Netzwerktester verwandelt jedes Kabel in eine <b>offiziell zertifizierte
Glücksleitung</b>. Er misst nicht nur Durchgang… er misst <b>Aura</b>, <b>Vibes</b> und <b>Respekt</b>. 💚📈
</p>
<div class="hero-actions">
<a class="btn primary" href="#features">😤 Ich brauche das jetzt <span class="shine" aria-hidden="true"></span></a>
<a class="btn" href="#testimonials">⭐ 100% echte Stimmen*</a>
<span class="meter-label">POWER-LEVEL: <b id="powerLevel">9000</b>+</span>
</div>
<div class="stats">
<div class="stat">
<b><span id="uptime">99.98</span>%</b>
<span>Hype-Uptime</span>
</div>
<div class="stat">
<b><span id="cables">0</span></b>
<span>Kabel gerettet</span>
</div>
<div class="stat">
<b><span id="wow">0</span>×</b>
<span>„WOW“ pro Minute</span>
</div>
</div>
</div>
<aside class="device" aria-label="Animiertes Showcase">
<div class="device-inner">
<div class="device-top">
<div class="dots" aria-hidden="true"><i></i><i></i><i></i></div>
<div class="mode">ULTRA DIAG MODE 🔥</div>
</div>
<div class="screen">
<!-- Bild 1: Person mit Netzwerkkabel -->
<img
src="https://cdn.pixabay.com/photo/2016/08/15/20/02/compression-pressure-recorder-1596369_1280.jpg"
alt="Person hält ein Netzwerkkabel"
loading="lazy"
/>
<div class="floaters" aria-hidden="true" id="floaters"></div>
</div>
<div class="device-bottom">
<div class="meter" aria-hidden="true"><span></span></div>
<div class="meter-label">SCAN: <span id="scanText">LINK UP ✅</span></div>
</div>
</div>
</aside>
</section>
<section id="features" class="reveal">
<div class="grid">
<div class="card">
<h2>Warum ist das Ding so legendär? 🏆</h2>
<p>
Weil er die Realität debuggt. Du steckst ein Kabel rein — und plötzlich macht die Welt Sinn.
(Zumindest das Patchfeld.) 🌍🔧
</p>
<ul class="bullets">
<li>
<div class="emoji">🧠</div>
<div>
<b>IQ-Boost für den Schaltschrank</b>
<span>Erkennt Fehler, bevor sie überhaupt passieren. (Gefühlt.)</span>
</div>
</li>
<li>
<div class="emoji"></div>
<div>
<b>Turbo-Scan in Lichtgeschwindigkeit</b>
<span>So schnell, dass dein Blick hinterher ruckelt.</span>
</div>
</li>
<li>
<div class="emoji">🛡️</div>
<div>
<b>Anti-“Warum gehts nicht?”-Shield</b>
<span>Blockt Ausreden, Drama und Kabel-Karma.</span>
</div>
</li>
</ul>
</div>
<div class="card">
<h2>Für wen? Für ALLE. Wirklich alle. 😤</h2>
<p>
Schüler, Azubis, Techniker, Netzwerkgötter, IT-Orakel, Menschen mit Kabeln, Menschen ohne Kabeln,
sogar Leute, die „LAN“ für eine Stadt halten. 🏙️🔌
</p>
<ul class="bullets">
<li>
<div class="emoji">📱</div>
<div>
<b>Handy & PC: Perfekt sichtbar</b>
<span>Responsive Layout, große Buttons, kein Zoomen wie 2012.</span>
</div>
</li>
<li>
<div class="emoji">🎛️</div>
<div>
<b>Animationen: Maximum Overdrive</b>
<span>Neon, Glitch, Laser, Floating Emojis… ja.</span>
</div>
</li>
<li>
<div class="emoji">🧪</div>
<div>
<b>Wissenschaftlich übertrieben</b>
<span>Messwerte wirken 17% seriöser, wenn sie leuchten.</span>
</div>
</li>
</ul>
</div>
</div>
</section>
<section id="compare" class="reveal">
<h2 style="margin:0 0 12px; font-size:28px; letter-spacing:-.5px;">
Der epische Vergleich: ALT vs. HYPER-ULTRA 😱
</h2>
<div class="compare">
<article class="img-card">
<!-- Bild 2: sehr alter Netzwerktester -->
<img
src="https://cdn.pixabay.com/photo/2013/03/09/02/36/tester-91696_1280.jpg"
alt="Sehr alter Netzwerktester"
loading="lazy"
/>
<div class="cap">
<span class="tag">🕰️ Altgerät</span>
<h3 style="margin:10px 0 6px;">Der Klassiker: „Geht… irgendwie“</h3>
<p style="margin:0; color:var(--muted); line-height:1.5;">
Misst Dinge. Manchmal. Mit Charme. Aber ohne Laser. Ohne Glitch. Ohne Drama. (Also langweilig.) 😴
</p>
</div>
</article>
<article class="img-card">
<div style="position:relative">
<img
src="https://cdn.pixabay.com/photo/2017/12/24/21/08/secret-3037639_1280.jpg"
alt="Person mit Netzwerkkabel"
loading="lazy"
style="filter:saturate(1.3) contrast(1.08);"
/>
<div style="
position:absolute; inset:0;
background: radial-gradient(circle at 20% 20%, rgba(122,252,255,.22), transparent 55%),
radial-gradient(circle at 70% 70%, rgba(255,79,216,.18), transparent 55%);
mix-blend-mode: screen;
pointer-events:none;
"></div>
</div>
<div class="cap">
<span class="tag">🚀 HYPE-DEVICE</span>
<h3 style="margin:10px 0 6px;">NETZWERKTESTER™: „Ich sehe alles.“</h3>
<p style="margin:0; color:var(--muted); line-height:1.5;">
Findet Fehler. Findet Sinn. Findet deinen verlorenen Schraubendreher (theoretisch). 🧲✨
Und sieht dabei gefährlich gut aus. 😎
</p>
</div>
</article>
</div>
</section>
<section id="testimonials" class="reveal">
<h2 style="margin:0 0 6px; font-size:28px; letter-spacing:-.5px;">
100% echte Stimmen* ⭐
</h2>
<p style="margin:0 0 10px; color:var(--muted); font-weight:700;">
*Echt im Sinne von: Niemand hat widersprochen. 🤝😇
</p>
<div class="quotes">
<blockquote>
<p>„Ich hab ihn einmal eingeschaltet und plötzlich war mein Leben geordnet. Sogar die Patchkabel.“</p>
<footer>Max, <small>zertifizierter Kabel-Flüsterer 🧙‍♂️</small></footer>
</blockquote>
<blockquote>
<p>„Er sagt LINK UP mit so viel Selbstbewusstsein, ich fühl mich direkt kompetenter.“</p>
<footer>Sara, <small>Fehlerfinderin auf Koffein ☕</small></footer>
</blockquote>
<blockquote>
<p>„Ich wollte keinen. Jetzt hab ich drei. Einen fürs Bett, einen fürs Auto, einen fürs Herz.“</p>
<footer>Ali, <small>LAN-Romantiker 💘</small></footer>
</blockquote>
</div>
</section>
<footer>
<div>© <span id="year"></span> Netzwerktester™ — „Wenns leuchtet, ists besser.“ ✨</div>
<div style="opacity:.85"><a href="https://www.fsae41.de">HOME</a></div>
</footer>
</main>
<script>
// ===== Scroll reveal =====
const revealEls = document.querySelectorAll('.reveal');
const io = new IntersectionObserver((entries)=>{
for (const e of entries){
if (e.isIntersecting) e.target.classList.add('on');
}
}, {threshold: 0.12});
revealEls.forEach(el => io.observe(el));
// ===== Floating emojis in the "device screen" =====
const floaters = document.getElementById('floaters');
const EMOJIS = ["🔌","⚡","✨","📶","🧪","😎","🟩","💥","🧠","🛠️","🌈","🚀"];
function spawnFloater(){
const el = document.createElement('div');
el.className = 'floater';
el.textContent = EMOJIS[Math.floor(Math.random()*EMOJIS.length)];
el.style.left = Math.random()*100 + "%";
el.style.bottom = (-10 - Math.random()*10) + "px";
el.style.animationDuration = (2.2 + Math.random()*2.8) + "s";
el.style.animationDelay = (Math.random()*0.2) + "s";
el.style.transform = `translateY(40px) rotate(${(Math.random()*18-9).toFixed(1)}deg)`;
floaters.appendChild(el);
setTimeout(()=> el.remove(), 5200);
}
setInterval(spawnFloater, 180);
// ===== Fake live scan text =====
const scanText = document.getElementById('scanText');
const scanPhrases = [
"LINK UP ✅", "PAIR OK ✅", "GIGABIT DREAM ✅", "VIBES: STABIL ✅",
"PATCHKABEL: GLÜCKLICH ✅", "KARMA: GEROUTET ✅", "FEHLER: GEFUNDEN 🎯"
];
setInterval(()=>{
scanText.textContent = scanPhrases[Math.floor(Math.random()*scanPhrases.length)];
}, 1300);
// ===== Counters =====
const yearEl = document.getElementById('year');
yearEl.textContent = new Date().getFullYear();
const uptimeEl = document.getElementById('uptime');
const cablesEl = document.getElementById('cables');
const wowEl = document.getElementById('wow');
const powerLevelEl = document.getElementById('powerLevel');
let cables = 0;
let wow = 0;
let power = 9001;
function tick(){
cables += Math.floor(1 + Math.random()*6);
wow += Math.floor(1 + Math.random()*4);
power += Math.floor(6 + Math.random()*18);
cablesEl.textContent = cables.toLocaleString('de-DE');
wowEl.textContent = wow.toLocaleString('de-DE');
powerLevelEl.textContent = power.toLocaleString('de-DE');
// uptime wiggle
const base = 99.90;
const v = (base + Math.random()*0.09).toFixed(2);
uptimeEl.textContent = v;
}
setInterval(tick, 900);
tick();
// ===== Confetti "BOOST" (simple canvas particles) =====
const canvas = document.getElementById('confetti');
const ctx = canvas.getContext('2d');
let W, H;
function resize(){
W = canvas.width = window.innerWidth * devicePixelRatio;
H = canvas.height = window.innerHeight * devicePixelRatio;
}
window.addEventListener('resize', resize);
resize();
let particles = [];
function burst(){
const count = 170;
for(let i=0;i<count;i++){
particles.push({
x: (Math.random()*window.innerWidth) * devicePixelRatio,
y: (-10 - Math.random()*60) * devicePixelRatio,
vx: (-1.2 + Math.random()*2.4) * devicePixelRatio,
vy: (2.0 + Math.random()*4.4) * devicePixelRatio,
r: (2 + Math.random()*4) * devicePixelRatio,
rot: Math.random()*Math.PI*2,
vr: (-0.18 + Math.random()*0.36),
life: 0,
max: 160 + Math.random()*120
});
}
}
function draw(){
ctx.clearRect(0,0,W,H);
for(const p of particles){
p.x += p.vx;
p.y += p.vy;
p.vy += 0.02 * devicePixelRatio; // gravity
p.rot += p.vr;
p.life++;
const alpha = Math.max(0, 1 - p.life / p.max);
ctx.save();
ctx.globalAlpha = alpha * 0.9;
ctx.translate(p.x, p.y);
ctx.rotate(p.rot);
// No fixed colors: use random-ish neon via hue
const hue = (p.life*2 + p.x/W*360) % 360;
ctx.fillStyle = `hsla(${hue}, 95%, 60%, 1)`;
ctx.fillRect(-p.r, -p.r, p.r*2.2, p.r*1.2);
ctx.restore();
}
particles = particles.filter(p => p.life < p.max && p.y < H + 120);
requestAnimationFrame(draw);
}
draw();
document.getElementById('boostBtn').addEventListener('click', (e)=>{
// allow anchor scroll, but also boost visuals
burst();
// micro screen shake
document.body.animate([
{ transform: 'translate(0,0)' },
{ transform: 'translate(2px,-2px)' },
{ transform: 'translate(-2px,2px)' },
{ transform: 'translate(1px,1px)' },
{ transform: 'translate(0,0)' }
], { duration: 320, iterations: 1 });
});
</script>
</body>
</html>