diff --git a/CSS/aboutstyle.css b/CSS/aboutstyle.css
new file mode 100644
index 0000000..a01e269
--- /dev/null
+++ b/CSS/aboutstyle.css
@@ -0,0 +1,184 @@
+/* Basic reset for margin and padding */
+html, body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+}
+
+*, *::before, *::after {
+ box-sizing: inherit;
+}
+
+body {
+ font-family: Montserrat, sans-serif;
+ background-color: #121212;
+ color: #d7d7d7;
+ display: flex;
+ flex-direction: column;
+ height: 100vh; /* Full viewport height */
+ overflow: auto; /* Enable scrolling for the whole body */
+}
+
+/* Hide scrollbar for Chrome, Safari and Opera */
+html::-webkit-scrollbar {
+ display: none;
+}
+
+/* Hide scrollbar for IE and Edge */
+html {
+ -ms-overflow-style: none; /* IE and Edge */
+}
+
+body {
+ overflow-y: scroll; /* Allows vertical scrolling */
+}
+
+h1 {
+ font-size: 20px;
+ margin: 0;
+ margin-bottom: 5px;
+ color: white;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+ font-weight: 400;
+ text-align: left; /* Ensure all h1 elements are aligned to the left */
+}
+
+h2 {
+ font-size: 16px;
+ margin: 0;
+ color: grey;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 400;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+}
+
+.top-container {
+ flex: 0 0 auto; /* Adjust the height as needed */
+ display: flex;
+ flex-direction: column-reverse;
+ justify-content: flex-end;
+ align-items: center;
+ padding-top: 10px;
+ border-bottom: 1px solid #2e2e2e;
+}
+
+hr {
+ opacity: .05;
+}
+
+.bottom-container {
+ flex: 1; /* Take up remaining space */
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px;
+ background-color: #151515;
+ width: 100%;
+ gap: 10px;
+ border-radius: 5px;
+}
+
+.horizontal-container {
+ width: 65%; /* Adjust the width as needed */
+ margin: auto;
+}
+
+.panel-title {
+ font-size: 24px;
+ margin-bottom: 10px;
+ color: #d7d7d7;
+ text-align: left; /* Align panel titles to the left */
+}
+
+p {
+ font-size: 16px;
+ line-height: 1.4;
+ color: grey;
+}
+
+.summary-panel p {
+ text-align: justify;
+}
+
+.navigation-buttons {
+ display: flex;
+ gap: 1rem;
+ justify-content: center; /* Ensure buttons are centered */
+ padding-bottom: 0; /* No padding at the bottom */
+}
+
+.nav-button {
+ background-color: inherit;
+ border: none;
+ color: grey;
+ font-family: 'Poppins', sans-serif;
+ text-decoration: none;
+ cursor: pointer;
+ padding: 10px;
+ margin-right: 10px;
+ font-size: 14px;
+ border-radius: .9em .9em 0 0;
+ transition: ease 0.5s;
+}
+
+.nav-button.active, .nav-button:hover {
+ background-color: #171717;
+ color: #f5ba13;
+}
+
+@media (max-width: 600px) {
+ body {
+ font-size: 14px; /* Adjust the base font size for better readability on small screens */
+ }
+
+ h1 {
+ font-size: 18px; /* Smaller font size for headings on mobile */
+ text-align: center; /* Center align headings for better presentation on mobile */
+ }
+
+ h2 {
+ font-size: 14px; /* Smaller font size for subheadings on mobile */
+ text-align: center; /* Center align subheadings for better presentation on mobile */
+ }
+
+ .top-container {
+ padding-top: 10px;
+ border-bottom: 1px solid #2e2e2e;
+ }
+
+ .bottom-container {
+ padding: 10px; /* Reduce padding for better fit on mobile */
+ gap: 5px; /* Reduce gap for better fit on mobile */
+ }
+
+ .horizontal-container {
+ width: 90%; /* Increase width to better fit mobile screens */
+ margin: 0 5%; /* Center the container */
+ }
+
+ .panel-title {
+ font-size: 20px; /* Adjust font size for panel titles on mobile */
+ text-align: center; /* Center align panel titles for better presentation on mobile */
+ }
+
+ p {
+ font-size: 14px; /* Adjust font size for paragraphs on mobile */
+ line-height: 1.6; /* Increase line height for better readability on mobile */
+ }
+
+ .navigation-buttons {
+ flex-direction: row; /* Stack navigation buttons vertically on mobile */
+ gap: 0.5rem; /* Adjust gap for better spacing on mobile */
+ }
+
+ .nav-button {
+ font-size: 12px; /* Adjust font size for navigation buttons on mobile */
+ padding: 8px; /* Reduce padding for better fit on mobile */
+ margin-right: 0; /* Remove margin for better fit on mobile */
+ border-radius: .5em .5em 0 0; /* Adjust border radius for better look on mobile */
+ }
+}
diff --git a/CSS/b2t.css b/CSS/b2t.css
new file mode 100644
index 0000000..e6c8349
--- /dev/null
+++ b/CSS/b2t.css
@@ -0,0 +1,19 @@
+.back-to-top-button {
+ display: none; /* Initially hidden */
+ position: fixed;
+ bottom: 20px;
+ z-index: 100;
+ background-color: #3333337a;
+ color: #fff;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
+ transition: background-color 0.3s;
+ }
+
+ .back-to-top-button:hover {
+ background-color: #121212;
+ }
\ No newline at end of file
diff --git a/CSS/indexstyle.css b/CSS/indexstyle.css
new file mode 100644
index 0000000..442bd7d
--- /dev/null
+++ b/CSS/indexstyle.css
@@ -0,0 +1,157 @@
+/* General Reset */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+/* Hide scrollbar for Chrome, Safari, and Opera */
+html::-webkit-scrollbar {
+ display: none;
+}
+
+/* Hide scrollbar for IE, Edge */
+html {
+ -ms-overflow-style: none; /* IE and Edge */
+}
+
+
+/* Body Styling */
+html, body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden; /* Prevent horizontal overflow */
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Montserrat, sans-serif;
+ background-color: #121212;
+ color: #d7d7d7;
+ display: flex;
+ flex-direction: column;
+}
+
+h1 {
+ font-size: 20px;
+ margin: 0;
+ margin-bottom: 5px;
+ color: #d7d7d7;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+}
+
+h2 {
+ font-size: 16px;
+ margin: 0;
+ color: grey;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 400;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+}
+
+.main-container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ flex: 1;
+ overflow-y: auto; /* Ensure main container scrolls if necessary */
+}
+
+.top-container {
+ flex: 0 0 auto; /* Adjust the height as needed */
+ display: flex;
+ flex-direction: column-reverse;
+ justify-content: flex-end;
+ align-items: center;
+ padding-top: 10px;
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ border-bottom: 1px solid #2e2e2e;
+}
+
+.navigation-buttons {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: center; /* Ensure buttons are centered */
+ padding-bottom: 0; /* No padding at the bottom */
+}
+
+.nav-button {
+ background-color: inherit;
+ border: none;
+ color: grey;
+ font-family: 'Poppins', sans-serif;
+ text-decoration: none;
+ cursor: pointer;
+ padding: 10px;
+ margin-right: 10px;
+ font-size: 14px;
+ border-radius: .9em .9em 0 0;
+ transition: ease 0.5s;
+}
+
+.nav-button.active, .nav-button:hover {
+ background-color: #171717;
+ color: #edb049;
+}
+
+.bottom-container {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ padding: 10px;
+ background-color: #151515;
+ border-radius: 5px;
+ width: 100%;
+ margin-top: 0; /* No margin at the top */
+}
+
+@media (max-width: 600px) {
+ body {
+ font-size: 14px; /* Adjust the base font size for better readability on small screens */
+ }
+
+ h1 {
+ font-size: 18px; /* Adjust font size for mobile */
+ text-align: center; /* Center align text for better readability on mobile */
+ }
+
+ h2 {
+ font-size: 14px; /* Adjust font size for mobile */
+ text-align: center; /* Center align text for better readability on mobile */
+ }
+
+ .main-container {
+ overflow-y: auto; /* Ensure main container scrolls if necessary */
+ padding-top: 5px;/* Add padding for better spacing on mobile */
+ }
+
+ .top-container {
+ padding-top: 5px; /* Adjust padding for mobile */
+ border-bottom: 1px solid #2e2e2e;
+ }
+
+ .navigation-buttons {
+ flex-direction: row; /* Stack navigation buttons vertically on mobile */
+ gap: 0.5rem; /* Adjust gap for better spacing on mobile */
+}
+
+.nav-button {
+ font-size: 12px; /* Adjust font size for navigation buttons on mobile */
+ padding: 8px; /* Reduce padding for better fit on mobile */
+ margin-right: 0; /* Remove margin for better fit on mobile */
+ border-radius: .5em .5em 0 0; /* Adjust border radius for better look on mobile */
+}
+
+ .bottom-container {
+ background-color: #171717;
+ padding: 8px; /* Adjust padding for mobile */
+ border-radius: 5px; /* Maintain border radius for consistent look */
+ margin-top: 0; /* Add margin at the top for spacing */
+ }
+}
diff --git a/CSS/main.css b/CSS/main.css
new file mode 100644
index 0000000..44db39d
--- /dev/null
+++ b/CSS/main.css
@@ -0,0 +1,13 @@
+/* main.css */
+@import url('projectsstyle.css');
+@import url('userinformationstyle.css');
+@import url('tagsstyle.css');
+@import url('statsstyle.css');
+@import url('b2t.css');
+@import url('navigation.css');
+@import url('slider.css');
+@import url('readmore.css');
+@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0');
+@import url('https://fonts.googleapis.com/css2?family=Material+Icons');
+@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
+@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css');
\ No newline at end of file
diff --git a/CSS/navigation.css b/CSS/navigation.css
new file mode 100644
index 0000000..f39c061
--- /dev/null
+++ b/CSS/navigation.css
@@ -0,0 +1,44 @@
+.nav-button {
+ background-color: #121212;
+ border: none;
+ border-radius: 0.1em;
+ color: white;
+ font-size: 24px;
+ padding: 10px;
+ cursor: pointer;
+ position: fixed;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 1000;
+ opacity: 0.5;
+ transition: opacity 0.3s;
+}
+
+.nav-button:hover {
+ opacity: 1;
+}
+
+#prev-project {
+ left: calc((100vw - 100%) / 2 + 10px); /* Adjust to the left within media-container */
+}
+
+#next-project {
+ right: calc((100vw - 60%) / 2 + 15px); /* Adjust to the right within media-container */
+}
+
+/* Responsive Design for Navigation Buttons */
+@media (max-width: 600px) {
+ .nav-button {
+ font-size: 18px; /* Smaller font size for mobile */
+ padding: 8px; /* Smaller padding for mobile */
+ opacity: 0.5;
+ }
+
+ #prev-project {
+ left: 10px; /* Keep the button at the center */
+ }
+
+ #next-project {
+ right: 10px; /* Keep the button at the center */
+ }
+}
diff --git a/CSS/productionsstyle.css b/CSS/productionsstyle.css
new file mode 100644
index 0000000..d71de6d
--- /dev/null
+++ b/CSS/productionsstyle.css
@@ -0,0 +1,135 @@
+.productions-subpanels {
+ display: flex;
+ flex-direction: column; /* Stacks children vertically */
+ align-items: center; /* Align items to the start (left) */
+ width: 100%; /* Uses the full width of its parent container */
+ gap: 5px; /* Space between each card */
+}
+
+.production-subpanel {
+ padding: 10px;
+ background-color: #121212;
+ border-radius: 5px;
+ width: 65%; /* Adjusts the width to be 80% of the parent container */
+ height: auto; /* Adjusts height based on content */
+ max-height: 400px; /* Limits maximum height for consistency */
+ margin-bottom: 20px; /* Space between each card */
+ display: flex; /* Enables flexbox layout */
+ align-items: flex-start; /* Aligns items to the start vertically */
+}
+
+.production-subpanel img {
+ width: 200px; /* Width of the thumbnail */
+ height: auto; /* Height adjusts based on aspect ratio */
+ object-fit: cover; /* Ensures cover fit for the image */
+ border-radius: 5px; /* Rounded corners */
+ margin-right: 20px; /* Space between image and text */
+}
+
+.production-details {
+ display: flex;
+ flex-direction: column; /* Stacks title, company, and time vertically */
+ align-items: flex-start; /* Aligns items to the start */
+}
+
+.production-description {
+ margin-top: 10px; /* Add space between details and description */
+}
+
+.production-content {
+ flex-grow: 1; /* Allows the description to take remaining space */
+ display: flex;
+ flex-direction: column; /* Optionally control layout of the description */
+ justify-content: flex-start; /* Aligns description to the start */
+}
+
+.productions-panel {
+ border-radius: 5px;
+ padding: 10px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start; /* Align items to the start (left) */
+ text-align: left; /* Align text to the left */
+ margin-bottom: 20px;
+ background-color: #151515;
+ width: 100%;
+}
+
+.productions-panel h1 {
+ margin-bottom: 20px;
+}
+
+.production-subpanel h2 {
+ font-size: 20px; /* Font size for the title */
+ margin: 5px 0; /* Margins for spacing */
+ color: #d7d7d7; /* Text color */
+ text-align: left; /* Aligns text to the left */
+}
+
+.production-subpanel p {
+ margin: 5px 0; /* Margins for spacing */
+ color: grey; /* Text color */
+ text-align: left; /* Aligns text to the left */
+}
+
+.production-subpanel p {
+ font-size: 16px; /* Font size for the paragraphs */
+}
+
+@media (max-width: 600px) {
+ .production-subpanel {
+ width: 90%; /* Increase width to better fit mobile screens */
+ margin-bottom: 15px; /* Adjust margin for better spacing on mobile */
+ display: grid; /* Use grid layout */
+ grid-template-columns: auto 1fr; /* Two columns: one for image, one for details */
+ grid-template-rows: auto auto; /* Two rows: one for image/details, one for description */
+ gap: 10px; /* Space between grid items */
+ align-items: start; /* Align items to the start */
+ padding: 10px; /* Add padding to ensure content has space */
+ background-color: #121212; /* Background color */
+ border-radius: 5px; /* Border radius for rounded corners */
+ }
+
+ .production-subpanel img {
+ width: 100px; /* Adjust the image width for mobile */
+ height: auto; /* Maintain aspect ratio */
+ grid-column: 1; /* Image in the first column */
+ grid-row: 1; /* Image in the first row */
+ align-self: start; /* Align image to the start of the cell */
+ }
+
+ .production-details {
+ align-items: flex-start; /* Align text to the start */
+ text-align: left; /* Ensure text is aligned to the left */
+ grid-column: 2; /* Details in the second column */
+ grid-row: 1; /* Details in the first row */
+ margin-left: -15px;
+ }
+
+ .production-description {
+ align-items: flex-start; /* Align content to the start */
+ text-align: left; /* Ensure text is aligned to the left */
+ margin-top: 10px; /* Add space between details and description */
+ grid-column: 1 / 3; /* Description spans both columns */
+ grid-row: 2; /* Description in the second row */
+ }
+
+ .production-content {
+ display: contents; /* Allow direct child elements to follow the grid */
+ }
+
+ .productions-panel {
+ padding: 8px; /* Adjust padding for mobile */
+ margin-bottom: 15px; /* Adjust margin for better spacing on mobile */
+ }
+
+ .production-subpanel h2 {
+ font-size: 18px; /* Adjust font size for mobile */
+ }
+
+ .production-subpanel p {
+ font-size: 14px; /* Adjust font size for mobile */
+ }
+}
+
diff --git a/CSS/projectsstyle.css b/CSS/projectsstyle.css
new file mode 100644
index 0000000..f8db5d9
--- /dev/null
+++ b/CSS/projectsstyle.css
@@ -0,0 +1,244 @@
+/* General Reset */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+/* Body Styling */
+body {
+ font-family: 'Poppins', sans-serif;
+ background-color: #121212;
+ color: #d7d7d7;
+ height: 100vh; /* Full viewport height */
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 0;
+}
+
+/* Main Container Styling */
+.container {
+ display: flex;
+ width: 100%;
+ height: 100%;
+}
+
+h1 {
+ font-size: 20px;
+ margin-bottom: 10px;
+ color: white;
+ -webkit-user-select: none; /* Standard property */
+ user-select: none; /* Safari and iOS support */
+}
+
+h2 {
+ font-size: 16px;
+ color: grey;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 400;
+ -webkit-user-select: none; /* Standard property */
+ user-select: none; /* Safari and iOS support */
+}
+
+h3 {
+ font-size: 16px;
+ font-weight: 100;
+ -webkit-user-select: none; /* Standard property */
+ user-select: none; /* Safari and iOS support */
+ padding-left: 10px;
+}
+
+.media-container {
+ position: relative;
+ flex: 0 0 80%; /* Take up 80% of the width */
+ background-color: #151515;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+ padding: 0;
+ overflow-y: auto; /* Enable vertical scrolling */
+ height: 100vh; /* Full viewport height */
+}
+
+
+/* Minimal Scrollbar Styling */
+.media-container::-webkit-scrollbar {
+ width: 6px; /* Width of the scrollbar */
+
+}
+
+.media-container::-webkit-scrollbar-track {
+ background: none; /* Background of the scrollbar track */
+}
+
+.media-container::-webkit-scrollbar-thumb {
+ background-color: #222222; /* Color of the scrollbar thumb */
+ border-radius: 2px; /* Roundness of the scrollbar thumb */
+}
+
+.media-container::-webkit-scrollbar-thumb:hover {
+ background-color: #333333; /* Hover color of the scrollbar thumb */
+}
+
+.info-container::-webkit-scrollbar {
+ display: none; /* Hide scrollbar for WebKit browsers */
+}
+
+/* Info Container Styling */
+.info-container {
+ flex: 0 0 20%; /* Take up 20% of the width */
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ padding: 10px;
+ gap: 10px;
+ overflow: hidden;
+ overflow: auto;
+}
+
+/* Top Container Styling */
+.top-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+/* Project Description Container Styling */
+.project-description-container {
+ padding: 10px;
+}
+
+.project-description-container h1 {
+ font-size: 16px;
+ margin: 0;
+ margin-bottom: 5px;
+ color: white;
+ -webkit-user-select: none; /* Standard property */
+ user-select: none; /* Safari and iOS support */
+ color: #d7d7d7;
+ font-weight: 400;
+}
+
+.project-description-container h2 {
+ font-size: 10px;
+ color: white;
+}
+
+.project-description-container p {
+ font-size: 14px;
+ margin: 0;
+ color: grey;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 400;
+ -webkit-user-select: none; /* Standard property */
+ user-select: none; /* Safari and iOS support */
+}
+
+.media-description {
+ margin-bottom: 10px;
+ color: grey;
+ font-size: 14px;
+ text-align: center;
+}
+
+/* Style for the media item container */
+.media-item {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+}
+
+/* Media Content Styling */
+#project-media {
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#project-media img {
+ display: block;
+ max-width: 100%;
+ max-height: 100%;
+ width: auto;
+ height: auto;
+ object-fit: contain;
+ margin: auto;
+}
+
+#project-media video {
+ width: 100%;
+ height: auto;
+}
+
+hr {
+ opacity: .05;
+}
+
+.link-button {
+ display: inline-block;
+ color: rgb(20, 20, 20);
+ background-color: #4d779f;
+ padding: 0 10px;
+ border-radius: 2px;
+ cursor: pointer;
+ text-decoration: none;
+ text-align: center;
+ margin-top: 10px;
+ transition: color 0.3s;
+ opacity: 0.5;
+}
+
+.link-button:hover {
+ opacity: 1;
+ color: #f5ba13;
+ background-color: #272727;
+}
+
+
+/* Responsive Iframe */
+.responsive-iframe-container {
+ position: relative;
+ width: 100%;
+ padding-bottom: 56.25%; /* 16:9 aspect ratio */
+ height: 0;
+ overflow: hidden;
+}
+
+.responsive-iframe-container iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+}
+
+.responsive-iframe-container iframe, #project-media video, #project-media img {
+ padding: 5px;
+}
+
+/* Responsive Design */
+@media (max-width: 600px) {
+ .container {
+ flex-direction: column;
+ overflow-y: auto; /* Enable vertical scrolling for the container */
+ }
+
+ .media-container,
+ .info-container {
+ width: 100%;
+ height: auto; /* Let the height be determined by the content */
+ overflow: visible; /* Remove individual scrolling */
+ }
+
+ .media-container {
+ flex: 1;
+ }
+
+ .info-container {
+ flex: 1;
+ }
+}
\ No newline at end of file
diff --git a/CSS/readmore.css b/CSS/readmore.css
new file mode 100644
index 0000000..43916d1
--- /dev/null
+++ b/CSS/readmore.css
@@ -0,0 +1,20 @@
+#toggle-description {
+ display: inline-block;
+ color: grey;
+ background-color: #222222;
+ padding: 2px 10px;
+ border-radius: 5px;
+ cursor: pointer;
+ text-decoration: none;
+ text-align: center;
+ margin-top: 10px;
+ transition: color 0.3s;
+ opacity: 0.5;
+ }
+
+ #toggle-description:hover {
+ opacity: 1;
+ color: #f5ba13;
+ background-color: #272727;
+ }
+
\ No newline at end of file
diff --git a/CSS/recommendationsstyle.css b/CSS/recommendationsstyle.css
new file mode 100644
index 0000000..37619a4
--- /dev/null
+++ b/CSS/recommendationsstyle.css
@@ -0,0 +1,224 @@
+.recommendation-content {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ position: relative;
+ width: 100%;
+ min-height: 350px;
+ overflow: hidden;
+}
+
+.recommendation {
+ width: 100%;
+ display: none;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ text-align: left;
+ transition: opacity 0.5s ease, transform 0.5s ease;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.recommendation.active {
+ display: flex;
+ opacity: 1;
+ visibility: visible;
+ position: relative;
+}
+
+.recommendation-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.recommendation-avatar {
+ width: 80px;
+ height: 80px;
+ border-radius: 0.5em;
+ margin-right: 15px;
+}
+
+.recommendation-details {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.recommendations-panel {
+ background-color: #121212;
+ border-radius: 5px;
+ padding: 10px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: left;
+ margin-bottom: 20px;
+}
+
+.recommendation h2 {
+ font-size: 20px;
+ margin: 0;
+ color: #edb049;
+}
+
+.recommendation h3 {
+ font-size: 18px;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 400;
+ margin: 0;
+ color: grey;
+}
+
+.recommendation-date {
+ font-size: 14px;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 400;
+ margin: 5px 0;
+ color: #444444;
+}
+
+.recommendation-quote {
+ font-size: 16px;
+ margin: 10px 0;
+ color: grey;
+ line-height: 1.5;
+}
+
+.recommendation-dots {
+ display: flex;
+ gap: 10px;
+ padding: 10px;
+}
+
+.dot {
+ height: 10px;
+ width: 10px;
+ background-color: #e0e0e0;
+ border-radius: 50%;
+ display: inline-block;
+ cursor: pointer;
+}
+
+.dot.active {
+ background-color: #f5ba13;
+}
+
+@media (max-width: 600px) {
+ .recommendation-content {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ min-height: 450px; /* Adjust the minimum height for mobile */
+ position: relative; /* Add relative positioning */
+ padding-bottom: 60px; /* Ensure enough space for dots */
+ }
+
+ .recommendation {
+ transition: transform 0.5s ease, opacity 0.5s ease;
+ flex-grow: 1;
+ margin-bottom: 60px; /* Add margin to create space for dots */
+ position: absolute;
+ width: 100%;
+ top: 0;
+ left: 0;
+ }
+
+ .recommendation-enter-left {
+ animation: slideInFromLeft 0.5s forwards;
+ }
+
+ .recommendation-exit-left {
+ animation: slideOutToLeft 0.5s forwards;
+ }
+
+ .recommendation-enter-right {
+ animation: slideInFromRight 0.5s forwards;
+ }
+
+ .recommendation-exit-right {
+ animation: slideOutToRight 0.5s forwards;
+ }
+
+ @keyframes slideInFromLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-100%);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ }
+
+ @keyframes slideOutToLeft {
+ from {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateX(-100%);
+ }
+ }
+
+ @keyframes slideInFromRight {
+ from {
+ opacity: 0;
+ transform: translateX(100%);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ }
+
+ @keyframes slideOutToRight {
+ from {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateX(100%);
+ }
+ }
+
+ .recommendation-avatar {
+ width: 80px; /* Smaller avatar size for mobile */
+ height: 80px; /* Smaller avatar size for mobile */
+ margin-right: 10px; /* Adjust margin for better spacing on mobile */
+ margin-left: 10px;
+ }
+
+ .recommendation h2 {
+ font-size: 18px; /* Adjust font size for mobile */
+ display: flex;
+ align-items: center;
+ }
+
+ .recommendation h3 {
+ font-size: 16px; /* Adjust font size for mobile */
+ }
+
+ .recommendation-date {
+ font-size: 12px; /* Adjust font size for mobile */
+ margin-bottom: -10px;
+ }
+
+ .recommendation-quote {
+ font-size: 14px; /* Adjust font size for mobile */
+ }
+
+ .recommendations-panel {
+ padding: 8px; /* Adjust padding for mobile */
+ }
+
+ .dot {
+ height: 10px; /* Adjust dot size for mobile */
+ width: 10px; /* Adjust dot size for mobile */
+ }
+}
diff --git a/CSS/slider.css b/CSS/slider.css
new file mode 100644
index 0000000..e38bbb4
--- /dev/null
+++ b/CSS/slider.css
@@ -0,0 +1,91 @@
+/* Style for the image container */
+.img-container {
+ position: relative;
+ width: 100%;
+ height: auto;
+ overflow: hidden;
+ }
+
+ /* Style for the images */
+ .img-container img {
+ display: block;
+ width: 100%;
+ height: auto;
+ }
+
+ .img-container .image-2 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 2;
+ clip-path: inset(0 0 0 50%); /* Start with the secondary image hidden */
+ }
+
+/* Style for the slider container */
+.slider-container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none; /* Ensure the slider container does not interfere with image clicks */
+ }
+
+ /* Style for the slider line */
+ .slider-line {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ width: 2px;
+ background: rgba(255, 255, 255, 0.7);
+ z-index: 3; /* Between the images and the slider thumb */
+ transform: translateX(-50%);
+ pointer-events: none;
+ }
+
+ /* Style for the image slider */
+ .image-slider {
+ -webkit-appearance: none;
+ appearance: none;
+ position: absolute;
+ z-index: 4; /* Higher than the slider line */
+ top: 50%;
+ width: 100%;
+ background: transparent;
+ outline: none;
+ cursor: pointer;
+ pointer-events: all; /* Allow the slider thumb to be clickable */
+ }
+
+ .image-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 8px;
+ height: 50px;
+ background: #ffffff;
+ cursor: grab;
+ border-radius: 1em;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
+ transition: background 0.3s;
+ z-index: 4; /* Ensure thumb is on top */
+ }
+
+ .image-slider::-moz-range-thumb {
+ width: 7px;
+ height: 50px;
+ background: #ffffff;
+ cursor: pointer;
+ border-radius: 1em;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
+ transition: background 0.3s;
+ z-index: 4; /* Ensure thumb is on top */
+ }
+
+ .image-slider::-webkit-slider-thumb:hover {
+ background: #dddddd;
+ }
+
+ .image-slider::-moz-range-thumb:hover {
+ background: #dddddd;
+ }
\ No newline at end of file
diff --git a/CSS/softwareskillsstyle.css b/CSS/softwareskillsstyle.css
new file mode 100644
index 0000000..df76826
--- /dev/null
+++ b/CSS/softwareskillsstyle.css
@@ -0,0 +1,48 @@
+.skills-panel {
+ background-color: #121212;
+ border-radius: 5px;
+ padding: 10px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start; /* Align items to the start (left) */
+ text-align: left; /* Align text to the left */
+ margin-bottom: 20px;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+}
+
+.skills-panel {
+ background: none;
+}
+
+.skills-panel {
+ display: flex;
+ flex-direction: column; /* Keep the title on top and tags below */
+ align-items: flex-start; /* Align items to the start (left) */
+ gap: 10px; /* Space between the title and the tag list */
+ padding: 10px;
+ border-radius: 5px;
+ width: 100%; /* Ensure it takes full width of its parent */
+}
+
+.skills-panel .software-tag-container {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start; /* Align items to the start (left) */
+ gap: 10px;
+ width: 100%;
+}
+
+.software-tag {
+ background-color: #2d2d2d;
+ color: lightgray;
+ padding: 5px 10px;
+ border-radius: .25em;
+ font-size: 14px;
+ white-space: nowrap; /* Prevent tags from breaking into multiple lines */
+}
+
+hr {
+ opacity: .05;
+}
\ No newline at end of file
diff --git a/CSS/statsstyle.css b/CSS/statsstyle.css
new file mode 100644
index 0000000..50ced41
--- /dev/null
+++ b/CSS/statsstyle.css
@@ -0,0 +1,63 @@
+.project-stats-container {
+ padding: 10px;
+}
+
+.stat {
+ display: flex;
+ align-items: center;
+ position: relative;
+ color: darkgrey;
+ font-size: 14px;
+ margin-right: 5px;
+ font-weight: 400;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+}
+
+.stat-icon {
+ margin-right: 10px;
+}
+
+.triangle-icon, .material-icon, .engine-icon, .size-icon, .td-icon, .workflow-icon, .collab-icon {
+ color: #373737; /* Pastel green */
+}
+
+.stat strong {
+ margin-right: 5px;
+ font-weight: 400;
+}
+
+.stat-info-icon {
+ margin-left: 5px;
+ color: #373737;
+ cursor: pointer;
+ position: relative;
+}
+
+.stat-info-icon:hover {
+ color: #f5ba13;
+}
+
+.tooltip {
+ display: none;
+ position: fixed; /* Use fixed positioning */
+ background-color: #333;
+ color: #fff;
+ padding: 5px 10px;
+ border-radius: .25em;
+ z-index: 1000; /* Ensure tooltip is above other elements */
+ font-size: 12px;
+ transition: opacity 0.3s ease;
+ white-space: nowrap;
+}
+
+.tooltip::after {
+ content: '';
+ position: absolute;
+ bottom: -5px;
+ left: 10px;
+ margin-left: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: #333 transparent transparent transparent;
+}
diff --git a/CSS/tagsstyle.css b/CSS/tagsstyle.css
new file mode 100644
index 0000000..4038015
--- /dev/null
+++ b/CSS/tagsstyle.css
@@ -0,0 +1,20 @@
+/* Project Tags Container Styling */
+.project-tags-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ padding-left: 10px;
+ padding-bottom: 10px;
+ padding-top: 10px;
+}
+
+.software-tag {
+ background-color: #2d2d2d;
+ color: darkgray;
+ padding: 5px 10px;
+ border-radius: 0.25em;
+ font-size: 14px;
+ -webkit-user-select: none; /* Safari */
+ user-select: none;
+ white-space: nowrap; /* Prevent tags from breaking into multiple lines */
+}
\ No newline at end of file
diff --git a/CSS/thumbnailstyle.css b/CSS/thumbnailstyle.css
new file mode 100644
index 0000000..8fac75b
--- /dev/null
+++ b/CSS/thumbnailstyle.css
@@ -0,0 +1,160 @@
+#thumbnail-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); /* Ensure items fill the space */
+ gap: 5px; /* Consistent gap between items */
+ box-sizing: border-box;
+ width: 100%; /* Ensure it spans the full width */
+ align-content: start; /* Ensure consistent vertical gaps */
+}
+
+@keyframes slideInLeft {
+ from {
+ transform: translateX(-30px);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+.thumbnail {
+ border-radius: 0.2em;
+ overflow: hidden;
+ position: relative;
+ cursor: pointer;
+ opacity: 1;
+ transition: opacity 0.3s;
+ background-color: #1e1e1e;
+ width: 100%; /* Full width of grid item */
+ padding-top: 100%; /* Makes the height equal to the width, keeping it square */
+ position: relative; /* Contain absolutely positioned elements */
+}
+
+.thumbnail {
+ border-radius: 0.2em;
+ overflow: hidden;
+ position: relative;
+ cursor: pointer;
+ opacity: 1;
+ transition: opacity 0.3s;
+ background-color: #1e1e1e;
+ width: 100%; /* Full width of grid item */
+ padding-top: 100%; /* Makes the height equal to the width, keeping it square */
+ position: relative; /* Contain absolutely positioned elements */
+}
+
+.thumbnail img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: block;
+ object-fit: cover;
+ object-position: center center;
+}
+
+.thumbnail-title {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ right: 10px;
+ z-index: 2;
+ color: white;
+ font-size: 18px;
+ opacity: 0;
+ transition: opacity 0.5s;
+
+}
+
+.thumbnail:hover .thumbnail-title {
+ opacity: 1;
+}
+
+.thumbnail:hover::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ transition: opacity 0.5s;
+ background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.9));
+ z-index: 1;
+}
+
+.overlay-icon {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ font-size: 14px;
+ color: white;
+ margin-right: 5px;
+ opacity: 0;
+ transition: opacity 0.5s;
+ z-index: 2;
+}
+
+.thumbnail:hover .overlay-icon {
+ opacity: 1;
+ animation: slideInLeft 0.5s forwards;
+}
+
+.overlay-icon + .overlay-icon {
+ left: 40px; /* Adjust spacing between icons */
+}
+
+.resize-buttons {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ z-index: 1000; /* Ensure they are above other elements */
+}
+
+.resize-button {
+ background-color: #121212;
+ color: white;
+ border: none;
+ border-radius: 50%;
+ width: 40px;
+ height: 40px;
+ font-size: 20px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.3s, color 0.3s;
+}
+
+.resize-button:hover {
+ background-color: #1e1e1e;
+ color: #edb049;
+}
+
+
+/* Responsive Design for small screens */
+@media screen and (min-width: 548px) and (max-width: 1280px) {
+ #thumbnail-container {
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); /* Adjusted min-width */
+ }
+ .thumbnail {
+ height: 200px;
+ }
+}
+
+@media screen and (min-width: 375px) and (max-width: 667px) {
+ #thumbnail-container {
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); /* Adjusted min-width */
+ }
+ .thumbnail {
+ height: 150px;
+ }
+ .thumbnail-title {
+ opacity: 0;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.75); /* Add this line */
+ }
+}
diff --git a/CSS/userinformationstyle.css b/CSS/userinformationstyle.css
new file mode 100644
index 0000000..a105625
--- /dev/null
+++ b/CSS/userinformationstyle.css
@@ -0,0 +1,75 @@
+/* User Information Styles */
+.user-info-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+}
+
+.user-info-panel {
+ border-radius: 5px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+}
+
+.user-name-link {
+ text-decoration: none;
+ color: inherit;
+}
+
+.user-name-link:hover {
+ color: #edb049; /* Example hover color */
+}
+
+.user-info-panel h1 {
+ font-size: 20px;
+ font-weight: 400;
+ margin-bottom: 5px;
+ color: #edb049;
+}
+
+.profile-pic {
+ width: 150px;
+ height: 150px;
+ border-radius: 50%;
+ margin-bottom: 10px;
+}
+
+/* Social Icons Styles */
+.social-icons {
+ display: flex;
+ gap: 1rem;
+ opacity: 1;
+ transition: opacity 0.5s;
+ margin-bottom: 10px;
+ margin-top: 5px;
+ color: grey !important;
+}
+
+.social-icons a {
+ color: #272727 !important;
+ font-size: 24px;
+ transition: color 0.3s;
+}
+
+.social-icons a:hover {
+ color: #edb049 !important;;
+}
+
+.user-location-container {
+ display: flex;
+ margin-top: 5px;
+ align-items: center;
+ gap: 5px; /* Adjust the gap between the icon and the text */
+}
+
+.material-symbols-outlined {
+ font-size: 1.2em; /* Adjust the size of the icon */
+ color: grey !important;
+}
+
+.user-location-container h2 {
+ margin: 0;
+}
diff --git a/Config/productions.txt b/Config/productions.txt
new file mode 100644
index 0000000..8bf0718
--- /dev/null
+++ b/Config/productions.txt
@@ -0,0 +1,27 @@
+# Each production should be separated by three dashes (---)
+# When you add one then productions.js will automatically add it after saving
+# Format: Title, Company, Time, Thumbnail URL, Description
+---
+Role
+Company
+Date (i.e 2020-present or 2016-2023)
+https://productionimage.jpg
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+---
+Role
+Company
+Date (i.e 2020-present or 2016-2023)
+https://productionimage.jpg
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+---
+Role
+Company
+Date (i.e 2020-present or 2016-2023)
+https://productionimage.jpg
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+---
+Role
+Company
+Date (i.e 2020-present or 2016-2023)
+https://productionimage.jpg
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
\ No newline at end of file
diff --git a/Config/projects.txt b/Config/projects.txt
new file mode 100644
index 0000000..e028900
--- /dev/null
+++ b/Config/projects.txt
@@ -0,0 +1,2 @@
+Example Project
+BigMegaGunExample
\ No newline at end of file
diff --git a/Config/recommendations.txt b/Config/recommendations.txt
new file mode 100644
index 0000000..17910b8
--- /dev/null
+++ b/Config/recommendations.txt
@@ -0,0 +1,23 @@
+Name
+https://profilepic.png
+Role @ Company
+Month/Day/Year
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+---
+Name
+https://profilepic.png
+Role @ Company
+Month/Day/Year
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+---
+Name
+https://profilepic.png
+Role @ Company
+Month/Day/Year
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+---
+Name
+https://profilepic.png
+Role @ Company
+Month/Day/Year
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
\ No newline at end of file
diff --git a/Config/skills.txt b/Config/skills.txt
new file mode 100644
index 0000000..c47452a
--- /dev/null
+++ b/Config/skills.txt
@@ -0,0 +1,11 @@
+3D Modelling
+Sculpting
+Texturing
+High Poly Modelling
+UV-Unwrapping
+Retopology
+Hand-painted Texturing
+Weapon Modelling
+Environment Art
+Tileable Materials
+punching faces
\ No newline at end of file
diff --git a/Config/software.txt b/Config/software.txt
new file mode 100644
index 0000000..bd3d895
--- /dev/null
+++ b/Config/software.txt
@@ -0,0 +1,10 @@
+Blender
+3ds Max
+Unity
+Zbrush
+Adobe Photoshop
+Topogun
+Substance 3d Painter
+Marvelous Designer
+Maya
+Marmoset Toolbag
\ No newline at end of file
diff --git a/Config/summary.txt b/Config/summary.txt
new file mode 100644
index 0000000..9779509
--- /dev/null
+++ b/Config/summary.txt
@@ -0,0 +1,3 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sodales justo vitae lacinia ornare. Pellentesque commodo mollis velit, finibus malesuada elit bibendum ut. Mauris sit amet nisi ante. Ut commodo auctor lorem id elementum. Ut at arcu congue, dictum est a, egestas neque. Aenean ipsum ipsum, elementum sit amet vehicula eu, congue sit amet risus. Aliquam a sem ac eros feugiat blandit vestibulum non diam.
+
+Pellentesque blandit leo nec tincidunt consectetur. Suspendisse auctor eu justo a vestibulum. Suspendisse bibendum, lacus id mattis finibus, turpis neque tempus arcu, at consectetur diam tellus in odio. Cras eu dolor laoreet ante dignissim sagittis malesuada vitae diam. Duis facilisis auctor quam eu porttitor. Pellentesque dictum dapibus nisi mollis hendrerit. Suspendisse at ante accumsan, venenatis diam eu, consectetur massa. Etiam non nisl in massa suscipit dignissim quis vitae mauris.
\ No newline at end of file
diff --git a/Config/userinformation.txt b/Config/userinformation.txt
new file mode 100644
index 0000000..e6ce839
--- /dev/null
+++ b/Config/userinformation.txt
@@ -0,0 +1,10 @@
+https://yourprofilepic.png
+Your Name
+Your Role @ Company
+Your Location
+https://x.com/ArtOfPilgrim
+http://youtube.com/c/ArtofPilgrim
+https://www.linkedin.com/in/thomasrwbutters/
+https://www.artstation.com/art_of_pilgrim
+https://discord.com/invite/WCnQ5bQa6Q
+youremail@email.com
\ No newline at end of file
diff --git a/JS/index.js b/JS/index.js
new file mode 100644
index 0000000..e5cce01
--- /dev/null
+++ b/JS/index.js
@@ -0,0 +1,119 @@
+// Function to create a thumbnail with overlay icons
+function createThumbnail(src, alt, galleryPageUrl, hasMultipleImages, hasVideo, hasYouTube, hasSketchfab) {
+ const thumbnailLink = document.createElement("a");
+ thumbnailLink.href = galleryPageUrl;
+
+ const thumbnailDiv = document.createElement("div");
+ thumbnailDiv.classList.add("thumbnail");
+
+ const thumbnailImg = document.createElement("img");
+ thumbnailImg.src = src;
+ thumbnailImg.alt = alt;
+
+ const thumbnailTitle = document.createElement("div");
+ thumbnailTitle.classList.add("thumbnail-title");
+ thumbnailTitle.innerText = alt;
+
+ let iconIndex = 0;
+
+ if (hasMultipleImages) {
+ const multipleImagesIcon = document.createElement("i");
+ multipleImagesIcon.className = "fa-solid fa-layer-group overlay-icon";
+ multipleImagesIcon.style.left = `${10 + iconIndex * 30}px`;
+ thumbnailDiv.appendChild(multipleImagesIcon);
+ iconIndex++;
+ }
+
+ if (hasVideo) {
+ const videoIcon = document.createElement("i");
+ videoIcon.className = "fa-solid fa-video overlay-icon";
+ videoIcon.style.left = `${10 + iconIndex * 30}px`;
+ thumbnailDiv.appendChild(videoIcon);
+ iconIndex++;
+ }
+
+ if (hasYouTube) {
+ const youtubeIcon = document.createElement("i");
+ youtubeIcon.className = "fa-brands fa-youtube overlay-icon";
+ youtubeIcon.style.left = `${10 + iconIndex * 30}px`;
+ thumbnailDiv.appendChild(youtubeIcon);
+ iconIndex++;
+ }
+
+ if (hasSketchfab) {
+ const sketchfabIcon = document.createElement("i");
+ sketchfabIcon.className = "fa-solid fa-cube overlay-icon";
+ sketchfabIcon.style.left = `${10 + iconIndex * 30}px`;
+ thumbnailDiv.appendChild(sketchfabIcon);
+ iconIndex++;
+ }
+
+ thumbnailDiv.appendChild(thumbnailImg);
+ thumbnailDiv.appendChild(thumbnailTitle);
+ thumbnailLink.appendChild(thumbnailDiv);
+
+ return thumbnailLink;
+}
+
+// Get the thumbnail container element
+const thumbnailContainer = document.getElementById("thumbnail-container");
+
+// Function to fetch and parse the description.txt file
+function fetchProjectData(projectName) {
+ const descriptionPath = `../Projects/${projectName}/description.txt`;
+ const mediaPath = `../Projects/${projectName}/media.txt`;
+
+ return Promise.all([
+ fetch(descriptionPath).then(response => response.text()),
+ fetch(mediaPath).then(response => response.text())
+ ])
+ .then(([descriptionText, mediaText]) => {
+ const [title, description, tags, thumbnailUrl, htmlFileName] = descriptionText.split('---').map(line => line.trim());
+ const galleryPageUrl = descriptionPath.replace('description.txt', htmlFileName);
+
+ const mediaLines = mediaText.split('\n').map(line => line.trim()).filter(line => line && !line.startsWith('#'));
+ const hasMultipleImages = mediaLines.filter(line => line.match(/\.(jpeg|jpg|gif|png)$/)).length > 1;
+ const hasVideo = mediaLines.some(line => line.match(/\.(mp4)$/));
+ const hasYouTube = mediaLines.some(line => line.includes('youtube.com'));
+ const hasSketchfab = mediaLines.some(line => line.includes('sketchfab.com'));
+
+ // Find the banner image
+ const bannerImageLine = mediaLines.find(line => line.endsWith('*'));
+ const bannerImageUrl = bannerImageLine ? bannerImageLine.replace('*', '').trim() : null;
+
+ return { src: thumbnailUrl, alt: title, galleryPageUrl, hasMultipleImages, hasVideo, hasYouTube, hasSketchfab, bannerImageUrl };
+ })
+ .catch(error => console.error('Error loading project data:', error));
+}
+
+// Function to fetch the projects.txt file
+function fetchProjects() {
+ return fetch('../Config/projects.txt')
+ .then(response => response.text())
+ .then(text => text.split('\n').map(line => line.trim()).filter(line => line))
+ .catch(error => console.error('Error loading projects:', error));
+}
+
+// Fetch projects and create thumbnails
+fetchProjects().then(projects => {
+ let bannerImageSet = false;
+ const fragment = document.createDocumentFragment(); // Create a document fragment
+
+ const fetchProjectDataPromises = projects.map(projectName => {
+ return fetchProjectData(projectName).then(artwork => {
+ const thumbnail = createThumbnail(artwork.src, artwork.alt, artwork.galleryPageUrl, artwork.hasMultipleImages, artwork.hasVideo, artwork.hasYouTube, artwork.hasSketchfab);
+ fragment.appendChild(thumbnail); // Append each thumbnail to the fragment
+
+ // Set the banner image if not already set
+ if (!bannerImageSet && artwork.bannerImageUrl) {
+ document.querySelector('.top-container').style.backgroundImage = `url(${artwork.bannerImageUrl})`;
+ bannerImageSet = true;
+ }
+ }).catch(error => console.error(`Error loading data for project: ${projectName}`, error));
+ });
+
+ // Once all project data has been fetched and processed, append the fragment to the container
+ Promise.all(fetchProjectDataPromises).then(() => {
+ thumbnailContainer.appendChild(fragment);
+ });
+});
diff --git a/JS/productions.js b/JS/productions.js
new file mode 100644
index 0000000..382e182
--- /dev/null
+++ b/JS/productions.js
@@ -0,0 +1,81 @@
+// Function to create a production card
+function createProductionCard(title, company, time, thumbnail, description) {
+ const card = document.createElement("div");
+ card.classList.add("production-subpanel");
+
+ const img = document.createElement("img");
+ img.src = thumbnail;
+ img.alt = title;
+
+ const detailsDiv = document.createElement("div");
+ detailsDiv.classList.add("production-details");
+
+ const titleElem = document.createElement("h2");
+ titleElem.textContent = title;
+
+ const companyElem = document.createElement("p");
+ companyElem.textContent = company;
+ companyElem.style.fontWeight = "bold"; // Inline style for bold text
+
+ const timeElem = document.createElement("p");
+ timeElem.textContent = time;
+ timeElem.style.fontStyle = "italic"; // Inline style for italic text
+
+ // Append text elements to the detailsDiv
+ detailsDiv.appendChild(titleElem);
+ detailsDiv.appendChild(companyElem);
+ detailsDiv.appendChild(timeElem);
+
+ // Create a new div for the description
+ const descDiv = document.createElement("div");
+ descDiv.classList.add("production-description");
+ const descElem = document.createElement("p");
+ descElem.textContent = description;
+ descDiv.appendChild(descElem);
+
+ // Create a container for the details and description
+ const contentContainer = document.createElement("div");
+ contentContainer.classList.add("production-content");
+ contentContainer.appendChild(detailsDiv);
+ contentContainer.appendChild(descDiv);
+
+ // Append img and contentContainer to the main card
+ card.appendChild(img);
+ card.appendChild(contentContainer);
+
+ return card;
+}
+
+// Fetch and append production cards on DOM content load
+document.addEventListener("DOMContentLoaded", async () => {
+ const productionsContainer = document.querySelector(".productions-subpanels");
+ if (!productionsContainer) {
+ console.error('Productions container not found');
+ return;
+ }
+
+ try {
+ const response = await fetch('../Config/productions.txt');
+ if (!response.ok) {
+ throw new Error(`Network response was not ok: ${response.statusText}`);
+ }
+ const text = await response.text();
+ const productions = text.split('---').map(prod => prod.trim()).filter(prod => prod);
+
+ const fragment = document.createDocumentFragment();
+
+ productions.forEach((prod, index) => {
+ const lines = prod.split('\n').map(line => line.trim()).filter(line => line && !line.startsWith('#'));
+ if (lines.length === 5) {
+ const card = createProductionCard(lines[0], lines[1], lines[2], lines[3], lines[4]);
+ fragment.appendChild(card);
+ } else {
+ console.error(`Invalid production data format at index ${index}:`, lines);
+ }
+ });
+
+ productionsContainer.appendChild(fragment);
+ } catch (error) {
+ console.error('Failed to load productions:', error);
+ }
+});
diff --git a/JS/projects.js b/JS/projects.js
new file mode 100644
index 0000000..7c706ce
--- /dev/null
+++ b/JS/projects.js
@@ -0,0 +1,412 @@
+document.addEventListener('DOMContentLoaded', () => {
+ let projects = [];
+
+ const fetchProjects = async () => {
+ try {
+ const response = await fetch('../../Config/projects.txt');
+ const text = await response.text();
+ return text.split('\n').map(line => line.trim()).filter(line => line);
+ } catch (error) {
+ console.error('Error loading projects:', error);
+ }
+ };
+
+ const fetchDescription = async () => {
+ try {
+ const response = await fetch('description.txt');
+ const text = await response.text();
+ const [title, description, tags] = text.split('---').map(line => line.trim());
+ document.getElementById('project-title').textContent = title;
+ document.title = title; // Set the document title as well
+
+ const descriptionContainer = document.getElementById('project-description');
+ const formattedDescription = convertUrlsToLinks(description);
+
+ if (description.length > 420) {
+ const shortDescription = formattedDescription.substring(0, 420);
+ descriptionContainer.innerHTML = `${shortDescription}...
Read More`;
+
+ const toggleDescription = document.getElementById('toggle-description');
+ const fullDescription = document.getElementById('full-description');
+ const ellipsis = document.getElementById('ellipsis');
+
+ toggleDescription.addEventListener('click', () => {
+ const isFullVisible = fullDescription.style.display === 'inline';
+ fullDescription.style.display = isFullVisible ? 'none' : 'inline';
+ ellipsis.style.display = isFullVisible ? 'inline' : 'none';
+ toggleDescription.textContent = isFullVisible ? 'Read More' : 'Read Less';
+ });
+ } else {
+ descriptionContainer.innerHTML = formattedDescription;
+ }
+
+ renderTags(tags);
+ } catch (error) {
+ console.error('Error loading project description:', error);
+ }
+ };
+
+ const convertUrlsToLinks = (text) => {
+ const urlPattern = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
+ return text.replace(urlPattern, (url) => {
+ const truncatedUrl = new URL(url).hostname;
+ return `${truncatedUrl}`;
+ });
+ };
+
+
+ const renderTags = (tags) => {
+ const tagsContainer = document.getElementById('project-tags');
+ tags.split(',').map(tag => tag.trim()).forEach(tag => {
+ const tagElement = document.createElement('div');
+ tagElement.className = 'software-tag';
+ tagElement.textContent = tag;
+ tagsContainer.appendChild(tagElement);
+ });
+ };
+
+ const loadMedia = async () => {
+ try {
+ const response = await fetch('media.txt');
+ const text = await response.text();
+ const mediaContainer = document.getElementById('project-media');
+ const lines = text.split('\n').map(line => line.trim()).filter(line => line && !line.startsWith('#'));
+
+ const basePath = window.location.pathname.split('/').slice(0, -1).join('/') + '/';
+ const fragment = document.createDocumentFragment();
+
+ let i = 0;
+ while (i < lines.length) {
+ let description = '';
+ let urls = [lines[i]];
+
+ // Check if the next line is a description
+ if (i + 1 < lines.length && !lines[i + 1].match(/\.(jpeg|jpg|gif|png|mp4|webm|mview)$/) && !lines[i + 1].includes('youtube.com') && !lines[i + 1].includes('sketchfab.com') && !lines[i + 1].includes(' // ')) {
+ description = lines[i + 1];
+ i += 1;
+ }
+
+ // Check if the line contains a pair of images
+ if (lines[i].includes(' // ')) {
+ urls = lines[i].split(' // ').map(url => url.trim());
+ }
+
+ // Adjust URLs for relative paths
+ urls = urls.map(url => (url.startsWith('http') ? url : basePath + url));
+
+ if (description.includes('(marmoset viewer)')) {
+ urls = [`${urls[0]}.mview`];
+ }
+
+ const mediaElement = createMediaElement(urls, description);
+ if (mediaElement) fragment.appendChild(mediaElement);
+ i += 1;
+ }
+
+ mediaContainer.appendChild(fragment);
+ } catch (error) {
+ console.error('Error loading project media:', error);
+ }
+ };
+
+ const createMarmosetViewerElement = (url) => {
+ const mediaElement = document.createElement('div');
+ mediaElement.className = 'media-item marmoset-item';
+
+ const iframe = document.createElement('iframe');
+ iframe.src = url;
+ iframe.allow = 'autoplay; fullscreen';
+ iframe.setAttribute('allowfullscreen', ''); // Ensure allowfullscreen is set correctly
+ iframe.title = 'Marmoset Viewer';
+
+ mediaElement.appendChild(iframe);
+ return mediaElement;
+ };
+
+ const createMediaElement = (urls, description) => {
+ let mediaElement;
+
+ if (urls[0].match(/\.(jpeg|jpg|gif|png)$/) != null) {
+ mediaElement = createImageElement(urls);
+ } else if (urls[0].match(/\.(mp4|webm)$/) != null) {
+ mediaElement = createVideoElement(urls[0]);
+ } else if (urls[0].includes('youtube.com')) {
+ mediaElement = createYouTubeElement(urls[0]);
+ } else if (urls[0].includes('sketchfab.com')) {
+ mediaElement = createSketchfabElement(urls[0]);
+ } else if (urls[0].match(/\.mview$/) != null) {
+ mediaElement = createMarmosetViewerElement(urls[0]);
+ }
+
+ if (mediaElement && description) {
+ const descElement = document.createElement('p');
+ descElement.className = 'media-description';
+ descElement.textContent = description;
+ mediaElement.appendChild(descElement);
+ }
+
+ return mediaElement;
+ };
+
+ const createImageElement = (urls) => {
+ const mediaElement = document.createElement('div');
+ mediaElement.className = 'media-item';
+
+ const imgContainer = document.createElement('div');
+ imgContainer.className = 'img-container';
+
+ const imgElement1 = document.createElement('img');
+ imgElement1.src = urls[0];
+ imgElement1.className = 'image-1';
+ imgElement1.alt = 'Primary image';
+ imgContainer.appendChild(imgElement1);
+
+ if (urls[1]) {
+ const imgElement2 = document.createElement('img');
+ imgElement2.src = urls[1];
+ imgElement2.className = 'image-2';
+ imgElement2.alt = 'Secondary image';
+ imgContainer.appendChild(imgElement2);
+
+ const sliderContainer = document.createElement('div');
+ sliderContainer.className = 'slider-container';
+
+ const sliderLine = document.createElement('div');
+ sliderLine.className = 'slider-line';
+
+ const slider = document.createElement('input');
+ slider.type = 'range';
+ slider.min = '0';
+ slider.max = '100';
+ slider.value = '50';
+ slider.className = 'image-slider';
+ slider.setAttribute('aria-label', 'Image comparison slider');
+ slider.addEventListener('input', () => {
+ const value = slider.value;
+ imgElement2.style.clipPath = `inset(0 0 0 ${value}%)`;
+ sliderLine.style.left = `calc(${value}% - 1px)`; // Ensure the line is aligned with the thumb
+ });
+
+ sliderContainer.appendChild(sliderLine);
+ sliderContainer.appendChild(slider);
+
+ mediaElement.appendChild(imgContainer);
+ mediaElement.appendChild(sliderContainer);
+ } else {
+ mediaElement.appendChild(imgContainer);
+ }
+
+ return mediaElement;
+ };
+
+ const createVideoElement = (url) => {
+ const mediaElement = document.createElement('div');
+ mediaElement.className = 'media-item';
+
+ const videoElement = document.createElement('video');
+ videoElement.src = url;
+ videoElement.controls = true;
+ videoElement.autoplay = true;
+ videoElement.loop = true;
+ videoElement.muted = true;
+ videoElement.title = 'Video content';
+
+ mediaElement.appendChild(videoElement);
+ return mediaElement;
+ };
+
+ const createYouTubeElement = (url) => {
+ const mediaElement = document.createElement('div');
+ mediaElement.className = 'media-item responsive-iframe-container';
+
+ const iframe = document.createElement('iframe');
+ iframe.src = `https://www.youtube.com/embed/${new URL(url).searchParams.get('v')}`;
+ iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
+ iframe.allowFullscreen = true;
+ iframe.title = 'YouTube video';
+
+ mediaElement.appendChild(iframe);
+ return mediaElement;
+ };
+
+ const createSketchfabElement = (url) => {
+ const mediaElement = document.createElement('div');
+ mediaElement.className = 'media-item responsive-iframe-container';
+
+ const sketchfabId = url.split('/').pop().split('-').pop();
+ const iframe = document.createElement('iframe');
+ iframe.src = `https://sketchfab.com/models/${sketchfabId}/embed`;
+ iframe.allow = 'autoplay; fullscreen; vr';
+ iframe.allowFullscreen = true;
+ iframe.title = 'Sketchfab model';
+
+ mediaElement.appendChild(iframe);
+ return mediaElement;
+ };
+
+ const fetchStats = async () => {
+ try {
+ const response = await fetch('stats.txt');
+ const text = await response.text();
+ const lines = text.split('\n').map(line => line.trim()).filter(line => line);
+ const statsContainer = document.getElementById('project-stats');
+ const iconMap = {
+ 'Triangles': 'change_history',
+ 'Materials': 'texture',
+ 'Texture Size': 'straighten',
+ 'Texel Density': 'square_foot',
+ 'Target Engine': 'gamepad',
+ 'Workflow': 'brush',
+ 'Collaborators': 'groups'
+ };
+
+ const iconClassMap = {
+ 'Triangles': 'triangle-icon',
+ 'Materials': 'material-icon',
+ 'Texture Size': 'size-icon',
+ 'Texel Density': 'td-icon',
+ 'Target Engine': 'engine-icon',
+ 'Workflow': 'workflow-icon',
+ 'Collaborators': 'collab-icon'
+ };
+
+ lines.forEach(line => {
+ let [key, value] = line.split(':').map(part => part.trim());
+ let info = '';
+
+ if (value.includes('(') && value.includes(')')) {
+ info = value.substring(value.indexOf('(') + 1, value.indexOf(')'));
+ value = value.substring(0, value.indexOf('(')).trim();
+ }
+
+ if (value) {
+ const statElement = document.createElement('div');
+ statElement.className = 'stat';
+
+ const icon = iconMap[key];
+ const iconClass = iconClassMap[key];
+ if (icon) {
+ const iconElement = document.createElement('span');
+ iconElement.className = `material-icons stat-icon ${iconClass}`;
+ iconElement.textContent = icon;
+ statElement.appendChild(iconElement);
+ }
+
+ const textElement = document.createElement('span');
+ textElement.innerHTML = `${key}: ${value}`;
+ statElement.appendChild(textElement);
+
+ if (info) {
+ const infoIcon = document.createElement('i');
+ infoIcon.className = 'fa-solid fa-circle-info stat-info-icon';
+ infoIcon.removeAttribute('title'); // Remove the title attribute to avoid default tooltip
+
+ const tooltip = document.createElement('div');
+ tooltip.className = 'tooltip';
+ tooltip.textContent = info;
+
+ statElement.appendChild(infoIcon);
+ statElement.appendChild(tooltip);
+
+ infoIcon.addEventListener('mouseover', (event) => {
+ tooltip.style.display = 'block';
+ positionTooltip(event, tooltip);
+ });
+
+ infoIcon.addEventListener('mousemove', (event) => {
+ positionTooltip(event, tooltip);
+ });
+
+ infoIcon.addEventListener('mouseout', () => {
+ tooltip.style.display = 'none';
+ });
+ }
+
+ statsContainer.appendChild(statElement);
+ }
+ });
+ } catch (error) {
+ console.error('Error loading project stats:', error);
+ }
+ };
+
+ const positionTooltip = (event, tooltip) => {
+ const tooltipRect = tooltip.getBoundingClientRect();
+ const viewportWidth = window.innerWidth;
+ const viewportHeight = window.innerHeight;
+
+ let top = event.clientY - tooltipRect.height - 10;
+ let left = event.clientX;
+
+ if (top < 0) {
+ top = event.clientY + 10;
+ }
+
+ if (left + tooltipRect.width > viewportWidth) {
+ left = viewportWidth - tooltipRect.width - 10;
+ }
+
+ tooltip.style.top = `${top}px`;
+ tooltip.style.left = `${left}px`;
+ };
+
+ const navigateProjects = async (direction) => {
+ const currentProject = window.location.pathname.split('/').slice(-2, -1)[0];
+ const currentIndex = projects.indexOf(currentProject);
+
+ if (currentIndex !== -1) {
+ let newIndex = currentIndex + direction;
+ if (newIndex < 0) newIndex = projects.length - 1;
+ if (newIndex >= projects.length) newIndex = 0;
+
+ const newProject = projects[newIndex];
+ try {
+ const response = await fetch(`../${newProject}/description.txt`);
+ const text = await response.text();
+ const htmlFileName = text.split('---')[4].trim(); // Extract the HTML filename from the description.txt
+ window.location.href = `../${newProject}/${htmlFileName}`;
+ } catch (error) {
+ console.error('Error loading next project description:', error);
+ }
+ }
+ };
+
+ // Back to Top Button Functionality
+ const backToTopButton = document.getElementById('back-to-top');
+
+ const mediaContainer = document.querySelector('.media-container');
+ mediaContainer.addEventListener('scroll', () => {
+ backToTopButton.style.display = mediaContainer.scrollTop > 1000 ? 'block' : 'none';
+ });
+
+ backToTopButton.addEventListener('click', () => {
+ mediaContainer.scrollTo({ top: 0, behavior: 'smooth' });
+ });
+
+ // Keyboard navigation
+ document.addEventListener('keydown', (event) => {
+ if (event.key === 'Escape') {
+ window.location.href = '../../index.html';
+ } else if (event.key === 'ArrowLeft') {
+ navigateProjects(-1);
+ } else if (event.key === 'ArrowRight') {
+ navigateProjects(1);
+ } else if (event.key === 'ArrowUp') {
+ mediaContainer.scrollBy({ top: -200, behavior: 'smooth' });
+ } else if (event.key === 'ArrowDown') {
+ mediaContainer.scrollBy({ top: 200, behavior: 'smooth' });
+ }
+ });
+
+ // Initialize the app
+ const init = async () => {
+ projects = await fetchProjects();
+ document.getElementById('prev-project').addEventListener('click', () => navigateProjects(-1));
+ document.getElementById('next-project').addEventListener('click', () => navigateProjects(1));
+ await fetchDescription();
+ await loadMedia();
+ await fetchStats(); // Fetch and display the stats
+ };
+
+ init();
+});
diff --git a/JS/recommendations.js b/JS/recommendations.js
new file mode 100644
index 0000000..3f76be8
--- /dev/null
+++ b/JS/recommendations.js
@@ -0,0 +1,160 @@
+document.addEventListener("DOMContentLoaded", async () => {
+ const recommendationContent = document.querySelector('.recommendation-content');
+ const dotsContainer = document.getElementById('recommendation-dots');
+
+ if (!recommendationContent || !dotsContainer) {
+ console.error('Recommendation content or dots container not found');
+ return;
+ }
+
+ try {
+ const response = await fetch('../Config/recommendations.txt');
+ if (!response.ok) {
+ throw new Error(`Network response was not ok: ${response.statusText}`);
+ }
+ const text = await response.text();
+ const recommendations = text.split('---').map(rec => rec.trim()).filter(rec => rec);
+
+ const recommendationFragment = document.createDocumentFragment();
+ const dotsFragment = document.createDocumentFragment();
+
+ recommendations.forEach((rec, index) => {
+ const lines = rec.split('\n').map(line => line.trim()).filter(line => line);
+ const hasAvatar = lines[1].startsWith('http');
+ const name = lines[0];
+ const avatar = hasAvatar ? lines[1] : '';
+ const position = hasAvatar ? lines[2] : lines[1];
+ const date = hasAvatar ? lines[3] : lines[2];
+ const quote = hasAvatar ? lines[4] : lines[3];
+
+ const recommendation = document.createElement("div");
+ recommendation.className = "recommendation";
+ if (index === 0) recommendation.classList.add("active");
+
+ recommendation.innerHTML = `
+
${date}
+"${quote}"
+ `; + + recommendationFragment.appendChild(recommendation); + + const dot = document.createElement("span"); + dot.classList.add("dot"); + if (index === 0) dot.classList.add("active"); + dot.dataset.index = index; + dotsFragment.appendChild(dot); + }); + + recommendationContent.appendChild(recommendationFragment); + dotsContainer.appendChild(dotsFragment); + + const recommendationsElements = document.querySelectorAll(".recommendation"); + const dots = document.querySelectorAll(".dot"); + let currentIndex = 0; + let isTransitioning = false; + + function showRecommendation(index, direction) { + if (isTransitioning) return; + isTransitioning = true; + + const current = recommendationsElements[currentIndex]; + const next = recommendationsElements[index]; + + if (direction === 'left') { + current.classList.add('recommendation-exit-left'); + next.classList.add('recommendation-enter-right'); + } else if (direction === 'right') { + current.classList.add('recommendation-exit-right'); + next.classList.add('recommendation-enter-left'); + } + + setTimeout(() => { + current.classList.remove('active', 'recommendation-exit-left', 'recommendation-exit-right'); + next.classList.add('active'); + next.classList.remove('recommendation-enter-left', 'recommendation-enter-right'); + currentIndex = index; + isTransitioning = false; + }, 500); // Match the CSS transition duration + + dots.forEach((dot, i) => { + dot.classList.toggle("active", i === index); + }); + } + + function debounce(func, wait) { + let timeout; + return function(...args) { + const context = this; + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(context, args), wait); + }; + } + + const debouncedShowRecommendation = debounce((index, direction) => showRecommendation(index, direction), 500); + + dotsContainer.addEventListener("click", (event) => { + if (event.target.classList.contains('dot')) { + const index = parseInt(event.target.dataset.index, 10); + if (index > currentIndex) { + debouncedShowRecommendation(index, 'left'); + } else if (index < currentIndex) { + debouncedShowRecommendation(index, 'right'); + } + } + }); + + // Swipe detection + let startX; + let isSwiping = false; + + recommendationContent.addEventListener('touchstart', (event) => { + startX = event.touches[0].clientX; + isSwiping = true; + }); + + recommendationContent.addEventListener('touchmove', (event) => { + if (!isSwiping) return; + const moveX = event.touches[0].clientX; + const diffX = startX - moveX; + + if (Math.abs(diffX) > 50) { + let newIndex; + if (diffX > 0) { + // Swiped left + newIndex = (currentIndex + 1) % recommendations.length; + } else { + // Swiped right + newIndex = (currentIndex - 1 + recommendations.length) % recommendations.length; + } + showRecommendation(newIndex); + isSwiping = false; + } + }); + + recommendationContent.addEventListener('touchend', () => { + isSwiping = false; + }); + + // Adjust height to ensure dots are always visible + function adjustHeight() { + const activeRecommendation = document.querySelector('.recommendation.active'); + if (activeRecommendation) { + const activeHeight = activeRecommendation.getBoundingClientRect().height; + recommendationContent.style.minHeight = `${activeHeight + 60}px`; // Add space for dots + } + } + + window.addEventListener('resize', adjustHeight); + adjustHeight(); // Initial call to set height + + } catch (error) { + console.error('Failed to load recommendations:', error); + } +}); diff --git a/JS/resize-thumbnails.js b/JS/resize-thumbnails.js new file mode 100644 index 0000000..751dfa2 --- /dev/null +++ b/JS/resize-thumbnails.js @@ -0,0 +1,52 @@ +document.addEventListener('DOMContentLoaded', () => { + const thumbnailContainer = document.getElementById('thumbnail-container'); + const plusButton = document.getElementById('plus-button'); + const minusButton = document.getElementById('minus-button'); + + // Retrieve the saved minWidth from localStorage or set default value + let minWidth = parseInt(localStorage.getItem('thumbnailMinWidth')) || 250; + + // Apply the initial size from localStorage + updateThumbnailSize(); + + plusButton.addEventListener('click', () => { + if (minWidth < 500) { + minWidth += 50; + updateThumbnailSize(); + localStorage.setItem('thumbnailMinWidth', minWidth); + } + }); + + minusButton.addEventListener('click', () => { + if (minWidth > 100) { + minWidth -= 50; + updateThumbnailSize(); + localStorage.setItem('thumbnailMinWidth', minWidth); + } + }); + + // Add event listener for the 'r', '+', and '-' keys + document.addEventListener('keydown', (event) => { + if (event.key === 'r') { + minWidth = 250; + updateThumbnailSize(); + localStorage.setItem('thumbnailMinWidth', minWidth); + } else if (event.key === '+' || event.key === '=') { // 'Equal' key for shift+'+' key on some keyboards + if (minWidth < 500) { + minWidth += 50; + updateThumbnailSize(); + localStorage.setItem('thumbnailMinWidth', minWidth); + } + } else if (event.key === '-') { + if (minWidth > 100) { + minWidth -= 50; + updateThumbnailSize(); + localStorage.setItem('thumbnailMinWidth', minWidth); + } + } + }); + + function updateThumbnailSize() { + thumbnailContainer.style.gridTemplateColumns = `repeat(auto-fill, minmax(${minWidth}px, 1fr))`; + } +}); diff --git a/JS/skills.js b/JS/skills.js new file mode 100644 index 0000000..da71e38 --- /dev/null +++ b/JS/skills.js @@ -0,0 +1,34 @@ +function addSkillsAndSoftware() { + const softwareContainer = document.querySelector('.skills-panel .software-tag-container:first-of-type'); + const skillsContainer = document.querySelector('.skills-panel .software-tag-container:last-of-type'); + + const fetchAndPopulate = async (url, container) => { + try { + const response = await fetch(url); + const text = await response.text(); + const itemsArray = text.split('\n').map(item => item.trim()).filter(item => item); + + // Create a document fragment to batch DOM manipulations + const fragment = document.createDocumentFragment(); + + // Populate the tags + itemsArray.forEach(item => { + const span = document.createElement("span"); + span.className = "software-tag"; + span.textContent = item; + fragment.appendChild(span); + }); + + // Append the fragment to the container + container.appendChild(fragment); + } catch (error) { + console.error(`Failed to load ${url}:`, error); + } + }; + + // Fetch and populate software and skills data + fetchAndPopulate('../Config/software.txt', softwareContainer); + fetchAndPopulate('../Config/skills.txt', skillsContainer); +} + +document.addEventListener("DOMContentLoaded", addSkillsAndSoftware); diff --git a/JS/summary.js b/JS/summary.js new file mode 100644 index 0000000..39721a3 --- /dev/null +++ b/JS/summary.js @@ -0,0 +1,19 @@ +document.addEventListener('DOMContentLoaded', async () => { + const summaryElement = document.querySelector('.summary-panel p'); + + if (!summaryElement) { + console.error('Summary element not found'); + return; + } + + try { + const response = await fetch('../Config/summary.txt'); + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.statusText}`); + } + const text = await response.text(); + summaryElement.textContent = text; + } catch (error) { + console.error('Failed to load summary:', error); + } +}); diff --git a/JS/userinformation.js b/JS/userinformation.js new file mode 100644 index 0000000..dd22068 --- /dev/null +++ b/JS/userinformation.js @@ -0,0 +1,133 @@ +function addUserInformation() { + // Determine the base path dynamically + let basePath = ''; + if (window.location.pathname.includes('/Projects/')) { + basePath = '../../Config/userinformation.txt'; + } else if (window.location.pathname.includes('/HTML/')) { + basePath = '../Config/userinformation.txt'; + } else { + basePath = 'Config/userinformation.txt'; // Default case if in root or unexpected location + } + + fetch(basePath) + .then(response => response.text()) + .then(data => { + const lines = data.split('\n').map(line => line.trim()); + const [profilePicUrl, profileName, profileRole, location, ...socials] = lines; + + // Get the container where the user info should be added + const container = document.querySelector('.top-container'); // Select the specific container + + // Create a document fragment for better performance + const fragment = document.createDocumentFragment(); + + // Create the user info panel + const userInfoPanel = document.createElement("div"); + userInfoPanel.className = "user-info-panel"; + + // Create and append the image + const img = document.createElement("img"); + img.src = profilePicUrl; // Your Profile Pic URL from txt + img.alt = "Profile Picture"; + img.className = "profile-pic"; + userInfoPanel.appendChild(img); + + // Create and append the user name as a link + const userNameLink = document.createElement("a"); + userNameLink.href = "../../index.html"; + userNameLink.className = "user-name-link"; + + const userName = document.createElement("h1"); + userName.className = "user-name"; + userName.textContent = profileName; // Your Profile Name from txt + userNameLink.appendChild(userName); + userInfoPanel.appendChild(userNameLink); + + // Create and append the user role + const userRole = document.createElement("h2"); + userRole.textContent = profileRole; // Your Current Title & Studio from txt + userInfoPanel.appendChild(userRole); + + // Create and append the location + const userLocationContainer = document.createElement("div"); + userLocationContainer.className = "user-location-container"; + + const locationIcon = document.createElement("span"); + locationIcon.className = "material-symbols-outlined"; + locationIcon.textContent = "near_me"; + userLocationContainer.appendChild(locationIcon); + + const userLocation = document.createElement("h2"); + userLocation.textContent = location; // Your Location from txt + userLocationContainer.appendChild(userLocation); + + userInfoPanel.appendChild(userLocationContainer); + + // Create and append the social icons + const socialIcons = document.createElement("div"); + socialIcons.className = "social-icons"; + userInfoPanel.appendChild(socialIcons); + + // Define the mapping of keywords to icon classes + const socialIconMap = { + 'x.com': "fa-brands fa-x-twitter", + 'facebook.com': "fa-brands fa-square-facebook", + 'discord.com': "fa-brands fa-discord", + 'discord.gg': "fa-brands fa-discord", + 'dsc.gg': "fa-brands fa-discord", + 'instagram.com': "fa-brands fa-instagram", + 'youtube.com': "fa-brands fa-youtube", + 'linkedin.com': "fab fa-linkedin", + 'artstation.com': "fa-brands fa-artstation", + 'github.com': "fab fa-github", + 'wordpress.com': "fab fa-wordpress", + 'vimeo.com': "fab fa-vimeo", + 'behance.net': "fab fa-behance", + 'playstation.com': "fab fa-playstation", + 'xbox.com': "fab fa-xbox", + 'vk.com': "fab fa-vk", + 'steamcommunity.com': "fab fa-steam", + 'tumblr.com': "fab fa-tumblr", + 'threads.net': "fab fa-threads", + 'patreon.com': "fab fa-patreon", + 'twitch.tv': "fab fa-twitch", + 'mixer.com': "fab fa-mixer", + 'mastodon.social': "fab fa-mastodon", + 'mailchimp.com': "fab fa-mailchimp", + 'email': "fas fa-envelope" + }; + + // Adding social links based on the detected type + socials.forEach(social => { + let iconClass; + let url = social; + + // Detect the type of social link + const socialType = Object.keys(socialIconMap).find(key => social.includes(key)) || 'email'; + iconClass = socialIconMap[socialType]; + if (socialType === 'email') { + url = `mailto:${social}`; + } + + if (iconClass) { + const a = document.createElement("a"); + a.href = url; + a.target = "_blank"; + const icon = document.createElement("i"); + icon.className = iconClass; + a.appendChild(icon); + socialIcons.appendChild(a); + } + }); + + // Append the user info panel to the fragment + fragment.appendChild(userInfoPanel); + + // Append the fragment to the container + container.appendChild(fragment); + }) + .catch(error => console.error('Error loading user information:', error)); +} + +// Call the function when the document is fully loaded +document.addEventListener("DOMContentLoaded", addUserInformation); diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f92dae5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Pilgrim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Projects/BigMegaGunExample/bigmegagun.html b/Projects/BigMegaGunExample/bigmegagun.html new file mode 100644 index 0000000..d18a840 --- /dev/null +++ b/Projects/BigMegaGunExample/bigmegagun.html @@ -0,0 +1,47 @@ + + + + + +