diff --git a/css/styles.css b/css/styles.css index c7059b2..0903ba7 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1,105 +1,528 @@ -#bg { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - object-fit: cover; - z-index: -100; - opacity: 90%; -} - -/* custom 3 buttons */ -.button { - border: none; - background: #4285f4; - outline: solid 1px #4285f4; - color: #fff; - text-transform: uppercase; - border-radius: 5px; - box-shadow: 0px 17px 10px -10px rgba(0, 0, 0, 0.4); - cursor: pointer; - -webkit-transition: all ease-in-out 300ms; - transition: all ease-in-out 300ms; -} - -.button:hover { - color: #4285f4; - outline: solid #4285f4; - box-shadow: 0px 37px 20px -20px rgba(0, 0, 0, 0.2); - -webkit-transform: translate(0px, -10px) scale(1.2); - transform: translate(0px, -10px) scale(1.2); -} - -/* custom containers */ -.sub-container-styles { - background-color: rgba(0, 0, 0, 0.8); - border-radius: 5px; - padding: 10px; - margin-bottom: 20px; - margin-left: 5px; - margin-right: 5px; -} - -.subject-container { - margin-bottom: 20px; -} - -.subject-container h5 { - background-color: #0047AB; - border-radius: 5px; - padding: 5px 0px 5px 15px; - color: rgb(143, 219, 255); -} - -.subject-container span { - display: inline-block; - padding-left: 10px; - font-size: 12px; - color: #0b1b2f -} - -.subject-container a { - text-decoration: none; - color: #0196f3; - font-style: normal; - transition: color 0.2s ease; -} - -.subject-container a:hover { - color: white; - transition: color 0.2s ease; -} - -.subject-container ul { - list-style-type: none; -} - -.subject-container li { - margin-top: 5px; - color: #0083d4; -} - -/* custom scrollbar */ -*::-webkit-scrollbar { - width: 5px; -} - -*::-webkit-scrollbar-track { - background: #00000025; -} - -*::-webkit-scrollbar-thumb { - background-color: rgba(150, 150, 150, 0.5); - border-radius: 5px; -} - -/* loader */ -#loading-container{ - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 200; -} \ No newline at end of file + + /* --- Theme Variables --- */ + :root { + --bg-main: #0a0a0a; + --bg-card: #0f0f0f; + --bg-card-inner: #161616; + --border-color: #262626; + --border-hover: #404040; + --text-primary: #ededed; + --text-secondary: #8a8a8a; + + --dock-bg: rgba(20, 20, 20, 0.65); + --dock-border: rgba(255, 255, 255, 0.1); + --dock-text: rgba(255, 255, 255, 0.6); + --dock-hover-text: #fff; + --item-hover: rgba(255, 255, 255, 0.1); + --folder-color: #8a8a8a; + + --loader-bg: rgba(10, 10, 10, 0.92); + --loader-track: rgba(255, 255, 255, 0.12); + --loader-spin: #4285F4; + } + + .light-theme { + --bg-main: #f9f9f9; + --bg-card: #ffffff; + --bg-card-inner: #f0f0f0; + --border-color: #e5e5e5; + --border-hover: #cccccc; + --text-primary: #111111; + --text-secondary: #666666; + + --dock-bg: rgba(255, 255, 255, 0.75); + --dock-border: rgba(0, 0, 0, 0.1); + --dock-text: rgba(0, 0, 0, 0.6); + --dock-hover-text: #000; + --item-hover: rgba(0, 0, 0, 0.05); + --folder-color: #666666; + + --loader-bg: rgba(249, 249, 249, 0.92); + --loader-track: rgba(0, 0, 0, 0.1); + --loader-spin: #0066cc; + } + + body, html { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + font-family: 'Inter', sans-serif; + background-color: var(--bg-main); + background-image: url('../images/bg-main.jpg'); + background-size: cover; + background-position: center; + background-attachment: fixed; + color: var(--text-primary); + transition: background-color 0.3s, color 0.3s; + overflow-x: hidden; + scrollbar-width: none; + -ms-overflow-style: none; + } + + body.light-theme { + background-image: url('../images/bg-light.png'); + } + + /* Blurred background overlay while viewing a semester */ + body::before { + content: ''; + position: fixed; + inset: -20px; + z-index: -1; + background-image: url('../images/bg-main.jpg'); + background-size: cover; + background-position: center; + filter: blur(14px); + opacity: 0; + transition: opacity 0.4s ease; + pointer-events: none; + } + body.viewing-semester::before { + opacity: 1; + } + body.light-theme::before { + background-image: url('../images/bg-light.png'); + } + + /* Dark scrim to hide the background while viewing a semester */ + body::after { + content: ''; + position: fixed; + inset: 0; + z-index: -1; + background: rgba(10, 10, 10, 0.85); + opacity: 0; + transition: opacity 0.4s ease; + pointer-events: none; + } + body.light-theme::after { + background: rgba(249, 249, 249, 0.9); + } + body.viewing-semester::after { + opacity: 1; + } + + /* --- Top Bar --- */ + .top-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 44px; + z-index: 60; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 0 16px; + background: transparent; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + transition: background 0.3s; + } + + .top-bar-left { + display: flex; + align-items: center; + gap: 8px; + text-decoration: none; + color: #fff; + font-size: 13px; + font-weight: 600; + padding: 5px 10px; + border-radius: 8px; + transition: background 0.2s; + white-space: nowrap; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8); + } + .top-bar-left:hover { background: rgba(255, 255, 255, 0.1); } + .top-bar-left i { + font-size: 17px; + color: #fff; + } + + .top-bar-right { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: rgba(255, 255, 255, 0.75); + white-space: nowrap; + overflow: hidden; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8); + } + .top-bar-right .contrib-label { + color: rgba(255, 255, 255, 0.6); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.04em; + } + .top-bar-right .contrib-name { + color: #fff; + font-weight: 500; + } + .top-bar-right .contrib-link { + text-decoration: none; + transition: color 0.2s; + } + .top-bar-right .contrib-link:hover { + color: #4285F4; + text-decoration: underline; + } + .top-bar-right .contrib-sep { + color: rgba(255, 255, 255, 0.5); + } + + .light-theme .top-bar { + border-bottom-color: rgba(0, 0, 0, 0.1); + } + .light-theme .top-bar-left, + .light-theme .top-bar-left i { + color: #111; + text-shadow: none; + } + .light-theme .top-bar-left:hover { + background: rgba(0, 0, 0, 0.05); + } + .light-theme .top-bar-right { + color: rgba(0, 0, 0, 0.7); + text-shadow: none; + } + .light-theme .top-bar-right .contrib-label { + color: rgba(0, 0, 0, 0.5); + } + .light-theme .top-bar-right .contrib-name { + color: #111; + } + .light-theme .top-bar-right .contrib-sep { + color: rgba(0, 0, 0, 0.4); + } + + /* --- View Transitions --- */ + .view-section { + display: none; + opacity: 0; + animation: fadeIn 0.4s ease-out forwards; + } + + .view-section.active { + display: flex; + } + + @keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } + } + + /* --- Framer Style Cards --- */ + .framer-card { + background-color: var(--bg-card); + border: 1px solid var(--border-color); + transition: all 0.2s ease; + } + + .framer-card:hover { + border-color: var(--border-hover); + transform: translateY(-2px); + } + + .framer-icon-box { + background-color: var(--bg-card-inner); + transition: background-color 0.2s ease; + } + + .framer-card:hover .framer-icon-box { + background-color: rgba(255,255,255,0.03); + } + .light-theme .framer-card:hover .framer-icon-box { + background-color: rgba(0,0,0,0.02); + } + + .link-card { + background-color: var(--bg-card); + border: 1px solid var(--border-color); + transition: all 0.2s ease; + } + + .link-card:hover { + background-color: var(--bg-card-inner); + border-color: var(--border-hover); + } + + /* Hide scrollbars but keep scroll functionality */ + ::-webkit-scrollbar { width: 0; height: 0; display: none; } + ::-webkit-scrollbar-track, ::-webkit-scrollbar-thumb { background: transparent; } + + /* --- Mac Desktop Icon --- */ + .mac-desktop-icon { + position: absolute; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + width: 80px; + cursor: pointer; + transition: transform 0.2s; + } + .mac-desktop-icon:active { + transform: scale(0.95); + } + .mac-desktop-icon-img { + width: 64px; + height: 64px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.2s; + } + .mac-desktop-icon:hover .mac-desktop-icon-img { + background-color: rgba(255, 255, 255, 0.1); + } + .light-theme .mac-desktop-icon:hover .mac-desktop-icon-img { + background-color: rgba(0, 0, 0, 0.05); + } + .mac-desktop-label { + font-size: 11px; + font-weight: 500; + text-align: center; + padding: 2px 6px; + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.3); + color: white; + text-shadow: 0 1px 2px rgba(0,0,0,0.8); + transition: background-color 0.2s; + } + .mac-desktop-icon:hover .mac-desktop-label { + background-color: #0066cc; + } + .light-theme .mac-desktop-label { + background-color: transparent; + color: #111; + text-shadow: none; + } + .light-theme .mac-desktop-icon:hover .mac-desktop-label { + background-color: #0066cc; + color: white; + } + + /* --- Dock Styles --- */ + .dock-container { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 8px; + padding: 10px 14px; + border-radius: 24px; + z-index: 50; + background: var(--dock-bg); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid var(--dock-border); + box-shadow: 0 10px 30px rgba(0,0,0,0.3); + transition: background 0.3s; + } + + .dock-item { + width: 44px; + height: 44px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.4rem; + color: var(--dock-text); + transition: transform 0.2s, color 0.2s, background 0.2s; + background: transparent; + position: relative; + border: none; + cursor: pointer; + } + + .dock-item:hover { + transform: translateY(-5px) scale(1.1); + color: var(--dock-hover-text); + background: var(--item-hover); + } + + .dock-divider { + width: 1px; + height: 28px; + background: var(--dock-border); + align-self: center; + margin: 0 4px; + } + + .dock-item::after { + content: attr(data-tooltip); + position: absolute; + top: -35px; + left: 50%; + transform: translateX(-50%) scale(0.8); + background: var(--bg-card); + color: var(--text-primary); + border: 1px solid var(--border-color); + font-size: 11px; + padding: 4px 10px; + border-radius: 6px; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: all 0.2s; + } + .dock-item:hover::after { + opacity: 1; + transform: translateX(-50%) scale(1); + } + + /* Settings button & menu only used on small screens */ + #btn-nav-settings, + .settings-menu { + display: none; + } + + .settings-menu { + position: fixed; + bottom: 92px; + right: calc((100vw - min(100vw - 32px, 400px)) / 2); + z-index: 55; + flex-direction: column; + gap: 4px; + padding: 8px; + border-radius: 16px; + background: var(--dock-bg); + border: 1px solid var(--dock-border); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + box-shadow: 0 10px 30px rgba(0,0,0,0.3); + } + .settings-menu.open { + display: flex; + } + + /* Modal for About */ + .modal-content { + background: var(--bg-card); + border: 1px solid var(--border-color); + } + + /* --- Loader --- */ + #loader { + position: fixed; + inset: 0; + z-index: 200; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 14px; + background: var(--loader-bg); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + transition: opacity 0.5s ease, visibility 0.5s ease; + } + + #loader.hidden { + opacity: 0; + visibility: hidden; + pointer-events: none; + } + + .loader-spinner { + width: 28px; + height: 28px; + border-radius: 50%; + border: 2.5px solid var(--loader-track); + border-top-color: var(--loader-spin); + animation: loaderSpin 0.8s linear infinite; + } + + @keyframes loaderSpin { + to { transform: rotate(360deg); } + } + + .loader-text { + font-size: 11px; + font-weight: 500; + letter-spacing: 0.02em; + color: var(--text-secondary); + } + + /* --- Responsive Design Overrides --- */ + @media (max-width: 768px) { + /* Keep scattered desktop icons on mobile (no grid) */ + #view-home.active { + height: auto; + min-height: 100vh; + overflow: visible; + } + .mac-desktop-icon { + width: 84px; + } + .mac-desktop-icon:active { + transform: scale(0.95) !important; + } + + /* Shrink the dock for mobile screens */ + .dock-container { + width: calc(100% - 32px); + max-width: 400px; + padding: 8px 10px; + gap: 2px; + justify-content: space-evenly; + border-radius: 20px; + overflow-x: auto; + } + .dock-item { + width: 38px; + height: 38px; + font-size: 1.25rem; + flex-shrink: 0; + } + .dock-divider { + height: 24px; + margin: 0 2px; + flex-shrink: 0; + } + .dock-item::after { + display: none; /* Hide tooltips on touch devices to prevent sticky hover states */ + } + + /* Collapse theme/about/github into the settings menu on small screens */ + #btn-nav-settings { + display: flex; + } + #btn-theme-toggle, + #btn-nav-about, + .dock-github-link { + display: none; + } + + /* Adjust semester view padding to account for safe areas */ + #view-semester { + padding-left: 1rem; + padding-right: 1rem; + } + + /* Top bar compact on mobile */ + .top-bar { + height: 40px; + padding: 0 12px; + } + .top-bar-left { + font-size: 12px; + gap: 6px; + padding: 4px 8px; + } + .top-bar-right { + font-size: 11px; + gap: 5px; + } + .top-bar-right .contrib-label { + display: none; + } + } + \ No newline at end of file diff --git a/images/bg-light.png b/images/bg-light.png new file mode 100644 index 0000000..5d47dfa Binary files /dev/null and b/images/bg-light.png differ diff --git a/images/bg-main.jpg b/images/bg-main.jpg new file mode 100644 index 0000000..91e681b Binary files /dev/null and b/images/bg-main.jpg differ diff --git a/images/kasun.jpg b/images/kasun.jpg new file mode 100644 index 0000000..1c2ef89 Binary files /dev/null and b/images/kasun.jpg differ diff --git a/images/yasas.png b/images/yasas.png new file mode 100644 index 0000000..989ddd5 Binary files /dev/null and b/images/yasas.png differ diff --git a/index.html b/index.html index 78887d4..071a32f 100644 --- a/index.html +++ b/index.html @@ -1,824 +1,233 @@ - - - - Notes + + TheMIU/Notes - - - - - - + + + - - - - - - + + + + - -
-
- Loading... + +
+
+
themiu/notes
-
- - - - - Background Image - - -
- - -
- - -
- -
-
-
-
-
-
-
- -
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
- -
-
-
-
-
-
+ +
+ + + themiu/notes + +
+ Contributors + Kasun Miuranga + , + Yasas Banu
+
- -
- - - - - - - - - - - - - - +
+ - diff --git a/js/scripts.js b/js/scripts.js index c1e67a6..bb9393f 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -1,116 +1,675 @@ -// Hide the loading when the document is fully loaded -document.onreadystatechange = function () { - if (document.readyState === "complete") { - $('#loading-container').hide(); - } -}; - -// Disable the right-click context menu -document.addEventListener("contextmenu", function (e) { - e.preventDefault(); -}); - -////// Enable tooltips -const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]') -const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)) - -////// add icon before h5 -var icon = $('').addClass('fa-solid fa-folder fa-xs'); - -var containers = $(".subject-container h5"); - -containers.each(function () { - $(this).prepend(icon.clone()); -}); - -////// add icon before list items -var icon = $('').addClass('fa-solid fa-file '); - -$(".subject-container ul li").each(function () { - $(this).prepend(icon.clone()); -}); - -////// Navigation -hideAll(); -changeNavLinksWhite(); -$("#home").css("color", "#45b0ff"); -$("#mainButtons").show(); - -$("#navHome").click(function () { - hideAll(); - $("#mainButtons").show(); - changeNavLinksWhite(); - $("#home").css("color", "#45b0ff"); - toggleNavbar(); -}); - -$("#home").click(function () { - hideAll(); - $("#mainButtons").show(); - changeNavLinksWhite(); - $("#home").css("color", "#45b0ff"); - toggleNavbar(); -}); - -$("#btn-sem-1, #navSEM01").click(function () { - hideAll(); - $("#sem-1").show(); - changeNavLinksWhite(); - $("#navSEM01").css("color", "#45b0ff"); - toggleNavbar(); -}); - -$("#btn-sem-2, #navSEM02").click(function () { - hideAll(); - $("#sem-2").show(); - changeNavLinksWhite(); - $("#navSEM02").css("color", "#45b0ff"); - toggleNavbar(); -}); - -$("#btn-sem-3, #navSEM03").click(function () { - hideAll(); - $("#sem-3").show(); - changeNavLinksWhite(); - $("#navSEM03").css("color", "#45b0ff"); - toggleNavbar(); -}); - -$("#btn-sem-4, #navSEM04").click(function () { - hideAll(); - $("#sem-4").show(); - changeNavLinksWhite(); - $("#navSEM04").css("color", "#45b0ff"); - toggleNavbar(); -}); - -function hideAll() { - $("#mainButtons").hide(); - $("#sem-1").hide(); - $("#sem-2").hide(); - $("#sem-3").hide(); - $("#sem-4").hide(); -} - -function changeNavLinksWhite() { - $("#navSEM01").css("color", "white"); - $("#navSEM02").css("color", "white"); - $("#navSEM03").css("color", "white"); - $("#navSEM04").css("color", "white"); - $("#home").css("color", "white"); -} - -////// Toggle navbar -function toggleNavbar() { - $(".navbar-collapse").collapse('hide'); -} - -$(document).on("click", function (event) { - // Check if the clicked element is not part of the navbar - if (!$(event.target).closest('.navbar').length) { - $(".navbar-collapse").collapse('hide'); - } -}); - -// display currant year -document.getElementById("yearParagraph").innerHTML += " " + new Date().getFullYear(); \ No newline at end of file + + // --- Data Structure --- + const courseData = [ + { + id: 'sem-1', + title: 'Semester 01', + subtitle: 'Foundations & Basics', + icon: 'number-circle-one', + color: 'text-blue-400', + pos: { x: 25, y: 35 }, // Percentages for scattered layout + subjects: [ + { + title: 'Programming Fundamentals (PRF)', icon: 'code', + links: [ + { url: 'https://drive.google.com/file/d/1geEL3NJZuWBqY5EHyJiE1XKJRTDYohRE', label: 'PRF Note (Handwritten)', type: 'document', desc: 'Scanned lecture notes.' }, + { url: 'https://docs.google.com/document/d/1RWOYfVfkMFTcvwZ551DAMVeP6LRLcj_7MEoTXc__HtE', label: 'PRF Short Note', type: 'document', desc: 'Quick syntax review.' }, + { url: 'https://drive.google.com/drive/folders/10eM0WhwDAjJv6Z_ej6-Lw7U4NT0Jq8oe?usp=drive_link', label: 'Codes', type: 'folder', desc: 'C/C++ code examples.' } + ] + }, + { + title: 'Database Management (DBM)', icon: 'database', + links: [ + { url: 'https://drive.google.com/drive/folders/1-OQimWQ1TPrX53ZpI4VJnnprC3f478jo?usp=drive_link', label: 'Image Notes & Queries', type: 'folder', desc: 'Diagrams & SQL snippets.' }, + { url: 'https://docs.google.com/document/d/1mzBjt9duh42fYJ5XWS19d8TG0ZkW6lLxnn9B6HFu608', label: 'SQL Queries Short', type: 'document', desc: 'Essential SQL cheat sheet.' }, + { url: 'https://docs.google.com/document/d/1JES6sbOvzQovbACCXnwKx0pRNCnpc9Bzk2rKZgh19c0', label: 'Sample Queries', type: 'document', desc: 'Practice SQL scenarios.' } + ] + }, + { + title: 'Object Oriented Programming (OOP)', icon: 'cube', + links: [ + { url: 'https://drive.google.com/file/d/1wzT7ez_SI8A-TkP0d-bh53DP2POEEkRM/view?usp=drive_link', label: 'OOP Note (Handwritten)', type: 'document', desc: 'Handwritten core concepts.' }, + { url: 'https://docs.google.com/document/d/1m3a9SUuj7KMVLrE0jovT0Ha7VjkDS2O0YxX9yuOQtKE', label: 'OOP Note', type: 'document', desc: 'Detailed OOP guide.' }, + { url: 'https://docs.google.com/document/d/14XSilB6IGR3vMQ-DKXu9tr8aObgRodk0UtlGV0n8FpU', label: 'OOP Short Note', type: 'document', desc: 'Brief review of principles.' } + ] + }, + { + title: 'Software Engineering (SE)', icon: 'desktop', + links: [ + { url: 'https://docs.google.com/document/d/1Xb7Vo06mj9QQqnzr3XbDYzmhRJFYoSr7-vxdWfPd6jI', label: 'SE Short Note', type: 'document', desc: 'Software dev lifecycles.' } + ] + }, + { + title: 'Java DataBase Connectivity (JDBC)', icon: 'plug', + links: [ + { url: 'https://docs.google.com/document/d/1l_0pc5tTHMwEp5YQMODIA_Qa4YeTBHWkm6N6k_YaPSc', label: 'DB Connectivity', type: 'document', desc: 'Connecting Java to DBs.' }, + { url: 'https://drive.google.com/drive/folders/1Narmotg-pzeb2ZpmTES9VMApsgVhn6fy?usp=drive_link', label: 'Codes & Projects', type: 'folder', desc: 'Sample JDBC projects.' } + ] + }, + { + title: 'Other Topics', icon: 'dots-three', + links: [ + { url: 'https://docs.google.com/document/d/1QVQCX-17LEUPltomSRwMLHRV5L9Pnt6E5VuoIhp1mPk', label: 'RegEx', type: 'document', desc: 'Regular expressions guide.' }, + { url: 'https://docs.google.com/document/d/16mYwmcoNXeGdLGHC-IyMoabT_2m70n_7P79_YSFzm3w', label: 'Generics', type: 'document', desc: 'Java Generics overview.' }, + { url: 'https://docs.google.com/document/d/1x_OILLbmuCumnU0wAd6rgEOxPcuvMSzXpJTLonqslAY', label: 'String API', type: 'document', desc: 'String manipulation.' }, + { url: 'https://docs.google.com/document/d/1UPXbpfI4k-zoOaCYrpIeBVPmqJQL9Q39VbI8IOmabNA', label: 'MultiThreading', type: 'document', desc: 'Thread management basics.' }, + { url: 'https://docs.google.com/document/d/1vcvI0DAtFxIvAvgrvcnfcOtB7z3ScufNYAUl6sm2ySQ', label: 'Collection Framework', type: 'document', desc: 'Lists, Sets, and Maps.' } + ] + } + ] + }, + { + id: 'sem-2', + title: 'Semester 02', + subtitle: 'Architecture & Networks', + icon: 'number-circle-two', + color: 'text-purple-400', + pos: { x: 70, y: 25 }, + subjects: [ + { + title: 'Layered Architecture', icon: 'stack', + links: [ + { url: 'https://docs.google.com/document/d/15P32PDxMH8tKaxi0kWaQjutezmFweAwzgCV1vMNBQ-8', label: 'Layered Note', type: 'document', desc: 'Architecture patterns.' }, + { url: 'https://docs.google.com/document/d/1hF6NZbv1_CWKJCVLfbZ0htZO1CdMA82J2cnHn0ShKJY', label: 'Layered Q & A', type: 'question', desc: 'Exam practice questions.' } + ] + }, + { + title: 'Object Relational Mapping (ORM)', icon: 'arrows-merge', + links: [ + { url: 'https://docs.google.com/document/d/1FNW3VSZ8x3nWFnYOKg_ctc0kiMMfidrbEGmb1mQ2QoI', label: 'ORM Note', type: 'document', desc: 'Hibernate & JPA basics.' }, + { url: 'https://docs.google.com/document/d/1cvslWNWbeK-a35fpKAhHoIqTppBwb6tCj0tN0udImLY', label: 'ORM Short', type: 'document', desc: 'Quick ORM concepts.' }, + { url: 'https://docs.google.com/document/d/1j-MUToAMdAGZR3V1j0W4jZEx2QeY9bDC26RXlgXh3ss', label: 'ORM Q & A', type: 'question', desc: 'Common ORM questions.' } + ] + }, + { + title: 'Network Programming', icon: 'share-network', + links: [ + { url: 'https://docs.google.com/document/d/1V_aKECSMXbJyEx5nZClEDStXz7nUA22FmvTaxHAHvek', label: 'Network Note', type: 'document', desc: 'Socket programming.' }, + { url: 'https://docs.google.com/document/d/15b8xIO7jcatAlFj1AD3xvOf39cB4yuyGfzwhsDvEb2g', label: 'Network Q & A', type: 'question', desc: 'Networking concepts Q&A.' } + ] + }, + { + title: 'Internet Technologies (IT)', icon: 'browser', + links: [ + { url: 'https://docs.google.com/document/d/1x_sJkyqDYj94Epr9gsMlemeRaE_hsHr2Ev-lV2VXtOc', label: 'IT Note', type: 'document', desc: 'Web fundamentals.' }, + { url: 'https://docs.google.com/document/d/19Dgj_-klq6c3biUoH4HZnD6a_H5GhO5oKx77ehmTN8g', label: 'HTML', type: 'code', desc: 'HTML markup basics.' }, + { url: 'https://docs.google.com/document/d/1QoOiGHfmQMlYdr7gHlMBC5KMENvyeyeus5HIZxplotc', label: 'CSS', type: 'code', desc: 'Styling with CSS.' }, + { url: 'https://docs.google.com/document/d/1tounuqQ-uKAFtLoTHklppEgssf9vOZd9WMBOYArHh_I', label: 'JavaScript', type: 'code', desc: 'Client side scripting.' }, + { url: 'https://docs.google.com/document/d/1M_9HOydnfbuWrAy7wY_DWbyQT4hMOlvB8w-sm-0Q2ds', label: 'IT Q & A', type: 'question', desc: 'Web dev practice.' } + ] + }, + { + title: 'Networking for SE (CNS)', icon: 'router', + links: [ + { url: 'https://docs.google.com/document/d/19bhhaxQBiD_euK2iCALqt03dQrnde77oZGhx6aIwLSo', label: 'CNS Note', type: 'document', desc: 'Computer networking core.' }, + { url: 'https://docs.google.com/document/d/1KZD-A_YD_0c7sjngYk5pJt2L6kxq9AUceEODXeb6cAI', label: 'CNS Short Note', type: 'document', desc: 'Networking summary.' } + ] + }, + { + title: 'Advance API Development (AAD)', icon: 'puzzle-piece', + links: [ + { url: 'https://docs.google.com/document/d/1SOQX9NF3E-TXYrvxfIaSs_b6477geF8y8NDTkj8exec', label: 'OOP in JS', type: 'document', desc: 'JavaScript prototypes.' }, + { url: 'https://docs.google.com/document/d/1KL3PGy00skrdpzhaAp3lgBR4wHdVk74bP936plBnqMw', label: 'Java EE', type: 'document', desc: 'Enterprise Java concepts.' }, + { url: 'https://docs.google.com/document/d/1EiE1xjPk07dRjhrkPdGXUhfBlSqQKIVkN5znKbIxq7E', label: 'Spring', type: 'document', desc: 'Spring framework intro.' }, + { url: 'https://docs.google.com/document/d/1Flx5saYRgZcsoosDtXLjK7K_cH4EiqDhTaJHx4GCaIc', label: 'Spring Q & A', type: 'question', desc: 'Spring interview prep.' } + ] + }, + { + title: 'Other Topics', icon: 'dots-three', + links: [ + { url: 'https://docs.google.com/document/d/1f7z0p9go86XCuqoaSEyRv_-E4n_Fm3Qipu7l1j6amv8', label: 'GitHub', type: 'document', desc: 'Version control basics.' }, + { url: 'https://docs.google.com/document/d/1wB8GHWQ4bQoR98MsDm2_yHlW2ApLjbihy_F5DlH6r6g', label: 'Internet Terminology', type: 'document', desc: 'Protocols & terms.' } + ] + } + ] + }, + { + id: 'sem-3', + title: 'Semester 03', + subtitle: 'Advanced Frameworks', + icon: 'number-circle-three', + color: 'text-green-400', + pos: { x: 20, y: 70 }, + subjects: [ + { + title: 'Architecture & Design Patterns 2', icon: 'compass-tool', + links: [ + { url: 'https://docs.google.com/document/d/1mMQwytSe2YWJ0kk0hmrHyeVWZLPcvPPTUwxYu1TDU6Q', label: 'AD2 note', type: 'document', desc: 'Advanced design patterns.' }, + { url: 'https://docs.google.com/document/d/1syvV_qYMYJYWIi3eXNptY_LF6jClJ2hmEi58KGg0R6c', label: 'Git, Branch mng. & Github', type: 'document', desc: 'Team workflow with Git.' }, + { url: 'https://docs.google.com/document/d/1aOZGX1uwO5PKqpjUdXTJeEmA_imYpT09Prps6cj8bns', label: 'Microservices', type: 'document', desc: 'Microservices architecture.' } + ] + }, + { + title: 'Data Structures & Algorithms', icon: 'tree-structure', + links: [ + { url: 'https://docs.google.com/document/d/1bwMpPaG2_6KKK7QXZcDd9Ugqy6qHZjndq6J-iIhgj_4', label: 'Data Structures', type: 'document', desc: 'Trees, graphs, and lists.' }, + { url: 'https://docs.google.com/document/d/1vhTBerPy1kag64MbYT04d7fOfBEV1OfCerGwfDIKbhs', label: 'Algorithms', type: 'document', desc: 'Sorting & searching.' } + ] + }, + { + title: 'Rapid Application Development (RAD)', icon: 'rocket-launch', + links: [ + { url: 'https://docs.google.com/document/d/19xY6hzT7mlFyuoqHJ2_NbMw9qapSL7B3wnpIanwyHaE', label: 'RAD note', type: 'document', desc: 'Rapid dev concepts.' }, + { url: 'https://docs.google.com/document/d/1N8O3mLuEFI8XCl7m7S6AdXC_gs1mg7uK3SWbTqSyXms', label: 'TypeScript', type: 'code', desc: 'Typed JavaScript.' }, + { url: 'https://docs.google.com/document/d/10LhKBZAp48qX8Ssb3ZdXgjIVL45jjLGOTNbuDSRcqvc', label: 'Tailwind CSS', type: 'code', desc: 'Utility first styling.' }, + { url: 'https://docs.google.com/document/d/1LeDNrMzEYk1pG9Ev03G3ge4rlf6Im-hL-hOsg9gTa6c', label: 'React', type: 'code', desc: 'UI component framework.' }, + { url: 'https://docs.google.com/document/d/1vw9styOKUntsj_UcLs_kQ1bMoWxt7DPLgtRb0F7Z8ik', label: 'Express & MongoDB', type: 'database', desc: 'MERN stack backend.' } + ] + }, + { + title: 'Advanced Mobile App Dev', icon: 'device-mobile', + links: [ + { url: 'https://docs.google.com/document/d/1sz8pqHMPQSkAYJA6njnlOy4vSUc2a-ps_R5vyvVUHnI', label: 'AMD note', type: 'document', desc: 'Mobile dev lifecycle.' }, + { url: 'https://docs.google.com/document/d/1h9z-SKMAcsxJoIHrMiu1BodpvUh4GYgiNelqY3CaIk0', label: 'React Native', type: 'code', desc: 'Cross platform mobile.' } + ] + }, + { + title: 'Industry Placement Prep', icon: 'handshake', + links: [ + { url: 'https://docs.google.com/document/d/1Xv1FtItN9zeRyLRKIb73HOWg9Ag14osoErPjHsbzKHE', label: 'Interview Q&A 01', type: 'question', desc: 'General prep questions.' }, + { url: 'https://docs.google.com/document/d/1DWbOwY7RWQq3ungJ7XMXP0MbQA22YPHqVwE-fzb5sr4', label: 'Interview Q&A 02', type: 'question', desc: 'Technical questions.' }, + { url: 'https://docs.google.com/document/d/1U5q3P7C6XMDMhNTP0b7JG7W71OCE-w8HIQaRTex1nwg', label: 'Interview Q&A 03', type: 'question', desc: 'Problem solving prep.' }, + { url: 'https://docs.google.com/document/d/1_TWPs6KO8jJDBuBgUAn6TIVk4Z9QyteYAFNcneiP6Js', label: 'Interview Q&A 04', type: 'question', desc: 'Core fundamentals.' }, + { url: 'https://docs.google.com/document/d/1KqANDHs10BtSY98ziKYk7ri6jfDz4uNrAu-EwK70ezk', label: 'Interview Q&A 05', type: 'question', desc: 'Advanced coding prep.' }, + { url: 'https://docs.google.com/document/d/1N13A_HydvmqwFETRujUgHpdnioV-4OzLqxGsjcquJzE', label: 'Interview Q&A (Spring)', type: 'question', desc: 'Spring boot specifics.' }, + { url: 'https://docs.google.com/document/d/1YH514z7g347_4qQIBAUg-wmJb0E1VL0eFBBJSiB_iS0', label: 'Java Codes', type: 'code', desc: 'Algorithm implementations.' }, + { url: 'https://docs.google.com/document/d/1rj6-zBRiND0N-nZpnticld0sRwVNr_73g7GmldS7GbQ', label: 'Common Questions', type: 'question', desc: 'HR and behavioral.' } + ] + } + ] + }, + { + id: 'sem-4', + title: 'Semester 04', + subtitle: 'Management & AI', + icon: 'number-circle-four', + color: 'text-amber-400', + pos: { x: 75, y: 65 }, + subjects: [ + { + title: 'Project Management', icon: 'chart-line-up', + links: [ + { url: 'https://docs.google.com/document/d/1qg3uusZqwU_JjxuykUg8aIBD05b-NTiN6yOfowbWxsc', label: 'Project Management', type: 'document', desc: 'Software lifecycles.' }, + { url: 'https://docs.google.com/document/d/1tkBPZhasj3OWd55vFi6CsK1Lfa_WpGzJOkjGuCUKFRA', label: 'Project Management Q&A', type: 'question', desc: 'Exam prep questions.' } + ] + }, + { + title: 'Machine Learning', icon: 'brain', + links: [ + { url: 'https://docs.google.com/document/d/1FbnSuEBTa3Jq94kXf7c2_VQRLp3LA1RFqqLO6DhKo0A', label: 'Docker', type: 'document', desc: 'Containerization basics.' }, + { url: 'https://docs.google.com/document/d/1H_0-cbztbi1MvX1aSmrEEnUnQW5y9vQSHaF5QLMgAL4', label: 'Machine Learning', type: 'document', desc: 'AI models and concepts.' }, + { url: 'https://docs.google.com/document/d/1BlaH6y3jqlMsP3GEEsQmgAamQxo9KAMxabfm8gxiWS8', label: 'AWS', type: 'cloud', desc: 'Cloud deployment.' }, + { url: 'https://docs.google.com/document/d/1TUoBLFhUSl8_BTHlMA45NVaVsIKt5CupdHoZg-4pGqo', label: 'Python', type: 'code', desc: 'Python for data science.' } + ] + } + ] + } + ]; + + // --- Helper Functions --- + const viewHome = document.getElementById('view-home'); + const viewSemester = document.getElementById('view-semester'); + const detailContent = document.getElementById('detail-content'); + + let currentSemesterData = null; + + // Abstract icons for the large framer cards based on link type + const getWireframeIcon = (type) => { + switch(type) { + case 'folder': return 'folder-open'; + case 'code': return 'code-block'; + case 'question': return 'seal-question'; + case 'database': return 'database'; + case 'cloud': return 'cloud'; + default: return 'file-text'; + } + }; + + // --- Render Home (Mac Desktop Layout) --- + function renderHome() { + viewHome.innerHTML = ''; + + const isMobile = window.innerWidth <= 768; + const iconW = isMobile ? 84 : 80; + const iconH = isMobile ? 95 : 100; + const availW = viewHome.clientWidth; + const availH = viewHome.clientHeight; + // Keep icons clear of the viewport edges + const xMin = availW * 0.08; + const xMax = availW * 0.9 - iconW; + const yMin = availH * 0.14; + // Keep icons above the dock so they stay visible on short viewports + const dockEl = document.querySelector('.dock-container'); + const dockTop = dockEl ? dockEl.getBoundingClientRect().top : availH; + const yMax = Math.max(yMin, Math.min(availH * 0.8 - iconH, dockTop - iconH - 24)); + + // Spread icons across the whole available area, keeping space between them + // Percentages are mapped onto the usable range (not the full width) so + // icons never get clamped into the far-right edge on narrow screens. + const positions = courseData.map(sem => ({ + x: Math.min(xMax, Math.max(xMin, xMin + (sem.pos.x / 100) * (xMax - xMin))), + y: Math.min(yMax, Math.max(yMin, yMin + (sem.pos.y / 100) * (yMax - yMin))) + })); + + // Minimum space between folder icons (keep clear gaps between them) + const gapX = iconW * 1.75; + const gapY = iconH * 1.5; + + // Push overlapping icons apart in both directions until every pair has clear space + for (let pass = 0; pass < 20; pass++) { + let moved = false; + for (let i = 0; i < positions.length; i++) { + for (let j = i + 1; j < positions.length; j++) { + const a = positions[i], b = positions[j]; + const dx = b.x - a.x; + const dy = b.y - a.y; + const distX = Math.abs(dx); + const distY = Math.abs(dy); + if (distX >= gapX || distY >= gapY) continue; + + const sgnX = dx >= 0 ? 1 : -1; + const sgnY = dy >= 0 ? 1 : -1; + const pushX = (gapX - distX) / 2; + const pushY = (gapY - distY) / 2; + + // Candidate positions (split the push between the two icons) + const aY2 = a.y - pushY * sgnY; + const bY2 = b.y + pushY * sgnY; + const aX2 = a.x - pushX * sgnX; + const bX2 = b.x + pushX * sgnX; + const yOK = aY2 >= yMin && aY2 <= yMax && bY2 >= yMin && bY2 <= yMax; + const xOK = aX2 >= xMin && aX2 <= xMax && bX2 >= xMin && bX2 <= xMax; + + if (yOK) { + a.y = aY2; b.y = bY2; + moved = true; + } else if (xOK) { + a.x = aX2; b.x = bX2; + moved = true; + } else { + // No room to split, so move a single icon the full distance + const aY3 = a.y - pushY * sgnY * 2; + const bY3 = b.y + pushY * sgnY * 2; + const aX3 = a.x - pushX * sgnX * 2; + const bX3 = b.x + pushX * sgnX * 2; + if (aY3 >= yMin && aY3 <= yMax) { + a.y = aY3; moved = true; + } else if (bY3 >= yMin && bY3 <= yMax) { + b.y = bY3; moved = true; + } else if (aX3 >= xMin && aX3 <= xMax) { + a.x = aX3; moved = true; + } else if (bX3 >= xMin && bX3 <= xMax) { + b.x = bX3; moved = true; + } + } + } + } + if (!moved) break; + } + + courseData.forEach((sem, index) => { + const icon = document.createElement('div'); + icon.className = 'mac-desktop-icon group'; + + icon.style.left = positions[index].x + 'px'; + icon.style.top = positions[index].y + 'px'; + + icon.innerHTML = ` +
+ +
+ ${sem.title} + `; + + icon.addEventListener('click', () => showSemester(sem.id)); + viewHome.appendChild(icon); + }); + } + + // --- View Transitions Logic --- + function showSemester(semesterId) { + currentSemesterData = courseData.find(s => s.id === semesterId); + if (!currentSemesterData) return; + + document.getElementById('detail-title').textContent = currentSemesterData.title; + document.getElementById('detail-subtitle').textContent = currentSemesterData.subtitle + " curated notes and resources."; + + // Reset search when opening a new semester + const searchInput = document.getElementById('searchInput'); + if(searchInput) searchInput.value = ''; + + renderSemesterDetail(); + + // Transition + document.body.classList.add('viewing-semester'); + viewHome.classList.remove('active'); + window.scrollTo({ top: 0, behavior: 'instant' }); + viewSemester.classList.add('active'); + } + + function closeSemester() { + viewSemester.classList.remove('active'); + window.scrollTo({ top: 0, behavior: 'instant' }); + viewHome.classList.add('active'); + document.body.classList.remove('viewing-semester'); + currentSemesterData = null; + } + + document.getElementById('btn-back').addEventListener('click', closeSemester); + + // --- Global Spotlight Search Logic --- + const spotlightWrapper = document.getElementById('spotlight-wrapper'); + const spotlightInput = document.getElementById('spotlight-input'); + const spotlightResults = document.getElementById('spotlight-results'); + const spotlightBackdrop = document.getElementById('spotlight-backdrop'); + + function toggleSpotlight(forceClose = false) { + const isHidden = spotlightWrapper.classList.contains('hidden'); + + if (!isHidden || forceClose) { + // Close Spotlight + spotlightWrapper.classList.remove('opacity-100'); + spotlightWrapper.classList.add('opacity-0'); + setTimeout(() => { + spotlightWrapper.classList.replace('flex', 'hidden'); + spotlightInput.value = ''; + spotlightResults.innerHTML = ''; + spotlightResults.classList.add('hidden'); + }, 200); + } else { + // Open Spotlight + spotlightWrapper.classList.replace('hidden', 'flex'); + // Trigger reflow for CSS transition + void spotlightWrapper.offsetWidth; + spotlightWrapper.classList.remove('opacity-0'); + spotlightWrapper.classList.add('opacity-100'); + spotlightInput.focus(); + } + } + + // Close when clicking outside + spotlightBackdrop.addEventListener('click', () => toggleSpotlight(true)); + + // Spotlight Shortcuts + document.addEventListener('keydown', (e) => { + // CMD/CTRL + F to open + if ((e.metaKey || e.ctrlKey) && e.key === 'f') { + e.preventDefault(); // Stop browser's default find behavior + toggleSpotlight(); + } + // ESC to close + if (e.key === 'Escape' && !spotlightWrapper.classList.contains('hidden')) { + e.preventDefault(); + toggleSpotlight(true); + } + }); + + // Dock Search Button + document.getElementById('btn-nav-search').addEventListener('click', () => { + toggleSpotlight(); + }); + + // Spotlight Search Filtering + spotlightInput.addEventListener('input', (e) => { + const query = e.target.value.toLowerCase().trim(); + if (!query) { + spotlightResults.innerHTML = ''; + spotlightResults.classList.add('hidden'); + return; + } + + let html = ''; + let count = 0; + + // Search through every single note in the database globally + courseData.forEach(sem => { + sem.subjects.forEach(sub => { + sub.links.forEach(link => { + const matchName = link.label.toLowerCase().includes(query); + const matchDesc = link.desc && link.desc.toLowerCase().includes(query); + const matchSub = sub.title.toLowerCase().includes(query); + + if (matchName || matchDesc || matchSub) { + count++; + const wireframe = getWireframeIcon(link.type); + + html += ` + +
+ +
+
+ ${link.label} + ${sem.title} • ${sub.title} +
+ +
+ `; + } + }); + }); + }); + + if (count === 0) { + html = `
No results found for "${query}"
`; + } + + spotlightResults.innerHTML = html; + spotlightResults.classList.remove('hidden'); + }); + + // --- Render Detailed View (Framer Cards) --- + function renderSemesterDetail(searchQuery = '') { + let html = ''; + const query = searchQuery.toLowerCase().trim(); + + currentSemesterData.subjects.forEach(subject => { + // Determine if subject title matches + const subjectMatches = subject.title.toLowerCase().includes(query); + + // Filter individual links + const matchingLinks = subject.links.filter(link => + link.label.toLowerCase().includes(query) || + (link.desc && link.desc.toLowerCase().includes(query)) + ); + + // If searching and nothing matches in this subject, skip rendering it + if (query && !subjectMatches && matchingLinks.length === 0) { + return; + } + + // If subject title matches, show all links. Otherwise, show only matching links. + const linksToDisplay = (subjectMatches && query) ? subject.links : (query ? matchingLinks : subject.links); + + html += ` +
+

