Initial dashboard commit

This commit is contained in:
Md. Parvej Hossain 2025-10-01 22:36:34 +06:00
commit 87619ed473

502
index.html Normal file
View File

@ -0,0 +1,502 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Remote Geospatial Career Navigator</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* New Professional Palette based on user's image, plus Glassmorphism */
:root {
--color-slate: #385A2A; /* Dark Green/Olive for primary text/headers */
--color-accent: #F0A700; /* Orange/Gold for highlights/active states */
--color-light-bg: #F9F5F0; /* Warm Off-White Background */
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--color-light-bg);
transition: all 0.3s ease-in-out;
line-height: 1.6; /* General improved line height */
}
.card {
background-color: rgba(255, 255, 255, 0.9); /* Slightly transparent white */
border-radius: 1.5rem; /* More rounded corners */
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 4px 6px -2px rgba(0, 0, 0, 0.08); /* Deeper, professional shadow */
padding: 2.5rem; /* Increased padding */
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.glass-card {
backdrop-filter: blur(10px); /* The glass effect */
-webkit-backdrop-filter: blur(10px);
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 15px 30px -5px rgba(0, 0, 0, 0.2), 0 6px 8px -3px rgba(0, 0, 0, 0.12);
}
.tab-button.active {
background-color: var(--color-accent);
color: var(--color-slate);
font-weight: 700;
box-shadow: 0 4px 10px rgba(240, 167, 0, 0.4); /* Custom gold shadow */
}
.tab-button {
transition: background-color 0.3s, color 0.3s, box-shadow 0.3s;
border: 1px solid #CFD8DC;
color: var(--color-slate);
}
.bar-segment {
transition: width 1s ease-out;
border-radius: 0.5rem;
}
.timeline-item::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 4px;
background-color: #CFD8DC; /* Light line */
border-radius: 2px;
}
.timeline-item::after {
content: '';
position: absolute;
left: -8px;
top: 10px;
width: 20px;
height: 20px;
background-color: var(--color-accent);
border-radius: 50%;
border: 4px solid var(--color-light-bg); /* Use background color for pop */
box-shadow: 0 0 0 2px var(--color-accent);
}
.timeline-item:last-child::before {
height: 10px;
}
.pill-slate {
background-color: #ECEFF1;
color: var(--color-slate);
font-weight: 500;
}
/* Custom styles for single-column skill display */
.skills-grid-row {
display: grid;
grid-template-columns: 1fr; /* Single column */
gap: 1.5rem; /* Space between rows */
}
/* Animation for Quotes Section */
@keyframes fadeInOut {
0%, 100% { opacity: 0; }
10%, 90% { opacity: 1; }
}
.quote-cycle {
/* 10s total duration: 1s fade-in, 8s display, 1s fade-out */
animation: fadeInOut 10s infinite;
}
</style>
</head>
<body class="p-4 md:p-12">
<!-- Header Section -->
<header class="text-center mb-10">
<h1 class="text-4xl md:text-5xl font-extrabold text-gray-800" style="color: var(--color-slate);">Geospatial Career Navigator Dashboard</h1>
<p class="text-xl mt-2 font-light" style="color: var(--color-accent);">Remote GIS & Remote Sensing Roadmap for Students</p>
</header>
<!-- Main Content Grid -->
<main class="grid grid-cols-1 lg:grid-cols-3 gap-10">
<!-- Row 1: Skills (2/3) and Compensation (1/3) -->
<section class="lg:col-span-2 card glass-card">
<h2 class="text-2xl font-bold border-b pb-4 mb-8" style="color: var(--color-slate); border-color: #ECEFF1;">1. Comprehensive Skills Progression</h2>
<div id="skills-tabs" class="flex flex-wrap gap-4 mb-8">
<!-- Tab buttons will be injected here -->
</div>
<div id="skills-content">
<!-- Skill categories and lists will be injected here -->
</div>
</section>
<section class="card glass-card">
<h2 class="text-2xl font-bold border-b pb-4 mb-8" style="color: var(--color-slate); border-color: #ECEFF1;">2. Compensation Benchmarks</h2>
<div class="mb-8">
<h3 class="text-xl font-semibold mb-5" style="color: var(--color-slate);">Salaried Roles (Annual USD)</h3>
<div id="salary-chart-container" class="space-y-8">
<!-- Salary bars will be injected here -->
</div>
</div>
<div class="mb-6">
<h3 class="text-xl font-semibold mb-5" style="color: var(--color-slate);">Freelance Hourly Rates (USD/hr)</h3>
<div id="freelance-chart-container" class="space-y-8">
<!-- Freelance rate bars will be injected here -->
</div>
<p class="mt-8 p-4 bg-red-50 rounded-xl text-base text-gray-700 border border-red-200 leading-relaxed">
<span class="font-bold text-red-700">Local Compensation Note:</span> Bangladesh local salaried roles (Dhaka estimate) range from **BDT 280k–315k/year** on average, with top roles reaching ~BDT 498k/year.
</p>
</div>
</section>
<!-- Row 2: Roadmap (2/3) and Side Panel (1/3) -->
<section class="lg:col-span-2 card glass-card">
<h2 class="text-2xl font-bold border-b pb-4 mb-8" style="color: var(--color-slate); border-color: #ECEFF1;">3. Project-Based Career Roadmap (Total 37 Projects)</h2>
<div id="roadmap-timeline" class="space-y-10 relative ml-4 pl-10">
<!-- Roadmap steps will be injected here -->
</div>
</section>
<!-- Side Panel Container (S4, S5, S6) -->
<section class="lg:col-span-1 grid grid-cols-1 gap-10">
<!-- S4: Targeted Job Titles -->
<div class="card glass-card">
<h2 class="text-2xl font-bold mb-4" style="color: var(--color-slate);">4. Targeted Job Titles</h2>
<ul id="job-titles-list" class="list-disc list-inside text-gray-700 space-y-2 ml-4 text-base leading-relaxed">
<!-- Job titles will be injected here -->
</ul>
</div>
<!-- S5: Key Tools & Libraries (Vertical List) -->
<div class="card glass-card">
<h2 class="text-2xl font-bold mb-4" style="color: var(--color-slate);">5. Key Tools & Libraries</h2>
<ul id="tools-list" class="list-disc list-inside text-gray-700 space-y-2 ml-4 text-base leading-relaxed">
<!-- Tools will be injected here -->
</ul>
</div>
<!-- S6: Job Acquisition Platforms -->
<div class="card glass-card">
<h3 class="text-2xl font-bold mb-4" style="color: var(--color-slate);">6. Job Acquisition Platforms</h3>
<ul class="list-disc list-inside text-gray-700 space-y-2 ml-4 text-base leading-relaxed">
<li><span class="font-medium">Freelance Hubs:</span> Upwork, Fiverr, Freelancer.com, Truelancer, PeoplePerHour</li>
<li><span class="font-medium">Specialized Boards:</span> GeoSearch, MyGISJobs.com, Arc.dev</li>
<li><span class="font-medium">Professional/Aggregators:</span> LinkedIn, Indeed, ZipRecruiter, Remote.co, FlexJobs</li>
</ul>
</div>
</section>
<!-- Row 3: Certs (Full Width, 3 Columns Internal) -->
<section class="lg:col-span-3 card glass-card border-t-4 p-6" style="border-color: #4CAF50;">
<h3 class="text-2xl font-bold border-b pb-4 mb-8" style="color: var(--color-slate); border-color: #ECEFF1;">7. Professional Geospatial Certifications</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8" id="certifications-container">
<!-- Certification columns injected here -->
</div>
</section>
<!-- Row 4: Quotes/Motivation (Full Width, Single Line) -->
<section class="lg:col-span-3 card glass-card border-t-4 p-6" style="border-color: #FF9800;">
<div id="quote-display" class="relative flex items-center justify-center text-center py-4">
<!-- Single animated quote injected here -->
</div>
</section>
</main>
<script>
// --- DATA OBJECTS ---
const SKILLS_DATA = {
"Beginner": [
{ "cat": "Core GIS", "items": ["ArcMap, ArcGIS Pro, QGIS", "Data editing, digitizing, and database management", "Map Making, Coordinate System, Metadata Creation"] },
{ "cat": "Remote Sensing (RS)", "items": ["Erdas Imagine, ENVI", "Landsat, Sentinel Data", "Multi-spectral and hyperspectral image analysis (basic)", "Image classification (supervised and unsupervised)", "NDVI, NDWI", "Change detection analysis", "Climate and environmental modeling (basic)"] },
{ "cat": "Programming & Soft Skills", "items": ["Python scripting for GIS automation", "SQL for spatial database queries", "Microsoft Excel for data analysis", "Basic understanding of cloud platforms", "Git version control", "Machine learning for image classification (basic)", "Basic deep learning (concepts)", "Statistical modeling and data analysis", "Strong attention to detail, Communication, Collaboration, Time management"] }
],
"Intermediate": [
{ "cat": "Advanced GIS & DB (3-5 Years)", "items": ["Spatial statistics and geostatistics", "Advanced spatial analysis", "GIS database design", "Mobile GIS and field data collection", "Web mapping (Leaflet, Mapbox, ArcGIS Online)"] },
{ "cat": "Remote Sensing & GEE", "items": ["Multi-spectral and hyperspectral image analysis (advanced)", "LiDAR data processing", "Google Earth Engine proficiency (JavaScript and Python API)"] },
{ "cat": "Programming & Web", "items": ["Advanced Python with libraries (GDAL, Rasterio, Geopandas, Shapely)", "R programming for spatial statistics", "JavaScript for web mapping applications", "Basic understanding of cloud platforms (AWS/GCP awareness)"] }
],
"Advanced": [
{ "cat": "Enterprise & Cloud (5+ Years)", "items": ["Spatial database optimization (PostGIS, Oracle Spatial)", "Server administration (ArcGIS Server, GeoServer)", "Integration of multiple geospatial platforms", "AWS, Google Cloud, Azure (Cloud Infrastructure)"] },
{ "cat": "RS, ML & Development", "items": ["Algorithm development for image analysis", "Time-series analysis of satellite data", "Google Earth Engine proficiency (Mastery level)", "ML Frameworks (TensorFlow, PyTorch, Scikit-learn)", "API development and integration", "Custom GIS application development", "Full-stack web development for geospatial applications"] },
{ "cat": "Management", "items": ["Project planning, execution, and budget management", "Team leadership and mentorship", "Client communication and proposal writing"] }
]
};
const SALARY_DATA = {
salaried_us: [
{ role: "Entry Level (Tech/Jr Analyst)", range_low: 40000, range_high: 75000, avg: 58000, color: "bg-amber-500" },
{ role: "Mid Level (Analyst/Specialist)", range_low: 60000, range_high: 100593, avg: 75010, color: "bg-amber-600" },
{ role: "Senior Level (Dev/Data Scientist)", range_low: 98572, range_high: 145963, avg: 122738, color: "bg-amber-700" }
],
freelance: [
{ region: "Starting (BD/South Asia)", rate_low: 5, rate_high: 15, target: 10, color: "bg-yellow-400", max_val: 150 },
{ region: "Mid-Level (Global)", rate_low: 15, rate_high: 55, target: 35, color: "bg-yellow-500", max_val: 150 },
{ region: "Expert (Global)", rate_low: 60, rate_high: 150, target: 90, color: "bg-yellow-600", max_val: 150 }
]
};
const ROADMAP_DATA = [
{ title: "Phase 1: Core GIS Skills (Projects 1-8)", projects: [
"University Campus Interactive Map", "Bangladesh Administrative Hierarchy (Division to Upazila)", "Field Data Collection Map (50+ GPS points with photos)", "Multi-layer Geodatabase (with domains and relationships)", "Coordinate System Transformation (showing distortion)", "Topology Error Correction (show before/after fixing)", "Digital Elevation Model Analysis (Slope, Aspect, Hillshade)", "Buffer Analysis Project (Multi-ring buffers with population data)"
], accent: "var(--color-slate)" },
{ title: "Phase 2: Remote Sensing (Projects 9-15)", projects: [
"Seasonal NDVI Comparison (4 seasons vegetation changes)", "Water Body Extraction (NDWI - wet vs dry season)", "Land Cover Classification (5-class supervised with accuracy report)", "Unsupervised vs Supervised Classification (Side-by-side comparison)", "False Color Composites (6 different band combinations with interpretation)", "Temporal Change Detection (10-year urban growth or forest loss)", "Spectral Signature Library (Signature curves for 10 land cover types)"
], accent: "var(--color-slate)" },
{ title: "Phase 3: Programming & Automation (Projects 16-22)", projects: [
"Python Batch Reprojection (Script to convert 50 files at once)", "Automated Map Series (Python script generating 64 district maps)", "GeoPandas Spatial Analysis (Jupyter notebook with 5 spatial operations)", "SQL Spatial Queries (20 queries demonstrating spatial relationships)", "Excel Dashboard Integration (Environmental data linked to GIS with charts)", "Web Scraping to GIS (Scrape location data, geocode, map)", "GitHub Portfolio Repository (Organized repos with all code and documentation)"
], accent: "var(--color-slate)" },
{ title: "Phase 4: Cloud & Advanced (Projects 23-28)", projects: [
"Google Earth Engine NDVI Time Series (5-year vegetation trend analysis)", "GEE Land Surface Temperature (Urban heat island mapping)", "Climate Data Visualization (20-year rainfall/temperature trends mapped)", "Random Forest Classification (ML-based land cover with accuracy)", "R Spatial Statistics (Spatial autocorrelation and regression analysis)", "Automated Change Detection (Script to detect changes between any two dates)"
], accent: "var(--color-slate)" },
{ title: "Phase 5: Professional Projects (Projects 29-35)", projects: [
"Environmental Impact Assessment (Multi-criteria suitability analysis)", "Flood Risk Mapping (Combine elevation, rainfall, land use for risk zones)", "Agricultural Suitability Analysis (Soil, climate, slope for crop selection)", "Pollution Hotspot Analysis (Point pattern analysis of pollution sources)", "Web Map Application (Interactive map with 5+ layers, popups, search)", "ArcGIS StoryMap (Narrative map combining text, maps, multimedia)", "Complete GIS Database (Enterprise geodatabase with 20+ feature classes)"
], accent: "var(--color-slate)" },
{ title: "Phase 6: Portfolio Presentation (Projects 36-37)", projects: [
"Professional Portfolio Website (Display all projects with descriptions and maps)", "GitHub Profile Setup (Pinned repos, complete README files, consistent commits)"
], accent: "var(--color-slate)" }
];
const JOB_TITLES = [
"GIS Technician",
"Cartographic Technician",
"GIS Analyst",
"GIS Specialist",
"Remote Sensing Analyst",
"Senior GIS Analyst",
"GIS Developer",
"Remote Sensing Specialist",
"GIS Project Manager"
];
const QUOTES = [
"A portfolio is not a record of work, it's a predictor of future success.",
"The map is not the territory, but the portfolio is your launchpad.",
"In remote work, your code is your handshake, and your projects are your resume.",
"Master the data, master the domain, master your destiny.",
"Every project finished is a paid lesson completed."
];
const COMPREHENSIVE_TOOLS = [
"ArcGIS Pro, ArcMap, QGIS (Desktop Tools)",
"ERDAS Imagine, ENVI (Raster Processing)",
"Python (GeoPandas, Rasterio, ArcPy, Scikit-learn)",
"Google Earth Engine (GEE, JS/Python API)",
"PostGIS, SQL Spatial, SpatiaLite (Databases)",
"Leaflet, Mapbox, OpenLayers (Web Mapping)",
"FME (ETL Tool)",
"LiDAR Tools (LAStools, PDAL)",
"UAV/Photogrammetry (Metashape, OpenDroneMap)",
"Git/GitHub (Version Control)",
"Cloud Services (AWS, Google Cloud, Azure)",
"Docker (DevOps)",
"R (sf, raster/terra for stats)"
];
const CERTIFICATIONS_DATA = {
"BEGINNER": [
{ name: "QGIS Tutorials - Self-paced learning", type: "FREE" },
{ name: "Google Earth Engine - Introductory Tutorials", type: "FREE" },
{ name: "NASA ARSET - Applied Remote Sensing Training", type: "FREE" },
{ name: "Alison - Diploma in Geographic Information Systems", type: "FREE" },
{ name: "Coursera - GIS Specialization by UC Davis", type: "PAID" },
{ name: "Spatial Thoughts - Python Foundation for Spatial Analysis", type: "PAID" },
],
"INTERMEDIATE": [
{ name: "Automating GIS Processes - University Materials", type: "FREE" },
{ name: "Google Earth Engine - Advanced Tutorials", type: "FREE" },
{ name: "GISP-E (Emerging GISP) - Exam-only certification", type: "PAID" },
{ name: "Penn State - Remote Sensing Certificate", type: "PAID" },
],
"ADVANCED": [
{ name: "NASA ARSET - Advanced Remote Sensing & ML", type: "FREE" },
{ name: "GISP (GIS Professional) - Portfolio + Exam", type: "PAID" },
{ name: "ASPRS - Certified Mapping Scientist (RS/Photogrammetrist)", type: "PAID" },
{ name: "USGIF - Certified GEOINT Professional (CGP-R/G/D)", type: "PAID" },
{ name: "USC / Penn State - Graduate Certificates (GIST/GEOINT)", type: "PAID" },
{ name: "QGIS.org Official Certification Program", type: "FREE" },
{ name: "GRASS GIS Tutorials & Certification", type: "FREE" },
]
};
// --- RENDERING FUNCTIONS ---
function renderSkillsMatrix() {
// ... (Unchanged from previous versions logic)
const tabsContainer = document.getElementById('skills-tabs');
const contentContainer = document.getElementById('skills-content');
const levels = Object.keys(SKILLS_DATA);
let first = true;
levels.forEach(level => {
// Create Tab Button
const button = document.createElement('button');
button.textContent = level;
button.className = `tab-button px-6 py-3 rounded-full text-base font-medium hover:bg-gray-200 ${first ? 'active' : ''}`;
button.setAttribute('data-level', level);
button.onclick = () => showSkillsContent(level);
tabsContainer.appendChild(button);
// Create Content Block (initially hidden)
const contentDiv = document.createElement('div');
contentDiv.id = `content-${level}`;
contentDiv.className = `p-4 skills-grid-row ${first ? '' : 'hidden'}`;
SKILLS_DATA[level].forEach(category => {
const catCard = document.createElement('div');
catCard.className = 'p-6 rounded-xl bg-white border border-gray-100 shadow-lg hover:shadow-xl transition-shadow duration-300';
catCard.innerHTML = `<h4 class="font-bold text-xl mb-4" style="color: var(--color-slate);">${category.cat}</h4>
<ul class="list-disc list-inside text-base text-gray-700 space-y-3 ml-4 leading-relaxed">
${category.items.map(item => `<li>${item}</li>`).join('')}
</ul>`;
contentDiv.appendChild(catCard);
});
contentContainer.appendChild(contentDiv);
first = false;
});
function showSkillsContent(level) {
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
document.querySelector(`[data-level="${level}"]`).classList.add('active');
document.querySelectorAll('#skills-content > div').forEach(div => div.classList.add('hidden'));
document.getElementById(`content-${level}`).classList.remove('hidden');
}
showSkillsContent(levels[0]);
}
function renderSalaryCharts() {
// ... (Unchanged from previous versions logic)
const salaryContainer = document.getElementById('salary-chart-container');
const freelanceContainer = document.getElementById('freelance-chart-container');
const maxSalary = Math.max(...SALARY_DATA.salaried_us.map(d => d.range_high));
SALARY_DATA.salaried_us.forEach(data => {
const percentLow = (data.range_low / maxSalary) * 100;
const percentHigh = (data.range_high / maxSalary) * 100;
const percentAvg = (data.avg / maxSalary) * 100;
const chartHtml = `
<div class="space-y-1">
<div class="flex justify-between items-center text-base font-medium text-gray-700">
<span>${data.role}</span>
<span class="font-bold" style="color: var(--color-accent);">$${(data.avg / 1000).toFixed(0)}K Avg</span>
</div>
<div class="relative h-4 bg-gray-200 rounded-full overflow-hidden">
<div class="absolute h-full bg-red-200 bar-segment" style="width: ${percentLow}%;"></div>
<div class="absolute h-full ${data.color} bar-segment" style="width: ${percentAvg}%;"></div>
<div class="absolute h-full border-2 border-dashed border-gray-600" style="left: ${percentHigh}%; top: 0; bottom: 0; width: 2px;" title="Max: $${(data.range_high / 1000).toFixed(0)}K"></div>
</div>
<div class="flex justify-between text-xs text-gray-500">
<span>$${(data.range_low / 1000).toFixed(0)}K</span>
<span>Range: $${(data.range_low / 1000).toFixed(0)}K - $${(data.range_high / 1000).toFixed(0)}K</span>
</div>
</div>
`;
salaryContainer.innerHTML += chartHtml;
});
const maxRate = SALARY_DATA.freelance[SALARY_DATA.freelance.length - 1].rate_high;
SALARY_DATA.freelance.forEach(data => {
const percentTarget = (data.target / maxRate) * 100;
const percentMax = (data.rate_high / maxRate) * 100;
const chartHtml = `
<div class="space-y-1">
<div class="flex justify-between items-center text-base font-medium text-gray-700">
<span>${data.region}</span>
<span class="font-bold text-orange-600">$${data.target}/hr Target</span>
</div>
<div class="relative h-4 bg-gray-200 rounded-full overflow-hidden">
<div class="absolute h-full ${data.color} bar-segment" style="width: ${percentTarget}%;"></div>
<div class="absolute h-full border-2 border-dashed border-gray-600" style="left: ${percentMax}%; top: 0; bottom: 0; width: 2px;" title="Max: $${data.rate_high}/hr"></div>
</div>
<div class="flex justify-between text-xs text-gray-500">
<span>$${data.rate_low}/hr (Start Low)</span>
<span>Max: $${data.rate_high}/hr</span>
</div>
</div>
`;
freelanceContainer.innerHTML += chartHtml;
});
}
function renderRoadmap() {
// ... (Unchanged from previous versions logic)
const container = document.getElementById('roadmap-timeline');
ROADMAP_DATA.forEach((item, index) => {
const roadmapItem = document.createElement('div');
roadmapItem.className = 'timeline-item relative pb-10';
roadmapItem.innerHTML = `
<div class="relative ml-4 p-6 card glass-card shadow-lg hover:shadow-xl transition-shadow duration-300">
<h3 class="font-bold text-xl mb-3" style="color: var(--color-slate);">${item.title}</h3>
<p class="text-base font-medium mb-4 text-gray-600 leading-relaxed">Total Projects in this phase: ${item.projects.length}</p>
<ul class="list-disc list-inside text-sm text-gray-700 space-y-2 ml-4 leading-relaxed">
${item.projects.map(project => `<li class="pl-2">${project}</li>`).join('')}
</ul>
</div>
`;
container.appendChild(roadmapItem);
});
}
function renderJobTitles() {
// ... (Unchanged from previous versions logic)
const list = document.getElementById('job-titles-list');
list.innerHTML = JOB_TITLES.map(title => `<li>${title}</li>`).join('');
}
function renderComprehensiveTools() {
const list = document.getElementById('tools-list');
// Populate the new vertical list for comprehensive tools
list.innerHTML = COMPREHENSIVE_TOOLS.map(tool => `<li>${tool}</li>`).join('');
}
function renderCertifications() {
const container = document.getElementById('certifications-container');
container.innerHTML = ''; // Clear existing content
for (const level in CERTIFICATIONS_DATA) {
const columnDiv = document.createElement('div');
columnDiv.className = 'p-6 rounded-xl bg-gray-50 border border-gray-200';
const listTitle = `<h4 class="font-bold text-xl mb-4" style="color: var(--color-accent);">${level.toUpperCase()} LEVEL</h4>`;
let listHtml = `<ul class="list-disc list-inside text-sm text-gray-700 space-y-3 ml-4 leading-relaxed">`;
CERTIFICATIONS_DATA[level].forEach(cert => {
const color = cert.type === "FREE" ? "text-green-600" : "text-yellow-600";
const itemHtml = `<li class="pl-2">${cert.name} <span class="${color} font-bold"> [${cert.type}]</span></li>`;
listHtml += itemHtml;
});
listHtml += `</ul>`;
columnDiv.innerHTML = listTitle + listHtml;
container.appendChild(columnDiv);
}
}
function renderQuotes() {
const container = document.getElementById('quote-display');
let quoteIndex = 0;
function updateQuote() {
container.innerHTML = `
<p class="text-xl md:text-2xl font-semibold italic text-gray-800 quote-cycle" style="color: var(--color-slate); animation-duration: 10s;">
&ldquo;${QUOTES[quoteIndex]}&rdquo;
</p>
`;
quoteIndex = (quoteIndex + 1) % QUOTES.length;
}
updateQuote();
setInterval(updateQuote, 10000);
}
// --- INITIALIZATION ---
window.onload = () => {
renderSkillsMatrix();
renderSalaryCharts();
renderRoadmap();
renderJobTitles();
renderComprehensiveTools();
renderCertifications();
renderQuotes();
};
</script>
</body>
</html>