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 +

+
+
+ +
+ +
+
+
+ ⚠️ +
+ Důležité upozornění +

+ Vždy se podívej před instalací nových pluginů na + dokumentaci na Githubu. Předejdeš tím případným + problémům! + Vždy si zálohuj stávající konfiguraci + Neovim! +

+
+
+
+
+ + +
+
+
+ ✅ + Vždy si aktualizujte a používejte nejnovější verzi Neovim a všech balíčků. +
+
+
+ + +
+
+ +

+ 🚀 Instalace a nastavení — Starter Edition + +

+ +
+

+ 👉 Pokud ještě nemáte, nainstalujte si Neovim.
+ 👉 Bude potřeba nainstalovat plug.vim abyste mohl/a instalovat rozšíření — návod zde.
+ 👉 Pokud už Neovim máte nainstalovaný, nejprve si zálohujte své aktuální nastavení! +

+
+ +

📁 Konfigurační soubory

+
+
+ + + + Cesty ke konfiguraci +
+
# Linux & Mac:
+/home/user/.config/nvim/init.vim
+
+# Windows:
+~/AppData/Local/nvim/init.vim
+
+ +

Neovim — Krok za krokem

+ +
+ +
+
1
+
+

Záloha stávající konfigurace

+

👉 Nejprve si zálohujte složku nvim — například přejmenováním na nvim.bak.

+
+
+ + + + Terminal + +
+
cp -r ~/.config/nvim ~/.config/nvim.bak
+
+
+
+ +
+
2
+
+

Stažení 🚀 ApolloNvim Starter

+

👉 Nejjednodušší způsob pro Linux a macOS — příkaz níže stažní celý repozitář do správné složky. Tento příkaz přepíše dosavadní složku nvim!

+
+
+ + + + Terminal + +
+
git clone -b nvim https://codeberg.org/ApolloNvim/ApolloNvim2025.git ~/.config/nvim
+
+
+
+ +
+
3
+
+

Spustěte Neovim

+

👉 Neovim pustíte v terminálu příkazem nvim. Odklikejte chybová hlášení — jsou očekávaná při prvním spuštění.

+
+
+ + + + Terminal + +
+
nvim
+
+
+
+ +
+
4
+
+

Instalace pluginů

+

👉 V Neovimu zadejte do command line :PlugInstall, tím nainstalujete všechny pluginy. Restartujte Neovim. Command Line vyvoláte klávesovou zkratkou Shift + :.

+

👉 Pokud některý plugin nechcete používat, stačí ho v souboru init.vim zakomentovat pomocjí dvojích uvozovek ".

+
+
+ + + + Neovim Command Line + +
+
:PlugInstall
+
+
+
+ +
+
5
+
+

Instalace Coc pluginů

+

👉 Do command line zadejte Coc příkaz pro doinstalujování zbýlých pluginů. Ještě jednou restartujte Neovim a vše máte připravené.

+
+
+ + + + Neovim Command Line + +
+
:CocInstall coc-markdown-preview-enhanced coc-webview
+
+
+ ✅ Hotovo! Restartujte Neovim a vše je připravené k použití.
+ 👉 Pokud některý plugin chybí, můžete lehce doinstalovat pomocí vim.plug.
+ 👉 Některé pluginy potřebují nakonfigurovat v jazyce Lua — viz. Instalační návody níže. +
+
+
+ +
+
+
+ + +
+
+ +

+ 🌐 Instalace a nastavení — FrontEnd Edition + +

+ +
+

+ 👉 Pokud ještě nemáte, nainstalujte si Neovim.
+ 👉 Bude potřeba nainstalovat plug.vim — návod zde.
+ 👉 Pokud už Neovim máte nainstalovaný, nejprve si zálohujte své aktuální nastavení! +

+
+ +

📁 Konfigurační soubory

+
+
+ + + + Cesty ke konfiguraci +
+
# Linux & Mac:
+/home/user/.config/nvim/init.vim
+
+# Windows:
+~/AppData/Local/nvim/init.vim
+
+ +

Node.js a LSP balíčky

+
+

