diff --git a/README.md b/README.md
index f1b6159..3b4f21b 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,141 @@
-# Automated web-based plotting tool
+# Gallery Application - Restructured
-## Installation
+This document explains the new modular structure of the gallery application.
-Requires `Jinja2`
+## Project Structure
-## Config
+```
+web/
+├── assets/
+│ ├── css/
+│ │ ├── main.css # Main CSS file (imports all others)
+│ │ ├── variables.css # CSS custom properties and themes
+│ │ ├── base.css # Base layout and typography
+│ │ ├── navigation.css # Breadcrumb and navigation styles
+│ │ ├── search.css # Search functionality styles
+│ │ ├── folder-tree.css # Folder tree component styles
+│ │ ├── grid.css # Plot grid and selection styles
+│ │ ├── sidebar.css # Recent plots sidebar styles
+│ │ ├── floating-elements.css # Floating buttons and help
+│ │ ├── stats.css # Gallery statistics styles
+│ │ ├── comparison.css # Plot comparison overlay styles
+│ │ └── responsive.css # Mobile and responsive styles
+│ └── js/
+│ ├── main.js # Main entry point
+│ ├── gallery-app.js # Main application orchestrator
+│ ├── theme-manager.js # Theme switching functionality
+│ ├── navigation-manager.js # Breadcrumb and folder tree
+│ ├── search-manager.js # Search functionality
+│ ├── recent-plots-manager.js # Recent plots sidebar
+│ ├── comparison-manager.js # Plot comparison features
+│ ├── stats-manager.js # Gallery statistics
+│ ├── keyboard-manager.js # Keyboard shortcuts
+│ └── utils.js # Utility functions
+├── templates/
+│ └── gallery.html # Clean HTML template
+├── template.html # Original monolithic file (backup)
+├── config.py
+├── config.yaml
+├── generate_gallery.py
+└── README.md
+```
-## Usage
\ No newline at end of file
+## Key Improvements
+
+### 1. **Separation of Concerns**
+- **CSS**: Organized into logical components (navigation, search, grid, etc.)
+- **JavaScript**: Split into focused managers with single responsibilities
+- **HTML**: Clean template focusing on structure
+
+### 2. **Modular Architecture**
+- Each JavaScript module handles a specific feature area
+- Modules can be independently maintained and tested
+- Clear dependencies and interfaces between modules
+
+### 3. **Maintainability**
+- Individual files are much smaller and focused
+- Easy to locate and modify specific functionality
+- Reduced cognitive load when working on features
+
+### 4. **Development Benefits**
+- Better IDE support with syntax highlighting and intellisense
+- Easier debugging with source maps
+- Ability to add build tools if needed
+
+## Module Responsibilities
+
+### CSS Modules
+- **variables.css**: Theme colors and CSS custom properties
+- **base.css**: Typography, basic layout, list styles
+- **navigation.css**: Breadcrumb and navigation button styles
+- **search.css**: Search box, results, and highlighting
+- **folder-tree.css**: Collapsible folder tree display
+- **grid.css**: Plot thumbnails grid and selection states
+- **sidebar.css**: Recent plots sidebar and overlay
+- **floating-elements.css**: Action buttons and keyboard help
+- **stats.css**: Gallery statistics display
+- **comparison.css**: Plot comparison overlay
+- **responsive.css**: Mobile and tablet adaptations
+
+### JavaScript Modules
+- **ThemeManager**: Light/dark theme switching and persistence
+- **NavigationManager**: Breadcrumb building and folder tree construction
+- **SearchManager**: Plot search with debouncing and results display
+- **RecentPlotsManager**: Recent plots tracking and sidebar management
+- **ComparisonManager**: Plot comparison functionality
+- **StatsManager**: Gallery statistics calculation and display
+- **KeyboardManager**: Keyboard shortcuts and escape handling
+- **Utils**: File size formatting, thumbnail highlighting, gallery refresh
+
+### Main Application
+- **GalleryApp**: Orchestrates all managers and provides unified interface
+- **main.js**: Entry point that initializes the application
+
+## Usage
+
+The restructured application maintains **full backward compatibility** with the original template. All existing functionality works exactly the same way.
+
+### For Python Backend
+Update your template reference to use the new template:
+```python
+# Instead of template.html, use:
+template_path = 'templates/gallery.html'
+```
+
+### CSS Asset Path
+The template expects CSS/JS assets to be served from `/assets/` relative to the gallery pages. Update your web server configuration to serve these static files.
+
+### No Breaking Changes
+- All onclick handlers work the same
+- All CSS classes remain unchanged
+- All IDs and functionality preserved
+- Jinja2 template variables work identically
+
+## Development Workflow
+
+### Adding New Features
+1. Identify which manager should handle the new functionality
+2. Add methods to the appropriate manager class
+3. Update the main GalleryApp class if needed
+4. Add any new CSS to the appropriate CSS module
+
+### Modifying Existing Features
+1. Locate the relevant manager (theme, search, navigation, etc.)
+2. Make changes to the specific module
+3. Test that the feature works as expected
+
+### Styling Changes
+1. Identify the component being styled
+2. Edit the appropriate CSS module
+3. The main.css file will automatically include changes
+
+## Benefits of This Structure
+
+1. **Easier Debugging**: Each feature is isolated in its own file
+2. **Better Performance**: Browser can cache individual modules
+3. **Team Development**: Multiple developers can work on different features simultaneously
+4. **Code Reuse**: Managers can be reused in other projects
+5. **Testing**: Individual modules can be unit tested
+6. **Documentation**: Each file has a clear, focused purpose
+
+This restructuring makes the gallery application much more maintainable while preserving all existing functionality.
diff --git a/assets/css/base.css b/assets/css/base.css
new file mode 100644
index 0000000..ae700dc
--- /dev/null
+++ b/assets/css/base.css
@@ -0,0 +1,54 @@
+/* ========================================
+ BASE LAYOUT AND TYPOGRAPHY
+ ======================================== */
+* {
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ margin: 0;
+ padding: 1rem;
+ padding-bottom: 2rem;
+ background-color: var(--bg-color);
+ color: var(--text-color);
+ transition: background-color 0.3s, color 0.3s;
+ line-height: 1.5;
+}
+
+h1 {
+ font-size: 1.8rem;
+ margin-bottom: 0.5rem;
+ font-weight: 600;
+}
+
+h2 {
+ color: var(--text-color);
+ font-size: 1.3rem;
+ margin: 1.5rem 0 0.5rem 0;
+}
+
+/* ========================================
+ SUBDIRECTORIES LIST
+ ======================================== */
+ul {
+ list-style: none;
+ padding: 0;
+}
+
+ul li {
+ margin: 0.5rem 0;
+}
+
+ul li a {
+ color: var(--link-color);
+ text-decoration: none;
+ padding: 0.3rem 0;
+ display: inline-block;
+ transition: color 0.2s ease;
+}
+
+ul li a:hover {
+ color: var(--link-hover);
+ text-decoration: underline;
+}
diff --git a/assets/css/comparison.css b/assets/css/comparison.css
new file mode 100644
index 0000000..7e2dee2
--- /dev/null
+++ b/assets/css/comparison.css
@@ -0,0 +1,180 @@
+/* ========================================
+ PLOT COMPARISON OVERLAY
+ ======================================== */
+.comparison-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.9);
+ z-index: 2000;
+ display: none;
+ backdrop-filter: blur(4px);
+}
+
+.comparison-overlay.open {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.comparison-container {
+ width: 95%;
+ height: 90%;
+ background: var(--bg-color);
+ border-radius: 12px;
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
+}
+
+.comparison-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.comparison-title {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: var(--text-color);
+ margin: 0;
+}
+
+.comparison-close {
+ background: var(--button-bg);
+ color: white;
+ border: none;
+ border-radius: 50%;
+ width: 40px;
+ height: 40px;
+ font-size: 1.2rem;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.comparison-close:hover {
+ background: var(--button-hover);
+ transform: scale(1.1);
+}
+
+.comparison-content {
+ flex: 1;
+ display: flex;
+ gap: 1rem;
+ overflow: hidden;
+}
+
+.comparison-panel {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background: var(--card-bg);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.comparison-panel-header {
+ background: var(--tree-bg);
+ padding: 0.8rem 1rem;
+ border-bottom: 1px solid var(--border-color);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.comparison-panel-title {
+ font-weight: 600;
+ color: var(--text-color);
+ font-size: 1rem;
+}
+
+.comparison-replace-btn {
+ background: var(--success-color);
+ color: white;
+ border: none;
+ padding: 0.4rem 0.8rem;
+ border-radius: 4px;
+ font-size: 0.85rem;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.comparison-replace-btn:hover {
+ background: var(--success-hover);
+}
+
+.comparison-panel-content {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 1rem;
+ overflow: auto;
+}
+
+.comparison-plot-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.comparison-plot {
+ max-width: 100%;
+ max-height: 100%;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+}
+
+.comparison-plot:hover {
+ transform: scale(1.02);
+}
+
+.comparison-placeholder {
+ width: 100%;
+ height: 300px;
+ border: 2px dashed var(--border-color);
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--breadcrumb-color);
+ font-size: 1.1rem;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.comparison-placeholder:hover {
+ border-color: var(--button-bg);
+ color: var(--button-bg);
+}
+
+.comparison-plot-info {
+ position: absolute;
+ bottom: 0.5rem;
+ left: 0.5rem;
+ right: 0.5rem;
+ background: rgba(0, 0, 0, 0.8);
+ color: white;
+ padding: 0.5rem;
+ border-radius: 4px;
+ font-size: 0.9rem;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.comparison-plot-container:hover .comparison-plot-info {
+ opacity: 1;
+}
diff --git a/assets/css/floating-elements.css b/assets/css/floating-elements.css
new file mode 100644
index 0000000..1c5bb2f
--- /dev/null
+++ b/assets/css/floating-elements.css
@@ -0,0 +1,95 @@
+/* ========================================
+ FLOATING ACTION BUTTONS
+ ======================================== */
+.floating-buttons {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ z-index: 1000;
+}
+
+.floating-btn {
+ width: 56px;
+ height: 56px;
+ border-radius: 50%;
+ border: none;
+ cursor: pointer;
+ font-size: 1.3rem;
+ box-shadow: 0 3px 10px rgba(0,0,0,0.3);
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.floating-btn:hover {
+ transform: scale(1.1);
+}
+
+.sidebar-toggle {
+ background: var(--button-bg);
+ color: white;
+}
+
+.theme-toggle {
+ background: var(--button-bg);
+ color: white;
+}
+
+.refresh-btn {
+ background: var(--success-color);
+ color: white;
+}
+
+.refresh-btn:hover {
+ background: var(--success-hover);
+}
+
+.refresh-btn:disabled {
+ background: var(--disabled-color);
+ cursor: not-allowed;
+ transform: none;
+}
+
+/* ========================================
+ KEYBOARD SHORTCUTS HELP
+ ======================================== */
+.shortcuts-help {
+ position: fixed;
+ bottom: 140px;
+ right: 20px;
+ background: var(--card-bg);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 1rem;
+ display: none;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
+ z-index: 1000;
+ font-size: 0.9rem;
+ max-width: 280px;
+}
+
+.shortcuts-help h4 {
+ margin: 0 0 0.8rem 0;
+ color: var(--text-color);
+ font-size: 1rem;
+}
+
+.shortcut-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 0.4rem 0;
+}
+
+.shortcut-key {
+ background: var(--border-color);
+ padding: 0.2rem 0.4rem;
+ border-radius: 4px;
+ font-family: 'JetBrains Mono', 'Courier New', monospace;
+ font-size: 0.8rem;
+ font-weight: 500;
+}
diff --git a/assets/css/folder-tree.css b/assets/css/folder-tree.css
new file mode 100644
index 0000000..3c394ad
--- /dev/null
+++ b/assets/css/folder-tree.css
@@ -0,0 +1,40 @@
+/* ========================================
+ FOLDER TREE
+ ======================================== */
+.folder-tree {
+ background: var(--tree-bg);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 1rem;
+ margin: 1rem 0;
+ font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
+ font-size: 0.85rem;
+ max-height: 350px;
+ overflow-y: auto;
+ transition: all 0.3s ease;
+}
+
+.tree-item {
+ margin: 0.2rem 0;
+ white-space: pre;
+ font-family: inherit;
+}
+
+.tree-current {
+ background: var(--tree-current-bg);
+ color: white;
+ padding: 0.2rem 0.4rem;
+ border-radius: 4px;
+ font-weight: 500;
+}
+
+.tree-link {
+ color: var(--link-color);
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.tree-link:hover {
+ text-decoration: underline;
+ color: var(--link-hover);
+}
diff --git a/assets/css/grid.css b/assets/css/grid.css
new file mode 100644
index 0000000..5f633a2
--- /dev/null
+++ b/assets/css/grid.css
@@ -0,0 +1,106 @@
+/* ========================================
+ PLOT GRID
+ ======================================== */
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 1.2rem;
+ padding: 1rem 0;
+}
+
+.grid-item {
+ text-align: center;
+ background: var(--card-bg);
+ border-radius: 8px;
+ padding: 0.8rem;
+ transition: all 0.2s ease;
+ border: 1px solid transparent;
+}
+
+.grid-item:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+ border-color: var(--border-color);
+}
+
+.grid-item img {
+ max-width: 100%;
+ border: 1px solid var(--border-color);
+ border-radius: 6px;
+ transition: all 0.2s ease;
+}
+
+.grid-item a {
+ color: var(--link-color);
+ text-decoration: none;
+}
+
+.grid-item.highlighted {
+ border-color: var(--button-bg);
+ box-shadow: 0 0 15px rgba(0, 120, 212, 0.3);
+ transform: translateY(-2px);
+ animation: highlightPulse 2s ease-in-out;
+}
+
+@keyframes highlightPulse {
+ 0%, 100% { transform: translateY(-2px) scale(1); }
+ 50% { transform: translateY(-2px) scale(1.02); }
+}
+
+.plot-name {
+ margin-top: 0.8rem;
+ word-wrap: break-word;
+ word-break: break-word;
+ hyphens: auto;
+ font-size: 0.9rem;
+ line-height: 1.3;
+ max-height: 3.9rem;
+ overflow: hidden;
+ padding: 0 0.2rem;
+ font-weight: 500;
+}
+
+/* Plot selection mode */
+.selecting-plots .grid-item {
+ cursor: pointer !important;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.selecting-plots .grid-item:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 6px 20px rgba(0, 120, 212, 0.3);
+ border-color: var(--button-bg);
+}
+
+.selecting-plots .grid-item::before {
+ content: '📊 Click to compare';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: rgba(0, 120, 212, 0.95);
+ color: white;
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ font-size: 0.9rem;
+ font-weight: 600;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+ pointer-events: none;
+ z-index: 10;
+ white-space: nowrap;
+}
+
+.selecting-plots .grid-item:hover::before {
+ opacity: 1;
+}
+
+/* Ensure grid items are clickable in selection mode */
+.selecting-plots .grid-item * {
+ pointer-events: none;
+}
+
+.selecting-plots .grid-item {
+ pointer-events: auto;
+}
diff --git a/assets/css/main.css b/assets/css/main.css
new file mode 100644
index 0000000..8162397
--- /dev/null
+++ b/assets/css/main.css
@@ -0,0 +1,20 @@
+/* ========================================
+ GALLERY STYLES - MAIN ENTRY POINT
+ ======================================== */
+
+/* Core styles */
+@import url('./variables.css');
+@import url('./base.css');
+
+/* Component styles */
+@import url('./navigation.css');
+@import url('./search.css');
+@import url('./folder-tree.css');
+@import url('./grid.css');
+@import url('./sidebar.css');
+@import url('./floating-elements.css');
+@import url('./stats.css');
+@import url('./comparison.css');
+
+/* Responsive design */
+@import url('./responsive.css');
diff --git a/assets/css/navigation.css b/assets/css/navigation.css
new file mode 100644
index 0000000..1dc5629
--- /dev/null
+++ b/assets/css/navigation.css
@@ -0,0 +1,57 @@
+/* ========================================
+ NAVIGATION COMPONENTS
+ ======================================== */
+.breadcrumb {
+ margin: 0.5rem 0 1rem 0;
+ font-size: 0.9rem;
+ color: var(--breadcrumb-color);
+}
+
+.breadcrumb a {
+ color: var(--link-color);
+ text-decoration: none;
+}
+
+.breadcrumb a:hover {
+ text-decoration: underline;
+ color: var(--link-hover);
+}
+
+.breadcrumb .separator {
+ margin: 0 0.3rem;
+ color: var(--breadcrumb-color);
+ opacity: 0.7;
+}
+
+.navigation {
+ margin: 1rem 0;
+ display: flex;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+}
+
+.nav-btn {
+ background: var(--button-bg);
+ color: white;
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ text-decoration: none;
+ transition: all 0.2s ease;
+ font-size: 0.9rem;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.3rem;
+}
+
+.nav-btn:hover {
+ background: var(--button-hover);
+ transform: translateY(-1px);
+}
+
+.nav-btn:disabled {
+ background: var(--disabled-color);
+ cursor: not-allowed;
+ transform: none;
+}
diff --git a/assets/css/responsive.css b/assets/css/responsive.css
new file mode 100644
index 0000000..7b2a7a8
--- /dev/null
+++ b/assets/css/responsive.css
@@ -0,0 +1,31 @@
+/* ========================================
+ RESPONSIVE DESIGN
+ ======================================== */
+@media (max-width: 768px) {
+ body {
+ padding: 0.5rem;
+ }
+
+ .grid {
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
+ gap: 0.8rem;
+ }
+
+ .sidebar {
+ width: 100%;
+ right: -100%;
+ }
+
+ .navigation {
+ gap: 0.3rem;
+ }
+
+ .nav-btn {
+ padding: 0.4rem 0.8rem;
+ font-size: 0.8rem;
+ }
+
+ .search-container {
+ max-width: 100%;
+ }
+}
diff --git a/assets/css/search.css b/assets/css/search.css
new file mode 100644
index 0000000..7bd6995
--- /dev/null
+++ b/assets/css/search.css
@@ -0,0 +1,72 @@
+/* ========================================
+ SEARCH FUNCTIONALITY
+ ======================================== */
+.search-container {
+ position: relative;
+ max-width: 500px;
+ margin: 1rem 0;
+}
+
+.search-box {
+ width: 100%;
+ padding: 0.8rem 3rem 0.8rem 1rem;
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ background: var(--card-bg);
+ color: var(--text-color);
+ font-size: 1rem;
+ outline: none;
+ transition: all 0.3s ease;
+}
+
+.search-box:focus {
+ border-color: var(--button-bg);
+ box-shadow: 0 0 0 3px rgba(0, 120, 212, 0.1);
+}
+
+.search-icon {
+ position: absolute;
+ right: 1rem;
+ top: 50%;
+ transform: translateY(-50%);
+ color: var(--text-color);
+ opacity: 0.6;
+ pointer-events: none;
+}
+
+.search-results {
+ background: var(--card-bg);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ margin-top: 0.5rem;
+ max-height: 400px;
+ overflow-y: auto;
+ display: none;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+ z-index: 100;
+}
+
+.search-result-item {
+ padding: 0.75rem;
+ margin: 0;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.search-result-item:last-child {
+ border-bottom: none;
+}
+
+.search-result-item:hover {
+ background: var(--button-bg);
+ color: white;
+}
+
+.search-highlight {
+ background: #ffeb3b;
+ color: #000;
+ font-weight: bold;
+ padding: 0.1rem 0.2rem;
+ border-radius: 2px;
+}
diff --git a/assets/css/sidebar.css b/assets/css/sidebar.css
new file mode 100644
index 0000000..7718fb1
--- /dev/null
+++ b/assets/css/sidebar.css
@@ -0,0 +1,121 @@
+/* ========================================
+ SIDEBAR (RECENT PLOTS)
+ ======================================== */
+.sidebar {
+ position: fixed;
+ top: 0;
+ right: -350px;
+ width: 330px;
+ height: 100vh;
+ background: var(--card-bg);
+ border-left: 2px solid var(--border-color);
+ z-index: 2000;
+ transition: right 0.3s ease;
+ overflow-y: auto;
+ box-shadow: -4px 0 15px rgba(0,0,0,0.2);
+}
+
+.sidebar.open {
+ right: 0;
+}
+
+.sidebar-header {
+ padding: 1.2rem;
+ border-bottom: 1px solid var(--border-color);
+ position: sticky;
+ top: 0;
+ background: var(--card-bg);
+ z-index: 1;
+}
+
+.sidebar-title {
+ margin: 0;
+ font-size: 1.2rem;
+ color: var(--text-color);
+ font-weight: 600;
+}
+
+.sidebar-close {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ color: var(--text-color);
+ padding: 0.2rem;
+ border-radius: 4px;
+ transition: background-color 0.2s ease;
+}
+
+.sidebar-close:hover {
+ background: var(--border-color);
+}
+
+.sidebar-content {
+ padding: 1rem;
+}
+
+.recent-plot {
+ display: flex;
+ gap: 0.7rem;
+ padding: 0.8rem;
+ margin-bottom: 0.8rem;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ border: 1px solid var(--border-color);
+}
+
+.recent-plot:hover {
+ background: var(--button-bg);
+ color: white;
+ transform: translateX(2px);
+}
+
+.recent-plot-thumb {
+ width: 60px;
+ height: 48px;
+ object-fit: cover;
+ border-radius: 4px;
+ flex-shrink: 0;
+}
+
+.recent-plot-info {
+ flex: 1;
+ min-width: 0;
+}
+
+.recent-plot-name {
+ font-size: 0.9rem;
+ font-weight: 600;
+ margin-bottom: 0.3rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.recent-plot-path {
+ font-size: 0.8rem;
+ color: var(--breadcrumb-color);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.sidebar-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0,0,0,0.5);
+ z-index: 1500;
+ display: none;
+ backdrop-filter: blur(2px);
+}
+
+.sidebar-overlay.open {
+ display: block;
+}
diff --git a/assets/css/stats.css b/assets/css/stats.css
new file mode 100644
index 0000000..cec72dc
--- /dev/null
+++ b/assets/css/stats.css
@@ -0,0 +1,40 @@
+/* ========================================
+ GALLERY STATISTICS
+ ======================================== */
+.gallery-stats {
+ position: fixed;
+ bottom: 20px;
+ left: 20px;
+ background: var(--card-bg);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 0.8rem 1rem;
+ font-size: 0.85rem;
+ color: var(--breadcrumb-color);
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+ z-index: 500;
+ opacity: 0.8;
+ transition: opacity 0.2s ease;
+ max-width: 200px;
+}
+
+.gallery-stats:hover {
+ opacity: 1;
+}
+
+.stats-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 0.2rem 0;
+ white-space: nowrap;
+}
+
+.stats-label {
+ margin-right: 0.8rem;
+}
+
+.stats-value {
+ font-weight: 600;
+ color: var(--text-color);
+}
diff --git a/assets/css/variables.css b/assets/css/variables.css
new file mode 100644
index 0000000..72c531e
--- /dev/null
+++ b/assets/css/variables.css
@@ -0,0 +1,33 @@
+/* ========================================
+ CSS VARIABLES AND THEME DEFINITIONS
+ ======================================== */
+:root {
+ --bg-color: #1e1e1e;
+ --text-color: #ffffff;
+ --card-bg: #2d2d2d;
+ --border-color: #404040;
+ --link-color: #569cd6;
+ --link-hover: #4a9eff;
+ --button-bg: #0078d4;
+ --button-hover: #106ebe;
+ --breadcrumb-color: #cccccc;
+ --tree-bg: #252526;
+ --tree-current-bg: #0078d4;
+ --success-color: #28a745;
+ --success-hover: #218838;
+ --disabled-color: #6c757d;
+}
+
+[data-theme="light"] {
+ --bg-color: #ffffff;
+ --text-color: #333333;
+ --card-bg: #f8f8f8;
+ --border-color: #ddd;
+ --link-color: #007acc;
+ --link-hover: #005a9e;
+ --button-bg: #007acc;
+ --button-hover: #005a9e;
+ --breadcrumb-color: #666;
+ --tree-bg: #f8f8f8;
+ --tree-current-bg: #007acc;
+}
diff --git a/assets/js/comparison-manager.js b/assets/js/comparison-manager.js
new file mode 100644
index 0000000..7056775
--- /dev/null
+++ b/assets/js/comparison-manager.js
@@ -0,0 +1,180 @@
+/**
+ * Plot comparison functionality
+ */
+export class ComparisonManager {
+ constructor() {
+ this.comparisonMode = false;
+ this.comparisonSlot = null; // 'left' or 'right'
+ this.plots = { left: null, right: null };
+ }
+
+ /**
+ * Toggle comparison mode
+ */
+ toggleCompareMode() {
+ this.comparisonMode = !this.comparisonMode;
+ const compareBtn = document.getElementById('compareToggle');
+
+ if (!compareBtn) return;
+
+ if (this.comparisonMode) {
+ compareBtn.style.background = 'var(--success-color)';
+ compareBtn.title = 'Exit Compare Mode (Ctrl+C)';
+ this.showComparisonOverlay();
+ } else {
+ compareBtn.style.background = 'var(--button-bg)';
+ compareBtn.title = 'Compare Plots (Ctrl+C)';
+ this.hideComparisonOverlay();
+ }
+ }
+
+ /**
+ * Show comparison overlay
+ */
+ showComparisonOverlay() {
+ const overlay = document.getElementById('comparisonOverlay');
+ if (overlay) overlay.classList.add('open');
+ }
+
+ /**
+ * Hide comparison overlay
+ */
+ hideComparisonOverlay() {
+ const overlay = document.getElementById('comparisonOverlay');
+ if (overlay) overlay.classList.remove('open');
+ this.comparisonMode = false;
+
+ const compareBtn = document.getElementById('compareToggle');
+ if (compareBtn) {
+ compareBtn.style.background = 'var(--button-bg)';
+ compareBtn.title = 'Compare Plots (Ctrl+C)';
+ }
+ }
+
+ /**
+ * Close comparison overlay (alias for hideComparisonOverlay)
+ */
+ closeComparison() {
+ this.hideComparisonOverlay();
+ }
+
+ /**
+ * Select plot for comparison - simple approach
+ */
+ selectPlotForComparison(slot) {
+ this.comparisonSlot = slot;
+
+ // Hide overlay temporarily by removing the 'open' class
+ const overlay = document.getElementById('comparisonOverlay');
+ if (overlay) overlay.classList.remove('open');
+
+ // Show simple alert with instructions
+ const instruction = document.createElement('div');
+ instruction.id = 'comparisonInstruction';
+ instruction.style.cssText = `
+ position: fixed;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ background: var(--button-bg);
+ color: white;
+ padding: 1rem 2rem;
+ border-radius: 8px;
+ z-index: 3000;
+ font-size: 1.1rem;
+ font-weight: 600;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.3);
+ `;
+ instruction.innerHTML = `📊 Click any plot to select for ${slot === 'left' ? 'Plot A' : 'Plot B'} (ESC to cancel)`;
+ document.body.appendChild(instruction);
+
+ // Add one-time click listener to all grid items
+ const gridItems = document.querySelectorAll('.grid-item');
+ const handleClick = (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const gridItem = event.currentTarget;
+ const img = gridItem.querySelector('img');
+ const nameEl = gridItem.querySelector('.plot-name');
+
+ if (img && nameEl) {
+ const plotInfo = {
+ name: nameEl.textContent.trim(),
+ imgSrc: img.src,
+ pdfSrc: img.src.replace('.png', '.pdf'),
+ path: window.location.pathname
+ };
+
+ this.addPlotToComparison(plotInfo, slot);
+ }
+
+ // Clean up
+ instruction.remove();
+ gridItems.forEach(item => item.removeEventListener('click', handleClick));
+ this.comparisonSlot = null;
+ if (overlay) overlay.classList.add('open');
+ };
+
+ gridItems.forEach(item => {
+ item.style.cursor = 'pointer';
+ item.style.border = '2px dashed var(--button-bg)';
+ item.addEventListener('click', handleClick);
+ });
+
+ // ESC to cancel
+ const handleEscape = (event) => {
+ if (event.key === 'Escape') {
+ instruction.remove();
+ gridItems.forEach(item => {
+ item.removeEventListener('click', handleClick);
+ item.style.cursor = '';
+ item.style.border = '';
+ });
+ this.comparisonSlot = null;
+ if (overlay) overlay.classList.add('open');
+ document.removeEventListener('keydown', handleEscape);
+ }
+ };
+ document.addEventListener('keydown', handleEscape);
+ }
+
+ /**
+ * Add plot to comparison panel
+ */
+ addPlotToComparison(plotInfo, slot) {
+ this.plots[slot] = plotInfo;
+
+ const container = document.getElementById(`${slot}PlotContainer`);
+ const title = document.getElementById(`${slot}PlotTitle`);
+ const replaceBtn = document.getElementById(`${slot}ReplaceBtn`);
+
+ if (container) {
+ container.innerHTML = `
+
+