feat: Website enhancements and new placeholder pages
- Remove knowledge graph from homepage (keep only on blog page) - Add placeholder page for Infrastructure as Code Templates under projects - Fix build errors in homelab.astro by implementing component-based architecture - Improve page navigation structure and consistency - Update styling for better mobile responsiveness
This commit is contained in:
		
							parent
							
								
									540155c638
								
							
						
					
					
						commit
						1fa31d0617
					
				|  | @ -0,0 +1,230 @@ | ||||||
|  | --- | ||||||
|  | // src/components/homelab/HeroSection.astro - Ultra minimal version | ||||||
|  | const { servicesCount } = Astro.props; | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <section id="home" class="hero"> | ||||||
|  |   <div class="container"> | ||||||
|  |     <div class="hero-content"> | ||||||
|  |       <div class="hero-text"> | ||||||
|  |         <h1 class="hero-title"> | ||||||
|  |           Enterprise-Grade <span class="highlight">Home Lab</span> Environment | ||||||
|  |         </h1> | ||||||
|  |         <p class="hero-description"> | ||||||
|  |           A production-ready infrastructure platform for DevOps experimentation, distributed systems, and automating everything with code. | ||||||
|  |         </p> | ||||||
|  | 
 | ||||||
|  |         <!-- Simple stats display --> | ||||||
|  |         <div class="stats-box"> | ||||||
|  |           <div class="stat-item"> | ||||||
|  |             <div class="stat-label">Services:</div> | ||||||
|  |             <div class="stat-value">{servicesCount}+</div> | ||||||
|  |           </div> | ||||||
|  |           <div class="stat-item"> | ||||||
|  |             <div class="stat-label">CPU Cores:</div> | ||||||
|  |             <div class="stat-value">32+</div> | ||||||
|  |           </div> | ||||||
|  |           <div class="stat-item"> | ||||||
|  |             <div class="stat-label">Memory:</div> | ||||||
|  |             <div class="stat-value">64GB</div> | ||||||
|  |           </div> | ||||||
|  |           <div class="stat-item"> | ||||||
|  |             <div class="stat-label">Storage:</div> | ||||||
|  |             <div class="stat-value">12TB</div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="cta-buttons"> | ||||||
|  |           <a href="/ansible-sandbox" class="btn btn-danger" id="ansible-sandbox-btn"> | ||||||
|  |             <span class="btn-text">Try Ansible Sandbox</span> | ||||||
|  |             <span class="offline-badge">Offline</span> | ||||||
|  |           </a> | ||||||
|  |           <a href="#architecture" class="btn btn-outline"> | ||||||
|  |             <span class="btn-text">Explore Architecture</span> | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <!-- Simple status box instead of terminal --> | ||||||
|  |       <div class="status-box"> | ||||||
|  |         <div class="status-header"> | ||||||
|  |           <div class="status-title">System Status</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="status-body"> | ||||||
|  |           <div class="status-line"> | ||||||
|  |             <span class="status-label">Nodes:</span> | ||||||
|  |             <span class="status-value">2 (Ready)</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="status-line"> | ||||||
|  |             <span class="status-label">Running Pods:</span> | ||||||
|  |             <span class="status-value">32</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="status-line"> | ||||||
|  |             <span class="status-label">Uptime:</span> | ||||||
|  |             <span class="status-value">154 days</span> | ||||||
|  |           </div> | ||||||
|  |           <div class="status-line"> | ||||||
|  |             <span class="status-label">Load:</span>  | ||||||
|  |             <span class="status-value">0.22, 0.18, 0.15</span> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |   .hero { | ||||||
|  |     min-height: 100vh; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     position: relative; | ||||||
|  |     overflow: hidden; | ||||||
|  |     padding: 6rem 0 4rem; | ||||||
|  |     background: linear-gradient(180deg, var(--bg-secondary), var(--bg-primary)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .hero-content { | ||||||
|  |     display: flex; | ||||||
|  |     flex-wrap: wrap; | ||||||
|  |     gap: 2rem; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: space-between; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .hero-text { | ||||||
|  |     flex: 1; | ||||||
|  |     min-width: 300px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .hero-title { | ||||||
|  |     font-size: 2.5rem; | ||||||
|  |     margin-bottom: 1rem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .highlight { | ||||||
|  |     color: var(--primary, #3b82f6); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .hero-description { | ||||||
|  |     font-size: 1.125rem; | ||||||
|  |     margin-bottom: 2rem; | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .stats-box { | ||||||
|  |     background-color: rgba(15, 23, 42, 0.7); | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     padding: 1.5rem; | ||||||
|  |     margin: 2rem 0; | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); | ||||||
|  |     gap: 1rem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .stat-item { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .stat-label { | ||||||
|  |     font-size: 0.875rem; | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .stat-value { | ||||||
|  |     font-size: 1.5rem; | ||||||
|  |     font-weight: bold; | ||||||
|  |     color: var(--success, #10b981); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .cta-buttons { | ||||||
|  |     display: flex; | ||||||
|  |     gap: 1rem; | ||||||
|  |     margin-top: 2rem; | ||||||
|  |     flex-wrap: wrap; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .btn { | ||||||
|  |     display: inline-flex; | ||||||
|  |     align-items: center; | ||||||
|  |     padding: 0.75rem 1.5rem; | ||||||
|  |     border-radius: 0.375rem; | ||||||
|  |     font-weight: 500; | ||||||
|  |     text-decoration: none; | ||||||
|  |     transition: all 0.2s ease; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .btn-danger { | ||||||
|  |     background-color: var(--error, #ef4444); | ||||||
|  |     color: white; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .btn-outline { | ||||||
|  |     background-color: transparent; | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |     color: var(--text-primary); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .offline-badge { | ||||||
|  |     background-color: rgba(0, 0, 0, 0.2); | ||||||
|  |     font-size: 0.75rem; | ||||||
|  |     padding: 0.25rem 0.5rem; | ||||||
|  |     border-radius: 1rem; | ||||||
|  |     margin-left: 0.5rem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-box { | ||||||
|  |     background-color: rgba(15, 23, 42, 0.9); | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     overflow: hidden; | ||||||
|  |     width: 100%; | ||||||
|  |     max-width: 350px; | ||||||
|  |     box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-header { | ||||||
|  |     padding: 0.75rem 1rem; | ||||||
|  |     background-color: rgba(30, 41, 59, 0.8); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-title { | ||||||
|  |     font-size: 0.875rem; | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-body { | ||||||
|  |     padding: 1rem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-line { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: space-between; | ||||||
|  |     margin-bottom: 0.75rem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-label { | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .status-value { | ||||||
|  |     color: var(--success, #10b981); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @media (max-width: 768px) { | ||||||
|  |     .hero-content { | ||||||
|  |       flex-direction: column; | ||||||
|  |       align-items: flex-start; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .stats-box { | ||||||
|  |       grid-template-columns: repeat(2, 1fr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .status-box { | ||||||
|  |       max-width: none; | ||||||
|  |       width: 100%; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,119 @@ | ||||||
|  | --- | ||||||
|  | // src/components/homelab/ProjectsSection.astro | ||||||
|  | // Making sure we properly handle the projectsData prop | ||||||
|  | const { projectsData } = Astro.props; | ||||||
|  | 
 | ||||||
|  | // If projectsData is undefined or not an array, provide a fallback | ||||||
|  | const projects = Array.isArray(projectsData) ? projectsData : []; | ||||||
|  | 
 | ||||||
|  | // Debug logging | ||||||
|  | console.log("ProjectsSection received projectsData:", !!projectsData); | ||||||
|  | console.log("ProjectsSection projects count:", projects.length); | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <section id="projects" class="projects section-padding alt-bg"> | ||||||
|  |   <div class="container"> | ||||||
|  |     <div class="section-header"> | ||||||
|  |       <h2 class="section-title">Related Projects</h2> | ||||||
|  |       <p class="section-description"> | ||||||
|  |         Explore associated projects and repositories related to the ArgoBox lab. | ||||||
|  |       </p> | ||||||
|  |     </div> | ||||||
|  |      | ||||||
|  |     <div class="projects-grid"> | ||||||
|  |       {projects.length > 0 ? ( | ||||||
|  |         projects.map(project => ( | ||||||
|  |           <a  | ||||||
|  |             href={project.url}  | ||||||
|  |             class="project-card"  | ||||||
|  |             target="_blank" | ||||||
|  |             rel="noopener noreferrer" | ||||||
|  |           > | ||||||
|  |             <div class="project-header"> | ||||||
|  |               <div class="project-icon"><i class={project.icon}></i></div> | ||||||
|  |               <h3 class="project-title">{project.title}</h3> | ||||||
|  |             </div> | ||||||
|  |             <p class="project-description">{project.description}</p> | ||||||
|  |             <div class="project-tech"> | ||||||
|  |               {project.tech && project.tech.map(tech => ( | ||||||
|  |                 <span class="tech-badge">{tech}</span> | ||||||
|  |               ))} | ||||||
|  |             </div> | ||||||
|  |           </a> | ||||||
|  |         )) | ||||||
|  |       ) : ( | ||||||
|  |         <div class="no-projects"> | ||||||
|  |           <p>No projects available at this time.</p> | ||||||
|  |         </div> | ||||||
|  |       )} | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |   .projects-grid { | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | ||||||
|  |     gap: 2rem; | ||||||
|  |     margin-top: 2rem; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .project-card { | ||||||
|  |     padding: 1.5rem; | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     background-color: var(--bg-card); | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |     transition: all 0.3s ease; | ||||||
|  |     text-decoration: none; | ||||||
|  |     color: var(--text-primary); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-card:hover { | ||||||
|  |     transform: translateY(-5px); | ||||||
|  |     box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-header { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     gap: 1rem; | ||||||
|  |     margin-bottom: 1rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-icon { | ||||||
|  |     font-size: 1.5rem; | ||||||
|  |     color: var(--primary); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-title { | ||||||
|  |     font-size: 1.25rem; | ||||||
|  |     margin: 0; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-description { | ||||||
|  |     margin-bottom: 1.5rem; | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-tech { | ||||||
|  |     display: flex; | ||||||
|  |     flex-wrap: wrap; | ||||||
|  |     gap: 0.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .tech-badge { | ||||||
|  |     padding: 0.25rem 0.5rem; | ||||||
|  |     border-radius: 0.25rem; | ||||||
|  |     background-color: var(--bg-secondary); | ||||||
|  |     font-size: 0.75rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .no-projects { | ||||||
|  |     grid-column: 1 / -1; | ||||||
|  |     text-align: center; | ||||||
|  |     padding: 3rem; | ||||||
|  |     background-color: var(--bg-card); | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,80 @@ | ||||||
|  | // src/components/homelab/ServiceCategory.astro | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | const { category } = Astro.props; | ||||||
|  | 
 | ||||||
|  | function getCategoryIcon(categoryKey) { | ||||||
|  |   if (categoryKey === 'development') return 'fa-code'; | ||||||
|  |   if (categoryKey === 'media') return 'fa-photo-video'; | ||||||
|  |   if (categoryKey === 'utilities') return 'fa-tools'; | ||||||
|  |   return 'fa-cogs'; // Default for infrastructure | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const categoryIcon = getCategoryIcon(category.key); | ||||||
|  | const categoryTitle = category.key.charAt(0).toUpperCase() + category.key.slice(1); | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <div class="services-category" data-category={category.key}> | ||||||
|  |   <h3 class="category-title"> | ||||||
|  |     <i class={`fas ${categoryIcon} category-icon`}></i> | ||||||
|  |     {categoryTitle} Tools | ||||||
|  |   </h3> | ||||||
|  |   <div class="service-items"> | ||||||
|  |     {category.services.map(service => ( | ||||||
|  |       <a | ||||||
|  |         href={service.available ? service.url : '#'} | ||||||
|  |         class={`service-item ${service.available ? 'available' : ''} service-${service.name.toLowerCase().replace(/\s+/g, '-')}`} | ||||||
|  |         target={service.available ? "_blank" : null} | ||||||
|  |         rel={service.available ? "noopener noreferrer" : null} | ||||||
|  |         aria-disabled={!service.available} | ||||||
|  |         data-service={service.name.toLowerCase().replace(/\s+/g, '-')} | ||||||
|  |       > | ||||||
|  |         <div class="service-icon"><i class={service.icon}></i></div> | ||||||
|  |         <div class="service-info"> | ||||||
|  |           <div class="service-name">{service.name}</div> | ||||||
|  |           <p class="service-description">{service.description}</p> | ||||||
|  |         </div> | ||||||
|  |         <span class={`service-status ${service.status}`}> | ||||||
|  |           <span class="status-dot"></span> | ||||||
|  |           {service.status.charAt(0).toUpperCase() + service.status.slice(1)} | ||||||
|  |         </span> | ||||||
|  |       </a> | ||||||
|  |     ))} | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |   .services-category { | ||||||
|  |     margin-bottom: 2rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-title { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     margin-bottom: 1rem; | ||||||
|  |     font-size: 1.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-icon { | ||||||
|  |     margin-right: 0.75rem; | ||||||
|  |     opacity: 0.8; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-items { | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | ||||||
|  |     gap: 1rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-item { | ||||||
|  |     display: flex; | ||||||
|  |     padding: 1.25rem; | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     background-color: var(--bg-card); | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |     transition: all 0.3s ease; | ||||||
|  |     text-decoration: none; | ||||||
|  |     color: var(--text-primary); | ||||||
|  |     position: relative; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,201 @@ | ||||||
|  | --- | ||||||
|  | // src/components/homelab/ServicesSection.astro | ||||||
|  | // Making sure servicesData is properly handled | ||||||
|  | 
 | ||||||
|  | const { servicesData } = Astro.props; | ||||||
|  | 
 | ||||||
|  | // Helper functions | ||||||
|  | function getCategoryIcon(category) { | ||||||
|  |   if (category === 'development') return 'fa-code'; | ||||||
|  |   if (category === 'media') return 'fa-photo-video'; | ||||||
|  |   if (category === 'utilities') return 'fa-tools'; | ||||||
|  |   return 'fa-cogs'; // Default for infrastructure | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function formatServiceName(name) { | ||||||
|  |   return name.toLowerCase().replace(/\s+/g, '-'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Create a safe version of the data in case it's undefined | ||||||
|  | const safeServicesData = servicesData || {}; | ||||||
|  | 
 | ||||||
|  | // Prepare data for rendering to minimize template expressions | ||||||
|  | const categoryEntries = Object.entries(safeServicesData).map(([key, services]) => { | ||||||
|  |   return { | ||||||
|  |     key, | ||||||
|  |     title: key.charAt(0).toUpperCase() + key.slice(1), | ||||||
|  |     services: Array.isArray(services) ? services : [] | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // Debug logging | ||||||
|  | console.log("ServicesSection received servicesData:", !!servicesData); | ||||||
|  | console.log("ServicesSection categories count:", categoryEntries.length); | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <section id="services" class="services section-padding"> | ||||||
|  |   <div class="container"> | ||||||
|  |     <div class="section-header"> | ||||||
|  |       <h2 class="section-title">Available Services</h2> | ||||||
|  |       <p class="section-description"> | ||||||
|  |         Explore the various services and applications hosted in the ArgoBox environment. | ||||||
|  |       </p> | ||||||
|  |     </div> | ||||||
|  |      | ||||||
|  |     <div class="services-info-banner"> | ||||||
|  |       <i class="fas fa-info-circle info-icon"></i> | ||||||
|  |       <p>Some services require authentication and are restricted. Available public services are highlighted and clickable.</p> | ||||||
|  |     </div> | ||||||
|  |      | ||||||
|  |     <div class="services-grid"> | ||||||
|  |       {categoryEntries.length > 0 ? ( | ||||||
|  |         categoryEntries.map(category => ( | ||||||
|  |           <div class="services-category" data-category={category.key}> | ||||||
|  |             <h3 class="category-title"> | ||||||
|  |               <i class={`fas ${getCategoryIcon(category.key)} category-icon`}></i> | ||||||
|  |               {category.title} Tools | ||||||
|  |             </h3> | ||||||
|  |             <div class="service-items"> | ||||||
|  |               {category.services.map(service => ( | ||||||
|  |                 <a | ||||||
|  |                   href={service.available ? service.url : '#'} | ||||||
|  |                   class={`service-item ${service.available ? 'available' : ''} service-${formatServiceName(service.name)}`} | ||||||
|  |                   target={service.available ? "_blank" : null} | ||||||
|  |                   rel={service.available ? "noopener noreferrer" : null} | ||||||
|  |                   aria-disabled={!service.available} | ||||||
|  |                 > | ||||||
|  |                   <div class="service-icon"><i class={service.icon}></i></div> | ||||||
|  |                   <div class="service-info"> | ||||||
|  |                     <div class="service-name">{service.name}</div> | ||||||
|  |                     <p class="service-description">{service.description}</p> | ||||||
|  |                   </div> | ||||||
|  |                   <span class={`service-status ${service.status}`}> | ||||||
|  |                     <span class="status-dot"></span> | ||||||
|  |                     {service.status.charAt(0).toUpperCase() + service.status.slice(1)} | ||||||
|  |                   </span> | ||||||
|  |                 </a> | ||||||
|  |               ))} | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         )) | ||||||
|  |       ) : ( | ||||||
|  |         <div class="no-services"> | ||||||
|  |           <p>No services available at this time.</p> | ||||||
|  |         </div> | ||||||
|  |       )} | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </section> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |   .services-grid { | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: 1fr; | ||||||
|  |     gap: 2rem; | ||||||
|  |     margin-top: 2rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .services-info-banner { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     padding: 1rem; | ||||||
|  |     background-color: rgba(59, 130, 246, 0.1); | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     margin-top: 1rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .info-icon { | ||||||
|  |     margin-right: 1rem; | ||||||
|  |     color: var(--primary, #3b82f6); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .services-category { | ||||||
|  |     margin-bottom: 2rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-title { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     margin-bottom: 1rem; | ||||||
|  |     font-size: 1.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-icon { | ||||||
|  |     margin-right: 0.75rem; | ||||||
|  |     opacity: 0.8; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-items { | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | ||||||
|  |     gap: 1rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-item { | ||||||
|  |     display: flex; | ||||||
|  |     padding: 1.25rem; | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     background-color: var(--bg-card); | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |     transition: all 0.3s ease; | ||||||
|  |     text-decoration: none; | ||||||
|  |     color: var(--text-primary); | ||||||
|  |     position: relative; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-item.available:hover { | ||||||
|  |     transform: translateY(-3px); | ||||||
|  |     box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-icon { | ||||||
|  |     margin-right: 1rem; | ||||||
|  |     font-size: 1.25rem; | ||||||
|  |     color: var(--primary); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-info { | ||||||
|  |     flex: 1; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-name { | ||||||
|  |     font-weight: 500; | ||||||
|  |     margin-bottom: 0.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-description { | ||||||
|  |     font-size: 0.875rem; | ||||||
|  |     color: var(--text-secondary); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-status { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     font-size: 0.75rem; | ||||||
|  |     padding: 0.25rem 0.5rem; | ||||||
|  |     border-radius: 1rem; | ||||||
|  |     background-color: rgba(0, 0, 0, 0.1); | ||||||
|  |     margin-left: 1rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .status-dot { | ||||||
|  |     width: 8px; | ||||||
|  |     height: 8px; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     margin-right: 0.25rem; | ||||||
|  |     background-color: var(--success); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .service-status.offline .status-dot { | ||||||
|  |     background-color: var(--error); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .no-services { | ||||||
|  |     grid-column: 1 / -1; | ||||||
|  |     text-align: center; | ||||||
|  |     padding: 3rem; | ||||||
|  |     background-color: var(--bg-card); | ||||||
|  |     border-radius: 0.5rem; | ||||||
|  |     border: 1px solid var(--border); | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  | @ -0,0 +1,40 @@ | ||||||
|  | export const servicesData = { | ||||||
|  |   development: [ | ||||||
|  |     { name: "Gitea", description: "Self-hosted Git service", url: "https://git.argobox.com", status: "live", icon: "fas fa-code-branch", available: true }, | ||||||
|  |     { name: "VS Code Server", description: "Remote development environment", url: "https://code.argobox.com", status: "live", icon: "fas fa-terminal", available: true }, | ||||||
|  |     { name: "Drone CI", description: "Continuous Integration server", url: "https://drone.argobox.com", status: "live", icon: "fas fa-cogs", available: true }, | ||||||
|  |   ], | ||||||
|  |   media: [ | ||||||
|  |     { name: "Plex", description: "Media streaming server", url: "https://plex.argobox.com", status: "live", icon: "fas fa-play-circle", available: true }, | ||||||
|  |     { name: "Jellyfin", description: "Open source media system", url: "https://jellyfin.argobox.com", status: "live", icon: "fas fa-film", available: true }, | ||||||
|  |     { name: "Sonarr", description: "TV show management", url: "#", status: "restricted", icon: "fas fa-tv", available: false }, | ||||||
|  |     { name: "Radarr", description: "Movie management", url: "#", status: "restricted", icon: "fas fa-video", available: false }, | ||||||
|  |     { name: "Prowlarr", description: "Indexer management", url: "#", status: "restricted", icon: "fas fa-search", available: false }, | ||||||
|  |   ], | ||||||
|  |   utilities: [ | ||||||
|  |      { name: "File Browser", description: "Web file manager", url: "https://files.argobox.com", status: "live", icon: "fas fa-folder-open", available: true }, | ||||||
|  |      { name: "Vaultwarden", description: "Password manager", url: "#", status: "restricted", icon: "fas fa-key", available: false }, | ||||||
|  |      { name: "Homepage", description: "Service dashboard", url: "https://dash.argobox.com", status: "live", icon: "fas fa-tachometer-alt", available: true }, | ||||||
|  |      { name: "Uptime Kuma", description: "Status monitoring", url: "https://status.argobox.com", status: "live", icon: "fas fa-heartbeat", available: true }, | ||||||
|  |   ], | ||||||
|  |   infrastructure: [ | ||||||
|  |      { name: "Proxmox VE", description: "Virtualization platform", url: "#", status: "restricted", icon: "fas fa-server", available: false }, | ||||||
|  |      { name: "Kubernetes (K3s)", description: "Container orchestration", url: "#", status: "restricted", icon: "fas fa-dharmachakra", available: false }, | ||||||
|  |      { name: "Traefik", description: "Ingress controller", url: "#", status: "restricted", icon: "fas fa-route", available: false }, | ||||||
|  |      { name: "OPNsense", description: "Firewall/Router", url: "#", status: "restricted", icon: "fas fa-shield-alt", available: false }, | ||||||
|  |   ] | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const projectsData = [ | ||||||
|  |     { title: "Ansible Playbooks", description: "Collection of playbooks for automating system configuration and application deployment.", icon: "fab fa-ansible", tech: ["Ansible", "YAML", "Jinja2"], url: "/resources/iac" }, | ||||||
|  |     { title: "Kubernetes Manifests", description: "YAML definitions for deploying various applications and services on Kubernetes.", icon: "fas fa-dharmachakra", tech: ["Kubernetes", "YAML", "Helm"], url: "/resources/kubernetes" }, | ||||||
|  |     { title: "Monitoring Dashboards", description: "Grafana dashboards for visualizing infrastructure and application metrics.", icon: "fas fa-chart-line", tech: ["Grafana", "PromQL", "JSON"], url: "/resources/config-files" }, | ||||||
|  |     { title: "Cloudflare Tunnel Setup", description: "Securely exposing home lab services to the internet using Cloudflare Tunnels.", icon: "fas fa-cloud", tech: ["Cloudflare", "Networking", "Security"], url: "/posts/cloudflare-tunnel-setup" } | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | export const dashboardsData = [ | ||||||
|  |     { title: "Infrastructure Overview", description: "Key metrics for Proxmox hosts, network devices, and storage.", previewClass: "infrastructure", url: "https://dash.argobox.com/goto/...", icon: "fas fa-server" }, | ||||||
|  |     { title: "Kubernetes Cluster", description: "Detailed view of K3s cluster resources, node status, and pod health.", previewClass: "kubernetes", url: "https://dash.argobox.com/goto/...", icon: "fas fa-dharmachakra" }, | ||||||
|  |     { title: "Network Traffic", description: "Real-time and historical network usage, firewall logs, and connection tracking.", previewClass: "network", url: "https://dash.argobox.com/goto/...", icon: "fas fa-network-wired" }, | ||||||
|  |     { title: "Service Performance", description: "Application-specific metrics, request latency, and error rates.", previewClass: "services", url: "https://dash.argobox.com/goto/...", icon: "fas fa-cogs" } | ||||||
|  | ]; | ||||||
|  | @ -79,9 +79,9 @@ const { | ||||||
|      |      | ||||||
|     <!-- Favicon --> |     <!-- Favicon --> | ||||||
|     <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |     <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | ||||||
| <link rel="icon" type="image/x-icon" href="/favicon.ico" /> |     <link rel="icon" type="image/x-icon" href="/favicon.ico" /> | ||||||
|     <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> |     <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> | ||||||
|     <link rel="manifest" href="/site.webmanifest" /> |     <link rel="manifest" href="/site.webmanifest" /> | ||||||
|      |      | ||||||
|     <!-- Theme CSS --> |     <!-- Theme CSS --> | ||||||
|     <link rel="stylesheet" href="/src/styles/theme.css" /> |     <link rel="stylesheet" href="/src/styles/theme.css" /> | ||||||
|  |  | ||||||
|  | @ -1,967 +1,56 @@ | ||||||
| align-items: center; |  | ||||||
|     justify-content: center; |  | ||||||
|     text-align: center; |  | ||||||
|     position: relative; |  | ||||||
|     overflow: hidden; |  | ||||||
|     transition: all 0.3s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node:hover { |  | ||||||
|     transform: translateY(-5px); |  | ||||||
|     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); |  | ||||||
|     z-index: 10; |  | ||||||
|     border-color: var(--primary, #3b82f6); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node::before { |  | ||||||
|     content: ''; |  | ||||||
|     position: absolute; |  | ||||||
|     width: 150%; |  | ||||||
|     height: 150%; |  | ||||||
|     background: radial-gradient(circle, rgba(99, 102, 241, 0.3) 0%, transparent 70%); |  | ||||||
|     opacity: 0; |  | ||||||
|     transition: opacity 0.3s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node:hover::before { |  | ||||||
|     opacity: 1; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.gateway { |  | ||||||
|     grid-column: 1 / 2; |  | ||||||
|     grid-row: 1 / 2; |  | ||||||
|     border-color: var(--warning, #f59e0b); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.firewall { |  | ||||||
|     grid-column: 2 / 3; |  | ||||||
|     grid-row: 1 / 2; |  | ||||||
|     border-color: var(--error, #ef4444); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.proxmox { |  | ||||||
|     grid-column: 3 / 5; |  | ||||||
|     grid-row: 1 / 2; |  | ||||||
|     border-color: var(--primary, #3b82f6); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.kubernetes { |  | ||||||
|     grid-column: 2 / 4; |  | ||||||
|     grid-row: 2 / 3; |  | ||||||
|     border-color: var(--accent, #6366f1); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.storage { |  | ||||||
|     grid-column: 1 / 2; |  | ||||||
|     grid-row: 2 / 3; |  | ||||||
|     border-color: var(--success, #10b981); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.monitoring { |  | ||||||
|     grid-column: 4 / 5; |  | ||||||
|     grid-row: 2 / 3; |  | ||||||
|     border-color: var(--primary, #3b82f6); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node.services { |  | ||||||
|     grid-column: 1 / 5; |  | ||||||
|     grid-row: 3 / 4; |  | ||||||
|     display: flex; |  | ||||||
|     flex-direction: row; |  | ||||||
|     justify-content: space-around; |  | ||||||
|     flex-wrap: wrap; |  | ||||||
|     background-color: rgba(15, 23, 42, 0.3); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .service-icon { |  | ||||||
|     display: flex; |  | ||||||
|     align-items: center; |  | ||||||
|     justify-content: center; |  | ||||||
|     flex-direction: column; |  | ||||||
|     width: 80px; |  | ||||||
|     height: 80px; |  | ||||||
|     margin: 0.5rem; |  | ||||||
|     transition: all 0.3s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .service-icon:hover { |  | ||||||
|     transform: scale(1.1); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .service-icon i { |  | ||||||
|     font-size: 1.75rem; |  | ||||||
|     margin-bottom: 0.5rem; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .service-icon span { |  | ||||||
|     font-size: 0.75rem; |  | ||||||
|     color: var(--text-secondary); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Connection lines for architecture diagram */ |  | ||||||
|   .connection-line { |  | ||||||
|     position: absolute; |  | ||||||
|     background-color: var(--border, rgba(148, 163, 184, 0.1)); |  | ||||||
|     transition: all 0.3s ease; |  | ||||||
|     z-index: 5; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .connection-line.horizontal { |  | ||||||
|     height: 2px; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .connection-line.vertical { |  | ||||||
|     width: 2px; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .connection-line.active { |  | ||||||
|     background-color: var(--accent, #6366f1); |  | ||||||
|     box-shadow: 0 0 10px var(--accent, #6366f1); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Enhanced Service Items */ |  | ||||||
|   .service-item { |  | ||||||
|     position: relative; |  | ||||||
|     overflow: hidden; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .service-item::before { |  | ||||||
|     content: ''; |  | ||||||
|     position: absolute; |  | ||||||
|     left: 0; |  | ||||||
|     top: 0; |  | ||||||
|     height: 100%; |  | ||||||
|     width: 0; |  | ||||||
|     background: linear-gradient(90deg, var(--accent, #6366f1) 0%, transparent 100%); |  | ||||||
|     opacity: 0.1; |  | ||||||
|     transition: width 0.3s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .service-item.available:hover::before { |  | ||||||
|     width: 100%; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Enhanced Card Hover Effects */ |  | ||||||
|   .tech-card { |  | ||||||
|     position: relative; |  | ||||||
|     overflow: hidden; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .tech-card::after { |  | ||||||
|     content: ''; |  | ||||||
|     position: absolute; |  | ||||||
|     bottom: 0; |  | ||||||
|     left: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 3px; |  | ||||||
|     background: linear-gradient(90deg, var(--primary, #3b82f6), var(--accent, #6366f1)); |  | ||||||
|     transform: scaleX(0); |  | ||||||
|     transform-origin: right; |  | ||||||
|     transition: transform 0.5s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .tech-card:hover::after { |  | ||||||
|     transform: scaleX(1); |  | ||||||
|     transform-origin: left; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Enhanced Project Cards */ |  | ||||||
|   .project-card { |  | ||||||
|     position: relative; |  | ||||||
|     overflow: hidden; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .project-card::before { |  | ||||||
|     content: ''; |  | ||||||
|     position: absolute; |  | ||||||
|     top: -50px; |  | ||||||
|     right: -50px; |  | ||||||
|     width: 100px; |  | ||||||
|     height: 100px; |  | ||||||
|     background: linear-gradient(45deg, var(--primary, #3b82f6), var(--accent, #6366f1)); |  | ||||||
|     transform: rotate(45deg); |  | ||||||
|     opacity: 0; |  | ||||||
|     transition: all 0.5s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .project-card:hover::before { |  | ||||||
|     opacity: 0.15; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Enhanced Dashboard Cards */ |  | ||||||
|   .dashboard-preview.infrastructure { |  | ||||||
|     background-image: linear-gradient(45deg, rgba(30, 41, 59, 0.7), rgba(15, 23, 42, 0.7)), url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNDAgMTIwIj48cmVjdCB4PSIxMCIgeT0iMTAiIHdpZHRoPSI1MCIgaGVpZ2h0PSIxMDAiIGZpbGw9IiMyNTYzZWIiIGZpbGwtb3BhY2l0eT0iMC4yIiByeD0iNSIvPjxyZWN0IHg9IjgwIiB5PSIxMCIgd2lkdGg9IjUwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iIzI1NjNlYiIgZmlsbC1vcGFjaXR5PSIwLjMiIHJ4PSI1Ii8+PHJlY3QgeD0iMTUwIiB5PSIxMCIgd2lkdGg9IjUwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iIzI1NjNlYiIgZmlsbC1vcGFjaXR5PSIwLjQiIHJ4PSI1Ii8+PC9zdmc+'); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .dashboard-preview.kubernetes { |  | ||||||
|     background-image: linear-gradient(45deg, rgba(30, 41, 59, 0.7), rgba(15, 23, 42, 0.7)), url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNDAgMTIwIj48Y2lyY2xlIGN4PSIxMjAiIGN5PSI2MCIgcj0iNDAiIGZpbGw9IiM2MzY2ZjEiIGZpbGwtb3BhY2l0eT0iMC4yIi8+PGNpcmNsZSBjeD0iNTAiIGN5PSI0MCIgcj0iMjAiIGZpbGw9IiM2MzY2ZjEiIGZpbGwtb3BhY2l0eT0iMC4zIi8+PGNpcmNsZSBjeD0iNTAiIGN5PSI5MCIgcj0iMjAiIGZpbGw9IiM2MzY2ZjEiIGZpbGwtb3BhY2l0eT0iMC4zIi8+PGNpcmNsZSBjeD0iMTkwIiBjeT0iNDAiIHI9IjIwIiBmaWxsPSIjNjM2NmYxIiBmaWxsLW9wYWNpdHk9IjAuMyIvPjxjaXJjbGUgY3g9IjE5MCIgY3k9IjkwIiByPSIyMCIgZmlsbD0iIzYzNjZmMSIgZmlsbC1vcGFjaXR5PSIwLjMiLz48bGluZSB4MT0iNTAiIHkxPSI0MCIgeDI9IjEyMCIgeTI9IjYwIiBzdHJva2U9IiM2MzY2ZjEiIHN0cm9rZS1vcGFjaXR5PSIwLjQiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIHgxPSI1MCIgeTE9IjkwIiB4Mj0iMTIwIiB5Mj0iNjAiIHN0cm9rZT0iIzYzNjZmMSIgc3Ryb2tlLW9wYWNpdHk9IjAuNCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjE5MCIgeTE9IjQwIiB4Mj0iMTIwIiB5Mj0iNjAiIHN0cm9rZT0iIzYzNjZmMSIgc3Ryb2tlLW9wYWNpdHk9IjAuNCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjE5MCIgeTE9IjkwIiB4Mj0iMTIwIiB5Mj0iNjAiIHN0cm9rZT0iIzYzNjZmMSIgc3Ryb2tlLW9wYWNpdHk9IjAuNCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+'); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .dashboard-preview.network { |  | ||||||
|     background-image: linear-gradient(45deg, rgba(30, 41, 59, 0.7), rgba(15, 23, 42, 0.7)), url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNDAgMTIwIj48cGF0aCBkPSJNMTAgMTAgQzIzMCAxMCAyMzAgNTAgMTAgNTAgQzIzMCA1MCAyMzAgOTAgMTAgOTAgQzIzMCA5MCAyMzAgMTMwIDEwIDEzMCIgc3Ryb2tlPSIjMTBiOTgxIiBzdHJva2Utb3BhY2l0eT0iMC40IiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz48L3N2Zz4='); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .dashboard-preview.services { |  | ||||||
|     background-image: linear-gradient(45deg, rgba(30, 41, 59, 0.7), rgba(15, 23, 42, 0.7)), url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNDAgMTIwIj48cmVjdCB4PSIxMCIgeT0iMTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2VmNDQ0NCIgZmlsbC1vcGFjaXR5PSIwLjIiIHJ4PSI1Ii8+PHJlY3QgeD0iMTAiIHk9IjQwIiB3aWR0aD0iNzAiIGhlaWdodD0iMjAiIGZpbGw9IiNlZjQ0NDQiIGZpbGwtb3BhY2l0eT0iMC4zIiByeD0iNSIvPjxyZWN0IHg9IjEwIiB5PSI3MCIgd2lkdGg9IjMwIiBoZWlnaHQ9IjIwIiBmaWxsPSIjZWY0NDQ0IiBmaWxsLW9wYWNpdHk9IjAuNCIgcng9IjUiLz48cmVjdCB4PSIxMDAiIHk9IjEwIiB3aWR0aD0iODAiIGhlaWdodD0iMjAiIGZpbGw9IiM2MzY2ZjEiIGZpbGwtb3BhY2l0eT0iMC4yIiByeD0iNSIvPjxyZWN0IHg9IjEwMCIgeT0iNDAiIHdpZHRoPSI1MCIgaGVpZ2h0PSIyMCIgZmlsbD0iIzYzNjZmMSIgZmlsbC1vcGFjaXR5PSIwLjMiIHJ4PSI1Ii8+PHJlY3QgeD0iMTAwIiB5PSI3MCIgd2lkdGg9IjEyMCIgaGVpZ2h0PSIyMCIgZmlsbD0iIzYzNjZmMSIgZmlsbC1vcGFjaXR5PSIwLjQiIHJ4PSI1Ii8+PC9zdmc+'); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Enhanced Dashboard Overlay */ |  | ||||||
|   .dashboard-overlay .overlay-icon { |  | ||||||
|     transition: transform 0.3s ease; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .dashboard-card:hover .overlay-icon { |  | ||||||
|     transform: scale(1.2); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Keep your existing responsive styles */ |  | ||||||
|   @media (max-width: 1024px) { |  | ||||||
|     .architecture-diagram { |  | ||||||
|       grid-template-columns: repeat(2, 1fr); |  | ||||||
|       grid-template-rows: repeat(6, auto); |  | ||||||
|       gap: 1rem; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .diagram-node.gateway { grid-column: 1 / 2; grid-row: 1 / 2; } |  | ||||||
|     .diagram-node.firewall { grid-column: 2 / 3; grid-row: 1 / 2; } |  | ||||||
|     .diagram-node.proxmox { grid-column: 1 / 3; grid-row: 2 / 3; } |  | ||||||
|     .diagram-node.kubernetes { grid-column: 1 / 3; grid-row: 3 / 4; } |  | ||||||
|     .diagram-node.storage { grid-column: 1 / 2; grid-row: 4 / 5; } |  | ||||||
|     .diagram-node.monitoring { grid-column: 2 / 3; grid-row: 4 / 5; } |  | ||||||
|     .diagram-node.services { grid-column: 1 / 3; grid-row: 5 / 6; } |  | ||||||
|   } |  | ||||||
| </style> |  | ||||||
| 
 |  | ||||||
| <script> |  | ||||||
|   // --- Enhanced Terminal Animation --- |  | ||||||
|   function animateTerminal() { |  | ||||||
|     const terminalBody = document.getElementById('animated-terminal'); |  | ||||||
|     if (!terminalBody) return; |  | ||||||
|      |  | ||||||
|     const lines = terminalBody.querySelectorAll('.terminal-line'); |  | ||||||
|     let delay = 100; |  | ||||||
|      |  | ||||||
|     lines.forEach((line, index) => { |  | ||||||
|       setTimeout(() => { |  | ||||||
|         line.classList.add('visible'); |  | ||||||
|       }, delay * index); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Terminal Cursor Animation --- |  | ||||||
|   function initTerminalTyping() { |  | ||||||
|     const cursor = document.querySelector('.cursor'); |  | ||||||
|     if (!cursor) return; |  | ||||||
|      |  | ||||||
|     setInterval(() => { |  | ||||||
|       cursor.style.opacity = cursor.style.opacity === '0' ? '1' : '0'; |  | ||||||
|     }, 600); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Particle Animation --- |  | ||||||
|   function createBackgroundParticles(count = 30) { |  | ||||||
|     const particlesContainer = document.getElementById('particles-container'); |  | ||||||
|     if (!particlesContainer) return; |  | ||||||
|     particlesContainer.innerHTML = ''; |  | ||||||
| 
 |  | ||||||
|     for (let i = 0; i < count; i++) { |  | ||||||
|       const particle = document.createElement('div'); |  | ||||||
|       particle.classList.add('particle'); |  | ||||||
|       const size = Math.random() * 3 + 1; |  | ||||||
|       particle.style.width = `${size}px`; |  | ||||||
|       particle.style.height = `${size}px`; |  | ||||||
|       particle.style.left = `${Math.random() * 100}%`; |  | ||||||
|       particle.style.top = `${Math.random() * 100}%`; |  | ||||||
|       particle.style.opacity = (Math.random() * 0.2 + 0.05).toString(); |  | ||||||
|       particle.style.animation = `float-particle ${Math.random() * 25 + 15}s linear infinite`; |  | ||||||
|       particle.style.animationDelay = `${Math.random() * 15}s`; |  | ||||||
|       particlesContainer.appendChild(particle); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Architecture Diagram Connections --- |  | ||||||
|   function createArchitectureDiagramConnections() { |  | ||||||
|     const diagram = document.querySelector('.architecture-diagram'); |  | ||||||
|     if (!diagram) return; |  | ||||||
| 
 |  | ||||||
|     // Define connections between nodes |  | ||||||
|     const connections = [ |  | ||||||
|       { from: 'gateway', to: 'firewall', type: 'horizontal' }, |  | ||||||
|       { from: 'firewall', to: 'proxmox', type: 'horizontal' }, |  | ||||||
|       { from: 'firewall', to: 'kubernetes', type: 'vertical' }, |  | ||||||
|       { from: 'proxmox', to: 'kubernetes', type: 'vertical' }, |  | ||||||
|       { from: 'storage', to: 'kubernetes', type: 'horizontal' }, |  | ||||||
|       { from: 'kubernetes', to: 'monitoring', type: 'horizontal' }, |  | ||||||
|       { from: 'kubernetes', to: 'services', type: 'vertical' } |  | ||||||
|     ]; |  | ||||||
| 
 |  | ||||||
|     // Create connection lines |  | ||||||
|     connections.forEach((connection, index) => { |  | ||||||
|       const fromNode = diagram.querySelector(`[data-node="${connection.from}"]`); |  | ||||||
|       const toNode = diagram.querySelector(`[data-node="${connection.to}"]`); |  | ||||||
|        |  | ||||||
|       if (!fromNode || !toNode) return; |  | ||||||
|        |  | ||||||
|       const fromRect = fromNode.getBoundingClientRect(); |  | ||||||
|       const toRect = toNode.getBoundingClientRect(); |  | ||||||
|       const diagramRect = diagram.getBoundingClientRect(); |  | ||||||
|        |  | ||||||
|       const line = document.createElement('div'); |  | ||||||
|       line.classList.add('connection-line', connection.type); |  | ||||||
|       line.id = `connection-${index}`; |  | ||||||
|        |  | ||||||
|       if (connection.type === 'horizontal') { |  | ||||||
|         const fromCenter = fromRect.left + fromRect.width / 2 - diagramRect.left; |  | ||||||
|         const toCenter = toRect.left + toRect.width / 2 - diagramRect.left; |  | ||||||
|         const top = (fromRect.top + fromRect.height / 2 - diagramRect.top); |  | ||||||
|          |  | ||||||
|         line.style.top = `${top}px`; |  | ||||||
|         line.style.left = `${Math.min(fromCenter, toCenter)}px`; |  | ||||||
|         line.style.width = `${Math.abs(toCenter - fromCenter)}px`; |  | ||||||
|       } else { // vertical |  | ||||||
|         const fromCenter = fromRect.top + fromRect.height / 2 - diagramRect.top; |  | ||||||
|         const toCenter = toRect.top + toRect.height / 2 - diagramRect.top; |  | ||||||
|         const left = (fromRect.left + fromRect.width / 2 - diagramRect.left); |  | ||||||
|          |  | ||||||
|         line.style.left = `${left}px`; |  | ||||||
|         line.style.top = `${Math.min(fromCenter, toCenter)}px`; |  | ||||||
|         line.style.height = `${Math.abs(toCenter - fromCenter)}px`; |  | ||||||
|       } |  | ||||||
|        |  | ||||||
|       diagram.appendChild(line); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Animate Active Connections --- |  | ||||||
|   function simulateActiveConnections() { |  | ||||||
|     const lines = document.querySelectorAll('.connection-line'); |  | ||||||
|     if (lines.length === 0) return; |  | ||||||
|      |  | ||||||
|     // Randomly activate connections |  | ||||||
|     setInterval(() => { |  | ||||||
|       const randomIndex = Math.floor(Math.random() * lines.length); |  | ||||||
|       const line = lines[randomIndex]; |  | ||||||
|        |  | ||||||
|       line.classList.add('active'); |  | ||||||
|       setTimeout(() => { |  | ||||||
|         line.classList.remove('active'); |  | ||||||
|       }, 1000); |  | ||||||
|     }, 2000); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Check Component Status --- |  | ||||||
|   function checkAnsibleSandboxStatus() { |  | ||||||
|     const btn = document.getElementById('ansible-sandbox-btn'); |  | ||||||
|     const badge = btn?.querySelector('.offline-badge'); |  | ||||||
|     if (!btn || !badge) return; |  | ||||||
| 
 |  | ||||||
|     // This is a placeholder - replace with actual API call |  | ||||||
|     const isOnline = false; // Simulate offline for now |  | ||||||
| 
 |  | ||||||
|     if (isOnline) { |  | ||||||
|       btn.classList.remove('btn-danger'); |  | ||||||
|       btn.classList.add('btn-primary'); |  | ||||||
|       badge.textContent = 'Online'; |  | ||||||
|       badge.classList.remove('offline-badge'); |  | ||||||
|       badge.classList.add('online-badge'); |  | ||||||
|       badge.style.display = 'inline-block'; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Intersection Observer for Scroll Animations --- |  | ||||||
|   function setupScrollAnimations() { |  | ||||||
|     const sections = document.querySelectorAll('.section-padding'); |  | ||||||
|      |  | ||||||
|     const observer = new IntersectionObserver((entries) => { |  | ||||||
|       entries.forEach(entry => { |  | ||||||
|         if (entry.isIntersecting) { |  | ||||||
|           entry.target.classList.add('section-visible'); |  | ||||||
|           observer.unobserve(entry.target); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     }, { threshold: 0.1 }); |  | ||||||
|      |  | ||||||
|     sections.forEach(section => { |  | ||||||
|       observer.observe(section); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // --- Initialize everything when DOM is loaded --- |  | ||||||
|   document.addEventListener('DOMContentLoaded', function() { |  | ||||||
|     // Create particle effect |  | ||||||
|     createBackgroundParticles(); |  | ||||||
|      |  | ||||||
|     // Animate terminal on load |  | ||||||
|     setTimeout(animateTerminal, 500); |  | ||||||
|     initTerminalTyping(); |  | ||||||
|      |  | ||||||
|     // Set up architecture diagram |  | ||||||
|     setTimeout(() => { |  | ||||||
|       try { |  | ||||||
|         createArchitectureDiagramConnections(); |  | ||||||
|         simulateActiveConnections(); |  | ||||||
|       } catch (error) { |  | ||||||
|         console.log('Architecture diagram not available:', error); |  | ||||||
|         document.querySelector('.architecture-fallback')?.style.display = 'block'; |  | ||||||
|         document.querySelector('.architecture-diagram-container')?.style.display = 'none'; |  | ||||||
|       } |  | ||||||
|     }, 500); |  | ||||||
|      |  | ||||||
|     // Check component status |  | ||||||
|     checkAnsibleSandboxStatus(); |  | ||||||
|      |  | ||||||
|     // Set up scroll animations |  | ||||||
|     setupScrollAnimations(); |  | ||||||
|      |  | ||||||
|     // Add smooth scrolling for anchor links |  | ||||||
|     document.querySelectorAll('a[href^="#"]').forEach(anchor => { |  | ||||||
|       anchor.addEventListener('click', function(e) { |  | ||||||
|         e.preventDefault(); |  | ||||||
|         const targetId = this.getAttribute('href'); |  | ||||||
|         const targetElement = document.querySelector(targetId); |  | ||||||
|          |  | ||||||
|         if (targetElement) { |  | ||||||
|           window.scrollTo({ |  | ||||||
|             top: targetElement.offsetTop - 80, |  | ||||||
|             behavior: 'smooth' |  | ||||||
|           }); |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   // --- Handle window resize --- |  | ||||||
|   window.addEventListener('resize', function() { |  | ||||||
|     // Clear existing connections and recreate them |  | ||||||
|     document.querySelectorAll('.connection-line').forEach(line => line.remove()); |  | ||||||
|      |  | ||||||
|     setTimeout(() => { |  | ||||||
|       createArchitectureDiagramConnections(); |  | ||||||
|     }, 200); |  | ||||||
|   }); |  | ||||||
| </script> |  | ||||||
| --- | --- | ||||||
| // src/pages/homelab.astro - Enhanced version | // src/pages/homelab.astro - Fixed main file with proper component imports | ||||||
| import BaseLayout from '../layouts/BaseLayout.astro'; | import BaseLayout from '../layouts/BaseLayout.astro'; | ||||||
| import Header from '../components/Header.astro'; | import Header from '../components/Header.astro'; | ||||||
| import Footer from '../components/Footer.astro'; | import Footer from '../components/Footer.astro'; | ||||||
|  | import HeroSection from '../components/homelab/HeroSection.astro'; | ||||||
|  | import ServicesSection from '../components/homelab/ServicesSection.astro'; | ||||||
|  | import ProjectsSection from '../components/homelab/ProjectsSection.astro'; | ||||||
|  | import { servicesData, projectsData, dashboardsData } from '../data/homelabData'; | ||||||
| 
 | 
 | ||||||
| const title = "Home Lab | ArgoBox - ArgoBox Tech Hub"; | const title = "Home Lab | ArgoBox - ArgoBox Tech Hub"; | ||||||
| const description = "ArgoBox - A production-grade Kubernetes homelab for DevOps experimentation, infrastructure automation, and containerized application deployment."; | const description = "ArgoBox - A production-grade Kubernetes homelab for DevOps experimentation, infrastructure automation, and containerized application deployment."; | ||||||
| 
 | 
 | ||||||
| // Using your existing data structure | // Calculate total services count for the hero section | ||||||
| const servicesData = { | const servicesCount = Object.values(servicesData).flat().length; | ||||||
|   development: [ |  | ||||||
|     { name: "Gitea", description: "Self-hosted Git service", url: "https://git.argobox.com", status: "live", icon: "fas fa-code-branch", available: true }, |  | ||||||
|     { name: "VS Code Server", description: "Remote development environment", url: "https://code.argobox.com", status: "live", icon: "fas fa-terminal", available: true }, |  | ||||||
|     { name: "Drone CI", description: "Continuous Integration server", url: "https://drone.argobox.com", status: "live", icon: "fas fa-cogs", available: true }, |  | ||||||
|   ], |  | ||||||
|   media: [ |  | ||||||
|     { name: "Plex", description: "Media streaming server", url: "https://plex.argobox.com", status: "live", icon: "fas fa-play-circle", available: true }, |  | ||||||
|     { name: "Jellyfin", description: "Open source media system", url: "https://jellyfin.argobox.com", status: "live", icon: "fas fa-film", available: true }, |  | ||||||
|     { name: "Sonarr", description: "TV show management", url: "#", status: "restricted", icon: "fas fa-tv", available: false }, |  | ||||||
|     { name: "Radarr", description: "Movie management", url: "#", status: "restricted", icon: "fas fa-video", available: false }, |  | ||||||
|     { name: "Prowlarr", description: "Indexer management", url: "#", status: "restricted", icon: "fas fa-search", available: false }, |  | ||||||
|   ], |  | ||||||
|   utilities: [ |  | ||||||
|      { name: "File Browser", description: "Web file manager", url: "https://files.argobox.com", status: "live", icon: "fas fa-folder-open", available: true }, |  | ||||||
|      { name: "Vaultwarden", description: "Password manager", url: "#", status: "restricted", icon: "fas fa-key", available: false }, |  | ||||||
|      { name: "Homepage", description: "Service dashboard", url: "https://dash.argobox.com", status: "live", icon: "fas fa-tachometer-alt", available: true }, |  | ||||||
|      { name: "Uptime Kuma", description: "Status monitoring", url: "https://status.argobox.com", status: "live", icon: "fas fa-heartbeat", available: true }, |  | ||||||
|   ], |  | ||||||
|   infrastructure: [ |  | ||||||
|      { name: "Proxmox VE", description: "Virtualization platform", url: "#", status: "restricted", icon: "fas fa-server", available: false }, |  | ||||||
|      { name: "Kubernetes (K3s)", description: "Container orchestration", url: "#", status: "restricted", icon: "fas fa-dharmachakra", available: false }, |  | ||||||
|      { name: "Traefik", description: "Ingress controller", url: "#", status: "restricted", icon: "fas fa-route", available: false }, |  | ||||||
|      { name: "OPNsense", description: "Firewall/Router", url: "#", status: "restricted", icon: "fas fa-shield-alt", available: false }, |  | ||||||
|   ] |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Use your existing project data |  | ||||||
| const projectsData = [ |  | ||||||
|     { title: "Ansible Playbooks", description: "Collection of playbooks for automating system configuration and application deployment.", icon: "fab fa-ansible", tech: ["Ansible", "YAML", "Jinja2"], url: "/resources/iac" }, |  | ||||||
|     { title: "Kubernetes Manifests", description: "YAML definitions for deploying various applications and services on Kubernetes.", icon: "fas fa-dharmachakra", tech: ["Kubernetes", "YAML", "Helm"], url: "/resources/kubernetes" }, |  | ||||||
|     { title: "Monitoring Dashboards", description: "Grafana dashboards for visualizing infrastructure and application metrics.", icon: "fas fa-chart-line", tech: ["Grafana", "PromQL", "JSON"], url: "/resources/config-files" }, |  | ||||||
|     { title: "Cloudflare Tunnel Setup", description: "Securely exposing home lab services to the internet using Cloudflare Tunnels.", icon: "fas fa-cloud", tech: ["Cloudflare", "Networking", "Security"], url: "/posts/cloudflare-tunnel-setup" } |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| // Use your existing dashboards data |  | ||||||
| const dashboardsData = [ |  | ||||||
|     { title: "Infrastructure Overview", description: "Key metrics for Proxmox hosts, network devices, and storage.", previewClass: "infrastructure", url: "https://dash.argobox.com/goto/...", icon: "fas fa-server" }, |  | ||||||
|     { title: "Kubernetes Cluster", description: "Detailed view of K3s cluster resources, node status, and pod health.", previewClass: "kubernetes", url: "https://dash.argobox.com/goto/...", icon: "fas fa-dharmachakra" }, |  | ||||||
|     { title: "Network Traffic", description: "Real-time and historical network usage, firewall logs, and connection tracking.", previewClass: "network", url: "https://dash.argobox.com/goto/...", icon: "fas fa-network-wired" }, |  | ||||||
|     { title: "Service Performance", description: "Application-specific metrics, request latency, and error rates.", previewClass: "services", url: "https://dash.argobox.com/goto/...", icon: "fas fa-cogs" } |  | ||||||
| ]; |  | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| <BaseLayout {title} {description}> | <BaseLayout {title} {description}> | ||||||
|   <Header slot="header" /> |   <Header slot="header" /> | ||||||
| 
 | 
 | ||||||
|   <main class="homelab-page"> |   <main class="homelab-page"> | ||||||
|     <!-- Hero Section (Enhanced) --> |     <HeroSection servicesCount={servicesCount} /> | ||||||
|     <section id="home" class="hero"> |     <ServicesSection servicesData={servicesData} /> | ||||||
|       <div class="particles-container" id="particles-container"></div> |     <ProjectsSection projectsData={projectsData} /> | ||||||
|       <div class="container"> |  | ||||||
|         <div class="hero-content"> |  | ||||||
|           <div class="hero-text"> |  | ||||||
|             <h1 class="hero-title"> |  | ||||||
|               Enterprise-Grade <span class="highlight">Home Lab</span> Environment |  | ||||||
|             </h1> |  | ||||||
|             <p class="hero-description"> |  | ||||||
|               A production-ready infrastructure platform for DevOps experimentation, distributed systems, and automating everything with code. |  | ||||||
|             </p> |  | ||||||
|              |  | ||||||
|             <!-- Dynamic terminal-style stats display --> |  | ||||||
|             <div class="terminal-style-stats"> |  | ||||||
|               <div class="stat-line">argobox:~$<span class="cmd"> ls -la /services | wc -l</span></div> |  | ||||||
|               <div class="stat-line output">{Object.values(servicesData).flat().length}+</div> |  | ||||||
|               <div class="stat-line">argobox:~$<span class="cmd"> nproc --all</span></div> |  | ||||||
|               <div class="stat-line output">32+</div> |  | ||||||
|               <div class="stat-line">argobox:~$<span class="cmd"> free -h | grep Mem | awk '{print $2}'</span></div> |  | ||||||
|               <div class="stat-line output">64GB</div> |  | ||||||
|               <div class="stat-line">argobox:~$<span class="cmd"> df -h /data | grep /data | awk '{print $2}'</span></div> |  | ||||||
|               <div class="stat-line output">12TB</div> |  | ||||||
|             </div> |  | ||||||
|              |  | ||||||
|             <div class="cta-buttons"> |  | ||||||
|               <a href="/ansible-sandbox" class="btn btn-danger" id="ansible-sandbox-btn"> |  | ||||||
|                 <i class="fab fa-ansible btn-icon"></i> |  | ||||||
|                 <span class="btn-text">Try Ansible Sandbox</span> |  | ||||||
|                 <span class="offline-badge">Offline</span> <!-- Status updated by JS --> |  | ||||||
|               </a> |  | ||||||
|               <a href="#architecture" class="btn btn-outline"> |  | ||||||
|                 <i class="fas fa-network-wired btn-icon"></i> |  | ||||||
|                 <span class="btn-text">Explore Architecture</span> |  | ||||||
|               </a> |  | ||||||
|             </div> |  | ||||||
|           </div> |  | ||||||
|            |  | ||||||
|           <!-- Enhanced terminal with animation --> |  | ||||||
|           <div class="hero-terminal"> |  | ||||||
|             <div class="terminal-header"> |  | ||||||
|               <div class="terminal-buttons"> |  | ||||||
|                 <span class="terminal-btn close"></span> |  | ||||||
|                 <span class="terminal-btn minimize"></span> |  | ||||||
|                 <span class="terminal-btn maximize"></span> |  | ||||||
|               </div> |  | ||||||
|               <div class="terminal-title">argobox ~ k8s-status</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="terminal-body" id="animated-terminal"> |  | ||||||
|               <div class="terminal-line" data-index="1">$ kubectl get nodes</div> |  | ||||||
|               <div class="terminal-line output" data-index="2">NAME           STATUS   ROLES                  AGE     VERSION</div> |  | ||||||
|               <div class="terminal-line output" data-index="3">argobox        Ready    control-plane,master   154d    v1.25.16+k3s1</div> |  | ||||||
|               <div class="terminal-line output" data-index="4">argobox-lite   Ready    worker                 154d    v1.25.16+k3s1</div> |  | ||||||
|               <div class="terminal-line blank" data-index="5"> </div> |  | ||||||
|               <div class="terminal-line" data-index="6">$ kubectl get pods -A | grep Running | wc -l</div> |  | ||||||
|               <div class="terminal-line output" data-index="7">32</div> |  | ||||||
|               <div class="terminal-line blank" data-index="8"> </div> |  | ||||||
|               <div class="terminal-line" data-index="9">$ uptime</div> |  | ||||||
|               <div class="terminal-line output" data-index="10">14:30:25 up 154 days, 23:12, 1 user, load average: 0.22, 0.18, 0.15</div> |  | ||||||
|               <div class="terminal-line blank" data-index="11"> </div> |  | ||||||
|               <div class="terminal-line" data-index="12">$ ansible-playbook status.yml</div> |  | ||||||
|               <div class="terminal-line output" data-index="13">PLAY [Check system status] *******************************************</div> |  | ||||||
|               <div class="terminal-line output" data-index="14">TASK [Gathering Facts] **********************************************</div> |  | ||||||
|               <div class="terminal-line output success" data-index="15">ok: [argobox]</div> |  | ||||||
|               <div class="terminal-line output success" data-index="16">ok: [argobox-lite]</div> |  | ||||||
|               <div class="terminal-line output" data-index="17">TASK [Check service status] *****************************************</div> |  | ||||||
|               <div class="terminal-line output success" data-index="18">ok: [argobox]</div> |  | ||||||
|               <div class="terminal-line output success" data-index="19">ok: [argobox-lite]</div> |  | ||||||
|               <div class="terminal-line output" data-index="20">PLAY RECAP **********************************************************</div> |  | ||||||
|               <div class="terminal-line output success" data-index="21">argobox     : ok=2    changed=0    unreachable=0    failed=0    skipped=0</div> |  | ||||||
|               <div class="terminal-line output success" data-index="22">argobox-lite: ok=2    changed=0    unreachable=0    failed=0    skipped=0</div> |  | ||||||
|               <div class="terminal-line typing" data-index="23">$ <span class="cursor">|</span></div> |  | ||||||
|             </div> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </section> |  | ||||||
| 
 |  | ||||||
|     <!-- Architecture Section (Enhanced with Interactive Diagram) --> |  | ||||||
|     <section id="architecture" class="architecture section-padding"> |  | ||||||
|       <div class="container"> |  | ||||||
|         <div class="section-header"> |  | ||||||
|           <h2 class="section-title">Infrastructure Architecture</h2> |  | ||||||
|           <p class="section-description"> |  | ||||||
|             Enterprise-grade network topology with redundancy, virtualization, and secure segmentation. |  | ||||||
|           </p> |  | ||||||
|         </div> |  | ||||||
| 
 |  | ||||||
|         <!-- Interactive Architecture Diagram --> |  | ||||||
|         <div class="architecture-diagram-container" id="arch-diagram"> |  | ||||||
|           <div class="architecture-diagram"> |  | ||||||
|             <div class="diagram-node gateway" data-node="gateway"> |  | ||||||
|               <div class="diagram-node-icon"><i class="fas fa-router"></i></div> |  | ||||||
|               <div class="diagram-node-title">Internet Gateway</div> |  | ||||||
|               <div class="diagram-node-detail">Multiple WAN connections with failover</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="diagram-node firewall" data-node="firewall"> |  | ||||||
|               <div class="diagram-node-icon"><i class="fas fa-shield-alt"></i></div> |  | ||||||
|               <div class="diagram-node-title">OPNsense Firewall</div> |  | ||||||
|               <div class="diagram-node-detail">Advanced security & traffic management</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="diagram-node proxmox" data-node="proxmox"> |  | ||||||
|               <div class="diagram-node-icon"><i class="fas fa-server"></i></div> |  | ||||||
|               <div class="diagram-node-title">Proxmox Cluster</div> |  | ||||||
|               <div class="diagram-node-detail">Virtualization platform with HA capabilities</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="diagram-node storage" data-node="storage"> |  | ||||||
|               <div class="diagram-node-icon"><i class="fas fa-database"></i></div> |  | ||||||
|               <div class="diagram-node-title">ZFS Storage</div> |  | ||||||
|               <div class="diagram-node-detail">RAID10 configuration with snapshots</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="diagram-node kubernetes" data-node="kubernetes"> |  | ||||||
|               <div class="diagram-node-icon"><i class="fas fa-dharmachakra"></i></div> |  | ||||||
|               <div class="diagram-node-title">Kubernetes (K3s)</div> |  | ||||||
|               <div class="diagram-node-detail">Container orchestration across multiple nodes</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="diagram-node monitoring" data-node="monitoring"> |  | ||||||
|               <div class="diagram-node-icon"><i class="fas fa-chart-line"></i></div> |  | ||||||
|               <div class="diagram-node-title">Monitoring Stack</div> |  | ||||||
|               <div class="diagram-node-detail">Prometheus, Grafana, & AlertManager</div> |  | ||||||
|             </div> |  | ||||||
|             <div class="diagram-node services" data-node="services"> |  | ||||||
|               <div class="service-icon"><i class="fas fa-code-branch" style="color: #3b82f6;"></i><span>Git</span></div> |  | ||||||
|               <div class="service-icon"><i class="fas fa-terminal" style="color: #6366f1;"></i><span>Code</span></div> |  | ||||||
|               <div class="service-icon"><i class="fas fa-play-circle" style="color: #ec4899;"></i><span>Media</span></div> |  | ||||||
|               <div class="service-icon"><i class="fas fa-key" style="color: #10b981;"></i><span>Vault</span></div> |  | ||||||
|               <div class="service-icon"><i class="fas fa-globe" style="color: #f59e0b;"></i><span>Web</span></div> |  | ||||||
|               <div class="service-icon"><i class="fas fa-project-diagram" style="color: #ef4444;"></i><span>CI/CD</span></div> |  | ||||||
|             </div> |  | ||||||
|             <!-- Connection lines will be added by JS --> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
| 
 |  | ||||||
|         <!-- Fallback for clients with JS disabled --> |  | ||||||
|         <div class="architecture-fallback"> |  | ||||||
|           <img src="/images/homelab/argobox-architecture.svg" alt="ArgoBox Architecture Diagram" class="architecture-diagram-image" /> |  | ||||||
|         </div> |  | ||||||
| 
 |  | ||||||
|         <div class="architecture-details"> |  | ||||||
|           <div class="detail-card"> |  | ||||||
|             <div class="detail-icon"><i class="fas fa-shield-alt"></i></div> |  | ||||||
|             <h3 class="detail-title">Network Security</h3> |  | ||||||
|             <p class="detail-description"> |  | ||||||
|               Enterprise firewall with network segmentation using VLANs and strict access controls. Redundant routing with automatic failover between OPNsense and OpenWrt. |  | ||||||
|             </p> |  | ||||||
|           </div> |  | ||||||
|           <div class="detail-card"> |  | ||||||
|             <div class="detail-icon"><i class="fas fa-cloud"></i></div> |  | ||||||
|             <h3 class="detail-title">Virtualization</h3> |  | ||||||
|             <p class="detail-description"> |  | ||||||
|               Proxmox virtualization platform with ZFS storage pools in RAID10 configuration. Optimized storage pools for VMs and containers with proper resource allocation. |  | ||||||
|             </p> |  | ||||||
|           </div> |  | ||||||
|           <div class="detail-card"> |  | ||||||
|             <div class="detail-icon"><i class="fas fa-route"></i></div> |  | ||||||
|             <h3 class="detail-title">High Availability</h3> |  | ||||||
|             <p class="detail-description"> |  | ||||||
|               Full redundancy with failover routing, replicated storage, and resilient services. Automatic service recovery and load balancing across nodes. |  | ||||||
|             </p> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </section> |  | ||||||
| 
 |  | ||||||
|     <!-- Technologies Section --> |  | ||||||
|     <section id="technologies" class="technologies section-padding alt-bg"> |  | ||||||
|       <div class="container"> |  | ||||||
|         <div class="section-header"> |  | ||||||
|           <h2 class="section-title">Core Technologies</h2> |  | ||||||
|           <p class="section-description"> |  | ||||||
|             The ArgoBox lab leverages cutting-edge open source technologies to create a powerful, flexible infrastructure. |  | ||||||
|           </p> |  | ||||||
|         </div> |  | ||||||
|         <div class="tech-grid"> |  | ||||||
|           <div class="tech-card"> |  | ||||||
|             <div class="tech-icon"><i class="fas fa-dharmachakra"></i></div> |  | ||||||
|             <h3 class="tech-title">Kubernetes (K3s)</h3> |  | ||||||
|             <p class="tech-description">Lightweight Kubernetes distribution running across multiple nodes for container orchestration. Powers all microservices and applications.</p> |  | ||||||
|             <div class="tech-features"><span class="tech-feature">Multi-node cluster</span><span class="tech-feature">Persistent volumes</span><span class="tech-feature">Traefik ingress</span><span class="tech-feature">Auto-healing</span></div> |  | ||||||
|           </div> |  | ||||||
|           <div class="tech-card featured"> |  | ||||||
|             <div class="tech-icon"><i class="fab fa-ansible"></i></div> |  | ||||||
|             <h3 class="tech-title">Ansible Automation</h3> |  | ||||||
|             <p class="tech-description">Infrastructure as code platform for automated provisioning, configuration management, and application deployment across the entire environment.</p> |  | ||||||
|             <div class="tech-features"><span class="tech-feature">Playbook library</span><span class="tech-feature">Role-based configs</span><span class="tech-feature">Interactive sandbox</span><span class="tech-feature">Idempotent workflows</span></div> |  | ||||||
|           </div> |  | ||||||
|           <div class="tech-card"> |  | ||||||
|             <div class="tech-icon"><i class="fas fa-server"></i></div> |  | ||||||
|             <h3 class="tech-title">Proxmox</h3> |  | ||||||
|             <p class="tech-description">Enterprise-class virtualization platform running virtual machines and containers with ZFS storage backend for data integrity.</p> |  | ||||||
|             <div class="tech-features"><span class="tech-feature">ZFS storage</span><span class="tech-feature">Resource balancing</span><span class="tech-feature">Live migration</span><span class="tech-feature">Hardware passthrough</span></div> |  | ||||||
|           </div> |  | ||||||
|           <div class="tech-card"> |  | ||||||
|             <div class="tech-icon"><i class="fas fa-shield-alt"></i></div> |  | ||||||
|             <h3 class="tech-title">Zero Trust Security</h3> |  | ||||||
|             <p class="tech-description">Comprehensive security architecture with Cloudflare tunnels, network segmentation, and authentication at all service boundaries.</p> |  | ||||||
|             <div class="tech-features"><span class="tech-feature">Cloudflare tunnels</span><span class="tech-feature">OPNsense firewall</span><span class="tech-feature">VLAN segmentation</span><span class="tech-feature">WireGuard VPN</span></div> |  | ||||||
|           </div> |  | ||||||
|           <div class="tech-card"> |  | ||||||
|             <div class="tech-icon"><i class="fas fa-database"></i></div> |  | ||||||
|             <h3 class="tech-title">PostgreSQL</h3> |  | ||||||
|             <p class="tech-description">Enterprise database cluster for application data storage with automated backups, replication, and performance optimization.</p> |  | ||||||
|             <div class="tech-features"><span class="tech-feature">Automated backups</span><span class="tech-feature">Connection pooling</span><span class="tech-feature">Optimized for K8s</span><span class="tech-feature">Multi-app support</span></div> |  | ||||||
|           </div> |  | ||||||
|           <div class="tech-card"> |  | ||||||
|             <div class="tech-icon"><i class="fas fa-chart-line"></i></div> |  | ||||||
|             <h3 class="tech-title">Monitoring Stack</h3> |  | ||||||
|             <p class="tech-description">Comprehensive monitoring with Prometheus, Grafana, and AlertManager for real-time visibility into all infrastructure components.</p> |  | ||||||
|             <div class="tech-features"><span class="tech-feature">Prometheus metrics</span><span class="tech-feature">Grafana dashboards</span><span class="tech-feature">Automated alerts</span><span class="tech-feature">Historical data</span></div> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </section> |  | ||||||
| 
 |  | ||||||
|     <!-- Services Section (Enhanced with Interactive Elements) --> |  | ||||||
|     <section id="services" class="services section-padding"> |  | ||||||
|       <div class="container"> |  | ||||||
|         <div class="section-header"> |  | ||||||
|           <h2 class="section-title">Available Services</h2> |  | ||||||
|           <p class="section-description"> |  | ||||||
|             Explore the various services and applications hosted in the ArgoBox environment. |  | ||||||
|           </p> |  | ||||||
|         </div> |  | ||||||
|         <div class="services-info-banner"> |  | ||||||
|           <i class="fas fa-info-circle info-icon"></i> |  | ||||||
|           <p>Some services require authentication and are restricted. Available public services are highlighted and clickable.</p> |  | ||||||
|         </div> |  | ||||||
|         <div class="services-grid"> |  | ||||||
|           {Object.entries(servicesData).map(([categoryKey, categoryServices]) => ( |  | ||||||
|             <div class="services-category" data-category={categoryKey}> |  | ||||||
|               <h3 class="category-title"> |  | ||||||
|                 <i class={`fas ${ |  | ||||||
|                   categoryKey === 'development' ? 'fa-code' : |  | ||||||
|                   categoryKey === 'media' ? 'fa-photo-video' : |  | ||||||
|                   categoryKey === 'utilities' ? 'fa-tools' : |  | ||||||
|                   'fa-cogs' // Default for infrastructure |  | ||||||
|                 } category-icon`}></i> |  | ||||||
|                 {categoryKey.charAt(0).toUpperCase() + categoryKey.slice(1)} Tools |  | ||||||
|               </h3> |  | ||||||
|               <div class="service-items"> |  | ||||||
|                 {categoryServices.map(service => ( |  | ||||||
|                   <a |  | ||||||
|                     href={service.available ? service.url : '#'} |  | ||||||
|                     class:list={["service-item", { available: service.available }, `service-${service.name.toLowerCase().replace(/\s+/g, '-')}`]} |  | ||||||
|                     target={service.available ? "_blank" : undefined} |  | ||||||
|                     rel={service.available ? "noopener noreferrer" : undefined} |  | ||||||
|                     aria-disabled={!service.available} |  | ||||||
|                     data-service={service.name.toLowerCase().replace(/\s+/g, '-')} |  | ||||||
|                   > |  | ||||||
|                     <div class="service-icon"><i class={service.icon}></i></div> |  | ||||||
|                     <div class="service-info"> |  | ||||||
|                       <div class="service-name">{service.name}</div> |  | ||||||
|                       <p class="service-description">{service.description}</p> |  | ||||||
|                     </div> |  | ||||||
|                     <span class={`service-status ${service.status}`}> |  | ||||||
|                       <span class="status-dot"></span> |  | ||||||
|                       {service.status.charAt(0).toUpperCase() + service.status.slice(1)} |  | ||||||
|                     </span> |  | ||||||
|                   </a> |  | ||||||
|                 ))} |  | ||||||
|               </div> |  | ||||||
|             </div> |  | ||||||
|           ))} |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </section> |  | ||||||
| 
 |  | ||||||
|     <!-- Projects Section --> |  | ||||||
|     <section id="projects" class="projects section-padding alt-bg"> |  | ||||||
|         <div class="container"> |  | ||||||
|             <div class="section-header"> |  | ||||||
|                 <h2 class="section-title">Related Projects & Code</h2> |  | ||||||
|                 <p class="section-description"> |  | ||||||
|                     Explore associated projects, configurations, and code repositories related to the ArgoBox lab. |  | ||||||
|                 </p> |  | ||||||
|             </div> |  | ||||||
|             <div class="projects-grid"> |  | ||||||
|                 {projectsData.map(project => ( |  | ||||||
|                     <a href={project.url} class="project-card" target={project.url.startsWith('http') ? '_blank' : undefined} rel={project.url.startsWith('http') ? 'noopener noreferrer' : undefined} data-project={project.title.toLowerCase().replace(/\s+/g, '-')}> |  | ||||||
|                         <div class="project-header"> |  | ||||||
|                             <div class="project-icon"><i class={project.icon}></i></div> |  | ||||||
|                             <h3 class="project-title">{project.title}</h3> |  | ||||||
|                         </div> |  | ||||||
|                         <p class="project-description">{project.description}</p> |  | ||||||
|                         <div class="project-tech"> |  | ||||||
|                             {project.tech.map(tech => <span class="tech-badge">{tech}</span>)} |  | ||||||
|                         </div> |  | ||||||
|                         <div class="project-cta"> |  | ||||||
|                             <span class="btn btn-sm btn-outline"> |  | ||||||
|                                 {project.url.startsWith('http') ? 'View Project' : 'View Details'} <i class="fas fa-arrow-right"></i> |  | ||||||
|                             </span> |  | ||||||
|                         </div> |  | ||||||
|                     </a> |  | ||||||
|                 ))} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|     </section> |  | ||||||
| 
 |  | ||||||
|     <!-- Dashboards Section (Enhanced) --> |  | ||||||
|     <section id="dashboards" class="dashboards section-padding"> |  | ||||||
|         <div class="container"> |  | ||||||
|             <div class="section-header"> |  | ||||||
|                 <h2 class="section-title">Live Dashboards</h2> |  | ||||||
|                 <p class="section-description"> |  | ||||||
|                     Real-time monitoring dashboards providing insights into the lab's performance and status. (Authentication Required) |  | ||||||
|                 </p> |  | ||||||
|             </div> |  | ||||||
|             <div class="services-info-banner"> |  | ||||||
|                 <i class="fas fa-lock info-icon"></i> |  | ||||||
|                 <p>Access to live dashboards requires authentication via Cloudflare Access.</p> |  | ||||||
|             </div> |  | ||||||
|             <div class="dashboard-grid"> |  | ||||||
|                 {dashboardsData.map(dash => ( |  | ||||||
|                     <a href={dash.url} class="dashboard-card" target="_blank" rel="noopener noreferrer" data-dashboard={dash.title.toLowerCase().replace(/\s+/g, '-')}> |  | ||||||
|                         <div class={`dashboard-preview ${dash.previewClass}`}> |  | ||||||
|                             <div class="dashboard-overlay"> |  | ||||||
|                                 <div class="overlay-content"> |  | ||||||
|                                     <div class="overlay-icon"><i class={dash.icon}></i></div> |  | ||||||
|                                     <div class="overlay-text">View Dashboard</div> |  | ||||||
|                                 </div> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
|                         <div class="dashboard-info"> |  | ||||||
|                             <h3 class="dashboard-title">{dash.title}</h3> |  | ||||||
|                             <p class="dashboard-description">{dash.description}</p> |  | ||||||
|                             <div class="dashboard-cta"> |  | ||||||
|                                 <span class="btn btn-sm btn-primary">Access Dashboard</span> |  | ||||||
|                             </div> |  | ||||||
|                         </div> |  | ||||||
|                     </a> |  | ||||||
|                 ))} |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|     </section> |  | ||||||
|   </main> |   </main> | ||||||
| 
 | 
 | ||||||
|   <Footer slot="footer" /> |   <Footer slot="footer" /> | ||||||
| </BaseLayout> | </BaseLayout> | ||||||
| 
 | 
 | ||||||
| <style is:global> | <style is:global> | ||||||
|   /* Keep all your original styles from homelab.astro */ |   /* Global styles - can be moved to a separate CSS file if needed */ | ||||||
|    |   .section-padding { | ||||||
|   /* Hero Section Enhancements */ |     padding: 5rem 0; | ||||||
|   .hero { |  | ||||||
|     min-height: 100vh; |  | ||||||
|     display: flex; |  | ||||||
|     align-items: center; |  | ||||||
|     position: relative; |  | ||||||
|     overflow: hidden; |  | ||||||
|     padding-top: 6rem; |  | ||||||
|     padding-bottom: 4rem; |  | ||||||
|     background: linear-gradient(180deg, var(--bg-secondary), var(--bg-primary)); |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /* Enhanced Particles Animation */ |   .alt-bg { | ||||||
|   .particles-container { |     background-color: var(--bg-secondary); | ||||||
|     position: absolute; |  | ||||||
|     top: 0; |  | ||||||
|     left: 0; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 100%; |  | ||||||
|     overflow: hidden; |  | ||||||
|     pointer-events: none; |  | ||||||
|     z-index: 0; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   .particle { |   .section-header { | ||||||
|     position: absolute; |     text-align: center; | ||||||
|     background-color: var(--accent); |     margin-bottom: 3rem; | ||||||
|     border-radius: 50%; |  | ||||||
|     pointer-events: none; |  | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   @keyframes float-particle { |   .section-title { | ||||||
|     0% { transform: translateY(0) translateX(0); opacity: 0.3; } |     font-size: 2.5rem; | ||||||
|     50% { transform: translateY(-100px) translateX(50px); opacity: 0.1; } |  | ||||||
|     100% { transform: translateY(0) translateX(0); opacity: 0.3; } |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   /* Terminal Style Stats */ |  | ||||||
|   .terminal-style-stats { |  | ||||||
|     background-color: rgba(15, 23, 42, 0.7); |  | ||||||
|     border: 1px solid var(--border); |  | ||||||
|     border-radius: 0.5rem; |  | ||||||
|     padding: 1rem; |  | ||||||
|     font-family: var(--font-mono, monospace); |  | ||||||
|     margin: 2rem 0; |  | ||||||
|     max-width: 90%; |  | ||||||
|     overflow: hidden; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .stat-line { |  | ||||||
|     margin-bottom: 0.5rem; |  | ||||||
|     white-space: nowrap; |  | ||||||
|     overflow: hidden; |  | ||||||
|     font-size: 0.9rem; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .stat-line .cmd { |  | ||||||
|     color: var(--primary, #3b82f6); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .stat-line.output { |  | ||||||
|     color: var(--success, #10b981); |  | ||||||
|     font-weight: bold; |  | ||||||
|     font-size: 1.1rem; |  | ||||||
|     margin-bottom: 1rem; |     margin-bottom: 1rem; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /* Enhanced terminal animation */ |   .section-description { | ||||||
|   .terminal-body { |     font-size: 1.125rem; | ||||||
|     padding: 1rem; |     color: var(--text-secondary); | ||||||
|     font-family: var(--font-mono, monospace); |     max-width: 700px; | ||||||
|     font-size: 0.875rem; |     margin: 0 auto; | ||||||
|     color: var(--text-primary); |  | ||||||
|     line-height: 1.4; |  | ||||||
|     max-height: 400px; |  | ||||||
|     overflow-y: auto; |  | ||||||
|   } |   } | ||||||
|    | </style> | ||||||
|   #animated-terminal .terminal-line { |  | ||||||
|     opacity: 0; |  | ||||||
|     transform: translateY(5px); |  | ||||||
|     transition: opacity 0.2s ease, transform 0.2s ease; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   #animated-terminal .terminal-line.visible { |  | ||||||
|     opacity: 1; |  | ||||||
|     transform: translateY(0); |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   /* Enhanced architecture diagram */ |  | ||||||
|   .architecture-diagram-container { |  | ||||||
|     position: relative; |  | ||||||
|     max-width: 1000px; |  | ||||||
|     margin: 3rem auto; |  | ||||||
|     aspect-ratio: 16 / 9; |  | ||||||
|     border-radius: 1rem; |  | ||||||
|     overflow: hidden; |  | ||||||
|     box-shadow: var(--card-shadow); |  | ||||||
|     border: 1px solid var(--border); |  | ||||||
|     background-color: rgba(15, 23, 42, 0.5); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .architecture-fallback { |  | ||||||
|     display: none; /* Hide by default, show if JS disabled */ |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .architecture-diagram { |  | ||||||
|     position: relative; |  | ||||||
|     width: 100%; |  | ||||||
|     height: 100%; |  | ||||||
|     display: grid; |  | ||||||
|     grid-template-rows: repeat(3, 1fr); |  | ||||||
|     grid-template-columns: repeat(4, 1fr); |  | ||||||
|     gap: 1rem; |  | ||||||
|     padding: 2rem; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .diagram-node { |  | ||||||
|     background-color: rgba(30, 41, 59, 0.7); |  | ||||||
|     border: 1px solid var(--border); |  | ||||||
|     border-radius: 0.5rem; |  | ||||||
|     padding: 1rem; |  | ||||||
|     display: flex; |  | ||||||
|     flex-direction: column; |  | ||||||
|     align-items: |  | ||||||
|  | @ -6,7 +6,6 @@ import Header from '../components/Header.astro'; | ||||||
| import Footer from '../components/Footer.astro'; | import Footer from '../components/Footer.astro'; | ||||||
| import Terminal from '../components/Terminal.astro'; | import Terminal from '../components/Terminal.astro'; | ||||||
| import PostCard from '../components/PostCard.astro'; | import PostCard from '../components/PostCard.astro'; | ||||||
| import MiniKnowledgeGraph from '../components/MiniKnowledgeGraph.astro'; |  | ||||||
| import '../styles/card-animations.css'; | import '../styles/card-animations.css'; | ||||||
| 
 | 
 | ||||||
| const title = "ArgoBox | Enterprise-Grade Home Lab & DevOps Hub"; | const title = "ArgoBox | Enterprise-Grade Home Lab & DevOps Hub"; | ||||||
|  | @ -48,32 +47,6 @@ const projectHighlights = [ | ||||||
|   } |   } | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| // Enhanced Graph Data for Homepage Feature |  | ||||||
| const miniGraphData = { |  | ||||||
|   nodes: [ |  | ||||||
|     { id: 'kubernetes', label: 'Kubernetes', type: 'tag' }, |  | ||||||
|     { id: 'homelab', label: 'Home Lab', type: 'tag' }, |  | ||||||
|     { id: 'devops', label: 'DevOps', type: 'tag' }, |  | ||||||
|     { id: 'automation', label: 'Automation', type: 'tag' }, |  | ||||||
|     { id: 'infrastructure', label: 'Infrastructure', type: 'tag' }, |  | ||||||
|     { id: 'post1', label: 'K3s Cluster Setup', type: 'post' }, |  | ||||||
|     { id: 'post2', label: 'GitOps Workflow', type: 'post' }, |  | ||||||
|     { id: 'post3', label: 'Home Lab Monitoring', type: 'post' }, |  | ||||||
|     { id: 'post4', label: 'IaC Best Practices', type: 'post' }, |  | ||||||
|   ], |  | ||||||
|   edges: [ |  | ||||||
|     { source: 'post1', target: 'kubernetes', type: 'post-tag' }, |  | ||||||
|     { source: 'post1', target: 'homelab', type: 'post-tag' }, |  | ||||||
|     { source: 'post2', target: 'devops', type: 'post-tag' }, |  | ||||||
|     { source: 'post2', target: 'kubernetes', type: 'post-tag' }, |  | ||||||
|     { source: 'post2', target: 'automation', type: 'post-tag' }, |  | ||||||
|     { source: 'post3', target: 'homelab', type: 'post-tag' }, |  | ||||||
|     { source: 'post3', target: 'infrastructure', type: 'post-tag' }, |  | ||||||
|     { source: 'post4', target: 'infrastructure', type: 'post-tag' }, |  | ||||||
|     { source: 'post4', target: 'automation', type: 'post-tag' }, |  | ||||||
|   ] |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Define Commands for Hero Terminal | // Define Commands for Hero Terminal | ||||||
| const heroCommands = [ | const heroCommands = [ | ||||||
|   { prompt: "[user@argobox]$ ", command: "uname -a", output: ["Linux argobox 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux"] }, |   { prompt: "[user@argobox]$ ", command: "uname -a", output: ["Linux argobox 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux"] }, | ||||||
|  | @ -99,7 +72,7 @@ const heroCommands = [ | ||||||
|             </p> |             </p> | ||||||
|             <div class="hero-cta"> |             <div class="hero-cta"> | ||||||
|               <a href="/blog" class="cta-button primary">Explore Guides</a> |               <a href="/blog" class="cta-button primary">Explore Guides</a> | ||||||
|               <a href="#knowledge-graph" class="cta-button secondary">Discover Connections</a> |               <a href="/blog#knowledge-graph" class="cta-button secondary">Discover Connections</a> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|           <div class="terminal-container"> |           <div class="terminal-container"> | ||||||
|  | @ -109,24 +82,6 @@ const heroCommands = [ | ||||||
|       </div> |       </div> | ||||||
|     </section> |     </section> | ||||||
| 
 | 
 | ||||||
|     <!-- Knowledge Graph Feature - Moved up for impact --> |  | ||||||
|     <section id="knowledge-graph" class="graph-section section-padding"> |  | ||||||
|       <div class="container"> |  | ||||||
|         <div class="section-header"> |  | ||||||
|           <h2 class="section-title">Knowledge Graph</h2> |  | ||||||
|           <p class="section-description"> |  | ||||||
|             Explore connections between infrastructure components, technologies, and implementation guides. |  | ||||||
|           </p> |  | ||||||
|         </div> |  | ||||||
|         <div class="graph-container"> |  | ||||||
|           <MiniKnowledgeGraph graphData={miniGraphData} height="450px" /> |  | ||||||
|         </div> |  | ||||||
|         <div class="graph-link-container"> |  | ||||||
|           <a href="/blog#knowledge-graph" class="cta-button primary">Explore Full Graph</a> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </section> |  | ||||||
| 
 |  | ||||||
|     <!-- Intro Section - Reframed as community resource --> |     <!-- Intro Section - Reframed as community resource --> | ||||||
|     <section class="intro-section section-padding"> |     <section class="intro-section section-padding"> | ||||||
|       <div class="container"> |       <div class="container"> | ||||||
|  | @ -310,27 +265,6 @@ const heroCommands = [ | ||||||
|   .hero-cta { display: flex; gap: 1rem; } |   .hero-cta { display: flex; gap: 1rem; } | ||||||
|   .terminal-container { flex: 1; max-width: 550px; } |   .terminal-container { flex: 1; max-width: 550px; } | ||||||
| 
 | 
 | ||||||
|   /* Graph Section - New Prominent Section */ |  | ||||||
|   .graph-section { |  | ||||||
|     background: var(--bg-primary); |  | ||||||
|     position: relative; |  | ||||||
|     overflow: hidden; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .graph-container { |  | ||||||
|     border: 1px solid var(--border-primary); |  | ||||||
|     border-radius: 12px; |  | ||||||
|     overflow: hidden; |  | ||||||
|     background: var(--bg-secondary); |  | ||||||
|     margin-bottom: 2rem; |  | ||||||
|     box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); |  | ||||||
|     position: relative; |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   .graph-link-container { |  | ||||||
|     text-align: center; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* Intro Section - Adjusted for community focus */ |   /* Intro Section - Adjusted for community focus */ | ||||||
|   .intro-section .section-title { text-align: center; margin-bottom: 1.5rem; } |   .intro-section .section-title { text-align: center; margin-bottom: 1.5rem; } | ||||||
|   .intro-text { |   .intro-text { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,470 @@ | ||||||
|  | --- | ||||||
|  | // src/pages/projects/infrastructure-templates.astro | ||||||
|  | import BaseLayout from '../../layouts/BaseLayout.astro'; | ||||||
|  | import Header from '../../components/Header.astro'; | ||||||
|  | import Footer from '../../components/Footer.astro'; | ||||||
|  | 
 | ||||||
|  | const title = "Infrastructure as Code Templates | ArgoBox"; | ||||||
|  | const description = "Curated collection of reusable Terraform, Ansible, and Kubernetes manifests for building modern infrastructure environments."; | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | <BaseLayout title={title} description={description}> | ||||||
|  |   <Header slot="header" /> | ||||||
|  |    | ||||||
|  |   <div class="container"> | ||||||
|  |     <div class="page-header"> | ||||||
|  |       <h1>Infrastructure as Code Templates</h1> | ||||||
|  |       <div class="header-accent"></div> | ||||||
|  |     </div> | ||||||
|  |      | ||||||
|  |     <div class="coming-soon-container"> | ||||||
|  |       <div class="coming-soon-card"> | ||||||
|  |         <div class="icon-container"> | ||||||
|  |           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="60" height="60" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |             <polyline points="16 18 22 12 16 6"></polyline> | ||||||
|  |             <polyline points="8 6 2 12 8 18"></polyline> | ||||||
|  |             <line x1="12" y1="2" x2="12" y2="22"></line> | ||||||
|  |           </svg> | ||||||
|  |         </div> | ||||||
|  |         <h2>IaC Templates Coming Soon</h2> | ||||||
|  |         <p class="description"> | ||||||
|  |           This section will provide a comprehensive collection of reusable Infrastructure as Code templates for building modern tech environments. These templates are designed to help you accelerate your infrastructure deployments with best practices built in. | ||||||
|  |         </p> | ||||||
|  |          | ||||||
|  |         <div class="template-categories"> | ||||||
|  |           <div class="template-category"> | ||||||
|  |             <div class="category-icon"> | ||||||
|  |               <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |                 <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect> | ||||||
|  |                 <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect> | ||||||
|  |                 <line x1="6" y1="6" x2="6.01" y2="6"></line> | ||||||
|  |                 <line x1="6" y1="18" x2="6.01" y2="18"></line> | ||||||
|  |               </svg> | ||||||
|  |             </div> | ||||||
|  |             <div class="category-content"> | ||||||
|  |               <h3>Terraform Modules</h3> | ||||||
|  |               <p>Modular infrastructure components for cloud providers and on-premises environments.</p> | ||||||
|  |               <ul class="template-list"> | ||||||
|  |                 <li>Multi-cloud networking templates</li> | ||||||
|  |                 <li>Kubernetes cluster provisioning</li> | ||||||
|  |                 <li>Database deployment patterns</li> | ||||||
|  |                 <li>Security & compliance configurations</li> | ||||||
|  |               </ul> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |            | ||||||
|  |           <div class="template-category"> | ||||||
|  |             <div class="category-icon"> | ||||||
|  |               <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |                 <path d="M18 20V10"></path> | ||||||
|  |                 <path d="M12 20V4"></path> | ||||||
|  |                 <path d="M6 20v-6"></path> | ||||||
|  |                 <path d="M18 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path> | ||||||
|  |                 <path d="M12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path> | ||||||
|  |                 <path d="M6 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path> | ||||||
|  |               </svg> | ||||||
|  |             </div> | ||||||
|  |             <div class="category-content"> | ||||||
|  |               <h3>Ansible Playbooks</h3> | ||||||
|  |               <p>Automation workflows for system configuration and application deployment.</p> | ||||||
|  |               <ul class="template-list"> | ||||||
|  |                 <li>Server hardening & compliance</li> | ||||||
|  |                 <li>Application deployment patterns</li> | ||||||
|  |                 <li>Monitoring setup automation</li> | ||||||
|  |                 <li>Disaster recovery procedures</li> | ||||||
|  |               </ul> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |            | ||||||
|  |           <div class="template-category"> | ||||||
|  |             <div class="category-icon"> | ||||||
|  |               <svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |                 <path d="M22 12H2"></path> | ||||||
|  |                 <path d="M5 12l3-3"></path> | ||||||
|  |                 <path d="M5 12l3 3"></path> | ||||||
|  |                 <path d="M19 12l-3-3"></path> | ||||||
|  |                 <path d="M19 12l-3 3"></path> | ||||||
|  |               </svg> | ||||||
|  |             </div> | ||||||
|  |             <div class="category-content"> | ||||||
|  |               <h3>Kubernetes Manifests</h3> | ||||||
|  |               <p>Production-ready configurations for containerized applications.</p> | ||||||
|  |               <ul class="template-list"> | ||||||
|  |                 <li>Application deployment blueprints</li> | ||||||
|  |                 <li>GitOps-ready repository structure</li> | ||||||
|  |                 <li>Security policies & network configurations</li> | ||||||
|  |                 <li>Stateful workload templates</li> | ||||||
|  |               </ul> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |          | ||||||
|  |         <div class="cta-container"> | ||||||
|  |           <a href="/blog?tag=infrastructure-as-code" class="cta-button secondary"> | ||||||
|  |             <span>Read Related Articles</span> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |               <line x1="5" y1="12" x2="19" y2="12"></line> | ||||||
|  |               <polyline points="12 5 19 12 12 19"></polyline> | ||||||
|  |             </svg> | ||||||
|  |           </a> | ||||||
|  |           <button class="cta-button primary"> | ||||||
|  |             <span>Notify Me When Available</span> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |               <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path> | ||||||
|  |               <path d="M13.73 21a2 2 0 0 1-3.46 0"></path> | ||||||
|  |             </svg> | ||||||
|  |           </button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |      | ||||||
|  |     <!-- Related Projects Section --> | ||||||
|  |     <div class="related-projects"> | ||||||
|  |       <h2 class="section-title">Related Projects</h2> | ||||||
|  |       <div class="projects-grid"> | ||||||
|  |         <a href="/projects/github" class="related-project"> | ||||||
|  |           <div class="project-icon"> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |               <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path> | ||||||
|  |             </svg> | ||||||
|  |           </div> | ||||||
|  |           <h3>GitHub Repositories</h3> | ||||||
|  |           <p>Open-source infrastructure code and automation scripts.</p> | ||||||
|  |           <span class="project-link">View Project <span class="arrow">→</span></span> | ||||||
|  |         </a> | ||||||
|  |          | ||||||
|  |         <a href="/projects/tech-stack" class="related-project"> | ||||||
|  |           <div class="project-icon"> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |               <line x1="16.5" y1="9.4" x2="7.5" y2="4.21"></line> | ||||||
|  |               <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path> | ||||||
|  |               <polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline> | ||||||
|  |               <line x1="12" y1="22.08" x2="12" y2="12"></line> | ||||||
|  |             </svg> | ||||||
|  |           </div> | ||||||
|  |           <h3>Tech Stack</h3> | ||||||
|  |           <p>Explore the technologies used in the ArgoBox infrastructure.</p> | ||||||
|  |           <span class="project-link">View Project <span class="arrow">→</span></span> | ||||||
|  |         </a> | ||||||
|  |          | ||||||
|  |         <a href="/homelab" class="related-project"> | ||||||
|  |           <div class="project-icon"> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||||||
|  |               <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect> | ||||||
|  |               <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect> | ||||||
|  |               <line x1="6" y1="6" x2="6.01" y2="6"></line> | ||||||
|  |               <line x1="6" y1="18" x2="6.01" y2="18"></line> | ||||||
|  |             </svg> | ||||||
|  |           </div> | ||||||
|  |           <h3>Home Lab Infrastructure</h3> | ||||||
|  |           <p>Enterprise-grade home lab environment with production services.</p> | ||||||
|  |           <span class="project-link">View Project <span class="arrow">→</span></span> | ||||||
|  |         </a> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  |    | ||||||
|  |   <Footer slot="footer" /> | ||||||
|  | </BaseLayout> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |   .container { | ||||||
|  |     max-width: 1280px; | ||||||
|  |     margin: 0 auto; | ||||||
|  |     padding: 0 var(--container-padding, 1.5rem); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .page-header { | ||||||
|  |     margin: 3rem 0 4rem; | ||||||
|  |     position: relative; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   h1 { | ||||||
|  |     font-size: var(--font-size-4xl, 2.5rem); | ||||||
|  |     background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4)); | ||||||
|  |     -webkit-background-clip: text; | ||||||
|  |     background-clip: text; | ||||||
|  |     color: transparent; | ||||||
|  |     display: inline-block; | ||||||
|  |     margin-bottom: 0.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .header-accent { | ||||||
|  |     width: 80px; | ||||||
|  |     height: 4px; | ||||||
|  |     background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4)); | ||||||
|  |     border-radius: 2px; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .coming-soon-container { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     padding: 2rem 0 4rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .coming-soon-card { | ||||||
|  |     background: var(--card-bg, #1e293b); | ||||||
|  |     border: 1px solid var(--card-border, #334155); | ||||||
|  |     border-radius: 16px; | ||||||
|  |     padding: 2.5rem; | ||||||
|  |     max-width: 900px; | ||||||
|  |     width: 100%; | ||||||
|  |     box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); | ||||||
|  |     position: relative; | ||||||
|  |     overflow: hidden; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .coming-soon-card::before { | ||||||
|  |     content: ''; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 0; | ||||||
|  |     left: 0; | ||||||
|  |     right: 0; | ||||||
|  |     bottom: 0; | ||||||
|  |     background: radial-gradient(circle at 30% 50%, rgba(59, 130, 246, 0.1), transparent 70%); | ||||||
|  |     pointer-events: none; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .icon-container { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     margin-bottom: 1.5rem; | ||||||
|  |     color: var(--accent-secondary, #3b82f6); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   h2 { | ||||||
|  |     text-align: center; | ||||||
|  |     margin-bottom: 1.5rem; | ||||||
|  |     font-size: var(--font-size-3xl, 1.875rem); | ||||||
|  |     color: var(--text-primary, #f1f5f9); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .description { | ||||||
|  |     text-align: center; | ||||||
|  |     margin-bottom: 3rem; | ||||||
|  |     color: var(--text-secondary, #cbd5e1); | ||||||
|  |     font-size: var(--font-size-lg, 1.125rem); | ||||||
|  |     line-height: 1.7; | ||||||
|  |     max-width: 800px; | ||||||
|  |     margin-left: auto; | ||||||
|  |     margin-right: auto; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-categories { | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | ||||||
|  |     gap: 2rem; | ||||||
|  |     margin-bottom: 3rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-category { | ||||||
|  |     background: rgba(30, 41, 59, 0.5); | ||||||
|  |     border: 1px solid var(--border-primary, #334155); | ||||||
|  |     border-radius: 12px; | ||||||
|  |     padding: 1.5rem; | ||||||
|  |     transition: all 0.3s ease; | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-category:hover { | ||||||
|  |     transform: translateY(-5px); | ||||||
|  |     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15); | ||||||
|  |     border-color: var(--accent-secondary, #3b82f6); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-icon { | ||||||
|  |     color: var(--accent-secondary, #3b82f6); | ||||||
|  |     margin-bottom: 1.5rem; | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-content { | ||||||
|  |     flex: 1; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-content h3 { | ||||||
|  |     font-size: var(--font-size-xl, 1.25rem); | ||||||
|  |     margin-bottom: 1rem; | ||||||
|  |     color: var(--text-primary, #f1f5f9); | ||||||
|  |     text-align: center; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .category-content p { | ||||||
|  |     color: var(--text-secondary, #cbd5e1); | ||||||
|  |     margin-bottom: 1.5rem; | ||||||
|  |     text-align: center; | ||||||
|  |     font-size: var(--font-size-md, 1rem); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-list { | ||||||
|  |     list-style-type: none; | ||||||
|  |     padding: 0; | ||||||
|  |     margin: 0; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-list li { | ||||||
|  |     padding: 0.75rem 0; | ||||||
|  |     border-bottom: 1px solid rgba(203, 213, 225, 0.1); | ||||||
|  |     color: var(--text-tertiary, #94a3b8); | ||||||
|  |     font-size: var(--font-size-sm, 0.875rem); | ||||||
|  |     position: relative; | ||||||
|  |     padding-left: 1.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-list li:last-child { | ||||||
|  |     border-bottom: none; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .template-list li::before { | ||||||
|  |     content: '→'; | ||||||
|  |     position: absolute; | ||||||
|  |     left: 0; | ||||||
|  |     color: var(--accent-primary, #06b6d4); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .cta-container { | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     gap: 1.5rem; | ||||||
|  |     margin-top: 2rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .cta-button { | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     gap: 0.75rem; | ||||||
|  |     padding: 0.75rem 1.5rem; | ||||||
|  |     border-radius: 8px; | ||||||
|  |     font-weight: 500; | ||||||
|  |     font-size: var(--font-size-md, 1rem); | ||||||
|  |     transition: all 0.3s ease; | ||||||
|  |     cursor: pointer; | ||||||
|  |     text-decoration: none; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .cta-button.primary { | ||||||
|  |     background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4)); | ||||||
|  |     color: var(--bg-primary, #0f172a); | ||||||
|  |     border: none; | ||||||
|  |     box-shadow: 0 5px 15px rgba(59, 130, 246, 0.2); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .cta-button.secondary { | ||||||
|  |     background: transparent; | ||||||
|  |     border: 1px solid var(--accent-secondary, #3b82f6); | ||||||
|  |     color: var(--accent-secondary, #3b82f6); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .cta-button.primary:hover { | ||||||
|  |     transform: translateY(-3px); | ||||||
|  |     box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .cta-button.secondary:hover { | ||||||
|  |     transform: translateY(-3px); | ||||||
|  |     background: rgba(59, 130, 246, 0.1); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   /* Related Projects Section */ | ||||||
|  |   .related-projects { | ||||||
|  |     margin: 4rem 0; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .section-title { | ||||||
|  |     font-size: var(--font-size-2xl, 1.5rem); | ||||||
|  |     color: var(--text-primary, #f1f5f9); | ||||||
|  |     margin-bottom: 2rem; | ||||||
|  |     position: relative; | ||||||
|  |     padding-bottom: 0.75rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .section-title::after { | ||||||
|  |     content: ''; | ||||||
|  |     position: absolute; | ||||||
|  |     bottom: 0; | ||||||
|  |     left: 0; | ||||||
|  |     width: 60px; | ||||||
|  |     height: 3px; | ||||||
|  |     background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4)); | ||||||
|  |     border-radius: 2px; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .projects-grid { | ||||||
|  |     display: grid; | ||||||
|  |     grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | ||||||
|  |     gap: 1.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .related-project { | ||||||
|  |     background: var(--card-bg, #1e293b); | ||||||
|  |     border: 1px solid var(--border-primary, #334155); | ||||||
|  |     border-radius: 12px; | ||||||
|  |     padding: 1.5rem; | ||||||
|  |     text-decoration: none; | ||||||
|  |     transition: all 0.3s ease; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .related-project:hover { | ||||||
|  |     transform: translateY(-5px); | ||||||
|  |     border-color: var(--accent-primary, #06b6d4); | ||||||
|  |     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-icon { | ||||||
|  |     color: var(--accent-primary, #06b6d4); | ||||||
|  |     margin-bottom: 1rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .related-project h3 { | ||||||
|  |     font-size: var(--font-size-lg, 1.125rem); | ||||||
|  |     color: var(--text-primary, #f1f5f9); | ||||||
|  |     margin-bottom: 0.75rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .related-project p { | ||||||
|  |     color: var(--text-tertiary, #94a3b8); | ||||||
|  |     font-size: var(--font-size-sm, 0.875rem); | ||||||
|  |     margin-bottom: 1.5rem; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .project-link { | ||||||
|  |     color: var(--accent-secondary, #3b82f6); | ||||||
|  |     font-size: var(--font-size-sm, 0.875rem); | ||||||
|  |     font-weight: 500; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .arrow { | ||||||
|  |     margin-left: 0.5rem; | ||||||
|  |     transition: transform 0.3s ease; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .related-project:hover .arrow { | ||||||
|  |     transform: translateX(5px); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   @media (max-width: 768px) { | ||||||
|  |     .template-categories { | ||||||
|  |       grid-template-columns: 1fr; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     .cta-container { | ||||||
|  |       flex-direction: column; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     .coming-soon-card { | ||||||
|  |       padding: 2rem 1.5rem; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     h1 { | ||||||
|  |       font-size: var(--font-size-3xl, 1.875rem); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     h2 { | ||||||
|  |       font-size: var(--font-size-2xl, 1.5rem); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | </style> | ||||||
		Loading…
	
		Reference in New Issue