Explore ${subject.title}

+
+ `; + + linksToDisplay.forEach(link => { + html += ` + +
+ + + + + + + + +
+

${link.label}

+

${link.desc || 'Google Drive note.'}

+
+ `; + }); + + html += ` +
+
+ `; + }); + + if (html === '' && query) { + html = `
No resources found matching "${searchQuery}"
`; + } + + detailContent.innerHTML = html; + } + + // --- Event Listeners & Initialization --- + + // Search Logic + const searchInput = document.getElementById('searchInput'); + if(searchInput) { + searchInput.addEventListener('input', (e) => { + if(currentSemesterData) { + renderSemesterDetail(e.target.value); + } + }); + } + + // Search Shortcut CMD/CTRL + K + document.addEventListener('keydown', (e) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + if (viewSemester.classList.contains('active') && searchInput) { + searchInput.focus(); + } + } + }); + + // Theme Logic + const themeBtn = document.getElementById('btn-theme-toggle'); + const themeIcon = document.getElementById('theme-icon'); + const themeBtnPop = document.getElementById('btn-theme-toggle-pop'); + const themeIconPop = document.getElementById('theme-icon-pop'); + + function toggleTheme() { + document.body.classList.toggle('light-theme'); + const isLight = document.body.classList.contains('light-theme'); + const iconClass = isLight ? 'ph-fill ph-sun' : 'ph-fill ph-moon'; + themeIcon.className = iconClass; + if (themeIconPop) themeIconPop.className = iconClass; + localStorage.setItem('theme', isLight ? 'light' : 'dark'); + } + + // Initialize Theme from storage + if(localStorage.getItem('theme') === 'light') { + document.body.classList.add('light-theme'); + themeIcon.className = 'ph-fill ph-sun'; + if (themeIconPop) themeIconPop.className = 'ph-fill ph-sun'; + } + + themeBtn.addEventListener('click', toggleTheme); + if (themeBtnPop) themeBtnPop.addEventListener('click', toggleTheme); + + // Navigation (Home button now just ensures modals are closed) + document.getElementById('btn-nav-home').addEventListener('click', () => { + closeSemester(); + closeAbout(); + }); + + // Modal Logic + const aboutModal = document.getElementById('about-modal'); + const aboutModalContent = document.getElementById('about-modal-content'); + const btnAbout = document.getElementById('btn-nav-about'); + + function openAbout() { + aboutModal.classList.remove('hidden'); + aboutModal.classList.add('flex'); + // Small delay to allow display:flex to apply before animating + setTimeout(() => { + aboutModal.classList.remove('opacity-0'); + aboutModalContent.classList.remove('scale-95'); + aboutModalContent.classList.add('scale-100'); + }, 10); + } + + function closeAbout() { + aboutModal.classList.add('opacity-0'); + aboutModalContent.classList.remove('scale-100'); + aboutModalContent.classList.add('scale-95'); + setTimeout(() => { + aboutModal.classList.add('hidden'); + aboutModal.classList.remove('flex'); + }, 300); + } + + btnAbout.addEventListener('click', openAbout); + document.querySelectorAll('.about-close').forEach(btn => btn.addEventListener('click', closeAbout)); + aboutModal.addEventListener('click', (e) => { + if (e.target === aboutModal) closeAbout(); + }); + + // Settings menu (mobile only) + const settingsBtn = document.getElementById('btn-nav-settings'); + const settingsMenu = document.getElementById('settings-menu'); + + function closeSettingsMenu() { + if (settingsMenu) settingsMenu.classList.remove('open'); + } + + if (settingsBtn && settingsMenu) { + settingsBtn.addEventListener('click', (e) => { + e.stopPropagation(); + settingsMenu.classList.toggle('open'); + }); + + const btnAboutPop = document.getElementById('btn-nav-about-pop'); + if (btnAboutPop) btnAboutPop.addEventListener('click', () => { + closeSettingsMenu(); + openAbout(); + }); + + document.addEventListener('click', (e) => { + if (settingsMenu.classList.contains('open') && + !settingsMenu.contains(e.target) && + !settingsBtn.contains(e.target)) { + closeSettingsMenu(); + } + }); + + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closeSettingsMenu(); + }); + } + + // Initialize App + document.addEventListener('DOMContentLoaded', () => { + renderHome(); + }); + + // Handle window resize to reposition nodes if switching between mobile/desktop + window.addEventListener('resize', () => { + if(viewHome.classList.contains('active')) { + renderHome(); + } + }); + + // --- Loader Logic --- + const loader = document.getElementById('loader'); + const loaderStart = Date.now(); + const MIN_LOADER_MS = 2500; + let loaderHidden = false; + + function hideLoader() { + if (loaderHidden) return; + loaderHidden = true; + + const elapsed = Date.now() - loaderStart; + const wait = Math.max(0, MIN_LOADER_MS - elapsed); + + setTimeout(() => { + loader.classList.add('hidden'); + setTimeout(() => loader.remove(), 600); + }, wait); + } + + window.addEventListener('load', hideLoader); + if (document.readyState === 'complete') hideLoader(); + setTimeout(hideLoader, MIN_LOADER_MS + 2000); + + \ No newline at end of file