+ 👉 Doporučuji mít nainstalovaný Node.js.
+ 👉 Nainstalujte Live-server příkazem npm i live-server.
+ 👉 Ve svém adresáři projektu spustím server příkazem npx live-server.
+ 👉 Pro správnou funkci LSP doinstalujte tyto balíčky: +

+
+ +
+ ⚠️ Pokud terminál vypíše chybu při použití příkazu npm, bude potřeba balíček instalovat jako sudo. +
+ + + +

Neovim — Krok za krokem

+ +
+ +
+
1
+
+

Záloha konfigurace

+
+
+ + + + Terminal + +
+
cp -r ~/.config/nvim ~/.config/nvim.bak
+
+
+
+ +
+
2
+
+

Stažení 🚀 ApolloNvim FrontEnd

+

👉 Pozor — tento příkaz přepíše dosavadní složku nvim v ~/.config/nvim. Nezačomente na zálohu!

+
+
+ + + + Terminal + +
+
git clone -b frontend https://codeberg.org/ApolloNvim/ApolloNvim2025.git ~/.config/nvim
+
+
+
+ +
+
3
+
+

Spustěte Neovim a instalujte pluginy

+

👉 Spusťte Neovim příkazem nvim, odklikejte chybová hlášení, pak spusťte :PlugInstall. Restartujte.

+
+
+ + + + Neovim + +
+
:PlugInstall
+
+
+
+ +
+
4
+
+

Instalace všech Coc rozšíření

+

👉 Zadejte Coc příkaz pro doinstalaci všech rozšíření. Ještě jednou restartujte Neovim.

+
+
+ + + + Neovim Command Line + +
+
:CocInstall coc-tailwindcss coc-json coc-html-css-support coc-html coc-emmet coc-xml coc-tsserver coc-lsp-wl coc-lit-html coc-htmldjango coc-css coc-markdown-preview-enhanced coc-webview coc-phpls coc-prettier coc-eslint
+
+
+ ✅ Hotovo! Restartujte Neovim a vše je připravené!
+ 👉 Pokud se vám vše nechce instalovat ručně, stačí jen použít příkaz :PlugInstall a vse bude hned fungovat.
+ 👉 Některé pluginy potřebují nakonfigurovat v jazyce Lua — viz. Instalační návody níže. +
+
+
+ +
+
5
+
+

Live Server — spuštění projektu

+

👉 Ve svém adresáři projektu budete moci pouštět index.html příkazem:

+
+
+ + + + Terminal (v adresáři projektu) + +
+
npx live-server
+
+

👉 Tuto konfiguraci používám na Linuxu i macOS.

+
+
+ +
+
+
+ + + + + +
+
+

+ Časté otázky +

+
+
+ + 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. +

+
+
+
+
+ + + +
+ + + + + + + + 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

+
+
+ + +
+
+
+

📋 Changelog

+

+ Historie verzí a provedených změn +

+
+
+ +
+
    + +
  1. +
    + v26.05 + Aktuální + +
    +
    +
      +
    • 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 +
    • +
    +
    +
  2. + + +
  3. +
    + v25.04 + Duben 2025 + +
    +
    +
      +
    • + Verze 25.04 – aktuální stabilní + vydání +
    • +
    +
    +
  4. + + +
  5. +
    + v25.02 + Únor 2025 + +
    +
    +
      +
    • Nový web ApolloNvim
    • +
    • Lokalizace do angličtiny
    • +
    • + Vylepšená instalace pro jednodušší + onboarding +
    • +
    • Podpora pouze pro Linux a Mac
    • +
    +
    +
  6. + + +
  7. +
    + v25.01 + Leden 2025 + +
    +
    +
      +
    • + Nová instalace s přepracovaným + procesem nastavení +
    • +
    • + Nový website – moderní design a + lepší přehlednost +
    • +
    • + Nový obrázek ApolloNvim – + aktualizovaná vizuální identita +
    • +
    +
    +
  8. + + +
  9. +
    + v24.10 + Říjen 2024 + +
    +
    +
      +
    • + 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 +
    • +
    +
    +
  10. +
+
+ +
+ +
+ + +
+
+
+

🗺️ Plán vývoje

+

+ Co připravujeme pro budoucí verze ApolloNvim +

