diff --git a/news.md b/news.md
index 2673128..b7cb789 100644
--- a/news.md
+++ b/news.md
@@ -15,6 +15,62 @@
- Rainbow Vim - plugin co vytvoří baravné zaávorky
- Gitgutter - plugin pro správu git
+## Refactor květen 2026
+
+✅ Co bylo vytvořeno
+
+### Struktura `web/`
+
+```ApolloNvim2025/web/
+├── html/
+│ ├── index.html — Hlavní stránka s hero, features, editions, stats, terminal ukázky
+│ ├── instalace.html — Průvodce instalací (Starter + FrontEnd), CZ/EN, FAQ
+│ ├── pluginy.html — Všechny pluginy s filtrem, Coc extensions, 19 témat
+│ ├── tutorialy.html — 20 tutoriálů v kartách + tipy z Hint.md
+│ ├── novinky.html — Timeline changelog + roadmap vývoje
+│ └── zkratky.html — Klávesové zkratky v tabulkách + tisk
+├── css/
+│ ├── style.css — ~2600 řádků (kompletní dark space design)
+│ └── animations.css — Keyframes, scroll animace, efekty
+├── js/
+│ ├── particles.js — Vesmírný particle systém na Canvas
+│ └── main.js — Navigace, tabye, copy to clipboard, filtr pluginů, ...
+└── img/
+ ├── apollonvim.jpg
+ └── neovim.jpeg
+```
+
+### Klíčové funkce webu
+- 🌌 **Vesmírný particle systém** – 200 hvězd, blikání, parallax, padající hvězdy
+- 🎨 **Dracula/dark kosmický design** – purple/pink/cyan paleta, glassmorphism navbar
+- 📱 **Plně responsivní** – hamburger menu na mobile, přizpůsobení pro všechny velikosti
+- ⚡ **Scroll animace** – IntersectionObserver, plynulé fade-in/slide-up efekty
+- 🔌 **Filtr pluginů** – filtrování dle kategorie (Editor, UI, Git, AI, LSP...)
+- 🇨🇿🇬🇧 **CZ/EN přepínač** – na instalaci a pluginech
+- 📋 **Copy to clipboard** – u všech terminálových bloků
+- 🔧 **FAQ accordion** – na stránce instalace
+- 🎨 **19 barevných témat** – s preview swatchi barev
+- ⌨️ **Klávesové zkratky** – s print podporou pro PDF
+
+web/
+├── index.html ← Hlavní stránka (kořen webu)
+├── css/
+│ ├── style.css ← Kompletní CSS (~2800 řádků)
+│ └── animations.css ← Keyframes a efekty
+├── html/
+│ ├── instalace.html ← Průvodce instalací
+│ ├── pluginy.html ← Seznam pluginů + barevná témata
+│ ├── tutorialy.html ← Tutoriály
+│ ├── novinky.html ← Changelog + roadmap
+│ └── zkratky.html ← Klávesové zkratky
+├── js/
+│ ├── particles.js ← Vesmírný particle systém
+│ └── main.js ← Veškerá JS logika
+└── img/
+ ├── apollonvim.jpg
+ └── neovim.jpeg
+
+
Copyright © 2024 - 2025 🚀 ApolloNvim / Lukáš Kaňka
\ No newline at end of file
diff --git a/web/css/animations.css b/web/css/animations.css
new file mode 100644
index 0000000..25767d8
--- /dev/null
+++ b/web/css/animations.css
@@ -0,0 +1,684 @@
+/* ============================================================
+ ApolloNvim – animations.css
+ Web: apollo-nvim.lukan.cz
+ All keyframes, scroll animations, stagger delays,
+ micro-interactions and loading screen transitions.
+ ============================================================ */
+
+/* ────────────────────────────────────────────────────────────
+ 1. CORE KEYFRAMES
+ ──────────────────────────────────────────────────────────── */
+
+/* 1.1 fadeIn ─ simple opacity */
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+/* 1.2 slideUp ─ rise + fade */
+@keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(40px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* 1.3 slideInLeft ─ enter from left */
+@keyframes slideInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-60px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+/* 1.4 slideInRight ─ enter from right */
+@keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(60px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+/* 1.5 float ─ gentle levitation for hero images / icons */
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0px);
+ }
+ 33% {
+ transform: translateY(-10px);
+ }
+ 66% {
+ transform: translateY(-6px);
+ }
+}
+
+/* 1.6 pulse-glow ─ pulsing purple box-shadow */
+@keyframes pulse-glow {
+ 0%, 100% {
+ box-shadow:
+ 0 0 0 0 rgba(189, 147, 249, 0),
+ 0 0 20px 2px rgba(189, 147, 249, 0.15);
+ }
+ 50% {
+ box-shadow:
+ 0 0 0 4px rgba(189, 147, 249, 0.08),
+ 0 0 40px 8px rgba(189, 147, 249, 0.35);
+ }
+}
+
+/* 1.7 typing ─ blinking cursor caret */
+@keyframes typing {
+ 0%, 100% { border-right-color: rgba(189, 147, 249, 1); }
+ 50% { border-right-color: transparent; }
+}
+
+/* Typewriter text reveal (pair with .typewriter class) */
+@keyframes typewriter {
+ from { width: 0; }
+ to { width: 100%; }
+}
+
+/* 1.8 spin-slow ─ slow full rotation (used by loading logo) */
+@keyframes spin-slow {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+}
+
+/* 1.9 glitch ─ subtle horizontal glitch for headings */
+@keyframes glitch {
+ 0% { clip-path: inset(0 0 100% 0); transform: translate(0); }
+ 10% { clip-path: inset(10% 0 60% 0); transform: translate(-3px, 1px); }
+ 20% { clip-path: inset(60% 0 10% 0); transform: translate(3px, -1px); }
+ 30% { clip-path: inset(30% 0 40% 0); transform: translate(-2px, 0); }
+ 40% { clip-path: inset(0 0 80% 0); transform: translate(2px, 1px); }
+ 50% { clip-path: inset(80% 0 0 0); transform: translate(-1px, -2px); }
+ 60% { clip-path: inset(20% 0 50% 0); transform: translate(1px, 0); }
+ 70% { clip-path: inset(50% 0 20% 0); transform: translate(-2px, 1px); }
+ 80% { clip-path: inset(5% 0 70% 0); transform: translate(2px, -1px); }
+ 90% { clip-path: inset(70% 0 5% 0); transform: translate(0, 2px); }
+ 100% { clip-path: inset(0 0 0 0); transform: translate(0); }
+}
+
+/* 1.10 rocket-launch ─ rocket rises with shrinking trail */
+@keyframes rocket-launch {
+ 0% {
+ transform: translateY(0) scale(1);
+ opacity: 1;
+ filter: drop-shadow(0 8px 16px rgba(189, 147, 249, 0.6));
+ }
+ 30% {
+ transform: translateY(-20px) scale(1.05);
+ filter: drop-shadow(0 16px 32px rgba(255, 121, 198, 0.7));
+ }
+ 70% {
+ transform: translateY(-80px) scale(0.9);
+ opacity: 0.85;
+ filter: drop-shadow(0 4px 8px rgba(139, 233, 253, 0.5));
+ }
+ 100% {
+ transform: translateY(-200px) scale(0.4);
+ opacity: 0;
+ filter: drop-shadow(0 0 0 transparent);
+ }
+}
+
+/* Rocket trail (::after pseudo) */
+@keyframes rocket-trail {
+ 0% { opacity: 0.8; transform: scaleY(1) scaleX(1); }
+ 50% { opacity: 0.5; transform: scaleY(1.4) scaleX(0.7); }
+ 100% { opacity: 0; transform: scaleY(2) scaleX(0.3); }
+}
+
+/* 1.11 star-twinkle ─ random opacity blink for star dots */
+@keyframes star-twinkle {
+ 0%, 100% { opacity: 0.15; transform: scale(0.85); }
+ 25% { opacity: 0.9; transform: scale(1.1); }
+ 50% { opacity: 0.4; transform: scale(0.95); }
+ 75% { opacity: 1; transform: scale(1.05); }
+}
+
+/* 1.12 gradient-shift ─ animated gradient background */
+@keyframes gradient-shift {
+ 0% { background-position: 0% 50%; }
+ 50% { background-position: 100% 50%; }
+ 100% { background-position: 0% 50%; }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 2. ANIMATION UTILITY CLASSES
+ ──────────────────────────────────────────────────────────── */
+
+/* Basic named animations */
+.anim-fade-in { animation: fadeIn 0.5s ease both; }
+.anim-slide-up { animation: slideUp 0.55s ease both; }
+.anim-slide-left { animation: slideInLeft 0.55s ease both; }
+.anim-slide-right { animation: slideInRight 0.55s ease both; }
+.anim-float { animation: float 4s ease-in-out infinite; }
+.anim-pulse-glow { animation: pulse-glow 2.5s ease-in-out infinite; }
+.anim-spin-slow { animation: spin-slow 20s linear infinite; }
+.anim-star-twinkle { animation: star-twinkle 3s ease-in-out infinite; }
+
+/* Gradient-shift background (set background-size: 200% 200% on element) */
+.anim-gradient-bg {
+ background-size: 200% 200%;
+ animation: gradient-shift 6s ease infinite;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 3. TYPEWRITER EFFECT
+ ──────────────────────────────────────────────────────────── */
+.typewriter {
+ display: inline-block;
+ overflow: hidden;
+ white-space: nowrap;
+ border-right: 2px solid;
+ /* combine reveal + cursor blink */
+ animation:
+ typewriter 2s steps(30, end) both,
+ typing 0.7s step-end infinite;
+}
+
+/* Standalone cursor only (no text reveal) */
+.cursor-blink {
+ display: inline-block;
+ width: 2px;
+ height: 1.1em;
+ background: #bd93f9;
+ vertical-align: text-bottom;
+ animation: typing 0.7s step-end infinite;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 4. GLITCH EFFECT
+ ──────────────────────────────────────────────────────────── */
+.glitch-text {
+ position: relative;
+ display: inline-block;
+}
+
+/* Glitch layers via pseudo-elements */
+.glitch-text::before,
+.glitch-text::after {
+ content: attr(data-text);
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+}
+
+.glitch-text::before {
+ color: #ff79c6;
+ animation: glitch 3.5s infinite linear alternate-reverse;
+ animation-delay: 0.1s;
+ opacity: 0.7;
+}
+
+.glitch-text::after {
+ color: #8be9fd;
+ animation: glitch 2.8s infinite linear alternate;
+ animation-delay: 0.25s;
+ opacity: 0.6;
+}
+
+/* On hover only */
+.glitch-text--hover::before,
+.glitch-text--hover::after {
+ animation-play-state: paused;
+}
+
+.glitch-text--hover:hover::before,
+.glitch-text--hover:hover::after {
+ animation-play-state: running;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 5. ROCKET LAUNCH
+ ──────────────────────────────────────────────────────────── */
+.rocket {
+ display: inline-block;
+ position: relative;
+ transition: transform 0.3s ease;
+}
+
+.rocket--launch {
+ animation: rocket-launch 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
+}
+
+/* Trail element */
+.rocket__trail {
+ position: absolute;
+ bottom: -8px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 6px;
+ height: 20px;
+ border-radius: 0 0 4px 4px;
+ background: linear-gradient(180deg, rgba(255, 121, 198, 0.8), transparent);
+ transform-origin: top center;
+ animation: rocket-trail 1.2s ease forwards;
+ opacity: 0;
+}
+
+.rocket--launch .rocket__trail {
+ opacity: 0.8;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 6. STAR FIELD
+ ──────────────────────────────────────────────────────────── */
+.star {
+ position: absolute;
+ width: 3px;
+ height: 3px;
+ background: #f8f8f2;
+ border-radius: 50%;
+ animation: star-twinkle var(--duration, 3s) ease-in-out infinite;
+ animation-delay: var(--delay, 0s);
+}
+
+.star--sm { width: 1.5px; height: 1.5px; }
+.star--md { width: 2.5px; height: 2.5px; }
+.star--lg { width: 4px; height: 4px; box-shadow: 0 0 4px 1px rgba(248, 248, 242, 0.4); }
+
+/* ────────────────────────────────────────────────────────────
+ 7. SCROLL-TRIGGERED ANIMATIONS
+ ──────────────────────────────────────────────────────────── */
+
+/* Base hidden state – applied by JS IntersectionObserver */
+.animate-on-scroll {
+ opacity: 0;
+ transform: translateY(30px);
+ transition: opacity 0.6s ease, transform 0.6s ease;
+}
+
+/* Revealed state – JS adds .visible class */
+.animate-on-scroll.visible {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+/* Directional variants */
+.animate-on-scroll--left {
+ opacity: 0;
+ transform: translateX(-40px);
+ transition: opacity 0.6s ease, transform 0.6s ease;
+}
+
+.animate-on-scroll--left.visible {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+.animate-on-scroll--right {
+ opacity: 0;
+ transform: translateX(40px);
+ transition: opacity 0.6s ease, transform 0.6s ease;
+}
+
+.animate-on-scroll--right.visible {
+ opacity: 1;
+ transform: translateX(0);
+}
+
+.animate-on-scroll--scale {
+ opacity: 0;
+ transform: scale(0.9);
+ transition: opacity 0.6s ease, transform 0.6s ease;
+}
+
+.animate-on-scroll--scale.visible {
+ opacity: 1;
+ transform: scale(1);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 8. STAGGER DELAYS
+ ──────────────────────────────────────────────────────────── */
+.delay-1 { animation-delay: 100ms; transition-delay: 100ms; }
+.delay-2 { animation-delay: 200ms; transition-delay: 200ms; }
+.delay-3 { animation-delay: 300ms; transition-delay: 300ms; }
+.delay-4 { animation-delay: 400ms; transition-delay: 400ms; }
+.delay-5 { animation-delay: 500ms; transition-delay: 500ms; }
+
+/* Extended stagger for large grids */
+.delay-6 { animation-delay: 600ms; transition-delay: 600ms; }
+.delay-7 { animation-delay: 700ms; transition-delay: 700ms; }
+.delay-8 { animation-delay: 800ms; transition-delay: 800ms; }
+
+/* ────────────────────────────────────────────────────────────
+ 9. HOVER MICRO-ANIMATIONS
+ ──────────────────────────────────────────────────────────── */
+
+/* ── Buttons ── */
+/* Primary button shine sweep */
+.btn-primary {
+ position: relative;
+ overflow: hidden;
+}
+
+.btn-primary::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -75%;
+ width: 50%;
+ height: 100%;
+ background: linear-gradient(
+ 120deg,
+ transparent 20%,
+ rgba(255, 255, 255, 0.22) 50%,
+ transparent 80%
+ );
+ transform: skewX(-20deg);
+ transition: left 0.55s ease;
+ pointer-events: none;
+}
+
+.btn-primary:hover::after {
+ left: 135%;
+}
+
+/* Secondary button border glow pulse */
+.btn-secondary {
+ position: relative;
+}
+
+.btn-secondary::before {
+ content: '';
+ position: absolute;
+ inset: -1px;
+ border-radius: inherit;
+ background: linear-gradient(135deg, transparent, rgba(189, 147, 249, 0.3), transparent);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ pointer-events: none;
+ z-index: -1;
+}
+
+.btn-secondary:hover::before {
+ opacity: 1;
+}
+
+/* ── Links ── */
+.link-underline {
+ position: relative;
+ text-decoration: none;
+ display: inline-block;
+}
+
+.link-underline::after {
+ content: '';
+ position: absolute;
+ bottom: -2px;
+ left: 0;
+ width: 100%;
+ height: 1px;
+ background: linear-gradient(90deg, #bd93f9, #ff79c6);
+ transform: scaleX(0);
+ transform-origin: right;
+ transition: transform 0.35s ease;
+}
+
+.link-underline:hover::after {
+ transform: scaleX(1);
+ transform-origin: left;
+}
+
+/* ── Card tilt (subtle) ── */
+.card-tilt {
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+ will-change: transform;
+}
+
+/* Managed by JS mousemove if desired – CSS fallback hover */
+.card-tilt:hover {
+ transform: perspective(800px) rotateX(2deg) rotateY(-2deg) translateY(-4px);
+}
+
+/* ── Icon bounce ── */
+.icon-bounce {
+ display: inline-block;
+ transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+
+.icon-bounce:hover {
+ transform: scale(1.25) translateY(-2px);
+}
+
+/* ── Icon spin on hover ── */
+.icon-spin-hover {
+ transition: transform 0.4s ease;
+ display: inline-block;
+}
+
+.icon-spin-hover:hover {
+ transform: rotate(180deg);
+}
+
+/* ── Pill/badge pop ── */
+.badge {
+ transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
+ box-shadow 0.2s ease;
+}
+
+.badge:hover {
+ transform: scale(1.08) translateY(-1px);
+ box-shadow: 0 4px 12px rgba(189, 147, 249, 0.3);
+}
+
+/* ── Nav link underline (already in style.css, reinforced here) ── */
+.navbar__link {
+ will-change: color;
+}
+
+/* ── Feature card shimmer border ── */
+.feature-card {
+ background-clip: padding-box;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 10. ANIMATED GRADIENT HERO BACKGROUND
+ ──────────────────────────────────────────────────────────── */
+.hero-gradient-bg {
+ background: linear-gradient(
+ -45deg,
+ #0d0d1a,
+ #12122a,
+ #1a0a2e,
+ #0d1a2e
+ );
+ background-size: 400% 400%;
+ animation: gradient-shift 12s ease infinite;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 11. LOADING SCREEN – FADE OUT
+ ──────────────────────────────────────────────────────────── */
+@keyframes loading-fade-out {
+ 0% { opacity: 1; transform: scale(1); }
+ 80% { opacity: 1; transform: scale(1.02); }
+ 100% { opacity: 0; transform: scale(0.98); }
+}
+
+/* Trigger by adding .is-hiding to .loading-screen via JS */
+.loading-screen.is-hiding {
+ animation: loading-fade-out 0.55s ease forwards;
+ pointer-events: none;
+}
+
+/* Orbiting dots spinner (used inside loading screen) */
+@keyframes orbit {
+ from { transform: rotate(0deg) translateX(28px) rotate(0deg); }
+ to { transform: rotate(360deg) translateX(28px) rotate(-360deg); }
+}
+
+.loader-orbit {
+ position: relative;
+ width: 60px;
+ height: 60px;
+}
+
+.loader-orbit__dot {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--purple, #bd93f9);
+ margin: -4px 0 0 -4px;
+ animation: orbit 1.4s linear infinite;
+}
+
+.loader-orbit__dot:nth-child(2) {
+ background: var(--pink, #ff79c6);
+ animation-delay: -0.46s;
+}
+
+.loader-orbit__dot:nth-child(3) {
+ background: var(--cyan, #8be9fd);
+ animation-delay: -0.93s;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 12. REVEAL ANIMATIONS FOR PAGE SECTIONS
+ ──────────────────────────────────────────────────────────── */
+
+/* Used by JS: section fades in as a block */
+.section-reveal {
+ opacity: 0;
+ transition: opacity 0.8s ease, transform 0.8s ease;
+ transform: translateY(24px);
+}
+
+.section-reveal.in-view {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+/* Hero content staggered entrance */
+.hero__content > * {
+ opacity: 0;
+ animation: slideUp 0.65s ease both;
+}
+
+.hero__content > *:nth-child(1) { animation-delay: 0.1s; }
+.hero__content > *:nth-child(2) { animation-delay: 0.25s; }
+.hero__content > *:nth-child(3) { animation-delay: 0.4s; }
+.hero__content > *:nth-child(4) { animation-delay: 0.55s; }
+.hero__content > *:nth-child(5) { animation-delay: 0.7s; }
+
+/* ────────────────────────────────────────────────────────────
+ 13. PROGRESS / SKILL BARS
+ ──────────────────────────────────────────────────────────── */
+@keyframes bar-fill {
+ from { width: 0%; }
+}
+
+.progress-bar {
+ height: 4px;
+ background: var(--border, rgba(189, 147, 249, 0.2));
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.progress-bar__fill {
+ height: 100%;
+ border-radius: 4px;
+ background: linear-gradient(90deg, var(--purple, #bd93f9), var(--pink, #ff79c6));
+ width: var(--progress, 0%);
+ animation: bar-fill 1.2s cubic-bezier(0.22, 1, 0.36, 1) both;
+ animation-delay: var(--delay, 0.2s);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 14. NOTIFICATION / TOAST ENTRANCE
+ ──────────────────────────────────────────────────────────── */
+@keyframes toast-in {
+ from {
+ opacity: 0;
+ transform: translateX(calc(100% + 24px));
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes toast-out {
+ from {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateX(calc(100% + 24px));
+ }
+}
+
+.toast {
+ animation: toast-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+.toast.closing {
+ animation: toast-out 0.3s ease forwards;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 15. REDUCED MOTION OVERRIDES
+ ──────────────────────────────────────────────────────────── */
+@media (prefers-reduced-motion: reduce) {
+ /* Disable all custom animations */
+ .anim-fade-in,
+ .anim-slide-up,
+ .anim-slide-left,
+ .anim-slide-right,
+ .anim-float,
+ .anim-pulse-glow,
+ .anim-spin-slow,
+ .anim-star-twinkle,
+ .anim-gradient-bg,
+ .typewriter,
+ .cursor-blink,
+ .glitch-text::before,
+ .glitch-text::after,
+ .hero__content > *,
+ .rocket--launch,
+ .rocket__trail,
+ .loader-orbit__dot,
+ .loading-screen__bar-fill,
+ .loading-screen__logo,
+ .section-reveal,
+ .progress-bar__fill,
+ .toast,
+ .toast.closing {
+ animation: none !important;
+ transition: none !important;
+ }
+
+ /* Scroll-triggered: instantly reveal instead */
+ .animate-on-scroll,
+ .animate-on-scroll--left,
+ .animate-on-scroll--right,
+ .animate-on-scroll--scale {
+ opacity: 1 !important;
+ transform: none !important;
+ transition: none !important;
+ }
+
+ /* Hero items also visible immediately */
+ .hero__content > * {
+ opacity: 1 !important;
+ }
+}
diff --git a/web/css/style.css b/web/css/style.css
new file mode 100644
index 0000000..57f6505
--- /dev/null
+++ b/web/css/style.css
@@ -0,0 +1,3026 @@
+/* ============================================================
+ ApolloNvim – style.css
+ Web: apollo-nvim.lukan.cz
+ Design: Dark space / cosmic theme inspired by Dracula
+ ============================================================ */
+
+/* ────────────────────────────────────────────────────────────
+ 1. CSS CUSTOM PROPERTIES (Design Tokens)
+ ──────────────────────────────────────────────────────────── */
+:root {
+ /* Backgrounds */
+ --bg-primary: #0d0d1a;
+ --bg-secondary: #12122a;
+ --bg-card: #1e1e3a;
+ --bg-nav: rgba(13, 13, 26, 0.95);
+
+ /* Brand colours */
+ --purple: #bd93f9;
+ --purple-dark: #9b6fd4;
+ --pink: #ff79c6;
+ --green: #50fa7b;
+ --cyan: #8be9fd;
+ --orange: #ffb86c;
+ --yellow: #f1fa8c;
+
+ /* Text */
+ --text: #f8f8f2;
+ --text-muted: #6272a4;
+ --text-secondary: #cdd6f4;
+
+ /* Accents */
+ --border: rgba(189, 147, 249, 0.2);
+ --glow: rgba(189, 147, 249, 0.3);
+
+ /* Spacing */
+ --nav-height: 70px;
+ --section-pad: 80px 20px;
+ --max-width: 1200px;
+ --radius: 12px;
+ --radius-sm: 8px;
+
+ /* Transitions */
+ --transition: all 0.3s ease;
+ --transition-slow: all 0.6s ease;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 2. RESET & BASE
+ ──────────────────────────────────────────────────────────── */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ scroll-behavior: smooth;
+ font-size: 16px;
+ -webkit-text-size-adjust: 100%;
+}
+
+body {
+ background-color: var(--bg-primary);
+ color: var(--text);
+ font-family:
+ "Inter",
+ system-ui,
+ -apple-system,
+ sans-serif;
+ line-height: 1.6;
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+
+img,
+svg,
+video {
+ display: block;
+ max-width: 100%;
+}
+
+a {
+ color: var(--purple);
+ text-decoration: none;
+ transition: var(--transition);
+}
+
+a:hover {
+ color: var(--pink);
+}
+
+ul,
+ol {
+ list-style: none;
+}
+
+button,
+input,
+select,
+textarea {
+ font: inherit;
+ border: none;
+ outline: none;
+ background: transparent;
+}
+
+button {
+ cursor: pointer;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 3. CUSTOM SCROLLBAR
+ ──────────────────────────────────────────────────────────── */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: var(--bg-secondary);
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--purple-dark);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--purple);
+}
+
+/* Firefox */
+* {
+ scrollbar-width: thin;
+ scrollbar-color: var(--purple-dark) var(--bg-secondary);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 4. TYPOGRAPHY
+ ──────────────────────────────────────────────────────────── */
+/* Google Fonts import expected in HTML: Inter + JetBrains Mono */
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: 700;
+ line-height: 1.2;
+ letter-spacing: -0.01em;
+}
+
+h1 {
+ font-size: clamp(2.4rem, 6vw, 4.5rem);
+}
+h2 {
+ font-size: clamp(1.8rem, 4vw, 2.8rem);
+}
+h3 {
+ font-size: clamp(1.3rem, 3vw, 1.8rem);
+}
+h4 {
+ font-size: 1.25rem;
+}
+h5 {
+ font-size: 1.1rem;
+}
+h6 {
+ font-size: 1rem;
+}
+
+.gradient-text {
+ background: linear-gradient(135deg, var(--purple), var(--pink));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+p {
+ color: var(--text-secondary);
+ max-width: 68ch;
+}
+
+code,
+kbd,
+pre,
+samp {
+ font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
+ font-size: 0.9em;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 5. NAVIGATION
+ ──────────────────────────────────────────────────────────── */
+.navbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ height: var(--nav-height);
+ background: var(--bg-nav);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ align-items: center;
+}
+
+.navbar__inner {
+ width: 100%;
+ max-width: var(--max-width);
+ margin: 0 auto;
+ padding: 0 24px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+}
+
+/* Logo */
+.navbar__logo {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ font-family: "JetBrains Mono", monospace;
+ font-size: 1.15rem;
+ font-weight: 700;
+ color: var(--text);
+ white-space: nowrap;
+ text-decoration: none;
+}
+
+.navbar__logo:hover {
+ color: var(--purple);
+}
+
+.navbar__logo-icon {
+ font-size: 1.5rem;
+ line-height: 1;
+}
+
+.navbar__logo-text span {
+ color: var(--purple);
+}
+
+/* Nav links */
+.navbar__links {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex-wrap: nowrap;
+}
+
+.navbar__link {
+ position: relative;
+ padding: 6px 14px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ color: var(--text-secondary);
+ border-radius: 6px;
+ white-space: nowrap;
+ transition: var(--transition);
+}
+
+.navbar__link::after {
+ content: "";
+ position: absolute;
+ bottom: 2px;
+ left: 14px;
+ right: 14px;
+ height: 2px;
+ background: linear-gradient(90deg, var(--purple), var(--pink));
+ border-radius: 1px;
+ transform: scaleX(0);
+ transform-origin: left;
+ transition: transform 0.3s ease;
+}
+
+.navbar__link:hover {
+ color: var(--text);
+}
+
+.navbar__link:hover::after,
+.navbar__link.active::after {
+ transform: scaleX(1);
+}
+
+.navbar__link.active {
+ color: var(--purple);
+}
+
+/* CTA in nav */
+.navbar__cta {
+ padding: 8px 20px;
+ background: linear-gradient(135deg, var(--purple), var(--pink));
+ color: var(--bg-primary) !important;
+ font-weight: 700;
+ border-radius: 8px;
+ transition: var(--transition);
+}
+
+.navbar__cta::after {
+ display: none;
+}
+
+.navbar__cta:hover {
+ opacity: 0.85;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 20px var(--glow);
+}
+
+/* Hamburger */
+.navbar__hamburger {
+ display: none;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 40px;
+ height: 40px;
+ gap: 6px;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 6px;
+ transition: var(--transition);
+}
+
+.navbar__hamburger:hover {
+ background: var(--border);
+}
+
+.navbar__hamburger span {
+ display: block;
+ width: 24px;
+ height: 2px;
+ background: var(--text);
+ border-radius: 2px;
+ transition: var(--transition);
+ transform-origin: center;
+}
+
+/* Hamburger → X */
+.navbar__hamburger.open span:nth-child(1) {
+ transform: translateY(8px) rotate(45deg);
+}
+
+.navbar__hamburger.open span:nth-child(2) {
+ opacity: 0;
+ transform: scaleX(0);
+}
+
+.navbar__hamburger.open span:nth-child(3) {
+ transform: translateY(-8px) rotate(-45deg);
+}
+
+/* Mobile menu */
+.navbar__mobile-menu {
+ display: none;
+ position: fixed;
+ top: var(--nav-height);
+ left: 0;
+ right: 0;
+ background: var(--bg-nav);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border-bottom: 1px solid var(--border);
+ padding: 16px 24px 24px;
+ flex-direction: column;
+ gap: 4px;
+ z-index: 999;
+ transform: translateY(-8px);
+ opacity: 0;
+ transition:
+ opacity 0.25s ease,
+ transform 0.25s ease;
+ pointer-events: none;
+}
+
+.navbar__mobile-menu.open {
+ display: flex;
+ opacity: 1;
+ transform: translateY(0);
+ pointer-events: all;
+}
+
+.navbar__mobile-menu .navbar__link {
+ padding: 10px 14px;
+ font-size: 1rem;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 6. HERO SECTION
+ ──────────────────────────────────────────────────────────── */
+.hero {
+ position: relative;
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: calc(var(--nav-height) + 40px) 20px 80px;
+ overflow: hidden;
+}
+
+/* Particle canvas */
+#particles-canvas {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 0;
+}
+
+/* Subtle radial gradient overlay */
+.hero::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(
+ ellipse 80% 60% at 50% 40%,
+ rgba(189, 147, 249, 0.08) 0%,
+ transparent 70%
+ ),
+ radial-gradient(
+ ellipse 50% 50% at 20% 80%,
+ rgba(255, 121, 198, 0.05) 0%,
+ transparent 60%
+ );
+ pointer-events: none;
+ z-index: 0;
+}
+
+.hero__content {
+ position: relative;
+ z-index: 1;
+ max-width: 860px;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 28px;
+}
+
+.hero__badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 18px;
+ background: rgba(189, 147, 249, 0.12);
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ font-size: 0.85rem;
+ font-family: "JetBrains Mono", monospace;
+ color: var(--purple);
+ letter-spacing: 0.03em;
+}
+
+.hero__title {
+ font-size: clamp(2.8rem, 8vw, 5.5rem);
+ font-weight: 800;
+ line-height: 1.08;
+ letter-spacing: -0.03em;
+}
+
+.hero__title .gradient-text {
+ display: block;
+}
+
+.hero__subtitle {
+ font-size: clamp(1rem, 2.5vw, 1.25rem);
+ color: var(--text-secondary);
+ max-width: 56ch;
+ text-align: center;
+}
+
+/* CTA buttons */
+.hero__actions {
+ display: flex;
+ gap: 16px;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+/* Scroll indicator */
+.hero__scroll {
+ position: absolute;
+ bottom: 36px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ z-index: 1;
+}
+
+.hero__scroll-arrow {
+ width: 24px;
+ height: 24px;
+ border-right: 2px solid var(--purple);
+ border-bottom: 2px solid var(--purple);
+ transform: rotate(45deg);
+ animation: hero-scroll-bounce 1.8s ease-in-out infinite;
+ opacity: 0.7;
+}
+
+@keyframes hero-scroll-bounce {
+ 0%,
+ 100% {
+ transform: rotate(45deg) translateY(-4px);
+ opacity: 0.4;
+ }
+ 50% {
+ transform: rotate(45deg) translateY(4px);
+ opacity: 0.9;
+ }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 7. UTILITY BUTTONS
+ ──────────────────────────────────────────────────────────── */
+.btn-primary {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 13px 28px;
+ background: linear-gradient(135deg, var(--purple), var(--pink));
+ color: #0d0d1a;
+ font-weight: 700;
+ font-size: 0.95rem;
+ border-radius: var(--radius-sm);
+ border: none;
+ cursor: pointer;
+ transition: var(--transition);
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+.btn-primary:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 8px 32px rgba(189, 147, 249, 0.4);
+ opacity: 0.92;
+ color: #0d0d1a;
+}
+
+.btn-primary:active {
+ transform: translateY(-1px);
+}
+
+.btn-secondary {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 12px 28px;
+ background: transparent;
+ color: var(--text);
+ font-weight: 600;
+ font-size: 0.95rem;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--border);
+ cursor: pointer;
+ transition: var(--transition);
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+.btn-secondary:hover {
+ border-color: var(--purple);
+ color: var(--purple);
+ background: rgba(189, 147, 249, 0.07);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 16px rgba(189, 147, 249, 0.15);
+}
+
+.btn-secondary:active {
+ transform: translateY(0);
+}
+
+/* Icon buttons */
+.btn-icon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ border-radius: 8px;
+ border: 1px solid var(--border);
+ color: var(--text-secondary);
+ transition: var(--transition);
+}
+
+.btn-icon:hover {
+ border-color: var(--purple);
+ color: var(--purple);
+ background: rgba(189, 147, 249, 0.07);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 8. SECTIONS
+ ──────────────────────────────────────────────────────────── */
+.section {
+ padding: var(--section-pad);
+}
+
+.section--alt {
+ background: var(--bg-secondary);
+}
+
+.section__inner {
+ max-width: var(--max-width);
+ margin: 0 auto;
+}
+
+/* Section title with gradient underline */
+.section-title {
+ font-size: clamp(1.7rem, 4vw, 2.5rem);
+ font-weight: 700;
+ margin-bottom: 12px;
+}
+
+.section-title__line {
+ display: block;
+ width: 60px;
+ height: 3px;
+ background: linear-gradient(90deg, var(--purple), var(--pink));
+ border-radius: 2px;
+ margin-top: 12px;
+}
+
+.section-title--center {
+ text-align: center;
+}
+
+.section-title--center .section-title__line {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.section__subtitle {
+ font-size: 1.05rem;
+ color: var(--text-secondary);
+ margin-top: 12px;
+ max-width: 60ch;
+}
+
+.section__subtitle--center {
+ text-align: center;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 9. CARDS
+ ──────────────────────────────────────────────────────────── */
+.card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 28px;
+ transition: var(--transition);
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
+ position: relative;
+ overflow: hidden;
+}
+
+.card::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 135deg,
+ rgba(189, 147, 249, 0.04),
+ transparent 60%
+ );
+ pointer-events: none;
+ border-radius: inherit;
+}
+
+.card:hover {
+ transform: translateY(-4px);
+ border-color: rgba(189, 147, 249, 0.45);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.4),
+ 0 0 0 1px rgba(189, 147, 249, 0.15),
+ 0 0 40px rgba(189, 147, 249, 0.12);
+}
+
+.card__title {
+ font-size: 1.15rem;
+ font-weight: 700;
+ margin-bottom: 10px;
+ color: var(--text);
+}
+
+.card__body {
+ color: var(--text-secondary);
+ font-size: 0.95rem;
+ line-height: 1.65;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 10. FEATURE GRID
+ ──────────────────────────────────────────────────────────── */
+.feature-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 24px;
+ margin-top: 48px;
+}
+
+.feature-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 32px 28px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ transition: var(--transition);
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.25);
+ position: relative;
+ overflow: hidden;
+}
+
+.feature-card::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, var(--purple), var(--pink));
+ border-radius: var(--radius) var(--radius) 0 0;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.feature-card:hover {
+ transform: translateY(-4px);
+ border-color: rgba(189, 147, 249, 0.4);
+ box-shadow:
+ 0 12px 40px rgba(0, 0, 0, 0.35),
+ 0 0 40px rgba(189, 147, 249, 0.1);
+}
+
+.feature-card:hover::before {
+ opacity: 1;
+}
+
+/* Feature icon */
+.feature-icon {
+ width: 52px;
+ height: 52px;
+ border-radius: 50%;
+ background: linear-gradient(
+ 135deg,
+ rgba(189, 147, 249, 0.2),
+ rgba(255, 121, 198, 0.1)
+ );
+ border: 1px solid rgba(189, 147, 249, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.4rem;
+ flex-shrink: 0;
+}
+
+.feature-card__title {
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--text);
+}
+
+.feature-card__body {
+ color: var(--text-secondary);
+ font-size: 0.92rem;
+ line-height: 1.65;
+ flex: 1;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 11. CODE / TERMINAL BLOCKS
+ ──────────────────────────────────────────────────────────── */
+.terminal-block {
+ background: #0a0a16;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ overflow: hidden;
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.875rem;
+ line-height: 1.7;
+}
+
+/* Terminal header bar */
+.terminal-block__header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 16px;
+ background: rgba(255, 255, 255, 0.03);
+ border-bottom: 1px solid var(--border);
+}
+
+.terminal-block__dot {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.terminal-block__dot--red {
+ background: #ff5f57;
+}
+.terminal-block__dot--yellow {
+ background: #febc2e;
+}
+.terminal-block__dot--green {
+ background: #28c840;
+}
+
+.terminal-block__title {
+ margin-left: 8px;
+ font-size: 0.78rem;
+ color: var(--text-muted);
+ letter-spacing: 0.05em;
+}
+
+.terminal-block__body {
+ padding: 20px 24px;
+ overflow-x: auto;
+}
+
+.terminal-block__body::-webkit-scrollbar {
+ height: 4px;
+}
+
+/* Syntax highlighting */
+.token-prompt {
+ color: var(--purple);
+}
+.token-cmd {
+ color: var(--green);
+}
+.token-flag {
+ color: var(--cyan);
+}
+.token-string {
+ color: var(--yellow);
+}
+.token-comment {
+ color: var(--text-muted);
+ font-style: italic;
+}
+.token-keyword {
+ color: var(--pink);
+}
+.token-number {
+ color: var(--orange);
+}
+.token-output {
+ color: var(--text-secondary);
+}
+
+/* Inline code */
+:not(.terminal-block) code {
+ background: rgba(189, 147, 249, 0.12);
+ border: 1px solid rgba(189, 147, 249, 0.2);
+ border-radius: 4px;
+ padding: 2px 7px;
+ font-size: 0.85em;
+ color: var(--purple);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 12. TABS (CZ / EN language switcher)
+ ──────────────────────────────────────────────────────────── */
+.tabs {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.tabs__nav {
+ display: flex;
+ gap: 0;
+ border-bottom: 1px solid var(--border);
+ overflow-x: auto;
+}
+
+.tabs__nav::-webkit-scrollbar {
+ height: 0;
+}
+
+.tabs__btn {
+ padding: 11px 22px;
+ font-size: 0.9rem;
+ font-weight: 600;
+ color: var(--text-muted);
+ background: transparent;
+ border: none;
+ border-bottom: 2px solid transparent;
+ margin-bottom: -1px;
+ cursor: pointer;
+ transition: var(--transition);
+ white-space: nowrap;
+ letter-spacing: 0.02em;
+}
+
+.tabs__btn:hover {
+ color: var(--text);
+}
+
+.tabs__btn.active {
+ color: var(--purple);
+ border-bottom-color: var(--purple);
+ background: rgba(189, 147, 249, 0.05);
+}
+
+.tabs__content {
+ padding-top: 24px;
+}
+
+.tabs__panel {
+ display: none;
+ animation: tabFadeIn 0.3s ease;
+}
+
+.tabs__panel.active {
+ display: block;
+}
+
+@keyframes tabFadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(6px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 13. PLUGIN LIST
+ ──────────────────────────────────────────────────────────── */
+.plugin-list {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-top: 24px;
+}
+
+.plugin-item {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding: 14px 18px;
+ border-radius: var(--radius-sm);
+ border: 1px solid transparent;
+ transition: var(--transition);
+ cursor: default;
+}
+
+.plugin-item:hover {
+ background: rgba(189, 147, 249, 0.06);
+ border-color: var(--border);
+}
+
+.plugin-item__icon {
+ font-size: 1.2rem;
+ flex-shrink: 0;
+ width: 28px;
+ text-align: center;
+}
+
+.plugin-item__name {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.9rem;
+ font-weight: 600;
+ color: var(--cyan);
+ min-width: 200px;
+ flex-shrink: 0;
+}
+
+.plugin-item__desc {
+ font-size: 0.88rem;
+ color: var(--text-secondary);
+ flex: 1;
+}
+
+.plugin-item__badge {
+ flex-shrink: 0;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 14. TIMELINE (changelog / novinky)
+ ──────────────────────────────────────────────────────────── */
+.timeline {
+ position: relative;
+ padding-left: 36px;
+ margin-top: 40px;
+}
+
+/* Vertical line */
+.timeline::before {
+ content: "";
+ position: absolute;
+ top: 8px;
+ bottom: 8px;
+ left: 10px;
+ width: 2px;
+ background: linear-gradient(
+ 180deg,
+ var(--purple),
+ rgba(189, 147, 249, 0.1)
+ );
+ border-radius: 2px;
+}
+
+.timeline__item {
+ position: relative;
+ margin-bottom: 36px;
+}
+
+/* Dot */
+.timeline__item::before {
+ content: "";
+ position: absolute;
+ left: -31px;
+ top: 8px;
+ width: 14px;
+ height: 14px;
+ background: var(--purple);
+ border: 3px solid var(--bg-primary);
+ border-radius: 50%;
+ box-shadow: 0 0 12px var(--glow);
+ transition: var(--transition);
+}
+
+.timeline__item:hover::before {
+ transform: scale(1.3);
+ box-shadow: 0 0 20px rgba(189, 147, 249, 0.6);
+}
+
+.timeline__header {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex-wrap: wrap;
+ margin-bottom: 8px;
+}
+
+.timeline__version {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.8rem;
+ font-weight: 700;
+ padding: 3px 10px;
+ background: linear-gradient(135deg, var(--purple), var(--purple-dark));
+ color: var(--bg-primary);
+ border-radius: 999px;
+}
+
+.timeline__date {
+ font-size: 0.82rem;
+ color: var(--text-muted);
+}
+
+.timeline__title {
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--text);
+ margin-bottom: 8px;
+}
+
+.timeline__body {
+ font-size: 0.92rem;
+ color: var(--text-secondary);
+ line-height: 1.65;
+}
+
+.timeline__body ul {
+ margin-top: 8px;
+ padding-left: 18px;
+ list-style: disc;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 15. KEYBOARD SHORTCUTS TABLE
+ ──────────────────────────────────────────────────────────── */
+.shortcuts-table-wrap {
+ overflow-x: auto;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--border);
+ margin-top: 28px;
+}
+
+.shortcuts-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.9rem;
+}
+
+.shortcuts-table thead {
+ background: rgba(189, 147, 249, 0.08);
+}
+
+.shortcuts-table th {
+ padding: 13px 20px;
+ text-align: left;
+ font-size: 0.8rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--purple);
+ border-bottom: 1px solid var(--border);
+ white-space: nowrap;
+}
+
+.shortcuts-table td {
+ padding: 12px 20px;
+ color: var(--text-secondary);
+ border-bottom: 1px solid rgba(189, 147, 249, 0.07);
+ vertical-align: middle;
+}
+
+.shortcuts-table tr:last-child td {
+ border-bottom: none;
+}
+
+.shortcuts-table tbody tr {
+ transition: background 0.2s ease;
+}
+
+.shortcuts-table tbody tr:hover {
+ background: rgba(189, 147, 249, 0.05);
+}
+
+/* Key badge */
+kbd,
+.key {
+ display: inline-flex;
+ align-items: center;
+ padding: 3px 8px;
+ background: rgba(255, 255, 255, 0.06);
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ border-bottom-width: 2px;
+ border-radius: 5px;
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.8rem;
+ color: var(--text);
+ line-height: 1.4;
+ white-space: nowrap;
+}
+
+.shortcut-keys {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ flex-wrap: wrap;
+}
+
+.shortcut-keys .key-sep {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 16. BADGES & PILLS
+ ──────────────────────────────────────────────────────────── */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 3px 10px;
+ border-radius: 999px;
+ font-size: 0.78rem;
+ font-weight: 600;
+ letter-spacing: 0.03em;
+ line-height: 1.4;
+ white-space: nowrap;
+}
+
+.badge--purple {
+ background: rgba(189, 147, 249, 0.15);
+ border: 1px solid rgba(189, 147, 249, 0.3);
+ color: var(--purple);
+}
+
+.badge--green {
+ background: rgba(80, 250, 123, 0.12);
+ border: 1px solid rgba(80, 250, 123, 0.25);
+ color: var(--green);
+}
+
+.badge--cyan {
+ background: rgba(139, 233, 253, 0.12);
+ border: 1px solid rgba(139, 233, 253, 0.25);
+ color: var(--cyan);
+}
+
+.badge--pink {
+ background: rgba(255, 121, 198, 0.12);
+ border: 1px solid rgba(255, 121, 198, 0.25);
+ color: var(--pink);
+}
+
+.badge--orange {
+ background: rgba(255, 184, 108, 0.12);
+ border: 1px solid rgba(255, 184, 108, 0.25);
+ color: var(--orange);
+}
+
+.badge--yellow {
+ background: rgba(241, 250, 140, 0.12);
+ border: 1px solid rgba(241, 250, 140, 0.25);
+ color: var(--yellow);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 17. FOOTER
+ ──────────────────────────────────────────────────────────── */
+.footer {
+ background: var(--bg-secondary);
+ border-top: 1px solid var(--border);
+ padding: 60px 20px 32px;
+}
+
+.footer__inner {
+ max-width: var(--max-width);
+ margin: 0 auto;
+}
+
+.footer__top {
+ display: grid;
+ grid-template-columns: 1.5fr repeat(3, 1fr);
+ gap: 48px;
+ padding-bottom: 48px;
+ border-bottom: 1px solid var(--border);
+}
+
+.footer__brand-name {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--text);
+ margin-bottom: 12px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.footer__brand-desc {
+ font-size: 0.9rem;
+ color: var(--text-muted);
+ line-height: 1.65;
+ max-width: 28ch;
+}
+
+.footer__col-title {
+ font-size: 0.8rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-muted);
+ margin-bottom: 16px;
+}
+
+.footer__links {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.footer__link {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ transition: var(--transition);
+}
+
+.footer__link:hover {
+ color: var(--purple);
+ transform: translateX(3px);
+}
+
+.footer__bottom {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-top: 28px;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.footer__copy {
+ font-size: 0.85rem;
+ color: var(--text-muted);
+}
+
+.footer__copy a {
+ color: var(--purple);
+}
+
+.footer__bottom-links {
+ display: flex;
+ gap: 20px;
+}
+
+.footer__bottom-link {
+ font-size: 0.85rem;
+ color: var(--text-muted);
+ transition: var(--transition);
+}
+
+.footer__bottom-link:hover {
+ color: var(--purple);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 18. UTILITY CLASSES
+ ──────────────────────────────────────────────────────────── */
+/* Text colour helpers */
+.text-purple {
+ color: var(--purple) !important;
+}
+.text-green {
+ color: var(--green) !important;
+}
+.text-cyan {
+ color: var(--cyan) !important;
+}
+.text-pink {
+ color: var(--pink) !important;
+}
+.text-orange {
+ color: var(--orange) !important;
+}
+.text-yellow {
+ color: var(--yellow) !important;
+}
+.text-muted {
+ color: var(--text-muted) !important;
+}
+
+/* Display */
+.d-flex {
+ display: flex;
+}
+.d-grid {
+ display: grid;
+}
+.d-none {
+ display: none;
+}
+.d-block {
+ display: block;
+}
+
+/* Flex helpers */
+.align-center {
+ align-items: center;
+}
+.justify-center {
+ justify-content: center;
+}
+.justify-between {
+ justify-content: space-between;
+}
+.flex-wrap {
+ flex-wrap: wrap;
+}
+.gap-8 {
+ gap: 8px;
+}
+.gap-16 {
+ gap: 16px;
+}
+.gap-24 {
+ gap: 24px;
+}
+
+/* Text alignment */
+.text-center {
+ text-align: center;
+}
+.text-left {
+ text-align: left;
+}
+.text-right {
+ text-align: right;
+}
+
+/* Spacing */
+.mt-8 {
+ margin-top: 8px;
+}
+.mt-16 {
+ margin-top: 16px;
+}
+.mt-24 {
+ margin-top: 24px;
+}
+.mt-48 {
+ margin-top: 48px;
+}
+.mb-8 {
+ margin-bottom: 8px;
+}
+.mb-16 {
+ margin-bottom: 16px;
+}
+.mb-24 {
+ margin-bottom: 24px;
+}
+.mb-48 {
+ margin-bottom: 48px;
+}
+
+/* Visibility (JS-toggled) */
+.fade-in {
+ animation: fadeIn 0.5s ease both;
+}
+.slide-up {
+ animation: slideUp 0.5s ease both;
+}
+
+/* Glow border */
+.glow-border {
+ border-color: var(--purple) !important;
+ box-shadow:
+ 0 0 0 1px var(--glow),
+ 0 0 24px var(--glow);
+}
+
+/* Divider */
+.divider {
+ height: 1px;
+ background: var(--border);
+ margin: 40px 0;
+ border: none;
+}
+
+/* Monospace span */
+.mono {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.88em;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 19. LOADING SCREEN
+ ──────────────────────────────────────────────────────────── */
+.loading-screen {
+ position: fixed;
+ inset: 0;
+ z-index: 9999;
+ background: var(--bg-primary);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 24px;
+ transition:
+ opacity 0.5s ease,
+ visibility 0.5s ease;
+}
+
+.loading-screen.hidden {
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+}
+
+.loading-screen__logo {
+ font-size: 4rem;
+ animation: spin-slow 3s linear infinite;
+}
+
+.loading-screen__text {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 1rem;
+ color: var(--purple);
+ letter-spacing: 0.1em;
+}
+
+.loading-screen__bar {
+ width: 200px;
+ height: 2px;
+ background: rgba(189, 147, 249, 0.15);
+ border-radius: 1px;
+ overflow: hidden;
+}
+
+.loading-screen__bar-fill {
+ height: 100%;
+ width: 0%;
+ background: linear-gradient(90deg, var(--purple), var(--pink));
+ border-radius: 1px;
+ animation: loading-fill 1.5s ease forwards;
+}
+
+@keyframes loading-fill {
+ to {
+ width: 100%;
+ }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 20. RESPONSIVE – TABLET (≤ 768px)
+ ──────────────────────────────────────────────────────────── */
+@media (max-width: 768px) {
+ :root {
+ --section-pad: 60px 16px;
+ }
+
+ /* Nav */
+ .navbar__links {
+ display: none;
+ }
+
+ .navbar__hamburger {
+ display: flex;
+ }
+
+ /* Hero */
+ .hero__actions {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ /* Feature grid */
+ .feature-grid {
+ grid-template-columns: 1fr;
+ gap: 16px;
+ }
+
+ /* Plugin list */
+ .plugin-item {
+ flex-wrap: wrap;
+ }
+
+ .plugin-item__name {
+ min-width: unset;
+ }
+
+ /* Footer */
+ .footer__top {
+ grid-template-columns: 1fr 1fr;
+ gap: 32px;
+ }
+
+ /* Table */
+ .shortcuts-table th,
+ .shortcuts-table td {
+ padding: 10px 14px;
+ }
+
+ /* Timeline */
+ .timeline {
+ padding-left: 28px;
+ }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 21. RESPONSIVE – MOBILE (≤ 480px)
+ ──────────────────────────────────────────────────────────── */
+@media (max-width: 480px) {
+ :root {
+ --section-pad: 48px 16px;
+ }
+
+ .hero {
+ padding-bottom: 60px;
+ }
+
+ .hero__title {
+ font-size: 2.4rem;
+ }
+
+ /* Footer */
+ .footer__top {
+ grid-template-columns: 1fr;
+ gap: 28px;
+ }
+
+ .footer__bottom {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .footer__bottom-links {
+ gap: 12px;
+ }
+
+ /* Buttons */
+ .btn-primary,
+ .btn-secondary {
+ width: 100%;
+ justify-content: center;
+ }
+
+ /* Tabs */
+ .tabs__btn {
+ padding: 10px 14px;
+ font-size: 0.85rem;
+ }
+
+ /* Timeline */
+ .timeline {
+ padding-left: 24px;
+ }
+
+ .timeline__item::before {
+ left: -27px;
+ }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 22. ACCESSIBILITY & REDUCED MOTION
+ ──────────────────────────────────────────────────────────── */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+
+ html {
+ scroll-behavior: auto;
+ }
+}
+
+/* Focus visible */
+:focus-visible {
+ outline: 2px solid var(--purple);
+ outline-offset: 3px;
+ border-radius: 4px;
+}
+
+/* High contrast selection */
+::selection {
+ background: rgba(189, 147, 249, 0.3);
+ color: var(--text);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 23. CONTAINER HELPER
+ ──────────────────────────────────────────────────────────── */
+.container {
+ max-width: var(--max-width);
+ margin: 0 auto;
+ padding: 0 24px;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 24. PAGE HERO (sub-pages – no particles canvas)
+ ──────────────────────────────────────────────────────────── */
+.page-hero {
+ padding: calc(var(--nav-height) + 60px) 20px 60px;
+ text-align: center;
+ background:
+ radial-gradient(
+ ellipse 80% 60% at 50% 40%,
+ rgba(189, 147, 249, 0.1) 0%,
+ transparent 70%
+ ),
+ var(--bg-primary);
+ position: relative;
+ overflow: hidden;
+}
+
+.page-hero::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 180deg,
+ transparent 60%,
+ var(--bg-primary) 100%
+ );
+ pointer-events: none;
+}
+
+.page-hero__content {
+ position: relative;
+ z-index: 1;
+ max-width: 720px;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 18px;
+}
+
+.page-hero__title {
+ font-size: clamp(2.2rem, 6vw, 3.8rem);
+ font-weight: 800;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ background: linear-gradient(135deg, var(--text) 30%, var(--purple));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.page-hero__subtitle {
+ font-size: 1.05rem;
+ color: var(--text-secondary);
+ max-width: 52ch;
+ text-align: center;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 25. NOTICE BOXES (warning, info)
+ ──────────────────────────────────────────────────────────── */
+.warning-box,
+.info-box {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+ padding: 20px 24px;
+ border-radius: var(--radius-sm);
+ font-size: 0.94rem;
+ line-height: 1.65;
+}
+
+.warning-box {
+ background: rgba(255, 184, 108, 0.07);
+ border: 1px solid rgba(255, 184, 108, 0.28);
+ color: var(--orange);
+}
+
+.info-box {
+ background: rgba(139, 233, 253, 0.07);
+ border: 1px solid rgba(139, 233, 253, 0.24);
+ color: var(--text-secondary);
+}
+
+.warning-box strong,
+.info-box strong {
+ display: block;
+ margin-bottom: 4px;
+ font-size: 1rem;
+ color: inherit;
+}
+
+.warning-box p,
+.info-box p {
+ margin: 0;
+ color: inherit;
+ max-width: none;
+}
+
+.warning-box__icon {
+ font-size: 1.5rem;
+ flex-shrink: 0;
+ line-height: 1;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 26. INSTALLATION PAGE COMPONENTS
+ ──────────────────────────────────────────────────────────── */
+.install-section {
+ margin-bottom: 48px;
+}
+
+.install-desc {
+ color: var(--text-secondary);
+ font-size: 0.97rem;
+ margin: 12px 0 24px;
+ max-width: 62ch;
+}
+
+/* Prerequisite cards */
+.prereq-card {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+ padding: 24px;
+}
+
+.prereq-card__icon {
+ width: 36px;
+ height: 36px;
+ min-width: 36px;
+ border-radius: 50%;
+ background: linear-gradient(
+ 135deg,
+ rgba(189, 147, 249, 0.2),
+ rgba(255, 121, 198, 0.1)
+ );
+ border: 1px solid rgba(189, 147, 249, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ color: var(--purple);
+ font-size: 0.85rem;
+}
+
+/* Steps */
+.steps {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ position: relative;
+}
+
+.steps::before {
+ content: "";
+ position: absolute;
+ left: 22px;
+ top: 44px;
+ bottom: 44px;
+ width: 2px;
+ background: linear-gradient(
+ 180deg,
+ var(--purple),
+ rgba(189, 147, 249, 0.05)
+ );
+ border-radius: 2px;
+ z-index: 0;
+}
+
+.step-card {
+ display: flex;
+ gap: 20px;
+ position: relative;
+ z-index: 1;
+ padding-bottom: 32px;
+}
+
+.step-card:last-child {
+ padding-bottom: 0;
+}
+
+.step-card__number {
+ flex-shrink: 0;
+ width: 44px;
+ height: 44px;
+ min-width: 44px;
+ border-radius: 50%;
+ background: var(--bg-card);
+ border: 2px solid var(--border);
+ color: var(--purple);
+ font-family: "JetBrains Mono", monospace;
+ font-weight: 700;
+ font-size: 0.85rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: var(--transition);
+}
+
+.step-card:hover .step-card__number {
+ border-color: var(--purple);
+ background: rgba(189, 147, 249, 0.1);
+ box-shadow: 0 0 16px rgba(189, 147, 249, 0.25);
+}
+
+.step-card__content {
+ flex: 1;
+ padding-top: 8px;
+}
+
+.step-card__content h3 {
+ font-size: 1.05rem;
+ font-weight: 700;
+ margin-bottom: 10px;
+ color: var(--text);
+}
+
+.step-card__content p {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ margin-bottom: 12px;
+ max-width: none;
+}
+
+.step-card__content .terminal-block {
+ margin-top: 10px;
+}
+
+/* FAQ */
+.faq-list {
+ max-width: 800px;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.faq-item {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ overflow: hidden;
+ transition:
+ border-color 0.3s ease,
+ box-shadow 0.3s ease;
+}
+
+.faq-item[open] {
+ border-color: rgba(189, 147, 249, 0.4);
+ box-shadow: 0 4px 20px rgba(189, 147, 249, 0.08);
+}
+
+.faq-item summary {
+ padding: 18px 22px;
+ font-size: 0.96rem;
+ font-weight: 600;
+ cursor: pointer;
+ list-style: none;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ color: var(--text);
+ user-select: none;
+ transition: color 0.2s ease;
+}
+
+.faq-item summary::-webkit-details-marker {
+ display: none;
+}
+
+.faq-item summary::after {
+ content: "+";
+ font-size: 1.3rem;
+ font-weight: 300;
+ color: var(--purple);
+ flex-shrink: 0;
+ transition: transform 0.3s ease;
+}
+
+.faq-item[open] summary::after {
+ content: "−";
+}
+
+.faq-item summary:hover {
+ color: var(--purple);
+}
+
+.faq-item p {
+ padding: 0 22px 18px;
+ border-top: 1px solid var(--border);
+ padding-top: 16px;
+ font-size: 0.93rem;
+ color: var(--text-secondary);
+ line-height: 1.72;
+ max-width: none;
+}
+
+.faq-item p a {
+ color: var(--purple);
+}
+.faq-item p a:hover {
+ color: var(--pink);
+}
+
+.faq-item code {
+ background: rgba(189, 147, 249, 0.1);
+ border: 1px solid rgba(189, 147, 249, 0.18);
+ border-radius: 4px;
+ padding: 1px 6px;
+ font-size: 0.85em;
+ color: var(--purple);
+ font-family: "JetBrains Mono", monospace;
+}
+
+/* Tabs variant (tab-btn / tab-content) */
+.tabs__buttons {
+ display: flex;
+ gap: 0;
+ border-bottom: 1px solid var(--border);
+ margin-bottom: 24px;
+ overflow-x: auto;
+}
+
+.tab-btn {
+ padding: 11px 22px;
+ font-size: 0.9rem;
+ font-weight: 600;
+ color: var(--text-muted);
+ background: transparent;
+ border: none;
+ border-bottom: 2px solid transparent;
+ margin-bottom: -1px;
+ cursor: pointer;
+ transition: var(--transition);
+ white-space: nowrap;
+ letter-spacing: 0.02em;
+}
+
+.tab-btn:hover {
+ color: var(--text);
+}
+
+.tab-btn.active {
+ color: var(--purple);
+ border-bottom-color: var(--purple);
+ background: rgba(189, 147, 249, 0.05);
+}
+
+.tab-content {
+ display: none;
+ animation: tabFadeIn 0.3s ease;
+}
+
+.tab-content.active {
+ display: block;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 27. PLUGIN PAGE COMPONENTS
+ ──────────────────────────────────────────────────────────── */
+/* Filter buttons */
+.filter-btns {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ padding: 24px 0;
+}
+
+.filter-btn {
+ padding: 7px 16px;
+ border-radius: 999px;
+ font-size: 0.82rem;
+ font-weight: 600;
+ border: 1px solid var(--border);
+ color: var(--text-muted);
+ background: transparent;
+ cursor: pointer;
+ transition: var(--transition);
+ letter-spacing: 0.02em;
+}
+
+.filter-btn:hover {
+ color: var(--text);
+ border-color: rgba(189, 147, 249, 0.5);
+ background: rgba(189, 147, 249, 0.07);
+}
+
+.filter-btn.active {
+ background: rgba(189, 147, 249, 0.15);
+ border-color: var(--purple);
+ color: var(--purple);
+}
+
+/* Plugin cards grid */
+.plugins-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
+ gap: 20px;
+ margin-top: 24px;
+}
+
+.plugin-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 22px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ transition: var(--transition);
+ position: relative;
+ overflow: hidden;
+}
+
+.plugin-card::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 135deg,
+ rgba(189, 147, 249, 0.04),
+ transparent 60%
+ );
+ pointer-events: none;
+ border-radius: inherit;
+}
+
+.plugin-card:hover {
+ transform: translateY(-4px);
+ border-color: rgba(189, 147, 249, 0.42);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.4),
+ 0 0 40px rgba(189, 147, 249, 0.1);
+}
+
+.plugin-card.hidden {
+ display: none;
+}
+
+.plugin-card__header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.plugin-card__icon {
+ font-size: 1.4rem;
+ flex-shrink: 0;
+}
+
+.plugin-card__header > div {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ flex: 1;
+}
+
+.plugin-card__name {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.92rem;
+ font-weight: 700;
+ color: var(--cyan);
+ margin: 0;
+}
+
+.plugin-card__desc {
+ font-size: 0.875rem;
+ color: var(--text-secondary);
+ line-height: 1.6;
+ flex: 1;
+ max-width: none;
+ margin: 0;
+}
+
+.plugin-card__link {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+ border: 1px solid var(--border);
+ color: var(--purple);
+ font-size: 1rem;
+ transition: var(--transition);
+ flex-shrink: 0;
+ margin-left: auto;
+ text-decoration: none;
+}
+
+.plugin-card__link:hover {
+ border-color: var(--purple);
+ background: rgba(189, 147, 249, 0.1);
+ color: var(--pink);
+ transform: translateX(2px);
+}
+
+/* Coc extension tags */
+.coc-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-top: 16px;
+}
+
+.coc-tags .badge {
+ cursor: pointer;
+ text-decoration: none;
+ transition: var(--transition);
+}
+
+.coc-tags .badge:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(189, 147, 249, 0.2);
+}
+
+/* Theme grid */
+.themes-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 16px;
+ margin-top: 24px;
+}
+
+.theme-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ transition: var(--transition);
+}
+
+.theme-card:hover {
+ transform: translateY(-4px);
+ border-color: rgba(189, 147, 249, 0.4);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.4),
+ 0 0 32px rgba(189, 147, 249, 0.08);
+}
+
+.theme-card__preview {
+ display: flex;
+ gap: 6px;
+}
+
+.theme-card__preview span {
+ display: block;
+ width: 24px;
+ height: 24px;
+ border-radius: 5px;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ flex-shrink: 0;
+}
+
+.theme-card__info {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.theme-card__name {
+ font-size: 0.95rem;
+ font-weight: 700;
+ color: var(--text);
+}
+
+.theme-card__link {
+ display: inline-block;
+ font-size: 0.78rem;
+ color: var(--purple);
+ font-family: "JetBrains Mono", monospace;
+ text-decoration: none;
+ transition: var(--transition);
+ margin-top: auto;
+}
+
+.theme-card__link:hover {
+ color: var(--pink);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 28. MOBILE NAVBAR (alternate class name used in HTML)
+ ──────────────────────────────────────────────────────────── */
+.navbar__mobile {
+ display: none;
+ position: fixed;
+ top: var(--nav-height);
+ left: 0;
+ right: 0;
+ background: var(--bg-nav);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border-bottom: 1px solid var(--border);
+ padding: 16px 24px 24px;
+ flex-direction: column;
+ gap: 4px;
+ z-index: 999;
+}
+
+.navbar__mobile:not([hidden]) {
+ display: flex;
+}
+
+.navbar__mobile ul {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.navbar__mobile a {
+ display: block;
+ padding: 10px 14px;
+ color: var(--text-secondary);
+ font-size: 1rem;
+ font-weight: 500;
+ border-radius: 6px;
+ transition: var(--transition);
+}
+
+.navbar__mobile a:hover,
+.navbar__mobile a.active {
+ color: var(--purple);
+ background: rgba(189, 147, 249, 0.07);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 29. FOOTER ALTERNATE CLASSES (used in HTML)
+ ──────────────────────────────────────────────────────────── */
+.footer__inner {
+ max-width: var(--max-width);
+ margin: 0 auto;
+ display: grid;
+ grid-template-columns: 1.5fr repeat(3, 1fr);
+ gap: 48px;
+ padding-bottom: 48px;
+ border-bottom: 1px solid var(--border);
+}
+
+.footer__brand {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.footer__logo {
+ font-family: "JetBrains Mono", monospace;
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--text);
+}
+
+.footer__brand p {
+ font-size: 0.9rem;
+ color: var(--text-muted);
+ max-width: 28ch;
+ margin: 0;
+}
+
+.footer__made {
+ font-size: 0.85rem;
+ color: var(--text-muted) !important;
+}
+
+.footer__col h4 {
+ font-size: 0.8rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-muted);
+ margin-bottom: 16px;
+}
+
+.footer__col ul {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.footer__col a {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ transition: var(--transition);
+}
+
+.footer__col a:hover {
+ color: var(--purple);
+ transform: translateX(3px);
+}
+
+.footer__bottom {
+ max-width: var(--max-width);
+ margin: 24px auto 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.footer__bottom p {
+ font-size: 0.85rem;
+ color: var(--text-muted);
+ max-width: none;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 30. TERMINAL EXTRAS (token classes, pre styling, copy btn)
+ ──────────────────────────────────────────────────────────── */
+.terminal-block pre {
+ margin: 0;
+ padding: 18px 22px;
+ background: transparent;
+ overflow-x: auto;
+}
+
+.terminal-block pre code {
+ color: var(--green);
+ font-size: 0.875rem;
+ line-height: 1.7;
+ white-space: pre;
+ display: block;
+ background: transparent;
+ border: none;
+ padding: 0;
+}
+
+.token-command {
+ color: var(--purple);
+ font-weight: 600;
+}
+.token-path {
+ color: var(--green);
+}
+
+.copy-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 4px 11px;
+ font-size: 0.76rem;
+ font-weight: 600;
+ font-family: "JetBrains Mono", monospace;
+ color: var(--text-muted);
+ background: rgba(255, 255, 255, 0.04);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 5px;
+ cursor: pointer;
+ transition: var(--transition);
+ white-space: nowrap;
+ flex-shrink: 0;
+ margin-left: auto;
+}
+
+.copy-btn:hover {
+ color: var(--purple);
+ border-color: rgba(189, 147, 249, 0.4);
+ background: rgba(189, 147, 249, 0.08);
+}
+
+.copy-btn.copied {
+ color: var(--green);
+ border-color: rgba(80, 250, 123, 0.3);
+}
+
+/* plugin-list used in instalace.html */
+.plugin-list {
+ list-style: none;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.plugin-list li a {
+ display: block;
+ padding: 9px 14px;
+ background: rgba(189, 147, 249, 0.05);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.85rem;
+ color: var(--purple);
+ transition: var(--transition);
+}
+
+.plugin-list li a:hover {
+ background: rgba(189, 147, 249, 0.1);
+ border-color: var(--purple);
+ color: var(--pink);
+}
+
+/* btn-sm */
+.btn-sm {
+ padding: 7px 14px !important;
+ font-size: 0.82rem !important;
+}
+
+/* Loading screen aliases */
+.loading-rocket {
+ font-size: 4rem;
+ animation: spin-slow 3s linear infinite;
+ display: block;
+ text-align: center;
+}
+
+.loading-bar {
+ width: 200px;
+ height: 2px;
+ background: rgba(189, 147, 249, 0.15);
+ border-radius: 1px;
+ overflow: hidden;
+ margin: 0 auto;
+}
+
+.loading-bar__fill {
+ height: 100%;
+ width: 0%;
+ background: linear-gradient(90deg, var(--purple), var(--pink));
+ border-radius: 1px;
+ animation: loading-fill 1.5s ease forwards;
+}
+
+/* ────────────────────────────────────────────────────────────
+ 31. RESPONSIVE ADDITIONS FOR NEW COMPONENTS
+ ──────────────────────────────────────────────────────────── */
+@media (max-width: 768px) {
+ .footer__inner {
+ grid-template-columns: 1fr 1fr;
+ gap: 32px;
+ }
+ .plugins-grid {
+ grid-template-columns: 1fr;
+ }
+ .themes-grid {
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ }
+ .steps::before {
+ left: 20px;
+ }
+ .step-card {
+ gap: 14px;
+ }
+ .step-card__number {
+ width: 40px;
+ height: 40px;
+ min-width: 40px;
+ }
+}
+
+@media (max-width: 480px) {
+ .footer__inner {
+ grid-template-columns: 1fr;
+ gap: 28px;
+ }
+ .themes-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+ .filter-btns {
+ gap: 6px;
+ }
+ .filter-btn {
+ padding: 6px 12px;
+ font-size: 0.78rem;
+ }
+ .page-hero__title {
+ font-size: 2rem;
+ }
+}
+
+/* ────────────────────────────────────────────────────────────
+ 32. BACK TO TOP BUTTON
+ ──────────────────────────────────────────────────────────── */
+.back-to-top {
+ position: fixed;
+ bottom: 28px;
+ right: 28px;
+ z-index: 900;
+ width: 44px;
+ height: 44px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, var(--purple), var(--pink));
+ color: #0d0d1a;
+ font-size: 1.4rem;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ cursor: pointer;
+ opacity: 0;
+ transform: translateY(16px);
+ pointer-events: none;
+ transition:
+ opacity 0.3s ease,
+ transform 0.3s ease,
+ box-shadow 0.3s ease;
+ box-shadow: 0 4px 16px rgba(189, 147, 249, 0.3);
+}
+
+.back-to-top.visible {
+ opacity: 1;
+ transform: translateY(0);
+ pointer-events: all;
+}
+
+.back-to-top:hover {
+ box-shadow: 0 6px 24px rgba(189, 147, 249, 0.5);
+ transform: translateY(-3px);
+}
+
+/* Loading screen fade-out */
+.loading-screen.fade-out {
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+}
+
+/* Animate-on-scroll - visible state (used by IntersectionObserver) */
+.animate-on-scroll {
+ opacity: 0;
+ transform: translateY(24px);
+ transition:
+ opacity 0.6s ease,
+ transform 0.6s ease;
+}
+
+.animate-on-scroll.visible {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.animate-on-scroll.delay-1 {
+ transition-delay: 0.1s;
+}
+.animate-on-scroll.delay-2 {
+ transition-delay: 0.2s;
+}
+.animate-on-scroll.delay-3 {
+ transition-delay: 0.3s;
+}
+.animate-on-scroll.delay-4 {
+ transition-delay: 0.4s;
+}
+.animate-on-scroll.delay-5 {
+ transition-delay: 0.5s;
+}
+
+/* copy-btn--auto (auto-generated by JS) */
+.copy-btn--auto {
+ position: absolute;
+ right: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 33. INDEX.HTML SPECIFIC COMPONENTS
+ ──────────────────────────────────────────────────────────── */
+
+/* Hero extras */
+.hero__description {
+ font-size: 1.05rem;
+ color: var(--text-secondary);
+ text-align: center;
+ max-width: 50ch;
+ line-height: 1.65;
+}
+
+.hero__description strong {
+ color: var(--purple);
+}
+
+.hero__cta {
+ display: flex;
+ gap: 16px;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.hero__badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 18px;
+ background: rgba(189, 147, 249, 0.12);
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ font-size: 0.85rem;
+ font-family: "JetBrains Mono", monospace;
+ color: var(--purple);
+ letter-spacing: 0.03em;
+}
+
+/* Editions grid */
+.editions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 28px;
+ margin-top: 48px;
+}
+
+.edition-card {
+ position: relative;
+}
+
+.edition-card--highlighted {
+ border-color: rgba(189, 147, 249, 0.4) !important;
+ box-shadow: 0 0 40px rgba(189, 147, 249, 0.12) !important;
+}
+
+.edition-card__badge {
+ margin-bottom: 12px;
+}
+
+.edition-card__title {
+ font-size: 1.4rem;
+ font-weight: 700;
+ color: var(--text);
+ margin-bottom: 12px;
+}
+
+.edition-card__desc {
+ font-size: 0.92rem;
+ color: var(--text-secondary);
+ line-height: 1.65;
+ margin-bottom: 20px;
+ max-width: none;
+}
+
+.edition-card__features {
+ list-style: none;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 28px;
+ font-size: 0.92rem;
+ color: var(--text-secondary);
+}
+
+.edition-card__features li {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.edition-card__btn {
+ display: block;
+ text-align: center;
+ width: 100%;
+ margin-top: auto;
+}
+
+/* Stats section */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 32px;
+ text-align: center;
+ max-width: 700px;
+ margin: 0 auto;
+}
+
+.stat-item__number {
+ display: block;
+ font-size: clamp(2.5rem, 7vw, 4.5rem);
+ font-weight: 800;
+ font-family: "JetBrains Mono", monospace;
+ background: linear-gradient(135deg, var(--purple), var(--pink));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ line-height: 1;
+ margin-bottom: 8px;
+}
+
+.stat-item__label {
+ display: block;
+ font-size: 0.9rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ font-weight: 600;
+}
+
+/* Community grid */
+.community-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 24px;
+ margin-top: 48px;
+}
+
+.community-card {
+ text-decoration: none;
+ color: inherit;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ cursor: pointer;
+}
+
+.community-card__icon {
+ font-size: 2rem;
+ line-height: 1;
+}
+
+.community-card__title {
+ font-size: 1.15rem;
+ font-weight: 700;
+ color: var(--text);
+}
+
+.community-card__desc {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ line-height: 1.65;
+ flex: 1;
+ max-width: none;
+}
+
+.community-card__link {
+ font-size: 0.85rem;
+ color: var(--purple);
+ font-family: "JetBrains Mono", monospace;
+ margin-top: auto;
+ transition: var(--transition);
+}
+
+.community-card:hover .community-card__link {
+ color: var(--pink);
+}
+
+.section-header {
+ margin-bottom: 0;
+}
+
+@media (max-width: 768px) {
+ .stats-grid {
+ grid-template-columns: 1fr 1fr 1fr;
+ gap: 16px;
+ }
+ .editions-grid {
+ grid-template-columns: 1fr;
+ }
+}
+@media (max-width: 480px) {
+ .stats-grid {
+ grid-template-columns: 1fr;
+ gap: 20px;
+ }
+ .hero__cta {
+ flex-direction: column;
+ align-items: center;
+ }
+}
+
+/* Install subheading + checklist (nová instalace bez tabů) */
+.install-subheading {
+ font-size: 1.05rem;
+ font-weight: 700;
+ color: var(--cyan);
+ margin: 28px 0 14px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.install-checklist {
+ margin-bottom: 20px;
+}
+
+.install-checklist__intro {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-left: 4px solid var(--purple);
+ border-radius: var(--radius-sm);
+ padding: 18px 22px;
+ font-size: 0.93rem;
+ line-height: 1.9;
+ color: var(--text-secondary);
+ max-width: none;
+ margin: 0;
+}
+
+.install-checklist__intro a {
+ color: var(--purple);
+}
+
+.install-checklist__intro strong {
+ color: var(--text);
+}
+
+/* ────────────────────────────────────────────────────────────
+ 34. GUIDE CARDS (Instalační návody na instalace.html)
+ ──────────────────────────────────────────────────────────── */
+
+.guides-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
+ gap: 28px;
+ margin-top: 48px;
+}
+
+.guide-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ overflow: hidden;
+ transition: var(--transition);
+ display: flex;
+ flex-direction: column;
+}
+
+.guide-card:hover {
+ border-color: rgba(189, 147, 249, 0.45);
+ box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.35),
+ 0 0 40px rgba(189, 147, 249, 0.08);
+ transform: translateY(-3px);
+}
+
+.guide-card__header {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 22px 24px;
+ background: rgba(189, 147, 249, 0.04);
+ border-bottom: 1px solid var(--border);
+}
+
+.guide-card__icon {
+ font-size: 1.8rem;
+ line-height: 1;
+ flex-shrink: 0;
+}
+
+.guide-card__header > div {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.guide-card__title {
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--text);
+ margin: 0;
+}
+
+.guide-card__body {
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ flex: 1;
+}
+
+.guide-card__body > p {
+ color: var(--text-secondary);
+ font-size: 0.93rem;
+ line-height: 1.65;
+ max-width: none;
+ margin: 0;
+}
+
+.guide-card__subtitle {
+ font-size: 0.88rem;
+ font-weight: 700;
+ color: var(--purple);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin: 4px 0 0;
+ padding-top: 4px;
+ border-top: 1px solid var(--border);
+}
+
+/* Remove border-top from first subtitle */
+.guide-card__body .guide-card__subtitle:first-of-type {
+ border-top: none;
+ padding-top: 0;
+}
+
+.guide-card__body .terminal-block {
+ margin: 0;
+}
+
+.guide-card__body .info-box {
+ font-size: 0.88rem;
+ padding: 14px 16px;
+}
+
+/* Guide CTA */
+.guide-cta {
+ margin-top: 48px;
+ padding: 32px;
+ text-align: center;
+ background: linear-gradient(
+ 135deg,
+ rgba(189, 147, 249, 0.07),
+ rgba(255, 121, 198, 0.04)
+ );
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20px;
+}
+
+.guide-cta p {
+ font-size: 1rem;
+ color: var(--text-secondary);
+ max-width: none;
+ margin: 0;
+}
+
+.guide-cta a {
+ text-decoration: none;
+}
+
+.guide-cta > div,
+.guide-cta {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 16px;
+}
+
+.guide-cta p {
+ width: 100%;
+ text-align: center;
+}
+
+@media (max-width: 900px) {
+ .guides-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 480px) {
+ .guide-card__header {
+ padding: 16px 18px;
+ }
+ .guide-card__body {
+ padding: 18px;
+ }
+ .guide-cta {
+ padding: 24px 16px;
+ }
+}
diff --git a/web/html/instalace.html b/web/html/instalace.html
new file mode 100644
index 0000000..e0bf812
--- /dev/null
+++ b/web/html/instalace.html
@@ -0,0 +1,1926 @@
+
+
+
+
+
+ 🔧 Instalace | ApolloNvim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
v25.04
+
+ 🔧 Instalace
+
+
+ Průvodce instalací pro Linux a macOS
+
+
+
+
+
+
+
+
+
+
+
+
+ ✅
+ Vždy si aktualizujte a používejte nejnovější verzi Neovim a všech balíčků.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⚠️ Pokud terminál vypíše chybu při použití příkazu npm, bude potřeba balíček instalovat jako sudo.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🇨🇿 Česky
+
+
+ 🇬🇧 English
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pro jaké operační systémy je instalace určena?
+
+
+ Instalace je primárně určena pro
+ Linux a macOS . Konfiguraci lze
+ s drobnými úpravami použít i na Windows.
+
+
+
+ Jak zálohuju původní konfiguraci?
+
+ Nejjednodušeji přejmenujete složku
+ ~/.config/nvim na
+ ~/.config/nvim.bak. Nebo použijte
+ příkaz
+ cp -r ~/.config/nvim
+ ~/.config/nvim.bak.
+
+
+
+ Co je plug.vim?
+
+ Plug.vim je minimalistický package manager pro
+ Neovim. Umožňuje jednoduché instalování,
+ aktualizaci a mazání pluginů přes soubor
+ init.vim.
+
+
+
+
+ Mohu kombinovat Starter a FrontEnd edici?
+
+
+ Doporučujeme vybrat jednu edici. FrontEnd edice
+ obsahuje vše ze Starter edice plus nástroje pro
+ webový vývoj.
+
+
+
+
+ Kde najdu pomoc pokud se mi instalace nepodaří?
+
+
+ Připojte se na náš
+ Telegram kanál
+ nebo si přečtěte
+ tutoriály .
+
+
+
+
+
+
+
+
+
+
+ Podrobné průvodce k nejdůležitějším krokům instalace a nastavení.
+
+
+
+
+
+
+
+
+
vim.plug je package manager pro Neovim. Bez něj nelze instalovat žádné pluginy. Nejprve si zálohuj stávající konfiguraci!
+
+
Cesty ke konfiguračním souborům
+
+
+
+~/.config/nvim/init.vim
+
+
+~\AppData\Local\nvim\init.vim
+
+
+
Instalace na Linux & macOS
+
+
+
sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
+ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
+
+
+
Instalace na Windows (PowerShell)
+
+
+
iwr -useb https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim |`
+ ni "$(@($env:XDG_DATA_HOME, $env:LOCALAPPDATA)[$null -eq $env:XDG_DATA_HOME])/nvim-data/site/autoload/plug.vim" -Force
+
+
+
Šablona init.vim pro vim.plug
+
+
+
call plug#begin(has('nvim') ? stdpath('data') . '/plugged' : '~/.vim/plugged')
+
+
+
+
+call plug#end()
+
+
+
+ 💡 Po přidání pluginu do init.vim spusť v Neovimu příkaz :PlugInstall a restart.
+
+
+
+
+
+
+
+
+
Ne každá distribuce nabízí nejnovější Neovim. Kompilací ze zdrojového kódu vždy získáš aktuální verzi. Ukázka pro distribuce založené na RHEL/Fedora — pro jiné distro nahraď dnf za apt, pacman nebo yay.
+
+
Krok 1 – Instalace nástrojů
+
+
+
sudo dnf groupinstall "Development Tools"
+sudo dnf install cmake gcc gcc-c++ make ninja-build unzip
+
+
+
Krok 2 – Stažení zdrojového kódu
+
+
+
git clone https://github.com/neovim/neovim.git
+cd neovim
+
+
+
Krok 3 – Kompilace
+
+
+
make CMAKE_BUILD_TYPE=Release
+
+
+
Krok 4 – Instalace
+
+
+
+
+
+
+
+
+
Doporučená základní konfigurace souboru init.vim. Otevřeš ho příkazem nvim init.vim a vložíš tato nastavení.
+
+
+
+
+syntax on
+
+
+set termguicolors
+
+
+set tabstop=4
+set softtabstop=4
+set expandtab
+
+
+set smartindent
+set shiftwidth=4
+
+
+set number
+set numberwidth=4
+
+
+set incsearch
+set nohlsearch
+
+
+set splitbelow
+set splitright
+
+
+set hidden
+
+
+set scrolloff=8
+
+
+set undofile
+
+
+set mouse=a
+
+
+set ignorecase
+set smartcase
+
+
+
+ 💡 Pro nápovědu k libovolnému nastavení použij uvnitř Neovimu příkaz :h název , např. :h number .
+
+
+
+
+
+
+
+
+
Některé pluginy vyžadují konfiguraci v jazyce Lua. Máš dvě možnosti:
+
+
Způsob 1 – přímo do init.vim
+
Jednodušší způsob – Lua blok vložíš přímo do init.vim.
+
+
+
lua << EOF
+require('název-pluginu').setup({
+
+})
+EOF
+
+
+
Způsob 2 – samostatné Lua soubory
+
Přehlednější způsob pro více pluginů. Struktura složek:
+
+
+
~/.config/nvim/
+├── init.vim
+└── lua/
+ └── mujNick/
+ ├── init.lua
+ └── gitsigns.lua
+
+
+
Krok 1 – Přidej do init.vim
+
+
+
lua require('mujNick')
+
+
+
Krok 2 – Vytvoř lua/mujNick/gitsigns.lua
+
+
+
require('gitsigns').setup {
+ current_line_blame = true,
+ current_line_blame_opts = {
+ virt_text_pos = 'right_align'
+ }
+}
+
+
+
Krok 3 – Importuj v lua/mujNick/init.lua
+
+
+
require('mujNick/gitsigns')
+
+
+
+
+
+
+
+
+
Pokud Prettier vypisuje chybu, pravděpodobně není nainstalovaný v projektu. Oprava je jednoduchá – stačí ho doinstalovat jako dev dependenci.
+
+
Instalace Prettier do projektu
+
+
+
npm install --save-dev prettier
+
+
+
+ 💡 Více informací o práci s Prettier v Neovimu najdeš na GitHubu
+
coc-prettier ↗ .
+
+
+
Instalace colorscheme po startu
+
Aby se tvoje oblíbené téma načetlo automaticky při každém startu Neovimu:
+
+
+
colorscheme dracula
+autocmd VimEnter * colorscheme dracula
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/html/novinky.html b/web/html/novinky.html
new file mode 100644
index 0000000..7657bac
--- /dev/null
+++ b/web/html/novinky.html
@@ -0,0 +1,822 @@
+
+
+
+
+
+ 📰 Novinky | ApolloNvim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📰 Changelog & Aktualizace
+
+
Novinky
+
Changelog a novinky ApolloNvim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nový redesign webu ApolloNvim
+
+ Vícestrankový web s HTML/CSS/JS
+ strukturou
+
+
+ Temný vesmírný design inspirovaný
+ Dracula tématem
+
+
+ Stránky: Domů, Instalace, Pluginy,
+ Tutoriály, Novinky, Zkratky
+
+
+ Particle systém s animovanými
+ hvězdičkami
+
+
+ Kopírovatelné terminálové bloky,
+ responzivní design
+
+
+
+
+
+
+
+
+
+
+
+ Verze 25.04 – aktuální stabilní
+ vydání
+
+
+
+
+
+
+
+
+
+
+ Nový web ApolloNvim
+ Lokalizace do angličtiny
+
+ Vylepšená instalace pro jednodušší
+ onboarding
+
+ Podpora pouze pro Linux a Mac
+
+
+
+
+
+
+
+
+
+
+ Nová instalace s přepracovaným
+ procesem nastavení
+
+
+ Nový website – moderní design a
+ lepší přehlednost
+
+
+ Nový obrázek ApolloNvim –
+ aktualizovaná vizuální identita
+
+
+
+
+
+
+
+
+
+
+
+ Podpora PHP – LSP a snippety pro PHP
+ vývojáře
+
+
+ Automatická instalace pluginů po
+ přidání do init.vim
+
+
+ Rainbow Vim – plugin pro barevné
+ závorky pro lepší čitelnost kódu
+
+
+ Gitgutter – plugin pro správu a
+ zobrazení git změn v editoru
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/html/pluginy.html b/web/html/pluginy.html
new file mode 100644
index 0000000..32c32a5
--- /dev/null
+++ b/web/html/pluginy.html
@@ -0,0 +1,1326 @@
+
+
+
+
+
+ 🔌 Pluginy | ApolloNvim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
v25.04
+
+ 🔌 Pluginy
+
+
+ Kompletní seznam pluginů v ApolloNvim distribuci
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Vše
+
+
+ Editor
+
+ UI
+
+ Git
+
+ AI
+
+ Syntax
+
+
+ LSP
+
+
+ Core
+
+
+ Produktivita
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Automatické uzavírání závorek, tagů a všech typů
+ párových znaků.
+
+
+
+
+
+
+ Package manager pro LSP rozšíření. Základ celé
+ ApolloNvim konfigurace.
+
+
+
+
+
+
+ Plugin pro HTML5 syntax highlighting a
+ dokončování.
+
+
+
+
+
+
+ Stromový souborový panel pro přehlednou navigaci
+ v projektu.
+
+
+
+
+
+
+ AI asistent - zdarma alternativa k GitHub
+ Copilotu. Výchozí aktivní.
+
+
+
+
+
+
+ Alternativní AI asistent. Zakomentován -
+ použijte místo Codeia.
+
+
+
+
+
+
+ Alternativní uvítací obrazovka s pokaždé jiným
+ IT mottem v NORMAL módu.
+
+
+
+
+
+
+ Zakomentování a odkomentování kódu zkratkou
+ Ctrl+K .
+
+
+
+
+
+
+ Měření času stráveného kódováním na různých
+ projektech. Propojení přes API.
+
+
+
+
+
+
+ Automatické ukládání souborů při přechodu z
+ INSERT do NORMAL módu.
+
+
+
+
+
+
+ Pokročilé zvýraznění syntaxe kódu pomocí AST
+ parseru.
+
+
+
+
+
+
+ Propojuje kód vertikálními linkami pro lepší
+ orientaci v odsazení.
+
+
+
+
+
+
+ Kompletní Git integrace přímo v Neovimu. Commit,
+ diff, blame a více.
+
+
+
+
+
+
+ Zobrazuje náhled barev přímo v kódu dle
+ hex/rgb/hsl kódu.
+
+
+
+
+
+
+ Pokročilá podpora JavaScript syntaxe a
+ dokončování.
+
+
+
+
+
+
+ Vylepšený integrovaný terminál s přepínáním a
+ plovoucím oknem.
+
+
+
+
+
+
+ Zobrazuje Git nápovědu ke změnám přímo v
+ číslovacím sloupci.
+
+
+
+
+
+
+ Zobrazuje změny v kódu oproti Git historii s
+ inline markery.
+
+
+
+
+
+
+ Lua utility library - závislost pro mnohé Neovim
+ pluginy.
+
+
+
+
+
+
+
+
+
+
+
+ Coc rozšíření přidávají pokročilou podporu pro různé
+ jazyky a nástroje.
+ Kompletní seznam ↗
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Přepnutí tématu v Neovimu:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/html/tutorialy.html b/web/html/tutorialy.html
new file mode 100644
index 0000000..3a8e500
--- /dev/null
+++ b/web/html/tutorialy.html
@@ -0,0 +1,1154 @@
+
+
+
+
+
+ 📚 Tutoriály | ApolloNvim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📚 Průvodci & Návody
+
+
Tutoriály
+
+ Průvodci a návody pro ApolloNvim
+
+
Na stránce se pracuje pro prohlédnutí tutoriálu prosím navštivvte náš repozitář na Codeberg . Omlouváme se za případné nepříjemnosti.
+
+
+
+
+
+
+
+
+
+
+
+ 01
+
+ Jak nainstalovat vim.plug
+
+
+ Instalace package manageru plug.vim do Neovim –
+ základ pro správu všech pluginů.
+
+
+
+
+
+
+ 02
+
+ Klávesové zkratky
+
+
+ Přehled výchozích klávesových zkratek Neovim pro
+ produktivní každodenní práci.
+
+
+
+
+
+
+ 03
+
+ Jak nainstalovat Codeium
+
+
+ Instalace AI asistenta Codeium zdarma –
+ inteligentní doplňování kódu přímo v editoru.
+
+
+
+
+
+
+ 04
+
+ Jak nainstalovat Telescope
+
+
+ Fuzzy finder pro Neovim – rychlé vyhledávání
+ souborů, symbolů a příkazů.
+
+
+
+
+
+
+ 05
+
+ Barevná schémata
+
+
+ Jak nastavit a přepínat barevná témata pro
+ pohodlnou práci ve dne i v noci.
+
+
+
+
+
+
+ 06
+
+ Autosave konfigurace
+
+
+ Nastavení automatického ukládání – nikdy
+ nepřijdete o neuloženou práci.
+
+
+
+
+
+
+ 07
+
+ Kopírování a vkládání
+
+
+ Kopírování z externích zdrojů do Neovim –
+ systémová schránka bez kompromisů.
+
+
+
+
+
+
+ 08
+
+ Markdown syntax
+
+
+ Práce s Markdown v Neovim – zvýraznění syntaxe,
+ náhled a Tree-sitter podpora.
+
+
+
+
+
+
+ 09
+
+ init.vim základní nastavení
+
+
+ Konfigurace základního nastavení – od čísel
+ řádků po odsazení a kódování.
+
+
+
+
+
+
+ 10
+
+ vim-fugitive – Git v Neovim
+
+
+ Správa Gitu přímo v editoru – commit, diff,
+ blame a více bez opuštění Neovim.
+
+
+
+
+
+
+ 11
+
+ Gitsigns
+
+
+ Zobrazení git změn přímo v kódu – přidané,
+ upravené a smazané řádky v reálném čase.
+
+
+
+
+
+
+ 12
+
+ Neovim s Zellij
+
+
+ Terminálový multiplexor pro lepší workflow –
+ panely, záložky a sdílené sezení.
+
+
+
+
+
+
+ 13
+
+ Relativní čísla řádků
+
+
+ Efektivní navigace pomocí relativních čísel –
+ rychlé skoky na přesné řádky.
+
+
+
+
+
+
+ 14
+
+ Inline Parameters
+
+
+ Alternativa pro zobrazení parametrů funkcí přímo
+ v kódu pro lepší přehlednost.
+
+
+
+
+
+
+ 15
+
+ Kopírování a vkládání (pokročilé)
+
+
+ Pokročilé techniky práce se schránkou –
+ registry, makra a cross-aplikační vkládání.
+
+
+
+
+
+
+ 16
+
+ Instalace nejnovějšího Neovim
+
+
+ Aktualizace Neovim na Linuxu – manuální
+ instalace nejnovější verze z AppImage nebo
+ zdrojového kódu.
+
+
+
+
+
+
+ 17
+
+ Jak opravit nefunkční Prettier
+
+
+ Řešení problémů s formátováním – diagnostika a
+ oprava nejčastějších chyb Prettier.
+
+
+
+
+
+
+ 18
+
+ Konfigurace Lua
+
+
+ Základy konfigurace v jazyce Lua – moduly,
+ tabulky, funkce a migrace z Vimscript.
+
+
+
+
+
+
+ 19
+
+ NERDTree konfigurace
+
+
+ Tipy a triky pro souborový panel NERDTree –
+ klávesové zkratky, skrytí souborů a integrace.
+
+
+
+
+
+
+ 20
+
+ Náhled Markdown v nvim
+
+
+ Jak zobrazit náhled markdown souborů v
+ prohlížeči přímo z Neovim v reálném čase.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/html/zkratky.html b/web/html/zkratky.html
new file mode 100644
index 0000000..7f8d537
--- /dev/null
+++ b/web/html/zkratky.html
@@ -0,0 +1,1493 @@
+
+
+
+
+
+ ⌨️ Klávesové zkratky | ApolloNvim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⌨️ Cheatsheet
+
+
+ Klávesové zkratky
+
+
+ Přehled výchozích klávesových zkratek Neovim
+
+
+
+
+
+
+
+
+
+ Neovim je moderní verze Vim. Zvládnutí
+ klávesových zkratek je klíčem k efektivní práci – ruce
+ zůstávají na klávesnici a každá operace má svůj přesný
+ příkaz. Chvíli to trvá, ale vrátí se to stonásobně.
+
+
+
+
+
+ Základní
+
+
+ Pokročilé
+
+
+ 🚀 ApolloNvim zkratky
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/img/apollonvim.jpg b/web/img/apollonvim.jpg
new file mode 100644
index 0000000..57fda21
Binary files /dev/null and b/web/img/apollonvim.jpg differ
diff --git a/web/img/neovim.jpeg b/web/img/neovim.jpeg
new file mode 100644
index 0000000..f357555
Binary files /dev/null and b/web/img/neovim.jpeg differ
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..6037ada
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,442 @@
+
+
+
+
+
+ 🚀 ApolloNvim | Neovim distribuce pro Front-End
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Verze 26.05
+
+
+
+
+
+
+
+
+
+ Rychlý, lehký a plně konfigurovatelný.
+ Postavený na Plug.vim , Coc a Lua .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ⚡
+ Rychlost & Lehkost
+ Minimální nároky na systém, maximální výkon. Spustí se během okamžiku.
+
+
+
+ 🎨
+ Krásný design
+ 19+ barevných témat. Výchozí Dracula téma pro příjemné prostředí při každé práci.
+
+
+
+ 🤖
+ AI asistence
+ Codeium – zdarma alternativa ke GitHub Copilotu. Také TabNine pro inteligentní doplňování.
+
+
+
+ 🔌
+ Bohatá sada pluginů
+ AutoSave, NERDTree, TreeSitter, Colorizer a mnoho dalšího. Vše přednastaveno a připraveno.
+
+
+
+ 🌐
+ Front-End zaměření
+ Plná podpora HTML, CSS, JS, TypeScript, PHP, TailwindCSS. Editor navržený pro webové vývojáře.
+
+
+
+ 📖
+ Skvělá dokumentace
+ Podrobné návody v češtině i angličtině. Začneš produktivně pracovat od prvního dne.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pro začátečníky
+
+ 🚀 Starter Edition
+
+ Zaměřená na práci s texty a Markdown. Ideální pro každého, kdo chce začít s Neovimem bez zbytečné složitosti.
+
+
+ ✅ Podpora Markdown a MDX
+ ✅ Zvýraznění syntaxe (TreeSitter)
+ ✅ 19+ barevných témat vč. Dracula
+ ✅ AutoSave – nikdy neztrácíš práci
+ ✅ Minimalistické, rychlé prostředí
+ ✅ NERDTree pro správu souborů
+
+
+ 🚀 Instalovat Starter
+
+
+
+
+
+
+ ⭐ Doporučeno
+
+ 🌐 FrontEnd Edition
+
+ Pro vývoj webů a webových aplikací. Inspirováno VSCode a WebStorm. Kompletní prostředí pro Front-End vývojáře.
+
+
+ ✅ Vše ze Starter Edition
+ ✅ LSP server přes Coc.nvim
+ ✅ HTML, CSS, JS, TS, PHP, TailwindCSS
+ ✅ AI: Codeium & TabNine (zdarma)
+ ✅ Emmet, Autopairs, Colorizer
+ ✅ Git integrace (fugitive + gitsigns)
+
+
+ 🛠️ Instalovat FrontEnd
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0+
+ Barevných témat
+
+
+
+ 0+
+ Pluginů
+
+
+
+ 0+
+ Tutoriálů
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/js/main.js b/web/js/main.js
new file mode 100644
index 0000000..46985f2
--- /dev/null
+++ b/web/js/main.js
@@ -0,0 +1,647 @@
+/**
+ * main.js — ApolloNvim Web Interaktivita
+ * Vanilla ES6+ JavaScript bez externích závislostí.
+ */
+
+"use strict";
+
+// ════════════════════════════════════════════════════════════════════════════
+// 8. LOADING SCREEN — fade-out při načtení stránky
+// (registrujeme jako první, aby běžel co nejdříve)
+// ════════════════════════════════════════════════════════════════════════════
+
+function initLoadingScreen() {
+ const loader = document.getElementById("loading-screen");
+ if (!loader) return;
+
+ const hide = () => {
+ loader.classList.add("fade-out");
+ // Po dokončení animace element skryjeme úplně
+ loader.addEventListener(
+ "transitionend",
+ () => {
+ loader.style.display = "none";
+ },
+ { once: true },
+ );
+ };
+
+ if (document.readyState === "complete") {
+ hide();
+ } else {
+ window.addEventListener("load", hide);
+ }
+}
+
+initLoadingScreen();
+
+// ════════════════════════════════════════════════════════════════════════════
+// Spuštění zbytku funkcí po načtení DOM
+// ════════════════════════════════════════════════════════════════════════════
+
+document.addEventListener("DOMContentLoaded", () => {
+ // ── Pořadí inicializace ──────────────────────────────────────────────────
+ initNavigation();
+ initHamburger();
+ initScrollAnimations();
+ initTabs();
+ initTypingEffects();
+ initCounters();
+ initCopyButtons();
+ initPluginFilters();
+ initTooltips();
+ initBackToTop();
+});
+
+// ════════════════════════════════════════════════════════════════════════════
+// 1. NAVIGATION
+// ════════════════════════════════════════════════════════════════════════════
+
+function initNavigation() {
+ const navbar =
+ document.querySelector(".navbar") || document.querySelector("nav");
+ const hamburger =
+ document.querySelector(".hamburger") ||
+ document.querySelector(".menu-toggle");
+ const navMenu =
+ document.querySelector(".nav-menu") || document.querySelector(".nav-links");
+ const navLinks = document.querySelectorAll(".nav-links a, .nav-menu a");
+
+ // ── Třída "scrolled" při odrolování ──
+ if (navbar) {
+ const onScroll = () => {
+ navbar.classList.toggle("scrolled", window.scrollY > 50);
+ };
+ window.addEventListener("scroll", onScroll, { passive: true });
+ onScroll(); // Okamžitá inicializace
+ }
+
+ // ── Hamburger menu toggle ──
+ if (hamburger && navMenu) {
+ hamburger.addEventListener("click", () => {
+ const isOpen = navMenu.classList.toggle("open");
+ hamburger.classList.toggle("active", isOpen);
+ hamburger.setAttribute("aria-expanded", String(isOpen));
+ // Zabránit scrollování pozadí při otevřeném menu
+ document.body.classList.toggle("menu-open", isOpen);
+ });
+ }
+
+ // ── Smooth scroll pro anchor linky ──
+ document.querySelectorAll('a[href^="#"]').forEach((link) => {
+ link.addEventListener("click", (e) => {
+ const targetId = link.getAttribute("href");
+ if (targetId === "#") return;
+
+ const target = document.querySelector(targetId);
+ if (!target) return;
+
+ e.preventDefault();
+ const navHeight = navbar ? navbar.offsetHeight : 0;
+ const top =
+ target.getBoundingClientRect().top + window.scrollY - navHeight - 16;
+
+ window.scrollTo({ top, behavior: "smooth" });
+
+ // Zavření mobilního menu po kliknutí na odkaz
+ closeNavMenu(navMenu, hamburger);
+ });
+ });
+
+ // ── Zavření menu kliknutím mimo ──
+ document.addEventListener("click", (e) => {
+ if (!navMenu || !hamburger) return;
+ if (!navMenu.contains(e.target) && !hamburger.contains(e.target)) {
+ closeNavMenu(navMenu, hamburger);
+ }
+ });
+
+ // ── Aktivní stav nav linků dle URL ──
+ setActiveNavLink(navLinks);
+}
+
+/** Zavře mobilní navigation menu */
+function closeNavMenu(navMenu, hamburger) {
+ if (!navMenu) return;
+ navMenu.classList.remove("open");
+ document.body.classList.remove("menu-open");
+ if (hamburger) {
+ hamburger.classList.remove("active");
+ hamburger.setAttribute("aria-expanded", "false");
+ }
+}
+
+/** Označí aktivní nav link dle aktuální URL */
+function setActiveNavLink(links) {
+ const path = window.location.pathname;
+ links.forEach((link) => {
+ const href = link.getAttribute("href") || "";
+ // Přesná shoda nebo shoda části cesty (pro víceúrovňové URL)
+ const isActive =
+ href !== "#" &&
+ (path === href ||
+ path.endsWith(href) ||
+ (href !== "/" && path.includes(href)));
+ link.classList.toggle("active", isActive);
+ if (isActive) link.setAttribute("aria-current", "page");
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 2. SCROLL ANIMACE — Intersection Observer
+// ════════════════════════════════════════════════════════════════════════════
+
+function initScrollAnimations() {
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ entry.target.classList.add("visible");
+ // Odpojit observer po animaci (každý element se animuje jen jednou)
+ observer.unobserve(entry.target);
+ }
+ });
+ },
+ {
+ threshold: 0.1,
+ rootMargin: "0px 0px -40px 0px", // spustí se dříve, než element dosáhne dna okna
+ },
+ );
+
+ document
+ .querySelectorAll(".animate-on-scroll")
+ .forEach((el) => observer.observe(el));
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 3. TAB SYSTEM — CZ / EN přepínač (a jakékoli další záložky)
+// ════════════════════════════════════════════════════════════════════════════
+
+function initTabs() {
+ // Podpora více nezávislých tab skupin na stránce
+ document.querySelectorAll("[data-tab-group]").forEach((group) => {
+ const buttons = group.querySelectorAll(".tab-btn");
+ const contents = group.querySelectorAll(".tab-content");
+ const storageKey = `apollo-tab-${group.dataset.tabGroup}`;
+
+ /** Přepne na záložku s daným id */
+ const switchTab = (targetId, saveToStorage = true) => {
+ buttons.forEach((btn) => {
+ const active = btn.dataset.tab === targetId;
+ btn.classList.toggle("active", active);
+ btn.setAttribute("aria-selected", String(active));
+ });
+
+ contents.forEach((content) => {
+ if (content.dataset.tabContent === targetId) {
+ content.classList.add("active");
+ content.classList.remove("hidden");
+ // Fade-in efekt
+ content.style.opacity = "0";
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ content.style.opacity = "1";
+ });
+ });
+ } else {
+ content.classList.remove("active");
+ content.classList.add("hidden");
+ }
+ });
+
+ if (saveToStorage) {
+ try {
+ localStorage.setItem(storageKey, targetId);
+ } catch (_) {}
+ }
+ };
+
+ // Event listenery
+ buttons.forEach((btn) => {
+ btn.addEventListener("click", () => switchTab(btn.dataset.tab));
+ });
+
+ // Obnova uložené preference
+ let savedTab = null;
+ try {
+ savedTab = localStorage.getItem(storageKey);
+ } catch (_) {}
+
+ const firstBtn = buttons[0];
+ const initTab = savedTab || (firstBtn ? firstBtn.dataset.tab : null);
+ if (initTab) switchTab(initTab, false);
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 4. TERMINAL TYPING EFFECT
+// ════════════════════════════════════════════════════════════════════════════
+
+/**
+ * Animuje psaní textu do elementu znak po znaku.
+ * @param {HTMLElement} element - Cílový element
+ * @param {string} text - Text, který se "napíše"
+ * @param {number} speed - Prodleva mezi znaky v ms (default 60)
+ * @param {Function} callback - Volitelný callback po dokončení
+ */
+function typeWriter(element, text, speed = 60, callback = null) {
+ if (!element) return;
+ element.textContent = "";
+ element.style.borderRight = "2px solid currentColor"; // kurzor
+
+ let i = 0;
+ const tick = () => {
+ if (i < text.length) {
+ element.textContent += text.charAt(i);
+ i++;
+ setTimeout(tick, speed + Math.random() * 30); // mírná variabilita
+ } else {
+ // Blikající kurzor po dokončení
+ element.classList.add("typing-done");
+ if (typeof callback === "function") callback();
+ }
+ };
+ tick();
+}
+
+/** Aplikuje typing efekt na všechny elementy s atributem [data-typewriter] */
+function initTypingEffects() {
+ const elements = document.querySelectorAll("[data-typewriter]");
+ if (!elements.length) return;
+
+ // Spustíme postupně s malým zpožděním pro každý element
+ elements.forEach((el, idx) => {
+ const text = el.dataset.typewriter || el.textContent;
+ const speed = parseInt(el.dataset.typeSpeed, 10) || 60;
+ const delay = parseInt(el.dataset.typeDelay, 10) || idx * 300;
+
+ el.textContent = ""; // Skryjeme původní text
+ setTimeout(() => typeWriter(el, text, speed), delay);
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 5. SMOOTH COUNTER ANIMATION
+// ════════════════════════════════════════════════════════════════════════════
+
+function initCounters() {
+ const counters = document.querySelectorAll("[data-count]");
+ if (!counters.length) return;
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (!entry.isIntersecting) return;
+ observer.unobserve(entry.target);
+ animateCounter(entry.target);
+ });
+ },
+ { threshold: 0.5 },
+ );
+
+ counters.forEach((el) => observer.observe(el));
+}
+
+/**
+ * Animuje čítač od 0 do cílové hodnoty.
+ * @param {HTMLElement} el - Element s atributem data-count
+ */
+function animateCounter(el) {
+ const target = parseInt(el.dataset.count, 10);
+ const duration = parseInt(el.dataset.countDuration, 10) || 1500; // ms
+ const suffix = el.dataset.countSuffix || "";
+ const start = performance.now();
+
+ const update = (now) => {
+ const elapsed = now - start;
+ const progress = Math.min(elapsed / duration, 1);
+ // Ease-out cubic
+ const eased = 1 - Math.pow(1 - progress, 3);
+ const current = Math.floor(eased * target);
+
+ el.textContent = current.toLocaleString("cs-CZ") + suffix;
+
+ if (progress < 1) requestAnimationFrame(update);
+ else el.textContent = target.toLocaleString("cs-CZ") + suffix;
+ };
+
+ requestAnimationFrame(update);
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 6. COPY TO CLIPBOARD
+// ════════════════════════════════════════════════════════════════════════════
+
+function initCopyButtons() {
+ // Podpora pro explicitní tlačítka s třídou .copy-btn
+ document.querySelectorAll(".copy-btn").forEach((btn) => {
+ btn.addEventListener("click", () => {
+ const targetSelector = btn.dataset.copyTarget;
+ const codeEl = targetSelector
+ ? document.querySelector(targetSelector)
+ : btn.closest(".code-block, pre")?.querySelector("code") ||
+ btn.previousElementSibling;
+
+ if (!codeEl) return;
+ copyText(codeEl.textContent.trim(), btn);
+ });
+ });
+
+ // Automaticky přidá tlačítka ke všem blokům bez tlačítka
+ document.querySelectorAll("pre:not(.no-copy)").forEach((pre) => {
+ if (pre.querySelector(".copy-btn")) return; // Již má tlačítko
+
+ const btn = document.createElement("button");
+ btn.className = "copy-btn copy-btn--auto";
+ btn.textContent = "Kopírovat";
+ btn.setAttribute("aria-label", "Kopírovat kód");
+ pre.style.position = "relative";
+ pre.appendChild(btn);
+
+ btn.addEventListener("click", () => {
+ const code = pre.querySelector("code") || pre;
+ copyText(code.textContent.trim(), btn);
+ });
+ });
+}
+
+/**
+ * Zkopíruje text do schránky a ukáže vizuální feedback na tlačítku.
+ * @param {string} text - Text ke zkopírování
+ * @param {HTMLElement} btn - Tlačítko pro vizuální feedback
+ */
+function copyText(text, btn) {
+ navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ const original = btn.textContent;
+ btn.textContent = "✓ Zkopírováno";
+ btn.classList.add("copied");
+ btn.disabled = true;
+
+ setTimeout(() => {
+ btn.textContent = original;
+ btn.classList.remove("copied");
+ btn.disabled = false;
+ }, 2000);
+ })
+ .catch(() => {
+ // Fallback pro starší prohlížeče
+ const ta = document.createElement("textarea");
+ ta.value = text;
+ ta.style.cssText = "position:fixed;top:-9999px;left:-9999px";
+ document.body.appendChild(ta);
+ ta.select();
+ document.execCommand("copy");
+ document.body.removeChild(ta);
+
+ btn.textContent = "✓ Zkopírováno";
+ btn.classList.add("copied");
+ setTimeout(() => {
+ btn.textContent = "Kopírovat";
+ btn.classList.remove("copied");
+ }, 2000);
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 7. THEME PARTICLE COLORS (při hover na sekce)
+// ════════════════════════════════════════════════════════════════════════════
+
+function initParticleTheme() {
+ if (!window.particleSystem) return;
+
+ // Mapování sekcí na palety barev
+ const sectionThemes = {
+ "#plugins": ["#bd93f9", "#ff79c6", "#ffffff"],
+ "#themes": ["#ff79c6", "#ffb86c", "#ffffff"],
+ "#installation": ["#50fa7b", "#8be9fd", "#ffffff"],
+ "#tutorials": ["#f1fa8c", "#ffb86c", "#ffffff"],
+ };
+
+ Object.entries(sectionThemes).forEach(([selector, colors]) => {
+ const section = document.querySelector(selector);
+ if (!section) return;
+
+ section.addEventListener("mouseenter", () => {
+ window.particleSystem.config.colors = colors;
+ });
+ section.addEventListener("mouseleave", () => {
+ // Obnovení výchozích barev
+ window.particleSystem.config.colors = [
+ "#ffffff",
+ "#8be9fd",
+ "#bd93f9",
+ "#ff79c6",
+ ];
+ });
+ });
+}
+
+// Spustíme až po inicializaci particle systému (je v DOMContentLoaded v particles.js)
+document.addEventListener("DOMContentLoaded", () => {
+ // Malé zpoždění pro jistotu, že particleSystem je vytvořen
+ setTimeout(initParticleTheme, 100);
+});
+
+// ════════════════════════════════════════════════════════════════════════════
+// 9. TOOLTIP SYSTEM
+// ════════════════════════════════════════════════════════════════════════════
+
+function initTooltips() {
+ let tooltip = document.getElementById("tooltip-global");
+
+ // Vytvoření globálního tooltip elementu pokud neexistuje
+ if (!tooltip) {
+ tooltip = document.createElement("div");
+ tooltip.id = "tooltip-global";
+ tooltip.className = "tooltip";
+ tooltip.setAttribute("role", "tooltip");
+ tooltip.setAttribute("aria-hidden", "true");
+ document.body.appendChild(tooltip);
+ }
+
+ let hideTimer = null;
+
+ /** Zobrazí tooltip */
+ const show = (text, anchorEl) => {
+ clearTimeout(hideTimer);
+ tooltip.textContent = text;
+ tooltip.style.display = "block";
+ tooltip.removeAttribute("aria-hidden");
+
+ // Pozicování nad elementem
+ const rect = anchorEl.getBoundingClientRect();
+ const tRect = tooltip.getBoundingClientRect();
+ let left = rect.left + rect.width / 2 - tRect.width / 2 + window.scrollX;
+ let top = rect.top - tRect.height - 8 + window.scrollY;
+
+ // Zamezit přetečení přes pravý okraj
+ if (left + tRect.width > window.innerWidth - 8) {
+ left = window.innerWidth - tRect.width - 8;
+ }
+ if (left < 8) left = 8;
+
+ tooltip.style.left = `${left}px`;
+ tooltip.style.top = `${top}px`;
+ tooltip.classList.add("visible");
+ };
+
+ /** Skryje tooltip */
+ const hide = () => {
+ hideTimer = setTimeout(() => {
+ tooltip.classList.remove("visible");
+ tooltip.setAttribute("aria-hidden", "true");
+ }, 150);
+ };
+
+ // Event delegation — posloucháme na document
+ document.addEventListener("mouseover", (e) => {
+ const target = e.target.closest("[data-tooltip]");
+ if (target) show(target.dataset.tooltip, target);
+ });
+
+ document.addEventListener("mouseout", (e) => {
+ if (e.target.closest("[data-tooltip]")) hide();
+ });
+
+ // Klávesnicová přístupnost
+ document.addEventListener("focusin", (e) => {
+ const target = e.target.closest("[data-tooltip]");
+ if (target) show(target.dataset.tooltip, target);
+ });
+
+ document.addEventListener("focusout", (e) => {
+ if (e.target.closest("[data-tooltip]")) hide();
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 10. BACK TO TOP BUTTON
+// ════════════════════════════════════════════════════════════════════════════
+
+function initBackToTop() {
+ let btn = document.getElementById("back-to-top");
+
+ // Vytvoření tlačítka pokud neexistuje v HTML
+ if (!btn) {
+ btn = document.createElement("button");
+ btn.id = "back-to-top";
+ btn.className = "back-to-top";
+ btn.innerHTML = "⇧"; // ⇧
+ btn.setAttribute("aria-label", "Zpět nahoru");
+ btn.setAttribute("title", "Zpět nahoru");
+ document.body.appendChild(btn);
+ }
+
+ // Zobrazení/skrytí dle scroll pozice
+ const SHOW_THRESHOLD = 300;
+
+ const toggleVisibility = () => {
+ const visible = window.scrollY > SHOW_THRESHOLD;
+ btn.classList.toggle("visible", visible);
+ btn.setAttribute("aria-hidden", String(!visible));
+ };
+
+ window.addEventListener("scroll", toggleVisibility, { passive: true });
+ toggleVisibility();
+
+ // Scroll nahoru po kliknutí
+ btn.addEventListener("click", () => {
+ window.scrollTo({ top: 0, behavior: "smooth" });
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// UTILITY — veřejné API pro použití z HTML atributů nebo jiných skriptů
+// ════════════════════════════════════════════════════════════════════════════
+
+// ════════════════════════════════════════════════════════════════════════════
+// 11. HAMBURGER MENU (pro HTML strukturu s .navbar__hamburger a .navbar__mobile)
+// ════════════════════════════════════════════════════════════════════════════
+
+function initHamburger() {
+ const hamburger = document.getElementById("hamburger");
+ const mobileMenu = document.getElementById("mobile-menu");
+ if (!hamburger || !mobileMenu) return;
+
+ hamburger.addEventListener("click", () => {
+ const isHidden = mobileMenu.hasAttribute("hidden");
+ if (isHidden) {
+ mobileMenu.removeAttribute("hidden");
+ hamburger.setAttribute("aria-expanded", "true");
+ hamburger.classList.add("open");
+ } else {
+ mobileMenu.setAttribute("hidden", "");
+ hamburger.setAttribute("aria-expanded", "false");
+ hamburger.classList.remove("open");
+ }
+ });
+
+ // Zavřít při kliknutí mimo menu
+ document.addEventListener("click", (e) => {
+ if (
+ !mobileMenu.hasAttribute("hidden") &&
+ !mobileMenu.contains(e.target) &&
+ !hamburger.contains(e.target)
+ ) {
+ mobileMenu.setAttribute("hidden", "");
+ hamburger.setAttribute("aria-expanded", "false");
+ hamburger.classList.remove("open");
+ }
+ });
+
+ // Zavřít při kliknutí na odkaz v menu
+ mobileMenu.querySelectorAll("a").forEach((link) => {
+ link.addEventListener("click", () => {
+ mobileMenu.setAttribute("hidden", "");
+ hamburger.setAttribute("aria-expanded", "false");
+ hamburger.classList.remove("open");
+ });
+ });
+}
+
+// ════════════════════════════════════════════════════════════════════════════
+// 12. PLUGIN CATEGORY FILTER (pro pluginy.html)
+// ════════════════════════════════════════════════════════════════════════════
+
+function initPluginFilters() {
+ const filterContainer = document.getElementById("plugin-filters");
+ const pluginsGrid = document.getElementById("plugins-grid");
+ if (!filterContainer || !pluginsGrid) return;
+
+ const buttons = filterContainer.querySelectorAll(".filter-btn");
+ const cards = pluginsGrid.querySelectorAll(".plugin-card");
+
+ buttons.forEach((btn) => {
+ btn.addEventListener("click", () => {
+ // Aktualizace aktivního tlačítka
+ buttons.forEach((b) => b.classList.remove("active"));
+ btn.classList.add("active");
+
+ const filter = btn.dataset.filter;
+
+ // Zobrazit / skrýt karty s animací
+ cards.forEach((card) => {
+ const category = card.dataset.category || "";
+ const match = filter === "all" || category === filter;
+
+ if (match) {
+ card.classList.remove("hidden");
+ // Krátké zpoždění pro plynulý efekt
+ requestAnimationFrame(() => {
+ card.style.opacity = "1";
+ card.style.transform = "";
+ });
+ } else {
+ card.classList.add("hidden");
+ }
+ });
+ });
+ });
+}
+
+window.ApolloNvim = {
+ typeWriter,
+ copyText,
+};
diff --git a/web/js/particles.js b/web/js/particles.js
new file mode 100644
index 0000000..bf5319d
--- /dev/null
+++ b/web/js/particles.js
@@ -0,0 +1,358 @@
+/**
+ * particles.js — ApolloNvim Vesmírný Particle Systém
+ * Canvas-based animace hvězd s parallaxem, blikáním, spojeními a padajícími hvězdami.
+ */
+
+class ParticleSystem {
+ constructor(canvasId) {
+ this.canvas = document.getElementById(canvasId);
+ this.ctx = this.canvas.getContext('2d');
+
+ // Konfigurace částic
+ this.config = {
+ count: 200,
+ minSize: 0.5,
+ maxSize: 2.5,
+ colors: ['#ffffff', '#8be9fd', '#bd93f9', '#ff79c6'],
+ connectionDist: 100, // px — maximální vzdálenost pro spojovací linky
+ driftSpeed: 0.25, // max px/frame náhodný drift
+ parallaxStrength: 0.04, // síla parallax efektu myši
+ blinkChance: 0.35, // podíl hvězd, které blikají
+ shootingInterval:[5000, 8000], // ms min/max mezi padajícími hvězdami
+ };
+
+ // Stav myši
+ this.mouse = { x: 0, y: 0, active: false };
+
+ // Pole částic a padající hvězdy
+ this.particles = [];
+ this.shootingStars = [];
+
+ this._animFrameId = null;
+ this._shootingTimer = null;
+
+ this.init();
+ }
+
+ // ─── Inicializace ─────────────────────────────────────────────────────────
+
+ init() {
+ this.resize();
+ this.createParticles();
+ this._bindEvents();
+ this._scheduleShootingStar();
+ this.animate();
+ }
+
+ /** Nastaví rozměry canvasu dle okna */
+ resize() {
+ this.canvas.width = window.innerWidth;
+ this.canvas.height = window.innerHeight;
+ }
+
+ /** Vytvoří pole hvězdičkových částic */
+ createParticles() {
+ this.particles = [];
+ const { count, minSize, maxSize, colors, blinkChance, driftSpeed } = this.config;
+
+ for (let i = 0; i < count; i++) {
+ const blinks = Math.random() < blinkChance;
+ this.particles.push({
+ x: Math.random() * this.canvas.width,
+ y: Math.random() * this.canvas.height,
+ baseX: 0, // bude nastaveno níže
+ baseY: 0,
+ size: minSize + Math.random() * (maxSize - minSize),
+ color: colors[Math.floor(Math.random() * colors.length)],
+ // drift – každá hvězda má svůj vektor
+ vx: (Math.random() - 0.5) * driftSpeed,
+ vy: (Math.random() - 0.5) * driftSpeed,
+ // blikání
+ blinks,
+ opacity: blinks ? 0.3 + Math.random() * 0.7 : 0.6 + Math.random() * 0.4,
+ blinkSpeed: blinks ? 0.005 + Math.random() * 0.015 : 0,
+ blinkDir: Math.random() < 0.5 ? 1 : -1,
+ // parallax offset (akumulovaný)
+ offsetX: 0,
+ offsetY: 0,
+ });
+
+ // baseX/baseY = počáteční pozice (pro parallax výpočet)
+ this.particles[i].baseX = this.particles[i].x;
+ this.particles[i].baseY = this.particles[i].y;
+ }
+ }
+
+ // ─── Hlavní animační smyčka ───────────────────────────────────────────────
+
+ animate() {
+ this._animFrameId = requestAnimationFrame(() => this.animate());
+ const ctx = this.ctx;
+
+ ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+
+ // Výpočet parallax posunu středu obrazovky
+ const cx = this.canvas.width / 2;
+ const cy = this.canvas.height / 2;
+ const mouseOffsetX = this.mouse.active
+ ? (this.mouse.x - cx) * this.config.parallaxStrength
+ : 0;
+ const mouseOffsetY = this.mouse.active
+ ? (this.mouse.y - cy) * this.config.parallaxStrength
+ : 0;
+
+ // ── Aktualizace a kreslení hvězd ──
+ for (const p of this.particles) {
+ // Drift
+ p.baseX += p.vx;
+ p.baseY += p.vy;
+
+ // Wrap-around přes okraje
+ if (p.baseX < -10) p.baseX = this.canvas.width + 10;
+ if (p.baseX > this.canvas.width + 10) p.baseX = -10;
+ if (p.baseY < -10) p.baseY = this.canvas.height + 10;
+ if (p.baseY > this.canvas.height + 10) p.baseY = -10;
+
+ // Parallax offset (každá hvězda reaguje trochu jinak dle velikosti)
+ const parallaxFactor = p.size / this.config.maxSize;
+ p.x = p.baseX + mouseOffsetX * parallaxFactor;
+ p.y = p.baseY + mouseOffsetY * parallaxFactor;
+
+ // Blikání opacity
+ if (p.blinks) {
+ p.opacity += p.blinkSpeed * p.blinkDir;
+ if (p.opacity >= 1.0) { p.opacity = 1.0; p.blinkDir = -1; }
+ if (p.opacity <= 0.15) { p.opacity = 0.15; p.blinkDir = 1; }
+ }
+
+ // Kreslení hvězdy jako malého hvězdičkového tvaru
+ this._drawStar(p);
+ }
+
+ // ── Spojovací linky ──
+ this.drawConnections();
+
+ // ── Padající hvězdy ──
+ this._updateShootingStars();
+ }
+
+ /** Nakreslí hvězdu (5-cípý tvar pro větší, kroužek pro malé) */
+ _drawStar(p) {
+ const ctx = this.ctx;
+ ctx.save();
+ ctx.globalAlpha = p.opacity;
+ ctx.fillStyle = p.color;
+
+ if (p.size >= 1.5) {
+ // Pěticípá hvězdička pro větší částice
+ this._drawStarShape(ctx, p.x, p.y, p.size * 2, p.size, 5);
+ } else {
+ // Jednoduchý kroužek pro malé hvězdičky
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ // Jemná záře kolem hvězdy
+ if (p.size > 1.2) {
+ const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 4);
+ grd.addColorStop(0, this._hexAlpha(p.color, 0.3 * p.opacity));
+ grd.addColorStop(1, this._hexAlpha(p.color, 0));
+ ctx.fillStyle = grd;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, p.size * 4, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ ctx.restore();
+ }
+
+ /** Nakreslí pěticípou hvězdičku */
+ _drawStarShape(ctx, cx, cy, outerR, innerR, points) {
+ const step = Math.PI / points;
+ ctx.beginPath();
+ for (let i = 0; i < points * 2; i++) {
+ const r = i % 2 === 0 ? outerR : innerR;
+ const angle = i * step - Math.PI / 2;
+ const x = cx + Math.cos(angle) * r;
+ const y = cy + Math.sin(angle) * r;
+ i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
+ }
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ // ─── Spojovací linky ─────────────────────────────────────────────────────
+
+ drawConnections() {
+ const ctx = this.ctx;
+ const dist = this.config.connectionDist;
+ const ps = this.particles;
+
+ for (let i = 0; i < ps.length; i++) {
+ for (let j = i + 1; j < ps.length; j++) {
+ const dx = ps[i].x - ps[j].x;
+ const dy = ps[i].y - ps[j].y;
+ const d = Math.sqrt(dx * dx + dy * dy);
+
+ if (d < dist) {
+ const alpha = (1 - d / dist) * 0.25; // max 25% opacity
+ ctx.save();
+ ctx.globalAlpha = alpha;
+ ctx.strokeStyle = '#8be9fd';
+ ctx.lineWidth = 0.5;
+ ctx.beginPath();
+ ctx.moveTo(ps[i].x, ps[i].y);
+ ctx.lineTo(ps[j].x, ps[j].y);
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ }
+ }
+
+ // ─── Padající hvězdy ─────────────────────────────────────────────────────
+
+ /** Naplánuje další padající hvězdu s náhodným zpožděním */
+ _scheduleShootingStar() {
+ const [min, max] = this.config.shootingInterval;
+ const delay = min + Math.random() * (max - min);
+ this._shootingTimer = setTimeout(() => {
+ this.createShootingStar();
+ this._scheduleShootingStar(); // naplánuje další
+ }, delay);
+ }
+
+ /** Vytvoří novou padající hvězdu */
+ createShootingStar() {
+ const angle = (15 + Math.random() * 25) * (Math.PI / 180); // 15°–40°
+ const speed = 6 + Math.random() * 8;
+ const length = 80 + Math.random() * 120;
+
+ this.shootingStars.push({
+ x: Math.random() * this.canvas.width * 0.8,
+ y: Math.random() * this.canvas.height * 0.3,
+ vx: Math.cos(angle) * speed,
+ vy: Math.sin(angle) * speed,
+ length,
+ opacity: 1,
+ trail: [], // pole minulých pozic pro ocas
+ maxTrail: Math.floor(length / speed),
+ done: false,
+ });
+ }
+
+ /** Aktualizuje a kreslí všechny padající hvězdy */
+ _updateShootingStars() {
+ const ctx = this.ctx;
+ this.shootingStars = this.shootingStars.filter(s => !s.done);
+
+ for (const s of this.shootingStars) {
+ // Uloží pozici do ocasu
+ s.trail.push({ x: s.x, y: s.y });
+ if (s.trail.length > s.maxTrail) s.trail.shift();
+
+ // Pohyb
+ s.x += s.vx;
+ s.y += s.vy;
+ s.opacity -= 0.012;
+
+ if (s.opacity <= 0 || s.x > this.canvas.width + 50 || s.y > this.canvas.height + 50) {
+ s.done = true;
+ continue;
+ }
+
+ // Kreslení ocasu (gradient linka)
+ if (s.trail.length > 1) {
+ const grad = ctx.createLinearGradient(
+ s.trail[0].x, s.trail[0].y,
+ s.x, s.y
+ );
+ grad.addColorStop(0, `rgba(255,255,255,0)`);
+ grad.addColorStop(1, `rgba(255,255,255,${s.opacity})`);
+
+ ctx.save();
+ ctx.strokeStyle = grad;
+ ctx.lineWidth = 1.5;
+ ctx.shadowColor = '#ffffff';
+ ctx.shadowBlur = 6;
+ ctx.beginPath();
+ ctx.moveTo(s.trail[0].x, s.trail[0].y);
+ for (const pt of s.trail) ctx.lineTo(pt.x, pt.y);
+ ctx.lineTo(s.x, s.y);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ // Jasná tečka na špičce
+ ctx.save();
+ ctx.globalAlpha = s.opacity;
+ ctx.fillStyle = '#ffffff';
+ ctx.shadowColor = '#8be9fd';
+ ctx.shadowBlur = 10;
+ ctx.beginPath();
+ ctx.arc(s.x, s.y, 1.5, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+ }
+ }
+
+ // ─── Pomocné metody ───────────────────────────────────────────────────────
+
+ /** Převede HEX barvu na rgba string s danou průhledností */
+ _hexAlpha(hex, alpha) {
+ const r = parseInt(hex.slice(1, 3), 16);
+ const g = parseInt(hex.slice(3, 5), 16);
+ const b = parseInt(hex.slice(5, 7), 16);
+ return `rgba(${r},${g},${b},${alpha})`;
+ }
+
+ /** Naváže posluchače událostí */
+ _bindEvents() {
+ // Resize
+ window.addEventListener('resize', () => {
+ this.resize();
+ // Po resize přepočítá baseX/baseY aby hvězdy zůstaly proporcionálně
+ const scaleX = this.canvas.width / (this.canvas.width || 1);
+ const scaleY = this.canvas.height / (this.canvas.height || 1);
+ for (const p of this.particles) {
+ p.baseX = Math.random() * this.canvas.width;
+ p.baseY = Math.random() * this.canvas.height;
+ }
+ });
+
+ // Mouse parallax
+ window.addEventListener('mousemove', (e) => {
+ this.mouse.x = e.clientX;
+ this.mouse.y = e.clientY;
+ this.mouse.active = true;
+ });
+
+ // Dotyk (mobile)
+ window.addEventListener('touchmove', (e) => {
+ if (e.touches.length > 0) {
+ this.mouse.x = e.touches[0].clientX;
+ this.mouse.y = e.touches[0].clientY;
+ this.mouse.active = true;
+ }
+ }, { passive: true });
+
+ window.addEventListener('mouseleave', () => {
+ this.mouse.active = false;
+ });
+ }
+
+ /** Zastaví animaci a časovače */
+ destroy() {
+ if (this._animFrameId) cancelAnimationFrame(this._animFrameId);
+ if (this._shootingTimer) clearTimeout(this._shootingTimer);
+ }
+}
+
+// ─── Auto-init při načtení DOM ─────────────────────────────────────────────
+
+document.addEventListener('DOMContentLoaded', () => {
+ if (document.getElementById('particles-canvas')) {
+ window.particleSystem = new ParticleSystem('particles-canvas');
+ }
+});