docs: Update README and CHANGELOG with web assets optimization

- Document web assets restructuring with minification pipeline
  * 32% compression (26KB → 18KB)
  * Bilingual support (EN/ES) preserved
  * Automated minify.sh script for version sync
  * Complete README.md guide with examples

- Add web assets structure to project directory layout in README
  - New assets/web/ section with source and production versions
  - Reference to minification script and documentation

- Include Just recipes documentation for local development
  - Show `just help` commands for discovering available recipes
  - Document 50+ recipes for build, test, and CI operations

- Update CHANGELOG with infrastructure improvements
  - Web assets optimization (32% compression)
  - Just recipes CI/CD system (50+ commands)
  - Code quality improvements and bug fixes
  - Markdown linting compliance achieved
  - All tests passing (55/55 in vapora-backend)

- Add build and test results to unreleased changes section
  - Clean compilation with 0 warnings in vapora-backend
  - 55 tests passing
  - Clippy compliance achieved
This commit is contained in:
Jesús Pérez 2026-01-11 20:12:56 +00:00
parent d14150da75
commit 8f6a884f6e
8 changed files with 1632 additions and 3 deletions

View File

@ -9,9 +9,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Intelligent learning system for multi-agent coordination
- Cost optimization with budget enforcement
- Gradual production deployment guide
- **Web Assets Optimization**: Restructured landing page with minification pipeline
- Separated source (`assets/web/src/index.html`) from minified production version
- Automated minification script (`assets/web/minify.sh`) for version synchronization
- 32% compression achieved (26KB → 18KB)
- Bilingual content (English/Spanish) preserved with localStorage persistence
- Complete documentation in `assets/web/README.md`
- **Infrastructure & Build System**
- Just recipes for CI/CD automation (50+ recipes organized by category)
- Parametrized help system for command discovery
- Integration with development workflows
### Changed
- **Code Quality Improvements**
- Removed unused imports from API and workflow modules (5+ files)
- Fixed 6 unnecessary `mut` keyword warnings in provider analytics
- Improved code patterns: converted verbose match to `matches!` macro (workflow/state.rs)
- Applied automatic clippy fixes for idiomatic Rust
- **Documentation & Linting**
- Fixed markdown linting compliance in `assets/web/README.md`
- Proper code fence language specifications (MD040)
- Blank lines around code blocks (MD031)
- Table formatting with compact style (MD060)
### Fixed
- **Compilation & Testing**
- Eliminated all unused import warnings in vapora-backend
- Suppressed architectural dead code with appropriate attributes
- All 55 tests passing in vapora-backend
- 0 compilation errors, clean build output
### Technical Details
- **Architecture**: Refactored unused imports from workflow and API modules
- Tests moved to test-only scope for AgentConfig/RegistryConfig types
- Intentional suppression for components not yet integrated
- Future-proof markers for architectural patterns
- **Build Status**: Clean compilation pipeline
- `cargo build -p vapora-backend`
- `cargo clippy -p vapora-backend` ✅ (5 nesting suggestions only)
- `cargo test -p vapora-backend` ✅ (55/55 passing)
## [1.2.0] - 2026-01-11

View File

@ -185,6 +185,12 @@
# Install dependencies
cargo build
# Available Just recipes (50+ commands)
just help # Show all available recipes
just help build # Show build recipes
just help test # Show test recipes
just help ci # Show CI recipes
# Setup SurrealDB (Docker)
docker run -d --name surrealdb \
-p 8000:8000 \
@ -356,6 +362,13 @@ provisioning workflow run workflows/deploy-full-stack.yaml
│ ├── vapora-analytics/ # Event pipeline + usage stats
│ ├── vapora-worktree/ # Git worktree management
│ └── vapora-doc-lifecycle/ # Documentation management
├── assets/
│ ├── web/ # Landing page (optimized + minified)
│ │ ├── src/index.html # Source (readable, 26KB)
│ │ ├── index.html # Production (minified, 18KB)
│ │ ├── minify.sh # Auto-minification script
│ │ └── README.md # Web assets guide
│ └── vapora.svg # Logo
├── kubernetes/ # K8s manifests (base, overlays, platform)
├── migrations/ # SurrealDB migrations
├── config/ # Configuration files (TOML)

218
assets/web/README.md Normal file
View File