+
+
+ +
+
+ +

+ Nová uvítací obrazovka +

+

+ Vlastní dashboard při startu – rychlé otevření + posledních souborů, zkratky a statistiky. +

+
+ Plánováno +
+
+ +
+ +

Nové pluginy

+

+ Rozšiřování knihovny pluginů o nejlepší nástroje + z ekosystému Neovim komunity. +

+
+ Plánováno +
+
+ +
+ +

+ Návody k pluginům +

+

+ Podrobné tutoriály pro každý zahrnutý plugin s + příklady a tipy pro efektivní použití. +

+
+ Probíhá +
+
+ +
+ +

+ Instalátor přes link z Git +

+

+ Jednořádkový instalátor přímo z Codeberg – bez + klonování, bez komplikací. +

+
+ Plánováno +
+
+
+ +
+ +
+ + +
+
+
+ +

+ Sledujte novinky na Telegramu +

+

+ Buďte první, kdo se dozví o nových verzích, + pluginech a tutoriálech ApolloNvim. +

+ + ✈️ Připojit se na Telegram + +
+
+
+
+ + + + + + + + + + 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 +

+
+
+ +
+ +
+
+
+ 🛡️ +
+ Bezpečnost a soukromí jsou prioritou +

+ Zde najdete všechny pluginy s odkazem na zdroj. + Pluginy které nechcete používat jednoduše + zakomentujte v init.vim pomocí + dvojitých uvozovek ". +

+
+
+
+
+ + +
+
+
+ + + + + + + + + +
+
+
+ + +
+
+

+ Hlavní pluginy +

+
+
+
+ 🔒 +
+

+ Autoclosetag +

+ Editor +
+ ↗ +
+

+ Automatické uzavírání závorek, tagů a všech typů + párových znaků. +

+
+ +
+
+ 📦 +
+

Coc.nvim

+ LSP +
+ ↗ +
+

+ Package manager pro LSP rozšíření. Základ celé + ApolloNvim konfigurace. +

+
+ +
+
+ 🌐 +
+

html5.vim

+ Syntax +
+ ↗ +
+

+ Plugin pro HTML5 syntax highlighting a + dokončování. +

+
+ +
+
+ 🌲 +
+

NERDTree

+ UI +
+ ↗ +
+

+ Stromový souborový panel pro přehlednou navigaci + v projektu. +

+
+ +
+
+ 🤖 +
+

Codeium

+ AI +
+ ↗ +
+

+ AI asistent - zdarma alternativa k GitHub + Copilotu. Výchozí aktivní. +

+
+ +
+
+ 🧠 +
+

TabNine

+ AI +
+ ↗ +
+

+ Alternativní AI asistent. Zakomentován - + použijte místo Codeia. +

+
+ +
+
+ 🏠 +
+

Startify

+ UI +
+ ↗ +
+

+ Alternativní uvítací obrazovka s pokaždé jiným + IT mottem v NORMAL módu. +

+
+ +
+
+ 💬 +
+

+ NERD Commenter +

+ Editor +
+ ↗ +
+

+ Zakomentování a odkomentování kódu zkratkou + Ctrl+K. +

+
+ +
+
+ ⏱️ +
+

WakaTime

+ Produktivita +
+ ↗ +
+

+ Měření času stráveného kódováním na různých + projektech. Propojení přes API. +

+
+ +
+
+ 💾 +
+

Auto-save

+ Editor +
+ ↗ +
+

+ Automatické ukládání souborů při přechodu z + INSERT do NORMAL módu. +

+
+ +
+
+ 🎨 +
+

+ Tree-sitter +

+ Syntax +
+ ↗ +
+

+ Pokročilé zvýraznění syntaxe kódu pomocí AST + parseru. +

+
+ +
+
+ 📏 +
+

Hlchunk

+ UI +
+ ↗ +
+

+ Propojuje kód vertikálními linkami pro lepší + orientaci v odsazení. +

+
+ +
+
+ 🔀 +
+

+ Vim-Fugitive +

+ Git +
+ ↗ +
+

+ Kompletní Git integrace přímo v Neovimu. Commit, + diff, blame a více. +

