diff --git a/src/static/script.js b/src/static/script.js index 1044193e..7fff2c05 100644 --- a/src/static/script.js +++ b/src/static/script.js @@ -1,3 +1,1098 @@ +// // DevPath client-side behavior. +// // Compatibility key for bookmarks.js: "devpathSavedProjects" + +// (function () { +// var html = document.documentElement; + +// function applyTheme(theme) { +// var isDark = theme === "dark"; +// html.setAttribute("data-theme", theme); +// try { +// localStorage.setItem("theme", theme); +// } catch (err) { +// // Storage can be unavailable in private browsing. +// } + +// document.querySelectorAll(".theme-toggle").forEach(function (button) { +// button.setAttribute("aria-pressed", isDark ? "true" : "false"); +// button.setAttribute("aria-label", isDark ? "Switch to light mode" : "Switch to dark mode"); +// }); + +// // Sync preview cards active styles +// var cards = document.querySelectorAll(".theme-preview-card"); +// cards.forEach(function (card) { +// if (card.getAttribute("data-theme-target") === theme) { +// card.style.borderColor = "var(--accent)"; +// } else { +// card.style.borderColor = "var(--border)"; +// } +// }); +// } + +// function initTheme() { +// var theme = "light"; + +// try { +// theme = localStorage.getItem("theme") || html.getAttribute("data-theme") || "light"; +// } catch (err) { +// theme = html.getAttribute("data-theme") || "light"; +// } + +// applyTheme(theme); + +// requestAnimationFrame(function () { +// html.classList.add("theme-ready"); +// }); +// } + +// document.addEventListener("DOMContentLoaded", function () { +// // Inject the theme modal HTML dynamically +// var modalHtml = ` +// +// `; +// document.body.insertAdjacentHTML("beforeend", modalHtml); + +// var modal = document.getElementById("theme-preview-modal"); +// var closeBtn = document.getElementById("close-theme-modal"); +// var cards = document.querySelectorAll(".theme-preview-card"); + +// function closeModal() { +// if (modal) { +// modal.style.display = "none"; +// modal.setAttribute("aria-hidden", "true"); +// } +// } + +// if (closeBtn) closeBtn.addEventListener("click", closeModal); +// if (modal) { +// modal.addEventListener("click", function (e) { +// if (e.target === modal) closeModal(); +// }); +// } + +// cards.forEach(function (card) { +// card.addEventListener("click", function () { +// var theme = this.getAttribute("data-theme-target"); +// applyTheme(theme); +// setTimeout(closeModal, 150); +// }); +// card.addEventListener("mouseenter", function () { +// if (this.getAttribute("data-theme-target") !== html.getAttribute("data-theme")) { +// this.style.borderColor = "var(--gray-400)"; +// } +// }); +// card.addEventListener("mouseleave", function () { +// if (this.getAttribute("data-theme-target") !== html.getAttribute("data-theme")) { +// this.style.borderColor = "var(--border)"; +// } +// }); +// }); + +// // Update active styles on modal load +// var currentTheme = html.getAttribute("data-theme") || "light"; +// cards.forEach(function (card) { +// if (card.getAttribute("data-theme-target") === currentTheme) { +// card.style.borderColor = "var(--accent)"; +// } else { +// card.style.borderColor = "var(--border)"; +// } +// }); +// }); + +// document.addEventListener("click", function (event) { +// var toggle = event.target.closest(".theme-toggle"); +// if (!toggle) return; +// event.preventDefault(); +// var modal = document.getElementById("theme-preview-modal"); +// if (modal) { +// modal.style.display = "flex"; +// modal.setAttribute("aria-hidden", "false"); +// } +// }); + +// initTheme(); +// })(); + +// (function initMobileNav() { +// var toggle = document.getElementById("nav-mobile-toggle"); +// var menu = document.getElementById("nav-mobile-menu"); +// if (!toggle || !menu) return; + +// function setOpen(isOpen) { +// menu.classList.toggle("open", isOpen); +// toggle.classList.toggle("open", isOpen); +// toggle.setAttribute("aria-expanded", isOpen ? "true" : "false"); +// } + +// toggle.addEventListener("click", function (e) { +// e.stopPropagation(); +// setOpen(!menu.classList.contains("open")); +// }); + +// // Only close menu when clicking actual navigation links, not buttons +// menu.querySelectorAll(".nav-mobile-link").forEach(function (link) { +// if (link.tagName.toLowerCase() === "a") { +// link.addEventListener("click", function () { +// setOpen(false); +// }); +// } +// }); + +// // Close when clicking outside the menu +// document.addEventListener("click", function (e) { +// if (menu.classList.contains("open") && +// !menu.contains(e.target) && +// !toggle.contains(e.target)) { +// setOpen(false); +// } +// }); + +// window.addEventListener("resize", function () { +// if (window.innerWidth >= 640) setOpen(false); +// }); +// })(); + +// var POINTS_PER_SEARCH = 5; +// var POINTS_PER_VIEW = 10; +// var POINTS_PER_CODE_OPEN = 15; +// var POINTS_PER_COMPLETION = 30; + +// var PROGRESS_TARGET_SEARCHES = 10; +// var PROGRESS_TARGET_VIEWS = 10; +// var PROGRESS_TARGET_CODE_OPENS = 10; +// var PROGRESS_TARGET_COMPLETIONS = 5; + +// var PROGRESS_MAX_POINTS = ( +// PROGRESS_TARGET_SEARCHES * POINTS_PER_SEARCH + +// PROGRESS_TARGET_VIEWS * POINTS_PER_VIEW + +// PROGRESS_TARGET_CODE_OPENS * POINTS_PER_CODE_OPEN + +// PROGRESS_TARGET_COMPLETIONS * POINTS_PER_COMPLETION +// ); + +// var STORAGE_KEY = "devpathUserProgress"; +// var progress = { +// searches: 0, +// projectViews: 0, +// codeOpens: 0, +// completions: 0, +// points: 0, +// viewedProjects: [], +// completedProjects: [], +// achievements: [], +// badges: { +// first_search: false, +// project_explorer: false, +// code_starter: false, +// completionist: false, +// roadmap_runner: false +// }, +// bestScore: 0 +// }; + +// function loadProgressState() { +// try { +// var saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || "null"); +// if (!saved || typeof saved !== "object") return; +// progress = Object.assign(progress, saved); +// progress.viewedProjects = Array.isArray(saved.viewedProjects) ? saved.viewedProjects : []; +// progress.completedProjects = Array.isArray(saved.completedProjects) ? saved.completedProjects : []; +// progress.achievements = Array.isArray(saved.achievements) ? saved.achievements : []; +// progress.badges = Object.assign(progress.badges, saved.badges || {}); +// } catch (err) { +// console.warn("Unable to load progress state", err); +// } +// } + +// function saveProgressState() { +// try { +// progress.bestScore = Math.max(progress.bestScore || 0, progress.points || 0); +// localStorage.setItem(STORAGE_KEY, JSON.stringify(progress)); +// } catch (err) { +// console.warn("Unable to save progress state", err); +// } +// } + +// function computeProgressPoints() { +// progress.points = progress.searches * POINTS_PER_SEARCH + progress.projectViews * POINTS_PER_VIEW + +// progress.codeOpens * POINTS_PER_CODE_OPEN + progress.completions * POINTS_PER_COMPLETION; +// } + +// function showAchievementToast(title, detail) { +// var toast = document.getElementById("achievement-toast"); +// if (!toast) return; +// toast.textContent = ""; +// var strong = document.createElement("strong"); +// strong.textContent = title; +// var span = document.createElement("span"); +// span.textContent = detail; +// toast.appendChild(strong); +// toast.appendChild(span); +// toast.classList.add("show"); +// window.clearTimeout(showAchievementToast.timeout); +// showAchievementToast.timeout = window.setTimeout(function () { +// toast.classList.remove("show"); +// }, 3200); +// } + +// function addAchievement(title, detail) { +// if (progress.achievements.some(function (item) { return item.title === title; })) return; +// progress.achievements.unshift({ +// title: title, +// description: detail, +// date: new Date().toLocaleDateString() +// }); +// progress.achievements = progress.achievements.slice(0, 5); +// } + +// function unlockBadge(id, title, detail) { +// if (progress.badges[id]) return; +// progress.badges[id] = true; +// addAchievement(title, detail); +// showAchievementToast("Badge unlocked", title + " - " + detail); +// } + +// function tryUnlockBadges() { +// if (progress.searches >= 1) unlockBadge("first_search", "First Search", "You used DevPath to find your first project."); +// if (progress.projectViews >= 1) unlockBadge("project_explorer", "Project Explorer", "You viewed a project detail."); +// if (progress.codeOpens >= 1) unlockBadge("code_starter", "Code Starter", "You opened starter code."); +// if (progress.completions >= 1) unlockBadge("completionist", "Completionist", "You marked a project complete."); +// if (progress.searches >= 5) unlockBadge("roadmap_runner", "Roadmap Runner", "You searched five times."); +// } + +// function projectIsCompleted(projectId) { +// return progress.completedProjects.some(function (item) { +// return (item && typeof item === "object" ? item.id : item) === projectId; +// }); +// } + +// function updateProfileWidgets() { +// var pointsEl = document.getElementById("progress-points"); +// var statsEl = document.getElementById("progress-stats"); +// var meterFill = document.getElementById("progress-meter-fill"); +// var badgesEl = document.getElementById("progress-badges"); +// var achievementList = document.getElementById("achievement-list"); +// var leaderboardList = document.getElementById("leaderboard-list"); +// var historyList = document.getElementById("completed-history-list"); +// var completionBtn = document.getElementById("btn-mark-complete"); + +// if (pointsEl) pointsEl.textContent = progress.points; +// if (statsEl) { +// statsEl.innerHTML = +// "
  • Searches" + progress.searches + "
  • " + +// "
  • Projects Viewed" + progress.projectViews + "
  • " + +// "
  • Code Opens" + progress.codeOpens + "
  • " + +// "
  • Projects Completed" + progress.completions + "
  • "; +// } +// if (meterFill) { +// var percentage = Math.min(100, Math.round((progress.points / PROGRESS_MAX_POINTS) * 100)); +// meterFill.style.width = percentage + "%"; +// meterFill.setAttribute("aria-valuenow", String(percentage)); +// meterFill.textContent = percentage + "%"; +// } +// if (badgesEl) { +// var badges = [ +// ["first_search", "First Search"], +// ["project_explorer", "Project Explorer"], +// ["code_starter", "Code Starter"], +// ["completionist", "Completionist"], +// ["roadmap_runner", "Roadmap Runner"] +// ]; +// badgesEl.innerHTML = badges.map(function (badge) { +// var unlocked = progress.badges[badge[0]]; +// return "
  • " + (unlocked ? "OK" : "*") + "" + badge[1] + "
  • "; +// }).join(""); +// } +// if (achievementList) { +// achievementList.innerHTML = progress.achievements.length +// ? progress.achievements.map(function (item) { +// return "
  • " + item.title + "" + +// item.description + "" + item.date + "
  • "; +// }).join("") +// : "
  • No achievements yet. Use DevPath and unlock the first badge.
  • "; +// } +// if (leaderboardList) { +// var entries = [ +// { name: "Ava", points: 245 }, +// { name: "Kai", points: 192 }, +// { name: "Sam", points: 176 }, +// { name: "You", points: progress.points } +// ].sort(function (a, b) { return b.points - a.points; }); +// leaderboardList.innerHTML = entries.map(function (entry, index) { +// return "
  • " + (index + 1) + ". " + entry.name + "" + entry.points + " pts
  • "; +// }).join(""); +// } +// if (historyList) { +// historyList.innerHTML = progress.completedProjects.length +// ? progress.completedProjects.slice(0, 5).map(function (item) { +// var title = item && typeof item === "object" ? item.title : "Project " + item; +// return "
  • " + title + "Completed
  • "; +// }).join("") +// : "
  • No completed projects yet. Mark one complete from a project page.
  • "; +// } +// if (completionBtn && typeof PROJECT_ID !== "undefined") { +// var completed = projectIsCompleted(PROJECT_ID); +// completionBtn.textContent = completed ? "Project Completed" : "Mark Project Complete"; +// completionBtn.disabled = completed; +// } +// } + +// function recordSearch() { +// progress.searches += 1; +// computeProgressPoints(); +// tryUnlockBadges(); +// saveProgressState(); +// updateProfileWidgets(); +// } + +// function recordProjectView() { +// if (typeof PROJECT_ID === "undefined") return; +// if (progress.viewedProjects.indexOf(PROJECT_ID) === -1) { +// progress.viewedProjects.push(PROJECT_ID); +// progress.projectViews = progress.viewedProjects.length; +// computeProgressPoints(); +// tryUnlockBadges(); +// saveProgressState(); +// updateProfileWidgets(); +// } +// } + +// function recordCodeOpen() { +// progress.codeOpens += 1; +// computeProgressPoints(); +// tryUnlockBadges(); +// saveProgressState(); +// updateProfileWidgets(); +// } + +// function recordCompletion(projectId, projectTitle) { +// if (!projectId || projectIsCompleted(projectId)) return; +// progress.completedProjects.push({ id: projectId, title: projectTitle || "Project " + projectId }); +// progress.completions = progress.completedProjects.length; +// computeProgressPoints(); +// tryUnlockBadges(); +// saveProgressState(); +// updateProfileWidgets(); +// } + +// loadProgressState(); +// updateProfileWidgets(); + +// (function initIndexPage() { +// var form = document.getElementById("recommend-form"); +// if (!form) return; + +// var submitBtn = document.getElementById("submit-btn"); +// var btnLabel = document.getElementById("btn-label"); +// var btnLoading = document.getElementById("btn-loading"); +// var resultsSection = document.getElementById("results-section"); +// var resultsGrid = document.getElementById("results-grid"); +// var resultsLoadingEl = document.getElementById("results-loading"); +// var resultsEmptyEl = document.getElementById("results-empty"); +// var emptyMessageEl = document.getElementById("empty-message"); +// var skillsHidden = document.getElementById("skills"); +// var skillsInput = document.getElementById("skills-input"); +// var selectedChips = document.getElementById("skill-chips-selected"); +// var suggestions = document.getElementById("skills-suggestions"); +// var skillWrap = document.getElementById("skill-input-wrap"); +// var quickPickChips = Array.prototype.slice.call(document.querySelectorAll(".skill-chip")); +// var selectedSkills = []; +// var availableSkills = (typeof skills !== "undefined" && Array.isArray(skills)) +// ? skills.map(function (item) { return item.label; }).filter(Boolean) +// : quickPickChips.map(function (chip) { return chip.getAttribute("data-skill"); }); +// var activeSuggestionIndex = -1; +// var visibleSuggestions = []; + +// function normalize(value) { +// return String(value || "").trim().toLowerCase(); +// } + +// function syncSkillsHiddenInput() { +// skillsHidden.value = JSON.stringify(selectedSkills); +// } + +// function isSelected(skill) { +// return selectedSkills.some(function (item) { return normalize(item) === normalize(skill); }); +// } + +// function canonicalSkill(rawSkill) { +// var trimmed = String(rawSkill || "").trim(); +// var match = availableSkills.find(function (skill) { return normalize(skill) === normalize(trimmed); }); +// return match || trimmed; +// } + +// function updateQuickPickState() { +// quickPickChips.forEach(function (chip) { +// var active = isSelected(chip.getAttribute("data-skill")); +// chip.classList.toggle("active", active); +// chip.classList.toggle("selected", active); +// chip.setAttribute("aria-pressed", active ? "true" : "false"); +// }); +// } + +// function renderSelectedChips() { +// selectedChips.textContent = ""; +// selectedSkills.forEach(function (skill) { +// var chip = document.createElement("span"); +// chip.className = "skill-chip-selected"; +// chip.appendChild(document.createTextNode(skill)); +// var button = document.createElement("button"); +// button.type = "button"; +// button.className = "skill-chip-remove"; +// button.setAttribute("aria-label", "Remove " + skill); +// button.textContent = "x"; +// button.addEventListener("click", function (event) { +// event.stopPropagation(); +// removeSkill(skill); +// }); +// chip.appendChild(button); +// selectedChips.appendChild(chip); +// }); +// } + +// window.addSkill = function addSkill(rawSkill) { +// var skill = canonicalSkill(rawSkill); +// if (!skill || isSelected(skill)) return; +// selectedSkills.push(skill); +// renderSelectedChips(); +// syncSkillsHiddenInput(); +// updateQuickPickState(); +// clearFieldError("skills-error"); +// if (skillsInput) skillsInput.focus(); +// }; + +// function removeSkill(skill) { +// selectedSkills = selectedSkills.filter(function (item) { return normalize(item) !== normalize(skill); }); +// renderSelectedChips(); +// syncSkillsHiddenInput(); +// updateQuickPickState(); +// } + +// function clearFieldError(id) { +// var el = document.getElementById(id); +// if (el) el.textContent = ""; +// } + +// function showFieldError(id, message) { +// var el = document.getElementById(id); +// if (el) el.textContent = message; +// } + +// function clearAllErrors() { +// ["skills-error", "level-error", "interest-error", "time-error"].forEach(clearFieldError); +// var general = document.getElementById("form-error-general"); +// if (general) general.textContent = ""; +// } + +// function hideSuggestions() { +// visibleSuggestions = []; +// activeSuggestionIndex = -1; +// suggestions.style.display = "none"; +// suggestions.textContent = ""; +// skillsInput.setAttribute("aria-expanded", "false"); +// } + +// function filteredSkills(query) { +// var q = normalize(query); +// if (!q) return []; +// return availableSkills.filter(function (skill) { +// return normalize(skill).indexOf(q) !== -1 && !isSelected(skill); +// }).slice(0, 8); +// } + +// function renderSuggestionState() { +// suggestions.querySelectorAll(".suggestion-item").forEach(function (item, index) { +// item.classList.toggle("suggestion-item--active", index === activeSuggestionIndex); +// item.setAttribute("aria-selected", index === activeSuggestionIndex ? "true" : "false"); +// }); +// } + +// function showSuggestions(items) { +// visibleSuggestions = items; +// activeSuggestionIndex = -1; +// suggestions.textContent = ""; +// if (!items.length) { +// hideSuggestions(); +// return; +// } +// items.forEach(function (skill, index) { +// var item = document.createElement("div"); +// item.className = "suggestion-item"; +// item.id = "skills-suggestion-" + index; +// item.setAttribute("role", "option"); +// item.setAttribute("aria-selected", "false"); +// item.textContent = skill; +// item.addEventListener("mousedown", function (event) { event.preventDefault(); }); +// item.addEventListener("mouseenter", function () { +// activeSuggestionIndex = index; +// renderSuggestionState(); +// }); +// item.addEventListener("click", function () { +// window.addSkill(skill); +// skillsInput.value = ""; +// hideSuggestions(); +// }); +// suggestions.appendChild(item); +// }); +// suggestions.style.display = "block"; +// skillsInput.setAttribute("aria-expanded", "true"); +// } + +// function validateForm() { +// var valid = true; +// if (!selectedSkills.length) { +// showFieldError("skills-error", "Please add at least one skill."); +// valid = false; +// } +// if (!document.getElementById("level").value) { +// showFieldError("level-error", "Please select your experience level."); +// valid = false; +// } +// if (!document.getElementById("interest").value) { +// showFieldError("interest-error", "Please select an area of interest."); +// valid = false; +// } +// if (!document.getElementById("time").value) { +// showFieldError("time-error", "Please select your time availability."); +// valid = false; +// } +// return valid; +// } + +// function setLoadingState(isLoading) { +// submitBtn.disabled = isLoading; +// submitBtn.setAttribute("aria-busy", isLoading ? "true" : "false"); +// btnLabel.style.display = isLoading ? "none" : "inline"; +// btnLoading.style.display = isLoading ? "inline-flex" : "none"; +// if (isLoading) { +// resultsSection.style.display = "block"; +// resultsLoadingEl.style.display = "block"; +// resultsGrid.style.display = "none"; +// resultsEmptyEl.style.display = "none"; +// resultsGrid.innerHTML = ""; +// resultsEmptyEl.style.display = "none"; +// resultsSection.scrollIntoView({ behavior: "smooth" }); +// } else { +// resultsLoadingEl.style.display = "none"; +// } +// } + +// function truncate(text, maxLength) { +// text = text || ""; +// return text.length > maxLength ? text.slice(0, maxLength) + "..." : text; +// } + +// function createTag(text, type) { +// var span = document.createElement("span"); +// span.className = "project-tag project-tag--" + normalize(type).replace(/[^a-z0-9_-]/g, "-"); +// span.textContent = text; +// return span; +// } + +// function buildProjectCard(project) { +// var card = document.createElement("div"); +// card.className = "project-card"; + +// var title = document.createElement("h3"); +// title.className = "project-card-title"; +// title.textContent = project.title; + +// var desc = document.createElement("p"); +// desc.className = "project-card-desc"; +// var descText = document.createElement("span"); +// descText.className = "project-card-desc-text"; +// descText.textContent = truncate(project.description, 120); +// desc.appendChild(descText); + +// if (project.description && project.description.length > 120) { +// var expanded = false; +// var readMore = document.createElement("button"); +// readMore.type = "button"; +// readMore.className = "read-more-btn"; +// readMore.textContent = "Read more"; +// readMore.setAttribute("aria-expanded", "false"); +// readMore.addEventListener("click", function () { +// expanded = !expanded; +// descText.textContent = expanded ? project.description : truncate(project.description, 120); +// readMore.textContent = expanded ? "Read less" : "Read more"; +// readMore.setAttribute("aria-expanded", expanded ? "true" : "false"); +// }); +// desc.appendChild(readMore); +// } + +// var tags = document.createElement("div"); +// tags.className = "project-card-tags"; +// (project.skills || []).forEach(function (skill) { tags.appendChild(createTag(skill, "skill")); }); +// tags.appendChild(createTag(project.level, project.level)); +// tags.appendChild(createTag("Time: " + project.time, "time")); + +// var footer = document.createElement("div"); +// footer.className = "project-card-footer"; + +// if (typeof DevPathBookmarks !== "undefined") { +// var saveBtn = document.createElement("button"); +// saveBtn.type = "button"; +// saveBtn.className = "btn-save-project"; +// saveBtn.setAttribute("data-save-project-id", project.id); +// var isSaved = DevPathBookmarks.isSaved(project.id); +// if (isSaved) saveBtn.classList.add("saved"); +// saveBtn.setAttribute("aria-pressed", isSaved ? "true" : "false"); +// DevPathBookmarks.setButtonContent(saveBtn, isSaved); +// saveBtn.addEventListener("click", function () { +// DevPathBookmarks.toggle(project, saveBtn); +// }); +// footer.appendChild(saveBtn); +// } + +// var link = document.createElement("a"); +// link.className = "btn-details"; +// link.textContent = "View Full Project"; +// link.href = "/project/" + project.id; +// footer.appendChild(link); + +// card.appendChild(title); +// card.appendChild(desc); +// card.appendChild(tags); +// card.appendChild(footer); +// return card; +// } + +// function renderResults(projects, message) { +// resultsSection.style.display = "block"; +// resultsLoadingEl.style.display = "none"; +// resultsGrid.innerHTML = ""; +// if (!projects || projects.length === 0) { +// resultsGrid.style.display = "none"; +// resultsEmptyEl.style.display = "block"; +// if (emptyMessageEl) { +// emptyMessageEl.textContent = message || "Try adjusting your skills or choosing a different interest area."; +// } +// resultsSection.scrollIntoView({ behavior: "smooth" }); +// return; +// } +// resultsEmptyEl.style.display = "none"; +// resultsGrid.style.display = "grid"; +// projects.forEach(function (project) { resultsGrid.appendChild(buildProjectCard(project)); }); +// resultsSection.scrollIntoView({ behavior: "smooth" }); +// } + +// skillsInput.setAttribute("role", "combobox"); +// skillsInput.setAttribute("aria-expanded", "false"); +// suggestions.setAttribute("role", "listbox"); + +// skillsInput.addEventListener("input", function () { +// showSuggestions(filteredSkills(skillsInput.value)); +// }); +// skillsInput.addEventListener("focus", function () { +// if (skillsInput.value.trim()) showSuggestions(filteredSkills(skillsInput.value)); +// }); +// skillsInput.addEventListener("blur", function () { +// window.setTimeout(hideSuggestions, 150); +// }); +// skillsInput.addEventListener("keydown", function (event) { +// if (event.key === "ArrowDown" || event.key === "ArrowUp") { +// if (!visibleSuggestions.length) showSuggestions(filteredSkills(skillsInput.value)); +// if (!visibleSuggestions.length) return; +// event.preventDefault(); +// activeSuggestionIndex = event.key === "ArrowDown" +// ? (activeSuggestionIndex + 1) % visibleSuggestions.length +// : (activeSuggestionIndex <= 0 ? visibleSuggestions.length - 1 : activeSuggestionIndex - 1); +// renderSuggestionState(); +// return; +// } +// if (event.key === "Escape") { +// hideSuggestions(); +// return; +// } +// if (event.key === "Enter") { +// event.preventDefault(); +// if (activeSuggestionIndex >= 0 && visibleSuggestions[activeSuggestionIndex]) { +// window.addSkill(visibleSuggestions[activeSuggestionIndex]); +// } else { +// window.addSkill(skillsInput.value); +// } +// skillsInput.value = ""; +// hideSuggestions(); +// } +// }); + +// quickPickChips.forEach(function (chip) { +// chip.addEventListener("click", function () { +// var skill = chip.getAttribute("data-skill"); +// if (isSelected(skill)) removeSkill(skill); +// else window.addSkill(skill); +// skillsInput.value = ""; +// hideSuggestions(); +// }); +// }); + +// if (skillWrap) { +// skillWrap.addEventListener("click", function () { skillsInput.focus(); }); +// } + +// // Toggle dropdown on button click +// var dropdownToggle = document.getElementById("skills-dropdown-toggle"); +// if (dropdownToggle) { +// dropdownToggle.addEventListener("click", function (event) { +// event.preventDefault(); +// event.stopPropagation(); +// var isExpanded = suggestions.style.display === "block"; +// if (isExpanded) { +// hideSuggestions(); +// } else { +// var unselectedSkills = availableSkills.filter(function (skill) { +// return !isSelected(skill); +// }); +// showSuggestions(unselectedSkills); +// } +// }); +// } + +// function resetFormAndState() { +// form.reset(); +// selectedSkills = []; +// renderSelectedChips(); +// syncSkillsHiddenInput(); +// updateQuickPickState(); +// clearAllErrors(); +// hideSuggestions(); +// resultsSection.style.display = "none"; +// if (skillsInput) skillsInput.focus(); +// } + +// var clearBtn = document.getElementById("clear-filters-btn"); +// if (clearBtn) { +// clearBtn.addEventListener("click", resetFormAndState); +// } + +// var inlineResetBtn = document.getElementById("reset-form-btn"); +// if (inlineResetBtn) { +// inlineResetBtn.addEventListener("click", resetFormAndState); +// } + +// var resetProgressBtn = document.getElementById("reset-progress-btn"); +// if (resetProgressBtn) { +// resetProgressBtn.addEventListener("click", function () { +// progress.searches = 0; +// progress.projectViews = 0; +// progress.codeOpens = 0; +// progress.completions = 0; +// progress.points = 0; +// progress.viewedProjects = []; +// progress.completedProjects = []; +// progress.achievements = []; +// progress.badges = { +// first_search: false, +// project_explorer: false, +// code_starter: false, +// completionist: false, +// roadmap_runner: false +// }; +// saveProgressState(); +// updateProfileWidgets(); +// showAchievementToast("Progress reset", "Your local profile has been cleared."); +// }); +// } + +// form.addEventListener("submit", function (event) { +// event.preventDefault(); +// clearAllErrors(); +// if (skillsInput.value.trim()) { +// window.addSkill(skillsInput.value); +// skillsInput.value = ""; +// hideSuggestions(); +// } +// if (!validateForm()) return; +// setLoadingState(true); +// fetch("/api/recommend", { +// method: "POST", +// headers: { "Content-Type": "application/json" }, +// body: JSON.stringify({ +// skills: JSON.stringify(selectedSkills), +// level: document.getElementById("level").value, +// interest: document.getElementById("interest").value, +// time: document.getElementById("time").value +// }) +// }) +// .then(function (response) { +// return response.json().then(function (data) { +// if (!response.ok) throw new Error(data.error || "Unable to generate recommendations."); +// return data; +// }); +// }) +// .then(function (data) { +// setLoadingState(false); +// recordSearch(); +// renderResults(data.projects || [], data.message); +// }) +// .catch(function (err) { +// setLoadingState(false); +// var general = document.getElementById("form-error-general"); +// if (general) general.textContent = err.message || "An unexpected error occurred. Please try again."; +// }); +// }); + +// var modal = document.getElementById("github-modal-overlay"); +// var openModalBtn = document.getElementById("btn-show-github"); +// var closeModalBtn = document.getElementById("btn-close-github"); +// var fetchBtn = document.getElementById("btn-fetch-github"); +// var githubInput = document.getElementById("github-username"); +// var errorMsg = document.getElementById("github-modal-error"); + +// function closeGithubModal() { +// modal.classList.remove("active"); +// githubInput.value = ""; +// errorMsg.textContent = ""; +// } + +// if (modal && openModalBtn && closeModalBtn && fetchBtn && githubInput && errorMsg) { +// openModalBtn.addEventListener("click", function () { +// modal.classList.add("active"); +// githubInput.focus(); +// }); +// closeModalBtn.addEventListener("click", closeGithubModal); +// modal.addEventListener("click", function (event) { +// if (event.target === modal) closeGithubModal(); +// }); +// fetchBtn.addEventListener("click", function () { +// var username = githubInput.value.trim(); +// errorMsg.textContent = ""; +// if (!username) { +// errorMsg.textContent = "Please enter a GitHub username."; +// return; +// } +// fetchBtn.disabled = true; +// fetchBtn.textContent = "Syncing..."; +// fetch("https://api.github.com/users/" + encodeURIComponent(username) + "/repos?sort=updated&per_page=100") +// .then(function (response) { +// if (!response.ok) throw new Error(response.status === 404 ? "Username not found." : "Unable to fetch GitHub repositories."); +// return response.json(); +// }) +// .then(function (repos) { +// var languages = []; +// repos.forEach(function (repo) { +// if (repo.language && languages.indexOf(repo.language) === -1) languages.push(repo.language); +// }); +// if (!languages.length) { +// errorMsg.textContent = "No public languages found."; +// return; +// } +// languages.forEach(window.addSkill); +// closeGithubModal(); +// }) +// .catch(function (err) { +// errorMsg.textContent = err.message || "Failed to fetch skills."; +// }) +// .finally(function () { +// fetchBtn.disabled = false; +// fetchBtn.textContent = "Fetch Skills"; +// }); +// }); +// } +// })(); + +// (function initDetailPage() { +// if (typeof PROJECT_ID === "undefined") return; +// recordProjectView(); + +// var codePanel = document.getElementById("code-panel"); +// var codePanelOverlay = document.getElementById("code-panel-overlay"); +// var codeContentEl = document.getElementById("code-content"); +// var codePanelFilename = document.getElementById("code-panel-filename"); +// var btnViewCode = document.getElementById("btn-view-code"); +// var btnViewCodeSm = document.getElementById("btn-view-code-sm"); +// var btnClosePanel = document.getElementById("code-panel-close"); +// var btnCopyCode = document.getElementById("btn-copy-code"); +// var copyToast = document.getElementById("copy-toast"); +// var completionBtn = document.getElementById("btn-mark-complete"); +// var codeFetched = false; + +// function renderCode(code) { +// codeContentEl.textContent = ""; +// String(code || "").split("\n").forEach(function (line, index) { +// var row = document.createElement("div"); +// row.className = "code-line"; +// var number = document.createElement("span"); +// number.className = "code-line-number"; +// number.setAttribute("aria-hidden", "true"); +// number.textContent = index + 1; +// var content = document.createElement("span"); +// content.className = "code-line-content"; +// content.textContent = line; +// row.appendChild(number); +// row.appendChild(content); +// codeContentEl.appendChild(row); +// }); +// } + +// function fetchStarterCode() { +// codeContentEl.textContent = "Loading starter code..."; +// fetch("/project/" + PROJECT_ID + "/code") +// .then(function (response) { +// return response.json().then(function (data) { +// if (!response.ok) throw new Error(data.error || "Starter code unavailable."); +// return data; +// }); +// }) +// .then(function (data) { +// codePanelFilename.textContent = data.filename; +// renderCode(data.code); +// codeFetched = true; +// }) +// .catch(function (err) { +// codeContentEl.textContent = err.message || "Could not load starter code. Try downloading it instead."; +// }); +// } + +// function openCodePanel() { +// if (!codePanel) return; +// codePanel.classList.add("active"); +// if (codePanelOverlay) codePanelOverlay.classList.add("active"); +// document.body.style.overflow = "hidden"; +// recordCodeOpen(); +// if (!codeFetched) fetchStarterCode(); +// } + +// function closeCodePanel() { +// if (!codePanel) return; +// codePanel.classList.remove("active"); +// if (codePanelOverlay) codePanelOverlay.classList.remove("active"); +// document.body.style.overflow = ""; +// } + +// if (btnViewCode) btnViewCode.addEventListener("click", openCodePanel); +// if (btnViewCodeSm) btnViewCodeSm.addEventListener("click", openCodePanel); +// if (btnClosePanel) btnClosePanel.addEventListener("click", closeCodePanel); +// if (codePanelOverlay) codePanelOverlay.addEventListener("click", closeCodePanel); +// document.addEventListener("keydown", function (event) { +// if (event.key === "Escape") closeCodePanel(); +// }); + +// if (btnCopyCode) { +// btnCopyCode.addEventListener("click", function () { +// var code = Array.prototype.slice.call(codeContentEl.querySelectorAll(".code-line-content")) +// .map(function (line) { return line.textContent; }) +// .join("\n"); +// if (!code) return; +// var done = function () { +// if (copyToast) { +// copyToast.classList.add("show"); +// window.setTimeout(function () { copyToast.classList.remove("show"); }, 2500); +// } +// }; +// if (navigator.clipboard && navigator.clipboard.writeText) { +// navigator.clipboard.writeText(code).then(done); +// } else { +// var textarea = document.createElement("textarea"); +// textarea.value = code; +// textarea.style.cssText = "position:fixed;top:-9999px;left:-9999px"; +// document.body.appendChild(textarea); +// textarea.focus(); +// textarea.select(); +// try { document.execCommand("copy"); } catch (err) {} +// document.body.removeChild(textarea); +// done(); +// } +// }); +// } + +// var roadmapCheckboxes = Array.prototype.slice.call(document.querySelectorAll(".roadmap-checkbox")); +// var progressFill = document.getElementById("roadmap-progress-fill"); +// var progressText = document.getElementById("roadmap-progress-text"); +// var progressBar = document.querySelector(".roadmap-progress-bar"); +// var roadmapStorageKey = "devpath-roadmap-progress-" + PROJECT_ID; + +// function updateRoadmapProgress() { +// if (!roadmapCheckboxes.length) return; +// var completed = roadmapCheckboxes.filter(function (checkbox) { return checkbox.checked; }).length; +// var percent = Math.round((completed / roadmapCheckboxes.length) * 100); +// roadmapCheckboxes.forEach(function (checkbox) { +// var step = checkbox.closest(".roadmap-step"); +// if (step) step.classList.toggle("completed", checkbox.checked); +// }); +// if (progressFill) progressFill.style.width = percent + "%"; +// if (progressText) progressText.textContent = percent + "% completed"; +// if (progressBar) progressBar.setAttribute("aria-valuenow", String(percent)); +// try { +// localStorage.setItem(roadmapStorageKey, JSON.stringify(roadmapCheckboxes.map(function (checkbox) { +// return checkbox.checked; +// }))); +// } catch (err) {} +// } + +// try { +// var saved = JSON.parse(localStorage.getItem(roadmapStorageKey) || "[]"); +// roadmapCheckboxes.forEach(function (checkbox, index) { +// checkbox.checked = !!saved[index]; +// }); +// } catch (err) {} +// roadmapCheckboxes.forEach(function (checkbox) { +// checkbox.addEventListener("change", updateRoadmapProgress); +// }); +// updateRoadmapProgress(); + +// if (completionBtn) { +// completionBtn.addEventListener("click", function () { +// recordCompletion(PROJECT_ID, typeof PROJECT_TITLE !== "undefined" ? PROJECT_TITLE : ""); +// showAchievementToast("Project completed", "Nice work finishing this project."); +// }); +// } +// })(); + +// (function initScrollButton() { +// var button = document.getElementById("scroll-top-btn"); +// var icon = document.getElementById("scroll-btn-icon"); +// if (!button) return; +// var atBottom = false; + +// function nearBottom() { +// return window.innerHeight + window.pageYOffset >= document.body.scrollHeight - 40; +// } + +// function update() { +// button.classList.toggle("visible", window.pageYOffset > 200); +// atBottom = nearBottom(); +// button.setAttribute("aria-label", atBottom ? "Scroll to top" : "Scroll to bottom"); +// button.title = atBottom ? "Scroll to top" : "Scroll to bottom"; +// if (icon) icon.innerHTML = atBottom ? '' : ''; +// } + +// window.addEventListener("scroll", update, { passive: true }); +// button.addEventListener("click", function () { +// window.scrollTo({ top: atBottom ? 0 : document.body.scrollHeight, behavior: "smooth" }); +// }); +// update(); +// })(); // DevPath client-side behavior. // Compatibility key for bookmarks.js: "devpathSavedProjects" @@ -148,20 +1243,37 @@ var menu = document.getElementById("nav-mobile-menu"); if (!toggle || !menu) return; + // Defensive: avoid accidental form submission behavior + if (!toggle.getAttribute("type")) toggle.setAttribute("type", "button"); + function setOpen(isOpen) { menu.classList.toggle("open", isOpen); toggle.classList.toggle("open", isOpen); toggle.setAttribute("aria-expanded", isOpen ? "true" : "false"); } - toggle.addEventListener("click", function () { + toggle.addEventListener("click", function (e) { + e.preventDefault(); + e.stopPropagation(); setOpen(!menu.classList.contains("open")); }); + // Only close menu when clicking actual navigation links, not buttons menu.querySelectorAll(".nav-mobile-link").forEach(function (link) { - link.addEventListener("click", function () { + if (link.tagName.toLowerCase() === "a") { + link.addEventListener("click", function () { + setOpen(false); + }); + } + }); + + // Close when clicking outside the menu + document.addEventListener("click", function (e) { + if (menu.classList.contains("open") && + !menu.contains(e.target) && + !toggle.contains(e.target)) { setOpen(false); - }); + } }); window.addEventListener("resize", function () { @@ -1282,4 +2394,4 @@ updateProfileWidgets(); window.scrollTo({ top: atBottom ? 0 : document.body.scrollHeight, behavior: "smooth" }); }); update(); -})(); +})(); \ No newline at end of file diff --git a/src/static/style.css b/src/static/style.css index 7721010a..474792e1 100644 --- a/src/static/style.css +++ b/src/static/style.css @@ -18,7 +18,7 @@ --indigo-600: #3347e0; --indigo-500: #4f6ef7; --indigo-100: #eef1ff; - --indigo-50: #f5f7ff; + --indigo-50: #f5f7ff; --purple-700: #5b21b6; --purple-600: #7c3aed; @@ -28,18 +28,18 @@ --yellow-300: #fcd34d; --yellow-100: #fef9e7; - --pink-100: #fce7f3; - --pink-500: #ec4899; + --pink-100: #fce7f3; + --pink-500: #ec4899; - --green-500: #10b981; - --green-100: #d1fae5; + --green-500: #10b981; + --green-100: #d1fae5; --orange-500: #f59e0b; - --red-500: #ef4444; + --red-500: #ef4444; /* ---- Neutrals ---- */ - --white: #ffffff; - --gray-50: #f9fafb; + --white: #ffffff; + --gray-50: #f9fafb; --gray-100: #f3f4f6; --gray-200: #e5e7eb; --gray-300: #d1d5db; @@ -51,9 +51,9 @@ /* ---- Semantic text colours ---- */ --text-heading: var(--gray-900); - --text-body: var(--gray-600); - --text-light: var(--gray-400); - --border: var(--gray-200); + --text-body: var(--gray-600); + --text-light: var(--gray-400); + --border: var(--gray-200); /* ============================================================= SECTION 2: SEMANTIC THEME TOKENS (light defaults) @@ -62,15 +62,20 @@ ============================================================= */ /* Backgrounds */ - --bg-primary: #ffffff; /* page body background */ - --bg-secondary: #f9fafb; /* alternate section background */ + --bg-primary: #ffffff; + /* page body background */ + --bg-secondary: #f9fafb; + /* alternate section background */ /* Surfaces (cards, modals, inputs) */ - --surface: #ffffff; /* default card / form surface */ - --surface-hover: #f3f4f6; /* hovered surface */ + --surface: #ffffff; + /* default card / form surface */ + --surface-hover: #f3f4f6; + /* hovered surface */ /* Nav-specific mobile menu background */ --bg-mobile-menu: var(--indigo-900); + --bg-mobile-overlay: rgba(2, 6, 23, 0.56); /* Body background for detail page */ --bg-page-detail: var(--gray-50); @@ -83,32 +88,32 @@ --shadow-xl: 0 16px 60px rgba(15, 21, 96, 0.22); /* Radii */ - --r-xs: 6px; - --r-sm: 10px; - --r-md: 16px; - --r-lg: 24px; - --r-xl: 32px; + --r-xs: 6px; + --r-sm: 10px; + --r-md: 16px; + --r-lg: 24px; + --r-xl: 32px; --r-full: 9999px; /* Typography */ --font-display: "Sora", sans-serif; - --font-body: "Inter", sans-serif; - --font-mono: "JetBrains Mono", "Courier New", monospace; + --font-body: "Inter", sans-serif; + --font-mono: "JetBrains Mono", "Courier New", monospace; /* Transitions */ --t: 0.2s ease; /* Entry animations (upstream) */ - --entry-duration: 700ms; - --entry-delay-step: 110ms; + --entry-duration: 700ms; + --entry-delay-step: 110ms; /* Accessibility — focus ring */ - --focus-ring-color: var(--yellow-400); + --focus-ring-color: var(--yellow-400); --focus-ring-offset: 3px; - --focus-ring-width: 2px; + --focus-ring-width: 2px; /* Accent shortcuts (components reference these) */ - --accent: var(--indigo-600); + --accent: var(--indigo-600); --accent-hover: var(--indigo-700); --tag-beginner-text: #065f46; --tag-intermediate-text: #78350f; @@ -126,23 +131,27 @@ html[data-theme="dark"] { /* ---- Semantic text colours ---- */ --text-heading: #e2e8f0; - --text-body: #94a3b8; - --text-light: #64748b; + --text-body: #94a3b8; + --text-light: #64748b; /* ---- Border ---- */ --border: #1e293b; /* ---- Semantic theme tokens ---- */ - --bg-primary: #080f1e; /* deep navy page body */ - --bg-secondary: #0d1526; /* slightly lighter sections */ - --surface: #0f172a; /* card / form surface */ - --surface-hover: #1e293b; /* hovered card surface */ + --bg-primary: #080f1e; + /* deep navy page body */ + --bg-secondary: #0d1526; + /* slightly lighter sections */ + --surface: #0f172a; + /* card / form surface */ + --surface-hover: #1e293b; + /* hovered card surface */ --bg-mobile-menu: #0d1526; --bg-page-detail: #080f1e; /* ---- Neutral scale remapped to dark equivalents ---- */ - --white: #0f172a; - --gray-50: #1e293b; + --white: #0f172a; + --gray-50: #1e293b; --gray-100: #1e293b; --gray-200: #334155; --gray-300: #475569; @@ -153,14 +162,14 @@ html[data-theme="dark"] { --gray-900: #f1f5f9; /* ---- Indigo tints softened for dark surfaces ---- */ - --indigo-50: #1e2a4a; + --indigo-50: #1e2a4a; --indigo-100: #1e3a6e; /* ---- Colour tints ---- */ --purple-100: #2d1b69; - --pink-100: #3b1130; + --pink-100: #3b1130; --yellow-100: #3b2a00; - --green-100: #052e16; + --green-100: #052e16; /* ---- Shadows (darker tone looks heavy; compensate) ---- */ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.30); @@ -181,9 +190,9 @@ html[data-theme="dark"] { .theme-ready *::after { transition: background-color 0.3s ease, - border-color 0.3s ease, - color 0.3s ease, - box-shadow 0.3s ease; + border-color 0.3s ease, + color 0.3s ease, + box-shadow 0.3s ease; } /* ============================================================= @@ -216,11 +225,21 @@ html[data-theme="dark"] { } /* Icon visibility controlled by data-theme on */ -.theme-toggle .icon-sun { display: none; } -.theme-toggle .icon-moon { display: block; } +.theme-toggle .icon-sun { + display: none; +} + +.theme-toggle .icon-moon { + display: block; +} -html[data-theme="dark"] .theme-toggle .icon-sun { display: block; } -html[data-theme="dark"] .theme-toggle .icon-moon { display: none; } +html[data-theme="dark"] .theme-toggle .icon-sun { + display: block; +} + +html[data-theme="dark"] .theme-toggle .icon-moon { + display: none; +} /* ============================================================= SECTION 6: RESET @@ -329,6 +348,7 @@ ol { opacity: 0; transform: translate3d(0, 18px, 0); } + to { opacity: 1; transform: translate3d(0, 0, 0); @@ -336,8 +356,13 @@ ol { } @keyframes entrySoftFade { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + + to { + opacity: 1; + } } html[data-entry-anim="true"] .navbar { @@ -345,9 +370,9 @@ html[data-entry-anim="true"] .navbar { animation: entrySoftFade 420ms ease forwards; } -html[data-entry-anim="true"] .hero-copy > *, -html[data-entry-anim="true"] .hero-visual > *, -html[data-entry-anim="true"] .stats-grid > *, +html[data-entry-anim="true"] .hero-copy>*, +html[data-entry-anim="true"] .hero-visual>*, +html[data-entry-anim="true"] .stats-grid>*, html[data-entry-anim="true"] .skill-strip-inner, html[data-entry-anim="true"] .how-section .section-eyebrow, html[data-entry-anim="true"] .how-section .section-title, @@ -356,7 +381,7 @@ html[data-entry-anim="true"] .how-section .step-card, html[data-entry-anim="true"] .features-section .section-eyebrow, html[data-entry-anim="true"] .features-section .section-title, html[data-entry-anim="true"] .features-section .section-sub, -html[data-entry-anim="true"] .features-grid > *, +html[data-entry-anim="true"] .features-grid>*, html[data-entry-anim="true"] .form-section .section-eyebrow, html[data-entry-anim="true"] .form-section .section-title, html[data-entry-anim="true"] .form-section .section-sub, @@ -367,61 +392,124 @@ html[data-entry-anim="true"] .form-card-outer { will-change: transform, opacity; } -html[data-entry-anim="true"] .hero-copy > *:nth-child(1) { animation-delay: 90ms; } -html[data-entry-anim="true"] .hero-copy > *:nth-child(2) { animation-delay: calc(90ms + var(--entry-delay-step)); } -html[data-entry-anim="true"] .hero-copy > *:nth-child(3) { animation-delay: calc(90ms + (var(--entry-delay-step) * 2)); } -html[data-entry-anim="true"] .hero-copy > *:nth-child(4) { animation-delay: calc(90ms + (var(--entry-delay-step) * 3)); } +html[data-entry-anim="true"] .hero-copy>*:nth-child(1) { + animation-delay: 90ms; +} + +html[data-entry-anim="true"] .hero-copy>*:nth-child(2) { + animation-delay: calc(90ms + var(--entry-delay-step)); +} + +html[data-entry-anim="true"] .hero-copy>*:nth-child(3) { + animation-delay: calc(90ms + (var(--entry-delay-step) * 2)); +} + +html[data-entry-anim="true"] .hero-copy>*:nth-child(4) { + animation-delay: calc(90ms + (var(--entry-delay-step) * 3)); +} + +html[data-entry-anim="true"] .hero-visual>*:nth-child(1) { + animation-delay: 160ms; +} + +html[data-entry-anim="true"] .hero-visual>*:nth-child(2) { + animation-delay: 260ms; +} + +html[data-entry-anim="true"] .hero-visual>*:nth-child(3) { + animation-delay: 360ms; +} + +html[data-entry-anim="true"] .stats-grid>*:nth-child(1) { + animation-delay: 80ms; +} -html[data-entry-anim="true"] .hero-visual > *:nth-child(1) { animation-delay: 160ms; } -html[data-entry-anim="true"] .hero-visual > *:nth-child(2) { animation-delay: 260ms; } -html[data-entry-anim="true"] .hero-visual > *:nth-child(3) { animation-delay: 360ms; } +html[data-entry-anim="true"] .stats-grid>*:nth-child(2) { + animation-delay: 170ms; +} -html[data-entry-anim="true"] .stats-grid > *:nth-child(1) { animation-delay: 80ms; } -html[data-entry-anim="true"] .stats-grid > *:nth-child(2) { animation-delay: 170ms; } -html[data-entry-anim="true"] .stats-grid > *:nth-child(3) { animation-delay: 260ms; } +html[data-entry-anim="true"] .stats-grid>*:nth-child(3) { + animation-delay: 260ms; +} html[data-entry-anim="true"] .how-section .section-eyebrow, html[data-entry-anim="true"] .features-section .section-eyebrow, -html[data-entry-anim="true"] .form-section .section-eyebrow { animation-delay: 40ms; } +html[data-entry-anim="true"] .form-section .section-eyebrow { + animation-delay: 40ms; +} html[data-entry-anim="true"] .how-section .section-title, html[data-entry-anim="true"] .features-section .section-title, -html[data-entry-anim="true"] .form-section .section-title { animation-delay: 120ms; } +html[data-entry-anim="true"] .form-section .section-title { + animation-delay: 120ms; +} html[data-entry-anim="true"] .how-section .section-sub, html[data-entry-anim="true"] .features-section .section-sub, -html[data-entry-anim="true"] .form-section .section-sub { animation-delay: 200ms; } +html[data-entry-anim="true"] .form-section .section-sub { + animation-delay: 200ms; +} + +html[data-entry-anim="true"] .how-section .step-card:nth-child(1) { + animation-delay: 120ms; +} + +html[data-entry-anim="true"] .how-section .step-card:nth-child(3) { + animation-delay: 210ms; +} + +html[data-entry-anim="true"] .how-section .step-card:nth-child(5) { + animation-delay: 300ms; +} + +html[data-entry-anim="true"] .features-grid>*:nth-child(1) { + animation-delay: 140ms; +} -html[data-entry-anim="true"] .how-section .step-card:nth-child(1) { animation-delay: 120ms; } -html[data-entry-anim="true"] .how-section .step-card:nth-child(3) { animation-delay: 210ms; } -html[data-entry-anim="true"] .how-section .step-card:nth-child(5) { animation-delay: 300ms; } +html[data-entry-anim="true"] .features-grid>*:nth-child(2) { + animation-delay: 230ms; +} -html[data-entry-anim="true"] .features-grid > *:nth-child(1) { animation-delay: 140ms; } -html[data-entry-anim="true"] .features-grid > *:nth-child(2) { animation-delay: 230ms; } -html[data-entry-anim="true"] .features-grid > *:nth-child(3) { animation-delay: 320ms; } +html[data-entry-anim="true"] .features-grid>*:nth-child(3) { + animation-delay: 320ms; +} -html[data-entry-anim="true"] .form-card-outer { animation-delay: 120ms; } +html[data-entry-anim="true"] .form-card-outer { + animation-delay: 120ms; +} -html[data-entry-anim="true"] .form-card form > .form-group, -html[data-entry-anim="true"] .form-card form > .skill-chips-row, -html[data-entry-anim="true"] .form-card form > .form-row, -html[data-entry-anim="true"] .form-card form > .btn-submit { +html[data-entry-anim="true"] .form-card form>.form-group, +html[data-entry-anim="true"] .form-card form>.skill-chips-row, +html[data-entry-anim="true"] .form-card form>.form-row, +html[data-entry-anim="true"] .form-card form>.btn-submit { opacity: 0; transform: translate3d(0, 14px, 0); animation: entryFadeUp 600ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards; will-change: transform, opacity; } -html[data-entry-anim="true"] .form-card form > .form-group:nth-child(1) { animation-delay: 180ms; } -html[data-entry-anim="true"] .form-card form > .skill-chips-row { animation-delay: 250ms; } -html[data-entry-anim="true"] .form-card form > .form-row { animation-delay: 320ms; } -html[data-entry-anim="true"] .form-card form > .btn-submit { animation-delay: 390ms; } +html[data-entry-anim="true"] .form-card form>.form-group:nth-child(1) { + animation-delay: 180ms; +} + +html[data-entry-anim="true"] .form-card form>.skill-chips-row { + animation-delay: 250ms; +} + +html[data-entry-anim="true"] .form-card form>.form-row { + animation-delay: 320ms; +} + +html[data-entry-anim="true"] .form-card form>.btn-submit { + animation-delay: 390ms; +} @media (prefers-reduced-motion: reduce) { + html[data-entry-anim="true"] .navbar, - html[data-entry-anim="true"] .hero-copy > *, - html[data-entry-anim="true"] .hero-visual > *, - html[data-entry-anim="true"] .stats-grid > *, + html[data-entry-anim="true"] .hero-copy>*, + html[data-entry-anim="true"] .hero-visual>*, + html[data-entry-anim="true"] .stats-grid>*, html[data-entry-anim="true"] .skill-strip-inner, html[data-entry-anim="true"] .how-section .section-eyebrow, html[data-entry-anim="true"] .how-section .section-title, @@ -430,15 +518,15 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat html[data-entry-anim="true"] .features-section .section-eyebrow, html[data-entry-anim="true"] .features-section .section-title, html[data-entry-anim="true"] .features-section .section-sub, - html[data-entry-anim="true"] .features-grid > *, + html[data-entry-anim="true"] .features-grid>*, html[data-entry-anim="true"] .form-section .section-eyebrow, html[data-entry-anim="true"] .form-section .section-title, html[data-entry-anim="true"] .form-section .section-sub, html[data-entry-anim="true"] .form-card-outer, - html[data-entry-anim="true"] .form-card form > .form-group, - html[data-entry-anim="true"] .form-card form > .skill-chips-row, - html[data-entry-anim="true"] .form-card form > .form-row, - html[data-entry-anim="true"] .form-card form > .btn-submit { + html[data-entry-anim="true"] .form-card form>.form-group, + html[data-entry-anim="true"] .form-card form>.skill-chips-row, + html[data-entry-anim="true"] .form-card form>.form-row, + html[data-entry-anim="true"] .form-card form>.btn-submit { animation: none; opacity: 1; transform: none; @@ -578,6 +666,7 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat background: rgba(255, 255, 255, 0.11); box-shadow: 0 0 0 3px rgba(79, 110, 247, 0.25); } + #topic-search, #topic-search-mobile { flex: 1; @@ -631,6 +720,7 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat width: 16px; height: 16px; } + .nav-right { display: flex; align-items: center; @@ -682,7 +772,7 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat opacity: 0; transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), - opacity 0.25s ease; + opacity 0.25s ease; } .nav-link:hover, @@ -726,6 +816,188 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat } /* Mobile hamburger button */ +.nav-mobile-toggle { + display: none; + width: 42px; + height: 42px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 5px; + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 10px; + background: rgba(255, 255, 255, 0.08); + cursor: pointer; + padding: 0; + margin-left: auto; + position: relative; + z-index: 1301; + transition: background var(--t), border-color var(--t), transform var(--t); +} + +.nav-mobile-toggle:hover { + background: rgba(255, 255, 255, 0.14); + border-color: rgba(255, 255, 255, 0.35); +} + +.nav-mobile-toggle:focus-visible { + outline: var(--focus-ring-width) solid var(--focus-ring-color); + outline-offset: var(--focus-ring-offset); + border-radius: 10px; +} + +.nav-mobile-toggle span { + display: block; + width: 18px; + height: 2px; + background: #ffffff; + border-radius: 2px; + transition: transform .22s ease, opacity .18s ease; +} + +.nav-mobile-toggle.open span:nth-child(1) { + transform: translateY(7px) rotate(45deg); +} + +.nav-mobile-toggle.open span:nth-child(2) { + opacity: 0; +} + +.nav-mobile-toggle.open span:nth-child(3) { + transform: translateY(-7px) rotate(-45deg); +} + +/* Mobile drawer */ +.nav-mobile-menu { + display: flex; + flex-direction: column; + position: fixed; + top: 0; + right: 0; + left: auto; + width: min(86vw, 340px); + height: 100dvh; + padding: 78px 16px 16px; + background: var(--bg-mobile-menu); + border-left: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: -16px 0 36px rgba(0, 0, 0, 0.3); + z-index: 1300; + overflow-y: auto; + transform: translateX(100%); + opacity: 1; + pointer-events: none; + transition: transform .28s ease; +} + +/* Backdrop */ +.nav-mobile-menu::before { + content: ""; + position: fixed; + inset: 0; + background: var(--bg-mobile-overlay); + opacity: 0; + pointer-events: none; + transition: opacity .25s ease; + z-index: -1; +} + +.nav-mobile-menu.open { + transform: translateX(0); + pointer-events: auto; +} + +.nav-mobile-menu.open::before { + opacity: 1; + pointer-events: auto; +} + +.nav-mobile-link { + position: relative; + font-size: 0.96rem; + font-weight: 600; + color: rgba(255, 255, 255, 0.88); + padding: 11px 10px; + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + border-radius: 10px; + transition: color var(--t), background var(--t); +} + +.nav-mobile-link::after { + content: ''; + position: absolute; + left: 10px; + right: 10px; + bottom: 4px; + height: 2px; + background: linear-gradient(90deg, var(--indigo-500), var(--yellow-400)); + border-radius: 999px; + transform: scaleX(0); + transform-origin: left; + opacity: 0; + transition: transform .3s ease, opacity .2s ease; +} + +.nav-mobile-link:hover, +.nav-mobile-link.active { + color: #ffffff; + background: rgba(255, 255, 255, 0.06); + text-decoration: none; +} + +.nav-mobile-link:hover::after, +.nav-mobile-link.active::after { + transform: scaleX(1); + opacity: 1; +} + +.nav-mobile-link:focus-visible { + outline: var(--focus-ring-width) solid var(--focus-ring-color); + outline-offset: var(--focus-ring-offset); + border-radius: var(--r-xs); +} + +.nav-mobile-link:last-child { + border-bottom: none; +} + +/* Mobile search in drawer */ +.navbar-search.navbar-search--mobile { + width: 100%; + max-width: 100%; + margin: 0 0 14px 0; + align-self: stretch; + flex-direction: row-reverse; +} + +.navbar-search--mobile .navbar-search-btn { + width: 36px; + height: 36px; + margin: 2px; + padding: 0; + border-radius: 999px; + background: rgba(255, 255, 255, 0.12); + border: none; + color: rgba(255, 255, 255, 0.9); +} + +.nav-mobile-menu .theme-toggle { + display: flex; + align-items: center; + gap: 10px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.12); + text-align: left; + cursor: pointer; + width: 100%; + padding: 10px 12px; + font-size: 0.95rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + border-radius: 10px; + margin-top: 8px; +} + +/* Mobile hamburger button .nav-mobile-toggle { display: none; flex-direction: column; @@ -769,10 +1041,10 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat display: none; flex-direction: column; position: fixed; - top: 0; + top: 64px; + height: calc(100vh - 64px); left: 0; width: 100%; - height: 100vh; padding: 100px 32px 20px; background: var(--indigo-900); @@ -795,7 +1067,7 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat } /* Animated underline for mobile links */ -.nav-mobile-link::after { +/* .nav-mobile-link::after { content: ''; position: absolute; left: 0; @@ -833,7 +1105,7 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat .nav-mobile-link:last-child { border-bottom: none; -} +} /* Responsive navbar — tighten before switching to mobile menu */ @media (max-width: 1100px) { @@ -871,11 +1143,10 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat } .navbar-search.navbar-search--mobile { - flex: 0 0 auto; - width: 100%; - max-width: 100%; - margin-left: 0; - align-self: stretch; + flex: none !important; + width: 100% !important; + max-width: 100% !important; + margin: 0 0 14px 0 !important; flex-direction: row-reverse; } @@ -919,11 +1190,16 @@ html[data-entry-anim="true"] .form-card form > .btn-submit { animat } /* Mobile toggle label: CSS content updates automatically without JS */ -.theme-toggle-label { font-size: 0; } /* hide raw text node */ +.theme-toggle-label { + font-size: 0; +} + +/* hide raw text node */ .theme-toggle-label::before { content: "Dark Mode"; font-size: 0.95rem; } + html[data-theme="dark"] .theme-toggle-label::before { content: "Light Mode"; } @@ -1157,9 +1433,17 @@ html[data-theme="dark"] .theme-toggle-label::before { flex-shrink: 0; } -.hvm-dot--red { background: #ff5f56; } -.hvm-dot--yellow { background: #ffbd2e; } -.hvm-dot--green { background: #27c93f; } +.hvm-dot--red { + background: #ff5f56; +} + +.hvm-dot--yellow { + background: #ffbd2e; +} + +.hvm-dot--green { + background: #27c93f; +} .hvm-filename { font-family: var(--font-mono); @@ -1178,15 +1462,35 @@ html[data-theme="dark"] .theme-toggle-label::before { line-height: 1.85; } -.hvm-line { display: block; color: #e6edf3; } -.hvm-indent { padding-left: 20px; } -.hvm-indent2 { padding-left: 40px; } +.hvm-line { + display: block; + color: #e6edf3; +} + +.hvm-indent { + padding-left: 20px; +} + +.hvm-indent2 { + padding-left: 40px; +} /* Code syntax colours — always on dark background, no theming needed */ -.hvm-kw { color: #ff7b72; } -.hvm-fn { color: #d2a8ff; } -.hvm-cm { color: #8b949e; } -.hvm-op { color: #79c0ff; } +.hvm-kw { + color: #ff7b72; +} + +.hvm-fn { + color: #d2a8ff; +} + +.hvm-cm { + color: #8b949e; +} + +.hvm-op { + color: #79c0ff; +} /* Hero decorative blobs */ .hero-blob { @@ -1256,9 +1560,20 @@ html[data-theme="dark"] .theme-toggle-label::before { flex-shrink: 0; } -.stat-icon--indigo { background: var(--indigo-100); color: var(--indigo-600); } -.stat-icon--green { background: var(--green-100); color: var(--green-500); } -.stat-icon--yellow { background: var(--yellow-100); color: var(--orange-500); } +.stat-icon--indigo { + background: var(--indigo-100); + color: var(--indigo-600); +} + +.stat-icon--green { + background: var(--green-100); + color: var(--green-500); +} + +.stat-icon--yellow { + background: var(--yellow-100); + color: var(--orange-500); +} .stat-info { display: flex; @@ -1388,11 +1703,11 @@ html[data-theme="dark"] .theme-toggle-label::before { padding: 6px 14px; box-shadow: 0 1px 2px rgba(15, 21, 96, 0.04); transition: - transform 180ms ease, - background 180ms ease, - color 180ms ease, - border-color 180ms ease, - box-shadow 180ms ease; + transform 180ms ease, + background 180ms ease, + color 180ms ease, + border-color 180ms ease, + box-shadow 180ms ease; } .ss-item:hover { @@ -1435,8 +1750,13 @@ html[data-theme="dark"] .theme-toggle-label::before { } @keyframes skill-strip-marquee { - from { transform: translateX(0); } - to { transform: translateX(-50%); } + from { + transform: translateX(0); + } + + to { + transform: translateX(-50%); + } } .ss-item-pink { @@ -1450,8 +1770,11 @@ html[data-theme="dark"] .theme-toggle-label::before { } @media (prefers-reduced-motion: reduce) { + /* Universal reduced-motion reset: instantly disables high-impact transitions and structural animations */ - *, ::before, ::after { + *, + ::before, + ::after { animation-delay: -1ms !important; animation-duration: 1ms !important; animation-iteration-count: 1 !important; @@ -1598,9 +1921,17 @@ html[data-theme="dark"] .theme-toggle-label::before { box-shadow: var(--shadow-md); } -.feature-card--pink { background: var(--pink-100); } -.feature-card--yellow { background: var(--yellow-100); } -.feature-card--purple { background: var(--purple-100); } +.feature-card--pink { + background: var(--pink-100); +} + +.feature-card--yellow { + background: var(--yellow-100); +} + +.feature-card--purple { + background: var(--purple-100); +} .feature-card-icon { width: 52px; @@ -1614,9 +1945,17 @@ html[data-theme="dark"] .theme-toggle-label::before { box-shadow: var(--shadow-xs); } -.feature-card--pink .feature-card-icon { color: var(--pink-500); } -.feature-card--yellow .feature-card-icon { color: var(--orange-500); } -.feature-card--purple .feature-card-icon { color: var(--purple-600); } +.feature-card--pink .feature-card-icon { + color: var(--pink-500); +} + +.feature-card--yellow .feature-card-icon { + color: var(--orange-500); +} + +.feature-card--purple .feature-card-icon { + color: var(--purple-600); +} .feature-card h3 { font-family: var(--font-display); @@ -1834,6 +2173,7 @@ label { opacity: 0; transform: translateY(-8px); } + to { opacity: 1; transform: translateY(0); @@ -1911,10 +2251,22 @@ label { } /* Scrollbar styling for suggestions dropdown */ -.skills-suggestions::-webkit-scrollbar { width: 6px; } -.skills-suggestions::-webkit-scrollbar-track { background: var(--gray-50); } -.skills-suggestions::-webkit-scrollbar-thumb { background: var(--indigo-500); border-radius: 3px; } -.skills-suggestions::-webkit-scrollbar-thumb:hover { background: var(--indigo-600); } +.skills-suggestions::-webkit-scrollbar { + width: 6px; +} + +.skills-suggestions::-webkit-scrollbar-track { + background: var(--gray-50); +} + +.skills-suggestions::-webkit-scrollbar-thumb { + background: var(--indigo-500); + border-radius: 3px; +} + +.skills-suggestions::-webkit-scrollbar-thumb:hover { + background: var(--indigo-600); +} /* Quick-select chips row */ .skill-chips-row { @@ -2185,8 +2537,13 @@ body.dark-theme .btn-clear:hover { } @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + + to { + opacity: 1; + } } /* Loading state UI (button spinner) */ @@ -2207,7 +2564,9 @@ body.dark-theme .btn-clear:hover { } @keyframes spin { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } /* ============================================================= @@ -2240,14 +2599,23 @@ body.dark-theme .btn-clear:hover { animation: dotBounce 1.2s ease-in-out infinite; } -.loading-dots span:nth-child(2) { animation-delay: 0.2s; } -.loading-dots span:nth-child(3) { animation-delay: 0.4s; } +.loading-dots span:nth-child(2) { + animation-delay: 0.2s; +} + +.loading-dots span:nth-child(3) { + animation-delay: 0.4s; +} @keyframes dotBounce { - 0%, 80%, 100% { + + 0%, + 80%, + 100% { transform: scale(0.6); opacity: 0.4; } + 40% { transform: scale(1.1); opacity: 1; @@ -2259,6 +2627,7 @@ body.dark-theme .btn-clear:hover { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); @@ -2415,11 +2784,30 @@ body.dark-theme .btn-clear:hover { border-radius: var(--r-full); } -.project-tag--skill { background: var(--indigo-100); color:white; } -.project-tag--time { background: var(--gray-100); color: var(--gray-600); } -.project-tag--beginner { background: var(--green-100); color: #065f46; } -.project-tag--intermediate { background: var(--yellow-100); color: #78350f; } -.project-tag--advanced { background: #fee2e2; color: #7f1d1d; } +.project-tag--skill { + background: var(--indigo-100); + color: var(--indigo-700); +} + +.project-tag--time { + background: var(--gray-100); + color: var(--gray-600); +} + +.project-tag--beginner { + background: var(--green-100); + color: #065f46; +} + +.project-tag--intermediate { + background: var(--yellow-100); + color: #78350f; +} + +.project-tag--advanced { + background: #fee2e2; + color: #7f1d1d; +} .project-card-footer { display: grid; @@ -2792,9 +3180,20 @@ body.dark-theme .btn-clear:hover { border-radius: var(--r-full); } -.detail-tag--level { background: rgba(255, 255, 255, 0.12); color: rgba(255, 255, 255, 0.85); } -.detail-tag--interest { background: rgba(6, 182, 212, 0.18); color: #a7f3f9; } -.detail-tag--time { background: rgba(251, 191, 36, 0.18); color: var(--yellow-300); } +.detail-tag--level { + background: rgba(255, 255, 255, 0.12); + color: rgba(255, 255, 255, 0.85); +} + +.detail-tag--interest { + background: rgba(6, 182, 212, 0.18); + color: #a7f3f9; +} + +.detail-tag--time { + background: rgba(251, 191, 36, 0.18); + color: var(--yellow-300); +} .detail-title { font-family: var(--font-display); @@ -3000,67 +3399,71 @@ body.dark-theme .btn-clear:hover { margin: 4px 0; } -.roadmap-step:last-child .roadmap-line { display: none; } +.roadmap-step:last-child .roadmap-line { + display: none; +} + /* Roadmap timeline structure */ -.roadmap-timeline{ - display:flex; - flex-direction:column; - width:100%; - padding:0; - margin:0; +.roadmap-timeline { + display: flex; + flex-direction: column; + width: 100%; + padding: 0; + margin: 0; } -.roadmap-step{ - display:flex; - width:100%; - list-style:none; +.roadmap-step { + display: flex; + width: 100%; + list-style: none; } -.roadmap-marker{ - display:flex; - flex-direction:column; - align-items:center; - flex-shrink:0; - width:36px; +.roadmap-marker { + display: flex; + flex-direction: column; + align-items: center; + flex-shrink: 0; + width: 36px; } -.roadmap-dot{ - width:14px; - height:14px; - border-radius:50%; - background:var(--surface); - border:3px solid var(--indigo-600); - margin-top:4px; - box-sizing:border-box; +.roadmap-dot { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--surface); + border: 3px solid var(--indigo-600); + margin-top: 4px; + box-sizing: border-box; } -.roadmap-line{ - flex:1; - width:0; - border-left:2px dashed var(--border); - background:transparent; - margin:4px 0; +.roadmap-line { + flex: 1; + width: 0; + border-left: 2px dashed var(--border); + background: transparent; + margin: 4px 0; } -.roadmap-step:last-child .roadmap-line{ - display:none; +.roadmap-step:last-child .roadmap-line { + display: none; } -.roadmap-content{ - padding:0 0 28px 16px; - flex:1; + +.roadmap-content { + padding: 0 0 28px 16px; + flex: 1; } /* keep checkbox row aligned */ -.roadmap-step-label{ - display:flex; - align-items:flex-start; - gap:12px; +.roadmap-step-label { + display: flex; + align-items: flex-start; + gap: 12px; } -.roadmap-text-wrap{ - display:flex; - flex-direction:column; - gap:6px; +.roadmap-text-wrap { + display: flex; + flex-direction: column; + gap: 6px; } .roadmap-step:last-child .roadmap-line { @@ -3120,47 +3523,46 @@ body.dark-theme .btn-clear:hover { } /* Progress container */ -.roadmap-progress-wrap{ - display:flex; - align-items:center; - gap:14px; - width:100%; - margin:18px 0 28px; +.roadmap-progress-wrap { + display: flex; + align-items: center; + gap: 14px; + width: 100%; + margin: 18px 0 28px; } /* Actual bar */ -.roadmap-progress-bar{ - flex:1; - width:100%; - max-width:100%; - height:10px; - background:#e5e7eb; - border-radius:999px; - overflow:hidden; - border:1px solid #d1d5db; +.roadmap-progress-bar { + flex: 1; + width: 100%; + max-width: 100%; + height: 10px; + background: #e5e7eb; + border-radius: 999px; + overflow: hidden; + border: 1px solid #d1d5db; } /* Fill animation */ -#roadmap-progress-fill{ - height:100%; - width:0%; - border-radius:999px; - background:linear-gradient( - 90deg, - #4f46e5, - #9333ea - ); - transition:width .35s ease; +#roadmap-progress-fill { + height: 100%; + width: 0%; + border-radius: 999px; + background: linear-gradient(90deg, + #4f46e5, + #9333ea); + transition: width .35s ease; } /* Percentage text */ -#roadmap-progress-text{ - white-space:nowrap; - min-width:120px; - font-size:.85rem; - font-weight:600; - color:#6b7280; +#roadmap-progress-text { + white-space: nowrap; + min-width: 120px; + font-size: .85rem; + font-weight: 600; + color: #6b7280; } + /* Resources list */ .resource-list { display: flex; @@ -3283,9 +3685,17 @@ body.dark-theme .btn-clear:hover { } /* Difficulty colours — light theme */ -.stats-value--beginner { color: #065f46; } -.stats-value--intermediate { color: #78350f; } -.stats-value--advanced { color: #7f1d1d; } +.stats-value--beginner { + color: #065f46; +} + +.stats-value--intermediate { + color: #78350f; +} + +.stats-value--advanced { + color: #7f1d1d; +} .sidebar-card-desc { font-size: 0.85rem; @@ -3610,7 +4020,7 @@ body.dark-theme .btn-clear:hover { color: #ffffff; font-size: 0.8rem; font-weight: 700; - text-shadow: 0 1px 2px rgba(0,0,0,0.4); + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); transition: width 0.4s ease; } @@ -4101,13 +4511,16 @@ body.dark-theme .btn-clear:hover { accidental overlap on mid-sized viewports ============================================================= */ .hero-visual { - gap: 18px; /* slightly larger gap to avoid tight stacking */ + gap: 18px; + /* slightly larger gap to avoid tight stacking */ } .hero-visual-main { position: relative; - z-index: 3; /* keep code preview above floating cards when necessary */ - min-height: 220px; /* ensure preview has vertical space on narrow viewports */ + z-index: 3; + /* keep code preview above floating cards when necessary */ + min-height: 220px; + /* ensure preview has vertical space on narrow viewports */ } .hero-visual-card { @@ -4119,12 +4532,14 @@ body.dark-theme .btn-clear:hover { can sometimes visually overlap hero content at certain widths. This override is scoped to the immediate following section to minimize layout changes elsewhere. */ -.hero + .stats-section { +.hero+.stats-section { margin-top: -20px; } @media (max-width: 1024px) { - .hero-visual-main { min-height: 260px; } + .hero-visual-main { + min-height: 260px; + } } @@ -4378,8 +4793,13 @@ html[data-theme="dark"] .skill-chip-selected { border-color: rgba(99, 102, 241, 0.3); } -html[data-theme="dark"] .skill-chip-remove { color: #a5b4fc; } -html[data-theme="dark"] .skill-chip-remove:hover { color: #f87171; } +html[data-theme="dark"] .skill-chip-remove { + color: #a5b4fc; +} + +html[data-theme="dark"] .skill-chip-remove:hover { + color: #f87171; +} /* Quick-pick chips (available row) */ html[data-theme="dark"] .skill-chip { @@ -4396,14 +4816,33 @@ html[data-theme="dark"] .skill-chip.active { } /* Difficulty colours — light green/amber/red unreadable on dark surfaces */ -html[data-theme="dark"] .stats-value--beginner { color: #6ee7b7; } -html[data-theme="dark"] .stats-value--intermediate { color: #fcd34d; } -html[data-theme="dark"] .stats-value--advanced { color: #fca5a5; } +html[data-theme="dark"] .stats-value--beginner { + color: #6ee7b7; +} + +html[data-theme="dark"] .stats-value--intermediate { + color: #fcd34d; +} + +html[data-theme="dark"] .stats-value--advanced { + color: #fca5a5; +} /* Project tag level variants */ -html[data-theme="dark"] .project-tag--beginner { color: #6ee7b7; background: #052e16; } -html[data-theme="dark"] .project-tag--intermediate { color: #fcd34d; background: #3b2a00; } -html[data-theme="dark"] .project-tag--advanced { color: #fca5a5; background: #3b0f0f; } +html[data-theme="dark"] .project-tag--beginner { + color: #6ee7b7; + background: #052e16; +} + +html[data-theme="dark"] .project-tag--intermediate { + color: #fcd34d; + background: #3b2a00; +} + +html[data-theme="dark"] .project-tag--advanced { + color: #fca5a5; + background: #3b0f0f; +} /* Sidebar code card gradient — adjusted for dark surfaces */ html[data-theme="dark"] .sidebar-card--code { @@ -4418,6 +4857,10 @@ html[data-theme="dark"] .btn-view-code-sm { border-color: rgba(99, 102, 241, 0.4); } +#scroll-top-btn.visible { + display: flex; +} + /* DARK MODE */ body.dark-theme { background: #0b1120; @@ -4427,12 +4870,12 @@ body.dark-theme { /* NAVBAR */ body.dark-theme .navbar { background: rgba(10, 15, 35, 0.96); - border-bottom: 1px solid rgba(255,255,255,0.06); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); } body.dark-theme .nav-link, body.dark-theme .nav-mobile-link { - color: rgba(255,255,255,0.82); + color: rgba(255, 255, 255, 0.82); } body.dark-theme .nav-link:hover, @@ -4462,14 +4905,12 @@ body.dark-theme .nav-mobile-menu { body.dark-theme .hero { background: radial-gradient(circle at top left, - rgba(99,102,241,0.22), + rgba(99, 102, 241, 0.22), transparent 32%), - linear-gradient( - 135deg, + linear-gradient(135deg, #0f172a 0%, #1e1b4b 55%, - #4c1d95 100% - ); + #4c1d95 100%); } body.dark-theme .hero-heading { @@ -4477,25 +4918,25 @@ body.dark-theme .hero-heading { } body.dark-theme .hero-subtext { - color: rgba(255,255,255,0.78); + color: rgba(255, 255, 255, 0.78); } /* hero buttons */ body.dark-theme .btn-hero-ghost { color: white; - border-color: rgba(255,255,255,0.16); - background: rgba(255,255,255,0.06); + border-color: rgba(255, 255, 255, 0.16); + background: rgba(255, 255, 255, 0.06); } body.dark-theme .btn-hero-ghost:hover { - background: rgba(255,255,255,0.12); + background: rgba(255, 255, 255, 0.12); } /* hero floating cards */ body.dark-theme .hero-visual-card { - background: rgba(255,255,255,0.96); + background: rgba(255, 255, 255, 0.96); } body.dark-theme .hvc-title { @@ -4518,7 +4959,7 @@ body.dark-theme .stats-section { body.dark-theme .skill-strip { background: #111827; - border-color: rgba(255,255,255,0.05); + border-color: rgba(255, 255, 255, 0.05); } /* @@ -4538,7 +4979,7 @@ body.dark-theme .footer-bottom-links a { } body.dark-theme .section-eyebrow { - background: rgba(255,255,255,0.08); + background: rgba(255, 255, 255, 0.08); color: #a5b4fc; } @@ -4548,7 +4989,7 @@ body.dark-theme .stat-card, body.dark-theme .step-card, body.dark-theme .form-card { background: #172033; - border: 1px solid rgba(255,255,255,0.06); + border: 1px solid rgba(255, 255, 255, 0.06); } /* @@ -4570,28 +5011,20 @@ body.dark-theme .feature-card h3 { } body.dark-theme .feature-card p { - color: rgba(255,255,255,0.8); + color: rgba(255, 255, 255, 0.8); } -body.dark-theme .feature-card-link, -[data-theme="dark"] .feature-card-link { +body.dark-theme .feature-card-link { background: rgba(255, 255, 255, 0.08); color: white; border-color: rgba(255, 255, 255, 0.14); } -body.dark-theme .feature-card-link:hover, -[data-theme="dark"] .feature-card-link:hover { - background: rgba(255, 255, 255, 0.16); - border-color: rgba(255, 255, 255, 0.3); - color: white; -} - /* ABOUT SECTION */ body.dark-theme .about-card { background: #172033; - border: 1px solid rgba(255,255,255,0.08); + border: 1px solid rgba(255, 255, 255, 0.08); } body.dark-theme .about-card h3, @@ -4602,7 +5035,7 @@ body.dark-theme .about-card p { /* FORMS */ body.dark-theme label { - color: rgba(255,255,255,0.9); + color: rgba(255, 255, 255, 0.9); } body.dark-theme input, @@ -4611,24 +5044,24 @@ body.dark-theme textarea, body.dark-theme .skill-input-wrap { background: #111827; color: white; - border-color: rgba(255,255,255,0.08); + border-color: rgba(255, 255, 255, 0.08); } body.dark-theme input::placeholder, body.dark-theme textarea::placeholder { - color: rgba(255,255,255,0.45); + color: rgba(255, 255, 255, 0.45); } /* dropdown */ body.dark-theme .skills-suggestions { background: #172033; - border-color: rgba(255,255,255,0.08); + border-color: rgba(255, 255, 255, 0.08); } body.dark-theme .suggestion-item { color: white; - border-bottom-color: rgba(255,255,255,0.04); + border-bottom-color: rgba(255, 255, 255, 0.04); } body.dark-theme .suggestion-item:hover, @@ -4639,12 +5072,12 @@ body.dark-theme .suggestion-item--active { /* github button */ body.dark-theme #btn-show-github { - background: rgba(255,255,255,0.08); + background: rgba(255, 255, 255, 0.08); color: white; } body.dark-theme #btn-show-github:hover { - background: rgba(255,255,255,0.14); + background: rgba(255, 255, 255, 0.14); } /* @@ -4654,7 +5087,7 @@ body.dark-theme .footer { } body.dark-theme .footer-inner { - border-color: rgba(255,255,255,0.06); + border-color: rgba(255, 255, 255, 0.06); } body.dark-theme .footer-links-list a:hover, @@ -4666,11 +5099,11 @@ body.dark-theme .footer-bottom-links a:hover { body.dark-theme .skill-strip-label, body.dark-theme .ss-item { - color: rgba(255,255,255,0.82); + color: rgba(255, 255, 255, 0.82); } body.dark-theme .ss-sep { - background: rgba(255,255,255,0.22); + background: rgba(255, 255, 255, 0.22); } /* @@ -4805,11 +5238,11 @@ body.dark-theme .theme-toggle:hover { --text-body: var(--gray-600); --text-light: var(--gray-400); --border: var(--gray-200); - --shadow-xs: 0 1px 2px rgba(0,0,0,0.3); - --shadow-sm: 0 2px 8px rgba(0,0,0,0.4); - --shadow-md: 0 4px 20px rgba(0,0,0,0.5); - --shadow-lg: 0 8px 40px rgba(0,0,0,0.6); - --shadow-xl: 0 16px 60px rgba(0,0,0,0.7); + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4); + --shadow-md: 0 4px 20px rgba(0, 0, 0, 0.5); + --shadow-lg: 0 8px 40px rgba(0, 0, 0, 0.6); + --shadow-xl: 0 16px 60px rgba(0, 0, 0, 0.7); --indigo-100: #1a1f3a; --indigo-50: #141830; --purple-100: #1e1335; @@ -5369,6 +5802,7 @@ body.dark-theme .theme-toggle:hover { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; } + /* Redesigned Stats Dashboard */ .stats-dashboard-grid { display: grid; @@ -5381,14 +5815,14 @@ body.dark-theme .theme-toggle:hover { background: var(--card-bg); border-radius: 1.25rem; padding: 1.25rem; - box-shadow: 0 1px 3px rgba(0,0,0,0.05); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); border: 1px solid var(--border); transition: transform 0.2s ease, box-shadow 0.2s ease; } .stat-dashboard-card:hover { transform: translateY(-2px); - box-shadow: 0 12px 24px -12px rgba(0,0,0,0.15); + box-shadow: 0 12px 24px -12px rgba(0, 0, 0, 0.15); } .stat-dashboard-header { @@ -5646,12 +6080,12 @@ body.dark-theme .theme-toggle:hover { grid-template-columns: 1fr; gap: 1rem; } - + .profile-bottom-grid { grid-template-columns: 1fr; gap: 1rem; } - + .activity-stats-row { grid-template-columns: repeat(4, 1fr); } @@ -5675,70 +6109,5 @@ body.dark-theme .theme-toggle:hover { color: #c4b5fd; } -.project-match-explanation { - display: flex; - align-items: flex-start; - gap: 6px; - font-size: 0.85rem; - color: var(--text-color); - opacity: 0.85; - background: var(--bg-hover); - padding: 10px 12px; - border-radius: 6px; - border-left: 3px solid var(--primary-color); - margin-top: 15px; - margin-bottom: 15px; - line-height: 1.4; -} -.explanation-icon { - flex-shrink: 0; - margin-top: 2px; - color: var(--primary-color); -} - -.project-match-score { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 10px; - font-size: 0.8rem; -} -.score-label { - font-weight: 600; - color: var(--text-body); - white-space: nowrap; -} -.score-value { - font-weight: 700; - color: var(--accent); - white-space: nowrap; -} -.score-bar { - flex: 1; - height: 6px; - background: var(--border); - border-radius: 3px; - overflow: hidden; -} -.score-bar-fill { - height: 100%; - background: var(--accent); - border-radius: 3px; - transition: width 0.4s ease; -} - -/* Surprise Me Button Styling */ -.btn-surprise { - background-color: #6b21a8; - color: #ffffff; - padding: 0.5rem 1rem; - border: none; - border-radius: 0.375rem; - font-weight: 600; - cursor: pointer; - transition: background-color 0.2s ease-in-out; - margin-left: 0.5rem; -} -.btn-surprise:hover { - background-color: #581c87; -} +/* ======================================== + SKILLS SECTION - LIGHT & DARK THEME diff --git a/src/templates/index.html b/src/templates/index.html index 7098c8fe..cddca091 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -7,9 +7,9 @@ DevPath — Find Projects Based On Your Skills - + + {% include 'partials/theme_head.html' %} @@ -62,13 +63,13 @@ - +