@ -0,0 +1,218 @@
# Vapora Web Assets
Web-based landing page and static content for Vapora.
## Directory Structure
```text
assets/web/
├── src/
│ └── index.html # Source HTML (readable, 26KB)
├── index.html # Minified/Production HTML (18KB)
└── README.md # This file
```
## Files
### `src/index.html` - Source Version
- **Purpose**: Development and maintenance
- **Size**: 26KB (uncompressed)
- **Content**:
- Full formatting and indentation
- Inline CSS and JavaScript
- Bilingual (English/Spanish) content
- Language-aware dynamic switching
- Interactive agent showcase
- Technology stack display
**Use for:**
- Editing content
- Understanding structure
- Version control
- Making translations updates
### `index.html` - Production Version
- **Purpose**: Served to browsers (fast loading)
- **Size**: 18KB (32% compression)
- **Optimizations**:
- Removed all comments
- Compressed CSS (removed spaces, combined rules)
- Minified JavaScript (single line)
- Removed whitespace between tags
- Preserved all functionality
**Use for:**
- Production web server
- CDN distribution
- Browser caching
- Fast load times
## How to Use
### Development
Edit `src/index.html`:
```bash
# Edit source file
nano assets/web/src/index.html
# Regenerate minified version (script below)
```
### Update Minified Version
When you update `src/index.html`, regenerate `index.html`:
```bash
# Using the minification script (Perl)
perl -e '
use strict;
use warnings;
open(my $fh, "<", "assets/web/src/index.html") or die $!;
my $content = do { local $/; <$fh> };
close($fh);
# Remove comments
$content =~ s/<!--.*?-->//gs;
# Compress whitespace in style tags
$content =~ s/(<style[^>]*>)(.*?)(<\/style>)/
my $before = $1;
my $style = $2;
my $after = $3;
$style =~ s{\/\*.*?\*\/}{}gs;
$style =~ s{\s+}{ }gs;
$style =~ s{\s*([{}:;,>+~])\s*}{$1}gs;
$before . $style . $after;
/gies;
# Compress whitespace in script tags
$content =~ s/(<script[^>]*>)(.*?)(<\/script>)/
my $before = $1;
my $script = $2;
my $after = $3;
$script =~ s{\/\/.*$}{}gm;
$script =~ s{\s+}{ }gs;
$script =~ s{\s*([{}();,])\s*}{$1}gs;
$before . $script . $after;
/gies;
# Remove whitespace between tags
$content =~ s/>\s+</></gs;
$content =~ s/\s+/ /gs;
$content =~ s/^\s+|\s+$//g;
open(my $out, ">", "assets/web/index.html") or die $!;
print $out $content;
close($out);
print "✅ Minified version created\n";
'
```
### Deployment
Serve `index.html` from your web server:
```bash
# Using Rust
cargo install static-web-server
static-web-server -d assets/web/
# Using Python
python3 -m http.server --directory assets/web
# Using Node.js
npx http-server assets/web
# Using nginx
# Point root to assets/web/
# Serve index.html as default
```
## Features
✅ **Responsive Design**
- Mobile-first approach
- Flexbox layouts
- Media queries for mobile
✅ **Performance**
- Inline CSS (no separate requests)
- Inline JavaScript (no blocking external scripts)
- Minimal dependencies (no frameworks)
- 18KB minified size
✅ **Bilingual**
- English and Spanish
- LocalStorage persistence
- Data attributes for translations
- Dynamic language switching
✅ **Modern CSS**
- CSS Gradients
- Animations (fadeInUp)
- Hover effects
- Grid layouts
✅ **Styling**
- Vapora color scheme
- Gradient backgrounds
- Monospace font (JetBrains Mono)
- Smooth transitions
## Content Sections
1. **Hero** - Title, tagline, logo
2. **Problems** - 4 problems Vapora solves
3. **How It Works** - Feature overview
4. **Technology Stack** - Tech badges
5. **Available Agents** - Agent showcase (12 agents)
6. **CTA** - Call-to-action button
7. **Footer** - Credits and links
## Translations
All text content is bilingual. Edit data attributes in `src/index.html`:
```html
<!-- English/Spanish example -->
<span data-en="Hello" data-es="Hola">Hello</span>
```
The JavaScript automatically updates based on selected language.
## Maintenance
- Source edits go in `src/index.html`
- Regenerate `index.html` when source changes
- Both files are versioned in git
- Keep them in sync
## Git Workflow
```bash
# Edit source
git add assets/web/src/index.html
git add assets/web/index.html
git commit -m "Update landing page content"
git push
```
## Compression Statistics
|File|Size|Type|
|---|---|---|
|`src/index.html`|26KB|Source (readable)|
|`index.html`|18KB|Production (minified)|
|**Compression**|**32%**|**Saved 8.8KB**|
---
**Last Updated**: 2026-01-11
**Version**: 1.2.0 (matches Vapora release)

1
assets/web/index.html Normal file

File diff suppressed because one or more lines are too long

87
assets/web/minify.sh Executable file
View File

@ -0,0 +1,87 @@
#!/bin/bash
# Minify index.html from src/ to production version
# Usage: ./minify.sh
set -e
SRC_FILE="$(dirname "$0")/src/index.html"
OUT_FILE="$(dirname "$0")/index.html"
TEMP_FILE="${OUT_FILE}.tmp"
if [ ! -f "$SRC_FILE" ]; then
echo "❌ Source file not found: $SRC_FILE"
exit 1
fi
echo "🔨 Minifying HTML..."
echo " Input: $SRC_FILE"
echo " Output: $OUT_FILE"
perl -e "
use strict;
use warnings;
open(my \$fh, '<', '$SRC_FILE') or die \$!;
my \$content = do { local \$/; <\$fh> };
close(\$fh);
# Remove HTML comments
\$content =~ s/<!--.*?-->//gs;
# Compress CSS (remove spaces and comments)
\$content =~ s/(<style[^>]*>)(.*?)(<\/style>)/
my \$before = \$1;
my \$style = \$2;
my \$after = \$3;
\$style =~ s{\/\*.*?\*\/}{}gs;
\$style =~ s{\s+}{ }gs;
\$style =~ s{\s*([{}:;,>+~])\s*}{\$1}gs;
\$before . \$style . \$after;
/gies;
# Compress JavaScript (remove comments and extra spaces)
\$content =~ s/(<script[^>]*>)(.*?)(<\/script>)/
my \$before = \$1;
my \$script = \$2;
my \$after = \$3;
\$script =~ s{\/\/.*\$}{}gm;
\$script =~ s{\s+}{ }gs;
\$script =~ s{\s*([{}();,])\s*}{\$1}gs;
\$before . \$script . \$after;
/gies;
# Remove whitespace between tags
\$content =~ s/>\s+</></gs;
# Compress general whitespace
\$content =~ s/\s+/ /gs;
# Trim
\$content =~ s/^\s+|\s+\$//g;
open(my \$out, '>', '$TEMP_FILE') or die \$!;
print \$out \$content;
close(\$out);
" || {
echo "❌ Minification failed"
rm -f "$TEMP_FILE"
exit 1
}
mv "$TEMP_FILE" "$OUT_FILE"
# Show statistics
original=$(wc -c < "$SRC_FILE")
minified=$(wc -c < "$OUT_FILE")
saved=$((original - minified))
percent=$((saved * 100 / original))
echo ""
echo "✅ Minification complete!"
echo ""
echo "📊 Compression statistics:"
printf " Original: %6d bytes\n" "$original"
printf " Minified: %6d bytes\n" "$minified"
printf " Saved: %6d bytes (%d%%)\n" "$saved" "$percent"
echo ""
echo "$OUT_FILE is ready for production"

868
assets/web/src/index.html Normal file
View File