+
+ +
+
+ 🎨 +
+

Colorizer

+ Editor +
+ ↗ +
+

+ Zobrazuje náhled barev přímo v kódu dle + hex/rgb/hsl kódu. +

+
+ +
+
+ ⚡ +
+

+ Vim-javascript +

+ Syntax +
+ ↗ +
+

+ Pokročilá podpora JavaScript syntaxe a + dokončování. +

+
+ +
+
+ 💻 +
+

+ Toggleterm +

+ UI +
+ ↗ +
+

+ Vylepšený integrovaný terminál s přepínáním a + plovoucím oknem. +

+
+ +
+
+ 📊 +
+

+ Vim-Gitgutter +

+ Git +
+ ↗ +
+

+ Zobrazuje Git nápovědu ke změnám přímo v + číslovacím sloupci. +

+
+ +
+
+ ✍️ +
+

Gitsigns

+ Git +
+ ↗ +
+

+ Zobrazuje změny v kódu oproti Git historii s + inline markery. +

+
+ +
+
+ 🔧 +
+

Plenary

+ Core +
+ ↗ +
+

+ Lua utility library - závislost pro mnohé Neovim + pluginy. +

+
+
+
+
+ + +
+
+

+ Coc Extensions +

+

+ Coc rozšíření přidávají pokročilou podporu pro různé + jazyky a nástroje. + Kompletní seznam ↗ +

+ +
+
+ + + + Neovim - Instalace všech Coc rozšíření + +
+
:CocInstall coc-tailwindcss coc-json coc-html-css-support coc-html coc-emmet coc-xml coc-tsserver coc-lsp-wl coc-lit-html coc-htmldjango coc-css coc-markdown-preview-enhanced coc-webview coc-phpls coc-prettier coc-eslint coc-highlighting
+
+ + +
+
+ + +
+
+

+ 🎨 Barevná témata +

+

+ Přepnutí tématu v Neovimu: +

+
+
+ + + + Neovim + +
+
:colorscheme dracula   # nebo Tab pro procházení
+
+ +
+
+
+ + + + +
+
+ Dracula + ⭐ Default +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Gruvbox +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Catppuccin +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Nord +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Tokyo Night +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Nightfox +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Sonokai +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ NeoSolarized +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Moonfly +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Onedark +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Edge +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ GitHub +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Nightfly +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Miasma +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Pink Moon +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Anderson +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Tender +
+ GitHub ↗ +
+ +
+
+ + + + +
+
+ Night Owl +
+ GitHub ↗ +
+
+
+
+
+ + + + + + + + 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.

+
+
+ + +
+
+
+

Všechny tutoriály

+

+ Krok za krokem od instalace až po pokročilé workflow +

+
+
+ +
+ +
+ 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. +

+ +
+
+ +
+ +
+ + +
+
+
+

💡 Rychlé tipy

+

+ Kratké rady pro každodenní práci s ApolloNvim +

+
+
+ +
+ +
+ 💡 Tip #1 +

+ Oprava Markdown syntax +

+

+ Pro správnou syntax highlighting Markdown v + Neovim je potřeba nainstalovat Tree-sitter + parser příkazem: +

+
+
+ + + + Neovim +
+
+ :TSInstall markdown +
+
+
+ + +
+ 💡 Tip #2 +

AutoSave nastavení

+

+ Plugin auto-save.nvim umožňuje + automatické ukládání s nastavitelným zpožděním + (delay v milisekundách). Přidejte do konfigurace + pluginu debounce_delay = 1000 pro + uložení 1 sekundu po ukončení psaní. +

+
+ + +
+ 💡 Tip #3 +

+ Colorscheme auto-start +

+

+ Chcete-li, aby se barevné téma načítalo + automaticky při spuštění, přidejte do + init.vim: +

+
+
+ + + + init.vim +
+
+ colorscheme dracula
autocmd + VimEnter * colorscheme dracula
+
+
+
+ + +
+ 💡 Tip #4 +

Kde najít init.vim

+

+ Konfigurační soubor Neovim se nachází na různých + místech podle OS:
+ Linux/Mac: + ~/.config/nvim/init.vim
+ Windows: + ~/AppData/Local/nvim/init.vim
+ Lua alternativa: init.lua ve + stejném adresáři. +

+
+ + +
+ 💡 Tip #5 +

+ Vkládání z externích zdrojů +

+

+ Pro vložení textu ze systémové schránky + (zkopírovaného z prohlížeče, jiné aplikace + apod.) použijte v INSERT módu kombinaci kláves + Ctrl+Shift+V. + Neovim tak správně vloží text bez automatické + indentace. +

+
+
+ +
+ +
+ + +
+
+
+ +

+ Chybí vám nějaký tutoriál? +

+

+ Napište nám na Telegram a my ho přidáme do seznamu! +

+ + ✈️ Telegram + +
+
+
+
+ + + + + + + + + + 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ě. +

+ + +
+ + + +
+ + +
+ +
+

🧭 Pohyb

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KlávesaAkce
hPohyb vlevo
jPohyb dolů
kPohyb nahoru
lPohyb vpravo
w + Přesun na začátek dalšího slova +
b + Přesun na začátek předchozího + slova +
ggPřesun na začátek dokumentu
GPřesun na konec dokumentu
+
+
+ + +
+

🔄 Módy

+ + +
+
+
+ NORMAL +
+ výchozí +
+ +
+
+ INSERT +
+ i, a, o +
+ +
+
+ VISUAL +
+ v, V +
+ +
+
+ COMMAND +
+ : +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KlávesaAkce
ESCPřepnutí do NORMAL módu
iINSERT – vložit před kurzor
I + INSERT – vložit na začátek řádku +
aINSERT – vložit za kurzor
A + INSERT – vložit na konec řádku +
vPřepnutí do VISUAL módu
+ Shift+: + Přepnutí do COMMAND módu
+
+
+ + +
+

+ ✏️ Editace v NORMAL módu +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KlávesaAkce
ddSmazání celého řádku
dwSmazání slova
yyKopírování řádku (yank)
pVložení pod kurzor
PVložení před kurzor
uVrácení změny (undo)
+ Ctrl+r + Krok vpřed (redo)
. + Opakování posledního příkazu +
+ Ctrl+w + Přepínání mezi okny
+
+
+ + +
+

🔍 Vyhledávání

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Klávesa / Příkaz + Akce
+ /text + Vyhledání textu dopředu
nPřechod na další výskyt
NPřechod na předchozí výskyt
:s/old/new/g + Nahrazení textu v celém + dokumentu +
+
+
+
+ + + + + + + + + + + + +
+ +
+
+ + + + + + + + + + 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 +
+ +

+ 🚀 ApolloNvim +

+ +

+ +

+ +

+ Rychlý, lehký a plně konfigurovatelný.
+ Postavený na Plug.vim, Coc a Lua. +

+ + + +
+ + + + +
+ + +
+
+ +
+

+ Proč ApolloNvim? + +

+

+ Vše co potřebuješ pro moderní vývoj – přehledně na jednom místě. +

+
+ +
+ +
+ +

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.

+
+ +
+
+
+ + +
+
+ +
+

+ Dvě edice pro každého + +

+

+ Vyber si edici, která odpovídá tvým potřebám. +

+
+ +
+ + +
+
+ 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ů +
+ +
+
+
+ + +
+
+ +
+

+ Jednoduchá instalace + +

+

+ Nastav si ApolloNvim ve třech krocích. Více detailů na stránce  + Instalace. +

+
+ +
+ +
+
+ + + + Krok 1 – Klonování repozitáře + +
+
git clone -b frontend https://codeberg.org/ApolloNvim/ApolloNvim2025.git ~/.config/nvim
+
+ +
+
+ + + + Krok 2 – Instalace pluginů (uvnitř Neovimu) + +
+
:PlugInstall
+
+ +
+
+ + + + Krok 3 – Instalace LSP serverů (FrontEnd Edition) + +
+
:CocInstall coc-html coc-css coc-tsserver coc-phpls coc-emmet coc-prettier coc-eslint
+
+ +
+ + + +
+
+ + +
+ +
+ +
+ + + + + + + + + + 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');
+  }
+});