@ -0,0 +1,868 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title
data-en="Vapora - Intelligent Development Orchestration"
data-es="Vapora - Orquestación Inteligente de Desarrollo"
>
Vapora
</title>
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700;800&display=swap"
rel="stylesheet"
/>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "JetBrains Mono", monospace;
background: #0a0118;
color: #ffffff;
overflow-x: hidden;
}
.gradient-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background:
radial-gradient(
circle at 20% 50%,
rgba(168, 85, 247, 0.15) 0%,
transparent 50%
),
radial-gradient(
circle at 80% 80%,
rgba(34, 211, 238, 0.15) 0%,
transparent 50%
),
radial-gradient(
circle at 40% 90%,
rgba(236, 72, 153, 0.1) 0%,
transparent 50%
);
}
.language-toggle {
position: fixed;
top: 2rem;
right: 2rem;
z-index: 100;
display: flex;
gap: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(34, 211, 238, 0.3);
border-radius: 20px;
padding: 0.3rem 0.3rem;
}
.lang-btn {
background: transparent;
border: none;
color: #94a3b8;
padding: 0.5rem 1rem;
border-radius: 18px;
cursor: pointer;
font-weight: 700;
font-size: 0.85rem;
text-transform: uppercase;
transition: all 0.3s ease;
font-family: "JetBrains Mono", monospace;
}
.lang-btn.active {
background: linear-gradient(135deg, #22d3ee 0%, #a855f7 100%);
color: #fff;
}
.lang-btn:hover {
color: #22d3ee;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
position: relative;
}
header {
text-align: center;
padding: 5rem 0 4rem;
animation: fadeInUp 0.8s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.status-badge {
display: inline-block;
background: rgba(34, 211, 238, 0.2);
border: 1px solid #22d3ee;
color: #22d3ee;
padding: 0.5rem 1.5rem;
border-radius: 50px;
font-size: 0.85rem;
font-weight: 700;
margin-bottom: 1.5rem;
}
.logo-container {
margin-bottom: 2rem;
}
.logo-container img {
max-width: 440px;
width: 100%;
height: auto;
filter: drop-shadow(0 0 30px rgba(34, 211, 238, 0.4));
}
.tagline {
font-size: 0.95rem;
color: #22d3ee;
font-weight: 400;
letter-spacing: 0.1em;
text-transform: uppercase;
margin-bottom: 1rem;
}
h1 {
font-size: 2.8rem;
font-weight: 800;
line-height: 1.2;
margin-bottom: 1.5rem;
background: linear-gradient(
135deg,
#22d3ee 0%,
#a855f7 50%,
#ec4899 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.15rem;
color: #cbd5e1;
max-width: 800px;
margin: 0 auto 2rem;
line-height: 1.8;
}
.highlight {
color: #22d3ee;
font-weight: 700;
}
.section {
margin: 4rem 0;
animation: fadeInUp 0.8s ease-out;
}
.section-title {
font-size: 2rem;
font-weight: 800;
margin-bottom: 2rem;
color: #22d3ee;
text-align: center;
}
.section-title span {
background: linear-gradient(135deg, #ec4899 0%, #a855f7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.problems-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.problem-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(168, 85, 247, 0.3);
border-radius: 12px;
padding: 2rem;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.problem-card:hover {
transform: translateY(-5px);
background: rgba(255, 255, 255, 0.05);
border-color: rgba(34, 211, 238, 0.5);
}
.problem-number {
font-size: 2rem;
font-weight: 800;
background: linear-gradient(135deg, #22d3ee 0%, #a855f7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1;
margin-bottom: 0.5rem;
}
.problem-card h3 {
color: #ec4899;
font-size: 1.05rem;
margin-bottom: 0.7rem;
font-weight: 700;
}
.problem-card p {
color: #cbd5e1;
font-size: 0.9rem;
line-height: 1.6;
}
.tech-stack {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-top: 2rem;
justify-content: center;
}
.tech-badge {
background: rgba(34, 211, 238, 0.15);
border: 1px solid #22d3ee;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.8rem;
color: #22d3ee;
font-weight: 700;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.feature-box {
background: linear-gradient(
135deg,
rgba(34, 211, 238, 0.1) 0%,
rgba(168, 85, 247, 0.1) 100%
);
border-radius: 12px;
padding: 2rem;
border-left: 4px solid #22d3ee;
transition: all 0.3s ease;
}
.feature-box:hover {
background: linear-gradient(
135deg,
rgba(34, 211, 238, 0.15) 0%,
rgba(168, 85, 247, 0.15) 100%
);
transform: translateY(-3px);
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.feature-title {
font-size: 1.15rem;
font-weight: 700;
color: #22d3ee;
margin-bottom: 0.7rem;
}
.feature-text {
color: #cbd5e1;
font-size: 0.95rem;
line-height: 1.7;
}
.agents-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.agent-item {
background: rgba(236, 72, 153, 0.1);
padding: 1.2rem;
border-radius: 8px;
font-size: 0.9rem;
border: 1px solid rgba(236, 72, 153, 0.3);
transition: all 0.2s ease;
text-align: center;
}
.agent-item:hover {
background: rgba(236, 72, 153, 0.15);
transform: translateY(-2px);
}
.agent-name {
color: #ec4899;
font-weight: 700;
display: block;
margin-bottom: 0.3rem;
}
.agent-role {
color: #94a3b8;
font-size: 0.85rem;
}
.cta-section {
text-align: center;
margin: 5rem 0 3rem;
padding: 4rem 2rem;
background: linear-gradient(
135deg,
rgba(34, 211, 238, 0.1) 0%,
rgba(236, 72, 153, 0.1) 100%
);
border-radius: 20px;
border: 1px solid rgba(168, 85, 247, 0.3);
}
.cta-title {
font-size: 2rem;
font-weight: 800;
margin-bottom: 1rem;
background: linear-gradient(135deg, #22d3ee 0%, #ec4899 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.cta-button {
display: inline-block;
background: linear-gradient(
135deg,
#22d3ee 0%,
#a855f7 50%,
#ec4899 100%
);
color: #fff;
padding: 1.1rem 2.8rem;
border-radius: 50px;
text-decoration: none;
font-weight: 800;
font-size: 1rem;
transition: all 0.3s ease;
box-shadow: 0 10px 30px rgba(34, 211, 238, 0.3);
text-transform: uppercase;
letter-spacing: 0.05em;
border: none;
cursor: pointer;
}
.cta-button:hover {
transform: translateY(-3px) scale(1.05);
box-shadow: 0 20px 50px rgba(34, 211, 238, 0.5);
}
footer {
text-align: center;
padding: 3rem 0 2rem;
color: #64748b;
border-top: 1px solid rgba(255, 255, 255, 0.1);
margin-top: 4rem;
font-size: 0.9rem;
}
footer p:first-child {
font-weight: 700;
color: #94a3b8;
}
footer p:last-child {
margin-top: 0.5rem;
font-size: 0.85rem;
}
.hidden {
display: none;
}
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
.hero-subtitle {
font-size: 1rem;
}
.logo-container img {
max-width: 352px;
}
.section-title {
font-size: 1.6rem;
}
.cta-title {
font-size: 1.6rem;
}
.language-toggle {
top: 1rem;
right: 1rem;
}
}
</style>
</head>
<body>
<div class="gradient-bg"></div>
<div class="language-toggle">
<button
class="lang-btn active"
data-lang="en"
onclick="switchLanguage('en')"
>
EN
</button>
<button class="lang-btn" data-lang="es" onclick="switchLanguage('es')">
ES
</button>
</div>
<div class="container">
<header>
<span class="status-badge" data-en="✅ v1.2.0" data-es="✅ v1.2.0"
>✅ v1.2.0</span
>
<div class="logo-container">
<img src="/vapora.svg" alt="Vapora - Development Orchestration" />
</div>
<p class="tagline">Evaporate complexity</p>
<h1
data-en="Development Flows<br>When Teams and AI Orchestrate"
data-es="El Desarrollo Fluye<br>Cuando Equipos e IA Orquestan"
>
Development Flows
</h1>
<p class="hero-subtitle">
<span
class="highlight"
data-en="Specialized agents"
data-es="Agentes especializados"
>Specialized agents</span
>
<span
data-en="orchestrate pipelines for design, implementation, testing, documentation and deployment. Agents learn from history and optimize costs automatically."
data-es="orquestan pipelines para diseño, implementación, testing, documentación y deployment. Los agentes aprenden del historial y optimizan costos automáticamente."
>orchestrate pipelines for design, implementation, testing,
documentation and deployment. Agents learn from history and optimize
costs automatically.</span
>
<strong data-en="100% self-hosted." data-es="100% self-hosted."
>100% self-hosted.</strong
>
<span data-en="" data-es=""></span>
</p>
</header>
<section class="section">
<h2 class="section-title">
<span
data-en="The 4 Problems It Solves"
data-es="Los 4 Problemas que Resuelve"
>The 4 Problems It Solves</span
>
</h2>
<div class="problems-grid">
<div class="problem-card">
<div class="problem-number">01</div>
<h3 data-en="Context Switching" data-es="Cambio de Contexto">
Context Switching
</h3>
<p
data-en="Developers jump between tools constantly. Vapora unifies everything in one intelligent system where context flows."
data-es="Los developers saltan constantemente entre herramientas. Vapora unifica todo en un sistema inteligente donde el contexto fluye."
>
Developers jump between tools constantly. Vapora unifies
everything in one intelligent system where context flows.
</p>
</div>
<div class="problem-card">
<div class="problem-number">02</div>
<h3
data-en="Knowledge Fragmentation"
data-es="Fragmentación de Conocimiento"
>
Knowledge Fragmentation
</h3>
<p
data-en="Decisions lost in threads, code scattered, docs unmaintained. RAG search and semantic indexing make knowledge discoverable."
data-es="Decisiones perdidas en threads, código disperso, docs desactualizadas. Búsqueda RAG e indexing semántico hacen el conocimiento visible."
>
Decisions lost in threads, code scattered, docs unmaintained. RAG
search and semantic indexing make knowledge discoverable.
</p>
</div>
<div class="problem-card">
<div class="problem-number">03</div>
<h3 data-en="Manual Coordination" data-es="Coordinación Manual">
Manual Coordination
</h3>
<p
data-en="Orchestrating code review, testing, documentation and deployment manually creates bottlenecks. Multi-agent workflows solve this."
data-es="Orquestar manualmente code review, testing, documentación y deployment crea cuellos. Los workflows multi-agente lo resuelven."
>
Orchestrating code review, testing, documentation and deployment
manually creates bottlenecks. Multi-agent workflows solve this.
</p>
</div>
<div class="problem-card">
<div class="problem-number">04</div>
<h3 data-en="Dev-Ops Friction" data-es="Fricción Dev-Ops">
Dev-Ops Friction
</h3>
<p
data-en="Handoffs between developers and operations lack visibility and context. Vapora maintains unified deployment readiness."
data-es="Los handoffs entre developers y operaciones carecen de visibilidad y contexto. Vapora mantiene unificada la deployment readiness."
>
Handoffs between developers and operations lack visibility and
context. Vapora maintains unified deployment readiness.
</p>
</div>
</div>
</section>
<section class="section">
<h2 class="section-title">
<span data-en="How It Works" data-es="Cómo Funciona"
>How It Works</span
>
</h2>
<div class="features-grid">
<div class="feature-box">
<div class="feature-icon">🤖</div>
<h3
class="feature-title"
data-en="Specialized Agents"
data-es="Agentes Especializados"
>
Specialized Agents
</h3>
<p
class="feature-text"
data-en="Customizable agents for every role: architecture, development, testing, documentation, deployment and more. Agents learn from execution history with recency bias for continuous improvement."
data-es="Agentes customizables para cada rol: arquitectura, desarrollo, testing, documentación, deployment y más. Los agentes aprenden del historial de ejecución con sesgo de recencia para mejora continua."
>
Customizable agents for every role: architecture, development,
testing, documentation, deployment and more. Agents learn from
execution history with recency bias for continuous improvement.
</p>
</div>
<div class="feature-box" style="border-left-color: #a855f7">
<div class="feature-icon">🧠</div>
<h3
class="feature-title"
style="color: #a855f7"
data-en="Intelligent Orchestration"
data-es="Orquestación Inteligente"
>
Intelligent Orchestration
</h3>
<p
class="feature-text"
data-en="Agents coordinate automatically based on dependencies, context and expertise. Learning-based selection improves over time. Budget enforcement with automatic fallback ensures cost control."
data-es="Los agentes se coordinan automáticamente basados en dependencias, contexto y expertise. La selección basada en aprendizaje mejora con el tiempo. La aplicación de presupuestos con fallback automático garantiza el control de costos."
>
Agents coordinate automatically based on dependencies, context and
expertise. Learning-based selection improves over time. Budget
enforcement with automatic fallback ensures cost control.
</p>
</div>
<div class="feature-box" style="border-left-color: #ec4899">
<div class="feature-icon">☸️</div>
<h3
class="feature-title"
style="color: #ec4899"
data-en="Cloud-Native & Self-Hosted"
data-es="Cloud-Native y Self-Hosted"
>
Cloud-Native & Self-Hosted
</h3>
<p
class="feature-text"
data-en="Deploy to any Kubernetes cluster (EKS, GKE, AKS, vanilla K8s). Local Docker Compose development. Zero vendor lock-in."
data-es="Despliega en cualquier cluster Kubernetes (EKS, GKE, AKS, vanilla K8s). Desarrollo local con Docker Compose. Sin vendor lock-in."
>
Deploy to any Kubernetes cluster (EKS, GKE, AKS, vanilla K8s).
Local Docker Compose development. Zero vendor lock-in.
</p>
</div>
</div>
</section>
<section class="section">
<h2 class="section-title">
<span data-en="Technology Stack" data-es="Stack Tecnológico"
>Technology Stack</span
>
</h2>
<div class="tech-stack">
<span class="tech-badge">Rust</span>
<span class="tech-badge">Axum</span>
<span class="tech-badge">SurrealDB</span>
<span class="tech-badge">NATS JetStream</span>
<span class="tech-badge">Leptos WASM</span>
<span class="tech-badge">Kubernetes</span>
<span class="tech-badge">Prometheus</span>
<span class="tech-badge">Grafana</span>
<span class="tech-badge">Knowledge Graph</span>
</div>
</section>
<section class="section">
<h2 class="section-title">
<span data-en="Available Agents" data-es="Agentes Disponibles"
>Available Agents</span
>
</h2>
<div class="agents-grid">
<div class="agent-item">
<span class="agent-name" data-en="Architect" data-es="Architect"
>Architect</span
><span
class="agent-role"
data-en="System design"
data-es="Diseño de sistemas"
>System design</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Developer" data-es="Developer"
>Developer</span
><span
class="agent-role"
data-en="Code implementation"
data-es="Implementación de código"
>Code implementation</span
>
</div>
<div class="agent-item">
<span
class="agent-name"
data-en="CodeReviewer"
data-es="CodeReviewer"
>CodeReviewer</span
><span
class="agent-role"
data-en="Quality assurance"
data-es="Aseguramiento de calidad"
>Quality assurance</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Tester" data-es="Tester"
>Tester</span
><span
class="agent-role"
data-en="Tests & benchmarks"
data-es="Tests y benchmarks"
>Tests & benchmarks</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Documenter" data-es="Documenter"
>Documenter</span
><span
class="agent-role"
data-en="Documentation"
data-es="Documentación"
>Documentation</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Marketer" data-es="Marketer"
>Marketer</span
><span
class="agent-role"
data-en="Marketing content"
data-es="Contenido marketing"
>Marketing content</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Presenter" data-es="Presenter"
>Presenter</span
><span
class="agent-role"
data-en="Presentations"
data-es="Presentaciones"
>Presentations</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="DevOps" data-es="DevOps"
>DevOps</span
><span
class="agent-role"
data-en="CI/CD deployment"
data-es="Despliegue CI/CD"
>CI/CD deployment</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Monitor" data-es="Monitor"
>Monitor</span
><span
class="agent-role"
data-en="Health & alerting"
data-es="Salud y alerting"
>Health & alerting</span
>
</div>
<div class="agent-item">
<span class="agent-name" data-en="Security" data-es="Security"
>Security</span
><span
class="agent-role"
data-en="Audit & compliance"
data-es="Auditoría y compliance"
>Audit & compliance</span
>
</div>
<div class="agent-item">
<span
class="agent-name"
data-en="ProjectManager"
data-es="ProjectManager"
>ProjectManager</span
><span
class="agent-role"
data-en="Roadmap tracking"
data-es="Tracking de roadmap"
>Roadmap tracking</span
>
</div>
<div class="agent-item">
<span
class="agent-name"
data-en="DecisionMaker"
data-es="DecisionMaker"
>DecisionMaker</span
><span
class="agent-role"
data-en="Conflict resolution"
data-es="Resolución de conflictos"
>Conflict resolution</span
>
</div>
</div>
</section>
<div class="cta-section">
<h2
class="cta-title"
data-en="Ready for intelligent orchestration?"
data-es="¿Listo para la orquestación inteligente?"
>
Ready for intelligent orchestration?
</h2>
<p
style="color: #94a3b8; margin-bottom: 2rem; font-size: 1.05rem"
data-en="Built with Rust 🦀 | Open Source | Self-Hosted"
data-es="Construido con Rust 🦀 | Open Source | Self-Hosted"
>
Built with Rust 🦀 | Open Source | Self-Hosted
</p>
<a
href="https://github.com/vapora-platform/vapora"
class="cta-button"
data-en="Explore on GitHub →"
data-es="Explorar en GitHub →"
>Explore on GitHub →</a
>
</div>
<footer>
<p data-en="Vapora v1.2.0" data-es="Vapora v1.2.0">Vapora v1.2.0</p>
<p
data-en="Made with Vapora dreams and Rust reality ✨"
data-es="Hecho con sueños Vapora y realidad Rust ✨"
>
Made with Vapora dreams and Rust reality ✨
</p>
<p
style="margin-top: 1rem; font-size: 0.8rem"
data-en="Intelligent Development Orchestration | Multi-Agent Multi-IA Platform"
data-es="Orquestación Inteligente de Desarrollo | Plataforma Multi-Agente Multi-IA"
>
Intelligent Development Orchestration | Multi-Agent Multi-IA Platform
</p>
</footer>
</div>
<script>
// Language management
const LANG_KEY = "vapora-lang";
function getCurrentLanguage() {
return localStorage.getItem(LANG_KEY) || "en";
}
function switchLanguage(lang) {
localStorage.setItem(LANG_KEY, lang);
// Update language buttons
document.querySelectorAll(".lang-btn").forEach((btn) => {
btn.classList.remove("active");
if (btn.dataset.lang === lang) {
btn.classList.add("active");
}
});
// Update all translatable elements
document.querySelectorAll("[data-en][data-es]").forEach((el) => {
const content = el.dataset[lang];
// Use innerHTML for headings that might contain <br>, textContent for others
if (
el.tagName === "H1" ||
el.tagName === "H2" ||
el.tagName === "H3"
) {
el.innerHTML = content;
} else {
el.textContent = content;
}
});
document.documentElement.lang = lang;
}
// Initialize language on page load
document.addEventListener("DOMContentLoaded", () => {
const currentLang = getCurrentLanguage();
switchLanguage(currentLang);
});
</script>
</body>
</html>

200
assets/web/src/vapora.svg Normal file
View File

@ -0,0 +1,200 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="170 40 590 300" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- Google Fonts import -->
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@800&amp;display=swap');
</style>
<!-- Gradiente principal -->
<linearGradient id="techGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#22d3ee;stop-opacity:1"/>
<stop offset="50%" style="stop-color:#a855f7;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#ec4899;stop-opacity:1"/>
</linearGradient>
<!-- Gradiente vertical -->
<linearGradient id="vertGrad" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#22d3ee;stop-opacity:1"/>
<stop offset="50%" style="stop-color:#a855f7;stop-opacity:0.8"/>
<stop offset="100%" style="stop-color:#ec4899;stop-opacity:0.4"/>
</linearGradient>
<!-- Filtro glow tech -->
<filter id="techGlow">
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Filtro glow fuerte -->
<filter id="strongGlow">
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Filtro glass -->
<filter id="glass">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.5" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo"/>
<feBlend in="SourceGraphic" in2="goo"/>
</filter>
</defs>
<!-- Fondo -->
<rect width="800" height="400" fill="#000000"/>
<!-- Grid de fondo técnico sutil -->
<g opacity="0.08" stroke="#22d3ee" stroke-width="1">
<line x1="0" y1="133" x2="800" y2="133"/>
<line x1="0" y1="200" x2="800" y2="200"/>
<line x1="0" y1="267" x2="800" y2="267"/>
<line x1="133" y1="0" x2="133" y2="400"/>
<line x1="267" y1="0" x2="267" y2="400"/>
<line x1="400" y1="0" x2="400" y2="400"/>
<line x1="533" y1="0" x2="533" y2="400"/>
<line x1="667" y1="0" x2="667" y2="400"/>
</g>
<!-- Símbolo técnico: flujo de datos ascendente -->
<g transform="translate(267, 280)">
<!-- Base: plataforma -->
<rect x="-25" y="0" width="50" height="6.67" fill="url(#techGrad)" opacity="0.8" rx="3.33"/>
<!-- Stream principal - línea central tipo señal -->
<path d="M 0 0 L 0 -50 L 8.33 -58 L -8.33 -75 L 8.33 -92 L -8.33 -108 L 8.33 -125 L 0 -133 L 0 -200" stroke="url(#vertGrad)" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#techGlow)">
<animate attributeName="stroke-dasharray" values="0,500;500,0;0,500" dur="4s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.8;1;0.8" dur="2s" repeatCount="indefinite"/>
</path>
<!-- Stream izquierdo 1 -->
<path d="M -33 0 L -33 -42 L -30 -58 L -37 -75 L -30 -92 L -37 -108 L -33 -125 L -33 -167" stroke="#22d3ee" stroke-width="3.33" fill="none" stroke-linecap="round" opacity="0.6" filter="url(#techGlow)">
<animate attributeName="stroke-dasharray" values="0,417;417,0;0,417" dur="4.5s" repeatCount="indefinite"/>
</path>
<!-- Stream izquierdo 2 -->
<path d="M -58 0 L -58 -33 L -53 -50 L -63 -67 L -53 -83 L -63 -100 L -58 -117 L -58 -142" stroke="#a855f7" stroke-width="2.5" fill="none" stroke-linecap="round" opacity="0.5">
<animate attributeName="stroke-dasharray" values="0,333;333,0;0,333" dur="5s" repeatCount="indefinite"/>
</path>
<!-- Stream derecho 1 -->
<path d="M 33 0 L 33 -42 L 30 -58 L 37 -75 L 30 -92 L 37 -108 L 33 -125 L 33 -167" stroke="#ec4899" stroke-width="3.33" fill="none" stroke-linecap="round" opacity="0.6" filter="url(#techGlow)">
<animate attributeName="stroke-dasharray" values="0,417;417,0;0,417" dur="4.2s" repeatCount="indefinite"/>
</path>
<!-- Stream derecho 2 -->
<path d="M 58 0 L 58 -33 L 53 -50 L 63 -67 L 53 -83 L 63 -100 L 58 -117 L 58 -142" stroke="#22d3ee" stroke-width="2.5" fill="none" stroke-linecap="round" opacity="0.5">
<animate attributeName="stroke-dasharray" values="0,333;333,0;0,333" dur="5.5s" repeatCount="indefinite"/>
</path>
<!-- Nodos de datos en el flujo principal -->
<circle cx="0" cy="-67" r="5" fill="#22d3ee" filter="url(#strongGlow)">
<animate attributeName="cy" values="-67;-183;-67" dur="3s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;1;0" dur="3s" repeatCount="indefinite"/>
</circle>
<circle cx="0" cy="-100" r="4.17" fill="#a855f7" filter="url(#strongGlow)">
<animate attributeName="cy" values="-100;-217;-100" dur="3.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;1;0" dur="3.5s" repeatCount="indefinite"/>
</circle>
<circle cx="0" cy="-133" r="3.33" fill="#ec4899" filter="url(#strongGlow)">
<animate attributeName="cy" values="-133;-233;-133" dur="4s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;1;0" dur="4s" repeatCount="indefinite"/>
</circle>
<!-- Partículas laterales -->
<circle cx="-33" cy="-83" r="3.33" fill="#22d3ee" opacity="0.7">
<animate attributeName="cy" values="-83;-175;-83" dur="3.8s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.7;0" dur="3.8s" repeatCount="indefinite"/>
</circle>
<circle cx="33" cy="-92" r="3.33" fill="#ec4899" opacity="0.7">
<animate attributeName="cy" values="-92;-175;-92" dur="4.2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.7;0" dur="4.2s" repeatCount="indefinite"/>
</circle>
<circle cx="-58" cy="-58" r="2.5" fill="#a855f7" opacity="0.5">
<animate attributeName="cy" values="-58;-142;-58" dur="4.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="4.5s" repeatCount="indefinite"/>
</circle>
<circle cx="58" cy="-67" r="2.5" fill="#22d3ee" opacity="0.5">
<animate attributeName="cy" values="-67;-142;-67" dur="5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="5s" repeatCount="indefinite"/>
</circle>
<!-- Hexágonos técnicos flotantes -->
<polygon points="0,-158 5,-162 5,-167 0,-170 -5,-167 -5,-162" stroke="#22d3ee" fill="none" stroke-width="1.67" opacity="0.6">
<animate attributeName="transform" values="translate(0,0);translate(0,-50);translate(0,0)" dur="4s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.6;0" dur="4s" repeatCount="indefinite"/>
</polygon>
<polygon points="-42,-117 -37,-120 -37,-125 -42,-128 -47,-125 -47,-120" stroke="#a855f7" fill="none" stroke-width="1.67" opacity="0.5">
<animate attributeName="transform" values="translate(0,0);translate(0,-42);translate(0,0)" dur="4.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="4.5s" repeatCount="indefinite"/>
</polygon>
<polygon points="42,-125 47,-128 47,-133 42,-137 37,-133 37,-128" stroke="#ec4899" fill="none" stroke-width="1.67" opacity="0.5">
<animate attributeName="transform" values="translate(0,0);translate(0,-33);translate(0,0)" dur="5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="5s" repeatCount="indefinite"/>
</polygon>
<!-- Líneas de conexión horizontales animadas -->
<line x1="-33" y1="-100" x2="-8" y2="-100" stroke="#22d3ee" stroke-width="0.83" opacity="0.4">
<animate attributeName="opacity" values="0;0.4;0" dur="2s" repeatCount="indefinite"/>
</line>
<line x1="8" y1="-117" x2="33" y2="-117" stroke="#ec4899" stroke-width="0.83" opacity="0.4">
<animate attributeName="opacity" values="0;0.4;0" dur="2.5s" repeatCount="indefinite"/>
</line>
<line x1="-58" y1="-83" x2="-37" y2="-83" stroke="#a855f7" stroke-width="0.83" opacity="0.3">
<animate attributeName="opacity" values="0;0.3;0" dur="3s" repeatCount="indefinite"/>
</line>
<line x1="37" y1="-92" x2="58" y2="-92" stroke="#22d3ee" stroke-width="0.83" opacity="0.3">
<animate attributeName="opacity" values="0;0.3;0" dur="3.5s" repeatCount="indefinite"/>
</line>
</g>
<!-- Texto VAPORA -->
<g filter="url(#glass)">
<text x="550" y="207" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="90" font-weight="800" fill="url(#techGrad)" letter-spacing="5" text-anchor="middle">
VAPORA
</text>
<text x="550" y="207" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="90" font-weight="800" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="0.83" letter-spacing="5" text-anchor="middle">
VAPORA
</text>
</g>
<!-- Glow en texto -->
<text x="550" y="207" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="90" font-weight="800" fill="url(#techGrad)" letter-spacing="5" filter="url(#techGlow)" opacity="0.3" text-anchor="middle">
VAPORA
</text>
<!-- Tagline -->
<text x="550" y="240" font-family="'Inter', sans-serif" font-size="20" fill="#a855f7" opacity="0.8" letter-spacing="0.25em" text-anchor="middle">
Evaporate complexity
</text>
<!-- Indicador técnico decorativo -->
<g transform="translate(550, 280)">
<rect x="0" y="0" width="2" height="13.33" fill="#22d3ee" opacity="0.6">
<animate attributeName="height" values="13.33;20;13.33" dur="1.5s" repeatCount="indefinite"/>
</rect>
<rect x="6.67" y="0" width="2" height="16.67" fill="#a855f7" opacity="0.6">
<animate attributeName="height" values="16.67;23.33;16.67" dur="1.8s" repeatCount="indefinite"/>
</rect>
<rect x="13.33" y="0" width="2" height="10" fill="#ec4899" opacity="0.6">
<animate attributeName="height" values="10;16.67;10" dur="1.3s" repeatCount="indefinite"/>
</rect>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

200
assets/web/vapora.svg Normal file
View File

@ -0,0 +1,200 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="170 40 590 300" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- Google Fonts import -->
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@800&amp;display=swap');
</style>
<!-- Gradiente principal -->
<linearGradient id="techGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#22d3ee;stop-opacity:1"/>
<stop offset="50%" style="stop-color:#a855f7;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#ec4899;stop-opacity:1"/>
</linearGradient>
<!-- Gradiente vertical -->
<linearGradient id="vertGrad" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#22d3ee;stop-opacity:1"/>
<stop offset="50%" style="stop-color:#a855f7;stop-opacity:0.8"/>
<stop offset="100%" style="stop-color:#ec4899;stop-opacity:0.4"/>
</linearGradient>
<!-- Filtro glow tech -->
<filter id="techGlow">
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Filtro glow fuerte -->
<filter id="strongGlow">
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Filtro glass -->
<filter id="glass">
<feGaussianBlur in="SourceGraphic" stdDeviation="0.5" result="blur"/>
<feColorMatrix in="blur" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo"/>
<feBlend in="SourceGraphic" in2="goo"/>
</filter>
</defs>
<!-- Fondo -->
<rect width="800" height="400" fill="#000000"/>
<!-- Grid de fondo técnico sutil -->
<g opacity="0.08" stroke="#22d3ee" stroke-width="1">
<line x1="0" y1="133" x2="800" y2="133"/>
<line x1="0" y1="200" x2="800" y2="200"/>
<line x1="0" y1="267" x2="800" y2="267"/>
<line x1="133" y1="0" x2="133" y2="400"/>
<line x1="267" y1="0" x2="267" y2="400"/>
<line x1="400" y1="0" x2="400" y2="400"/>
<line x1="533" y1="0" x2="533" y2="400"/>
<line x1="667" y1="0" x2="667" y2="400"/>
</g>
<!-- Símbolo técnico: flujo de datos ascendente -->
<g transform="translate(267, 280)">
<!-- Base: plataforma -->
<rect x="-25" y="0" width="50" height="6.67" fill="url(#techGrad)" opacity="0.8" rx="3.33"/>
<!-- Stream principal - línea central tipo señal -->
<path d="M 0 0 L 0 -50 L 8.33 -58 L -8.33 -75 L 8.33 -92 L -8.33 -108 L 8.33 -125 L 0 -133 L 0 -200" stroke="url(#vertGrad)" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" filter="url(#techGlow)">
<animate attributeName="stroke-dasharray" values="0,500;500,0;0,500" dur="4s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.8;1;0.8" dur="2s" repeatCount="indefinite"/>
</path>
<!-- Stream izquierdo 1 -->
<path d="M -33 0 L -33 -42 L -30 -58 L -37 -75 L -30 -92 L -37 -108 L -33 -125 L -33 -167" stroke="#22d3ee" stroke-width="3.33" fill="none" stroke-linecap="round" opacity="0.6" filter="url(#techGlow)">
<animate attributeName="stroke-dasharray" values="0,417;417,0;0,417" dur="4.5s" repeatCount="indefinite"/>
</path>
<!-- Stream izquierdo 2 -->
<path d="M -58 0 L -58 -33 L -53 -50 L -63 -67 L -53 -83 L -63 -100 L -58 -117 L -58 -142" stroke="#a855f7" stroke-width="2.5" fill="none" stroke-linecap="round" opacity="0.5">
<animate attributeName="stroke-dasharray" values="0,333;333,0;0,333" dur="5s" repeatCount="indefinite"/>
</path>
<!-- Stream derecho 1 -->
<path d="M 33 0 L 33 -42 L 30 -58 L 37 -75 L 30 -92 L 37 -108 L 33 -125 L 33 -167" stroke="#ec4899" stroke-width="3.33" fill="none" stroke-linecap="round" opacity="0.6" filter="url(#techGlow)">
<animate attributeName="stroke-dasharray" values="0,417;417,0;0,417" dur="4.2s" repeatCount="indefinite"/>
</path>
<!-- Stream derecho 2 -->
<path d="M 58 0 L 58 -33 L 53 -50 L 63 -67 L 53 -83 L 63 -100 L 58 -117 L 58 -142" stroke="#22d3ee" stroke-width="2.5" fill="none" stroke-linecap="round" opacity="0.5">
<animate attributeName="stroke-dasharray" values="0,333;333,0;0,333" dur="5.5s" repeatCount="indefinite"/>
</path>
<!-- Nodos de datos en el flujo principal -->
<circle cx="0" cy="-67" r="5" fill="#22d3ee" filter="url(#strongGlow)">
<animate attributeName="cy" values="-67;-183;-67" dur="3s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;1;0" dur="3s" repeatCount="indefinite"/>
</circle>
<circle cx="0" cy="-100" r="4.17" fill="#a855f7" filter="url(#strongGlow)">
<animate attributeName="cy" values="-100;-217;-100" dur="3.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;1;0" dur="3.5s" repeatCount="indefinite"/>
</circle>
<circle cx="0" cy="-133" r="3.33" fill="#ec4899" filter="url(#strongGlow)">
<animate attributeName="cy" values="-133;-233;-133" dur="4s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;1;0" dur="4s" repeatCount="indefinite"/>
</circle>
<!-- Partículas laterales -->
<circle cx="-33" cy="-83" r="3.33" fill="#22d3ee" opacity="0.7">
<animate attributeName="cy" values="-83;-175;-83" dur="3.8s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.7;0" dur="3.8s" repeatCount="indefinite"/>
</circle>
<circle cx="33" cy="-92" r="3.33" fill="#ec4899" opacity="0.7">
<animate attributeName="cy" values="-92;-175;-92" dur="4.2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.7;0" dur="4.2s" repeatCount="indefinite"/>
</circle>
<circle cx="-58" cy="-58" r="2.5" fill="#a855f7" opacity="0.5">
<animate attributeName="cy" values="-58;-142;-58" dur="4.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="4.5s" repeatCount="indefinite"/>
</circle>
<circle cx="58" cy="-67" r="2.5" fill="#22d3ee" opacity="0.5">
<animate attributeName="cy" values="-67;-142;-67" dur="5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="5s" repeatCount="indefinite"/>
</circle>
<!-- Hexágonos técnicos flotantes -->
<polygon points="0,-158 5,-162 5,-167 0,-170 -5,-167 -5,-162" stroke="#22d3ee" fill="none" stroke-width="1.67" opacity="0.6">
<animate attributeName="transform" values="translate(0,0);translate(0,-50);translate(0,0)" dur="4s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.6;0" dur="4s" repeatCount="indefinite"/>
</polygon>
<polygon points="-42,-117 -37,-120 -37,-125 -42,-128 -47,-125 -47,-120" stroke="#a855f7" fill="none" stroke-width="1.67" opacity="0.5">
<animate attributeName="transform" values="translate(0,0);translate(0,-42);translate(0,0)" dur="4.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="4.5s" repeatCount="indefinite"/>
</polygon>
<polygon points="42,-125 47,-128 47,-133 42,-137 37,-133 37,-128" stroke="#ec4899" fill="none" stroke-width="1.67" opacity="0.5">
<animate attributeName="transform" values="translate(0,0);translate(0,-33);translate(0,0)" dur="5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0.5;0" dur="5s" repeatCount="indefinite"/>
</polygon>
<!-- Líneas de conexión horizontales animadas -->
<line x1="-33" y1="-100" x2="-8" y2="-100" stroke="#22d3ee" stroke-width="0.83" opacity="0.4">
<animate attributeName="opacity" values="0;0.4;0" dur="2s" repeatCount="indefinite"/>
</line>
<line x1="8" y1="-117" x2="33" y2="-117" stroke="#ec4899" stroke-width="0.83" opacity="0.4">
<animate attributeName="opacity" values="0;0.4;0" dur="2.5s" repeatCount="indefinite"/>
</line>
<line x1="-58" y1="-83" x2="-37" y2="-83" stroke="#a855f7" stroke-width="0.83" opacity="0.3">
<animate attributeName="opacity" values="0;0.3;0" dur="3s" repeatCount="indefinite"/>
</line>
<line x1="37" y1="-92" x2="58" y2="-92" stroke="#22d3ee" stroke-width="0.83" opacity="0.3">
<animate attributeName="opacity" values="0;0.3;0" dur="3.5s" repeatCount="indefinite"/>
</line>
</g>
<!-- Texto VAPORA -->
<g filter="url(#glass)">
<text x="550" y="207" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="90" font-weight="800" fill="url(#techGrad)" letter-spacing="5" text-anchor="middle">
VAPORA
</text>
<text x="550" y="207" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="90" font-weight="800" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="0.83" letter-spacing="5" text-anchor="middle">
VAPORA
</text>
</g>
<!-- Glow en texto -->
<text x="550" y="207" font-family="'JetBrains Mono', 'Fira Code', monospace" font-size="90" font-weight="800" fill="url(#techGrad)" letter-spacing="5" filter="url(#techGlow)" opacity="0.3" text-anchor="middle">
VAPORA
</text>
<!-- Tagline -->
<text x="550" y="240" font-family="'Inter', sans-serif" font-size="20" fill="#a855f7" opacity="0.8" letter-spacing="0.25em" text-anchor="middle">
Evaporate complexity
</text>
<!-- Indicador técnico decorativo -->
<g transform="translate(550, 280)">
<rect x="0" y="0" width="2" height="13.33" fill="#22d3ee" opacity="0.6">
<animate attributeName="height" values="13.33;20;13.33" dur="1.5s" repeatCount="indefinite"/>
</rect>
<rect x="6.67" y="0" width="2" height="16.67" fill="#a855f7" opacity="0.6">
<animate attributeName="height" values="16.67;23.33;16.67" dur="1.8s" repeatCount="indefinite"/>
</rect>
<rect x="13.33" y="0" width="2" height="10" fill="#ec4899" opacity="0.6">
<animate attributeName="height" values="10;16.67;10" dur="1.3s" repeatCount="indefinite"/>
</rect>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB