Compare commits
	
		
			8 commits
		
	
	
		
			35aad0589e
			...
			e934e3132f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e934e3132f | |||
| 6e98a0c133 | |||
| fd57d47956 | |||
| 2e961232f1 | |||
| 58a8b9825f | |||
| 44f24a07ac | |||
| 9115a41488 | |||
| 4cb566828a | 
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
node_modules/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,2 +0,0 @@
 | 
			
		|||
# pages
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										61
									
								
								dropdown.js
									
										
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *        InterstellarDevelopment website
 | 
			
		||||
 *        Copyright (C) 2024 interstellar_development
 | 
			
		||||
 *
 | 
			
		||||
 *        This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *        it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 *        published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 *        License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *        This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *        GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *        You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 *        along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
document.addEventListener("DOMContentLoaded", () => {
 | 
			
		||||
  const menu = document.querySelector(".menu");
 | 
			
		||||
  const burgerMenu = document.querySelector(".burger-menu");
 | 
			
		||||
 | 
			
		||||
  if (!menu || !burgerMenu) {
 | 
			
		||||
    console.warn(
 | 
			
		||||
      "Menu or burger menu element not found. Ensure they exist in the DOM."
 | 
			
		||||
    );
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Toggle the menu visibility
 | 
			
		||||
  function toggleMenu() {
 | 
			
		||||
    menu.classList.toggle("active");
 | 
			
		||||
 | 
			
		||||
    if (menu.classList.contains("active")) {
 | 
			
		||||
      // Add click listener to close menu when clicking outside
 | 
			
		||||
      document.addEventListener("click", closeMenu);
 | 
			
		||||
    } else {
 | 
			
		||||
      // Remove the click listener when menu is closed
 | 
			
		||||
      document.removeEventListener("click", closeMenu);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Close the menu if clicking outside of it
 | 
			
		||||
  function closeMenu(event) {
 | 
			
		||||
    if (
 | 
			
		||||
      !menu.contains(event.target) &&
 | 
			
		||||
      !event.target.classList.contains("burger-menu")
 | 
			
		||||
    ) {
 | 
			
		||||
      menu.classList.remove("active");
 | 
			
		||||
      document.removeEventListener("click", closeMenu);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Attach click event to the burger menu button
 | 
			
		||||
  burgerMenu.addEventListener("click", (event) => {
 | 
			
		||||
    event.stopPropagation(); // Prevent click from immediately triggering closeMenu
 | 
			
		||||
    toggleMenu();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
// @license-end
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
This favicon was generated using the following graphics from Twitter Twemoji:
 | 
			
		||||
 | 
			
		||||
- Graphics Title: 1f680.svg
 | 
			
		||||
- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
 | 
			
		||||
- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f680.svg
 | 
			
		||||
- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 14 KiB  | 
| 
		 Before Width: | Height: | Size: 47 KiB  | 
| 
		 Before Width: | Height: | Size: 13 KiB  | 
| 
		 Before Width: | Height: | Size: 751 B  | 
| 
		 Before Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 15 KiB  | 
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
 | 
			
		||||
							
								
								
									
										68
									
								
								footer.js
									
										
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1,68 +0,0 @@
 | 
			
		|||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *        interstellar_development website
 | 
			
		||||
 *        Copyright (C) 2024 interstellar_development
 | 
			
		||||
 *
 | 
			
		||||
 *        This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *        it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 *        published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 *        License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *        This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *        GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *        You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 *        along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class Footer extends HTMLElement {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  connectedCallback() {
 | 
			
		||||
    this.innerHTML = `
 | 
			
		||||
    <center>
 | 
			
		||||
        <footer>
 | 
			
		||||
        <div class="footer-content">
 | 
			
		||||
            <p>2024 Interstellar Development</p>
 | 
			
		||||
            <!-- Hidden Button -->
 | 
			
		||||
            <button class="secret-button">👀</button>
 | 
			
		||||
        </div>     
 | 
			
		||||
      </footer>
 | 
			
		||||
    </center>
 | 
			
		||||
    `;
 | 
			
		||||
 | 
			
		||||
    // Add event listener for button click
 | 
			
		||||
    this.querySelector(".secret-button").addEventListener("click", () => {
 | 
			
		||||
      window.open("secret/index.html", "_blank");
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("footer-component", Footer);
 | 
			
		||||
 | 
			
		||||
// CSS for the hidden button
 | 
			
		||||
const style = document.createElement("style");
 | 
			
		||||
style.textContent = `
 | 
			
		||||
.secret-button {
 | 
			
		||||
  display: none;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  bottom: 10px;
 | 
			
		||||
  right: 10px;
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  border: none;
 | 
			
		||||
  font-size: 24px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .footer-content:hover .secret-button {
 | 
			
		||||
    display: block;
 | 
			
		||||
    }
 | 
			
		||||
    `;
 | 
			
		||||
document.head.appendChild(style);
 | 
			
		||||
 | 
			
		||||
// @license-end
 | 
			
		||||
							
								
								
									
										46
									
								
								header.js
									
										
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *        interstellar_development website
 | 
			
		||||
 *        Copyright (C) 2024 interstellar_development
 | 
			
		||||
 *
 | 
			
		||||
 *        This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 *        it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 *        published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 *        License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *        This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *        GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *        You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 *        along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
class Header extends HTMLElement {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  connectedCallback() {
 | 
			
		||||
    this.innerHTML = `
 | 
			
		||||
    <header>
 | 
			
		||||
      <div class="header-content">
 | 
			
		||||
        <div><a href="index.html" class="project-name">Interstellar Development</a></div>
 | 
			
		||||
        <button class="burger-menu" onclick="toggleMenu()">☰</button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </header>
 | 
			
		||||
    <div class="div-menu">
 | 
			
		||||
        <ul class="menu">
 | 
			
		||||
          <li><a href="index.html#project">Our Projects</a></li>
 | 
			
		||||
          <li><a href="index.html#cards">Our Team</a></li>
 | 
			
		||||
          <li><a href="index.html#about">About us</a></li>
 | 
			
		||||
          <li><a href="index.html#vision">Our Vision</a></li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("header-component", Header);
 | 
			
		||||
// @license-end
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 156 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/star.jpg
									
										
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 545 KiB  | 
							
								
								
									
										657
									
								
								index.html
									
										
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1,233 +1,496 @@
 | 
			
		|||
<!--
 | 
			
		||||
    interstellar development website
 | 
			
		||||
    Copyright (C) 2024 interstellar_development
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU Affero General Public License as
 | 
			
		||||
    published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
    License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU Affero General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <script src="dropdown.js" type="text/javascript" defer></script>
 | 
			
		||||
    <script src="header.js" type="text/javascript" defer></script>
 | 
			
		||||
    <script src="footer.js" type="text/javascript" defer></script>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <title>Interstellar Development | Free and Open Source Software</title>
 | 
			
		||||
    <meta
 | 
			
		||||
      name="description"
 | 
			
		||||
      content="We develop high-quality free and open source software, with a focus on gaming solutions that respect user freedom."
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
    <!-- Font Awesome -->
 | 
			
		||||
    <link
 | 
			
		||||
      rel="stylesheet"
 | 
			
		||||
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
 | 
			
		||||
    />
 | 
			
		||||
    <!-- Google Fonts -->
 | 
			
		||||
    <link
 | 
			
		||||
      href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"
 | 
			
		||||
      rel="stylesheet"
 | 
			
		||||
    />
 | 
			
		||||
    <!-- Main CSS -->
 | 
			
		||||
    <link rel="stylesheet" href="src/styles/styles.css" />
 | 
			
		||||
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="favicon_io/apple-touch-icon.png"
 | 
			
		||||
      href="src/favicon/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="favicon_io/favicon-32x32.png"
 | 
			
		||||
      href="src/favicon/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="favicon_io/favicon-16x16.png"
 | 
			
		||||
      href="src/favicon/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="favicon_io/site.webmanifest" />
 | 
			
		||||
    <title>Interstellar Development</title>
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body>
 | 
			
		||||
    <!-- Custom header component -->
 | 
			
		||||
    <header-component></header-component>
 | 
			
		||||
    <!-- Animated background -->
 | 
			
		||||
    <div class="animated-bg"></div>
 | 
			
		||||
    <div class="stars" id="stars"></div>
 | 
			
		||||
 | 
			
		||||
    <article>
 | 
			
		||||
      <section id="project">
 | 
			
		||||
        <h1>Our Projects</h1>
 | 
			
		||||
 | 
			
		||||
        <h2>Our Games</h2>
 | 
			
		||||
        <ul>
 | 
			
		||||
          <a href="webGames/index.html" target="_blank" class="listElement">
 | 
			
		||||
            <li>Web game collection</li>
 | 
			
		||||
          </a>
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
        <ul>
 | 
			
		||||
          <li>
 | 
			
		||||
            <p>Previously we had more unfinished Games listed here.</p>
 | 
			
		||||
            <p>
 | 
			
		||||
              We decided against displaying them and giving people the
 | 
			
		||||
              impression we are working on them currently.
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>In the Future we will display the released games here</p>
 | 
			
		||||
          </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
        <h2>Our Other Projects</h2>
 | 
			
		||||
        <ul>
 | 
			
		||||
          <a href="foss_alternatives/" target="_blank" class="listElement">
 | 
			
		||||
            <li>FOSS Alternatives</li>
 | 
			
		||||
          </a>
 | 
			
		||||
          <a href="react/" target="_blank" class="listElement">
 | 
			
		||||
            <li>React</li>
 | 
			
		||||
          </a>
 | 
			
		||||
        </ul>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- Cards section with team members -->
 | 
			
		||||
      <h1>Our Team</h1>
 | 
			
		||||
      <section class="cards" id="cards">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <a
 | 
			
		||||
            href="https://interstellardevelopment.org/code/Patrick_Pluto"
 | 
			
		||||
            class="card-link"
 | 
			
		||||
            target="_blank"
 | 
			
		||||
          >
 | 
			
		||||
            <img src="images/Patrick.png" alt="Patrick" />
 | 
			
		||||
            <h3>Patrick_Pluto</h3>
 | 
			
		||||
            <p>
 | 
			
		||||
              The system administrator and our lead coder. He is the one you
 | 
			
		||||
              will need to blame for bugs in the games
 | 
			
		||||
            </p>
 | 
			
		||||
          </a>
 | 
			
		||||
    <!-- Navigation -->
 | 
			
		||||
    <nav id="navigation">
 | 
			
		||||
      <div class="nav-container">
 | 
			
		||||
        <div class="logo">
 | 
			
		||||
          <a href="#welcome-screen">Interstellar Development</a>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <a
 | 
			
		||||
            href="https://interstellardevelopment.org/code/sageTheDm"
 | 
			
		||||
            class="card-link"
 | 
			
		||||
            target="_blank"
 | 
			
		||||
          >
 | 
			
		||||
            <img src="images/sage.png" alt="Sage" />
 | 
			
		||||
            <h3>sageTheDM</h3>
 | 
			
		||||
            <p>
 | 
			
		||||
              Our mostly competent web developer and secondary coder, if you
 | 
			
		||||
              experience any bugs on the website or spelling mistake he is to
 | 
			
		||||
              blame
 | 
			
		||||
            </p>
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <button
 | 
			
		||||
          class="mobile-menu-btn"
 | 
			
		||||
          id="burger-menu"
 | 
			
		||||
          aria-label="Toggle navigation"
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fas fa-bars"></i>
 | 
			
		||||
        </button>
 | 
			
		||||
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <a
 | 
			
		||||
            href="https://interstellardevelopment.org/code/Patrick_Pluto"
 | 
			
		||||
            class="card-link"
 | 
			
		||||
            target="_blank"
 | 
			
		||||
          >
 | 
			
		||||
            <img src="images/nicolas.png" alt="Patrick" />
 | 
			
		||||
            <h3>St. Nicolaus</h3>
 | 
			
		||||
        <ul class="nav-links" id="main-nav">
 | 
			
		||||
          <li><a href="#projects">Projects</a></li>
 | 
			
		||||
          <li><a href="#team">Team</a></li>
 | 
			
		||||
          <li><a href="#about">About</a></li>
 | 
			
		||||
          <li><a href="#vision">Vision</a></li>
 | 
			
		||||
          <li><a href="#contact">Contact</a></li>
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
    </nav>
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
      <!-- Hero Section -->
 | 
			
		||||
      <section class="hero" id="welcome-screen">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="hero-content">
 | 
			
		||||
            <h1>
 | 
			
		||||
              Advancing <span class="highlight">Free Software</span> Development
 | 
			
		||||
            </h1>
 | 
			
		||||
            <p>
 | 
			
		||||
              Our game level and asset designer. He is responsible for all
 | 
			
		||||
              assets in our FreeFTF game. So if an asset looks ugly be mad at
 | 
			
		||||
              him. Also, we are not sure if he is human or just a drunk wizard
 | 
			
		||||
              cat but one thing is very clear he is still a novice at his job.
 | 
			
		||||
              We develop high-quality free and open source software solutions,
 | 
			
		||||
              with a focus on gaming and applications that respect user freedom.
 | 
			
		||||
            </p>
 | 
			
		||||
          </a>
 | 
			
		||||
            <div class="hero-buttons">
 | 
			
		||||
              <a href="#projects" class="btn btn-primary">
 | 
			
		||||
                <i class="fas fa-rocket"></i>
 | 
			
		||||
                View Our Projects
 | 
			
		||||
              </a>
 | 
			
		||||
              <a href="#about" class="btn btn-secondary">
 | 
			
		||||
                <i class="fas fa-info-circle"></i>
 | 
			
		||||
                Learn More
 | 
			
		||||
              </a>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- About Us section -->
 | 
			
		||||
      <section id="about">
 | 
			
		||||
        <h2>About Us</h2>
 | 
			
		||||
        <p>
 | 
			
		||||
          Welcome to Interstellar Development! We are a small, passionate
 | 
			
		||||
          international team dedicated to transforming the programming world
 | 
			
		||||
          into a free-and-open-source future. Our diverse backgrounds and
 | 
			
		||||
          experiences fuel our commitment to creating free and open-source
 | 
			
		||||
          software, particularly in the realm of gaming.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          Our journey began with a shared vision: to better organize our efforts
 | 
			
		||||
          in making free and open-source games more efficient and accessible. We
 | 
			
		||||
          recognized that many current free software games are either lacking in
 | 
			
		||||
          quality or simply do not exist. This realization inspired us to focus
 | 
			
		||||
          on developing games that are unplayable on GNU/Linux and FreeBSD
 | 
			
		||||
          systems, as well as creating free software alternatives for those in
 | 
			
		||||
          need.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          At Interstellar Development, we believe that true freedom for computer
 | 
			
		||||
          users can only be achieved through the use of free software. That’s
 | 
			
		||||
          why we are committed to licensing all of our projects under copyleft
 | 
			
		||||
          free and open-source software licenses, ensuring that our games remain
 | 
			
		||||
          free for everyone to enjoy, forever.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          While we are not currently accepting donations, we welcome your
 | 
			
		||||
          support in the form of feedback and suggestions for improvements. If
 | 
			
		||||
          you wish to contribute financially, we encourage you to donate to the
 | 
			
		||||
          Free Software Foundation, as without them, we would have never started
 | 
			
		||||
          this.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          Join us as we strive to create a vibrant community around free
 | 
			
		||||
          software and gaming. Together, we can make a difference and pave the
 | 
			
		||||
          way for a free future!
 | 
			
		||||
        </p>
 | 
			
		||||
      <!-- Projects Section -->
 | 
			
		||||
      <section id="projects" class="section">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="section-header">
 | 
			
		||||
            <h2 class="section-title">Our Projects</h2>
 | 
			
		||||
            <p class="section-subtitle">
 | 
			
		||||
              High-quality free software solutions built with passion and
 | 
			
		||||
              dedication
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <!-- Portfolio Filter Controls -->
 | 
			
		||||
          <div class="portfolio-controls">
 | 
			
		||||
            <div class="filter-buttons">
 | 
			
		||||
              <button class="filter-btn active" data-filter="all">All</button>
 | 
			
		||||
              <button class="filter-btn" data-filter="gaming">Gaming</button>
 | 
			
		||||
              <button class="filter-btn" data-filter="software">
 | 
			
		||||
                Software
 | 
			
		||||
              </button>
 | 
			
		||||
              <button class="filter-btn" data-filter="web">Web</button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="search-box">
 | 
			
		||||
              <input
 | 
			
		||||
                type="text"
 | 
			
		||||
                id="project-search"
 | 
			
		||||
                placeholder="Search projects..."
 | 
			
		||||
              />
 | 
			
		||||
              <i class="fas fa-search"></i>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="portfolio-grid">
 | 
			
		||||
            <!-- Gaming Projects -->
 | 
			
		||||
            <div class="portfolio-grid">
 | 
			
		||||
              <portfolio-card
 | 
			
		||||
                icon="fas fa-rocket"
 | 
			
		||||
                title="Projects Coming Soon"
 | 
			
		||||
                desc="We're currently working on exciting new free and open source software projects. Stay tuned for updates as we develop high-quality solutions that respect user freedom."
 | 
			
		||||
                tags="all"
 | 
			
		||||
                collaboration="Internal Project"
 | 
			
		||||
              ></portfolio-card>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <section id="vision">
 | 
			
		||||
        <h2>Our Vision</h2>
 | 
			
		||||
        <h2>Interstellar Development: Free Software is Our Passion</h2>
 | 
			
		||||
        <p>
 | 
			
		||||
          At Interstellar Development, we embarked on this journey to create a
 | 
			
		||||
          more organized and efficient approach to developing free and
 | 
			
		||||
          open-source software, with a particular focus on gaming. We recognized
 | 
			
		||||
          a significant gap in the availability and quality of free software
 | 
			
		||||
          games, which often fail to meet the expectations of users or simply do
 | 
			
		||||
          not exist. Our mission is to fill this void and elevate the standards
 | 
			
		||||
          of free gaming experiences.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          Our primary goal is to target games that are currently unplayable on
 | 
			
		||||
          GNU/Linux and FreeBSD systems. We aim to develop high-quality
 | 
			
		||||
          alternatives that not only provide enjoyable gameplay but also adhere
 | 
			
		||||
          to the principles of free software. Additionally, we are committed to
 | 
			
		||||
          identifying and addressing other areas within the software ecosystem
 | 
			
		||||
          that lack free alternatives, ensuring that users have access to a
 | 
			
		||||
          diverse range of tools and applications that respect their freedom.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          At Interstellar Development, we firmly believe that the path to true
 | 
			
		||||
          freedom for computer users lies in the adoption and promotion of free
 | 
			
		||||
          software. To this end, we are dedicated to licensing all of our
 | 
			
		||||
          projects under copyleft free and open-source software licenses. This
 | 
			
		||||
          commitment ensures that our games and software remain free for
 | 
			
		||||
          everyone to play, modify, and share, fostering a culture of
 | 
			
		||||
          collaboration and innovation within the community.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          We understand that community involvement is crucial to our success.
 | 
			
		||||
          While we are not currently accepting donations, we invite you to
 | 
			
		||||
          support us by providing feedback, reporting issues, and suggesting
 | 
			
		||||
          improvements. Your insights are invaluable in helping us refine our
 | 
			
		||||
          projects and enhance the user experience.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          If you feel compelled to contribute financially, we encourage you to
 | 
			
		||||
          consider donating to the Free Software Foundation. Their unwavering
 | 
			
		||||
          support and advocacy for free software principles have been
 | 
			
		||||
          instrumental in our journey, and your contributions to them help
 | 
			
		||||
          sustain the broader movement that enables us to create these games.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>
 | 
			
		||||
          Together, we can build a vibrant community around free software and
 | 
			
		||||
          gaming, paving the way for a future where everyone has access to
 | 
			
		||||
          high-quality, open-source alternatives. Join us in our mission to make
 | 
			
		||||
          a difference and champion the cause of free software for all!
 | 
			
		||||
        </p>
 | 
			
		||||
      </section>
 | 
			
		||||
    </article>
 | 
			
		||||
      <!-- Team Section -->
 | 
			
		||||
      <section id="team" class="section">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="section-header">
 | 
			
		||||
            <h2 class="section-title">Our Team</h2>
 | 
			
		||||
            <p class="section-subtitle">
 | 
			
		||||
              A small but dedicated international team committed to free
 | 
			
		||||
              software
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
    <!-- Custom footer component -->
 | 
			
		||||
    <footer-component></footer-component>
 | 
			
		||||
          <div class="team-grid">
 | 
			
		||||
            <div class="team-member">
 | 
			
		||||
              <div class="member-avatar">
 | 
			
		||||
                <i class="fas fa-server"></i>
 | 
			
		||||
              </div>
 | 
			
		||||
              <h3>Patrick</h3>
 | 
			
		||||
              <div class="team-role">
 | 
			
		||||
                Lead Programmer & System Administrator
 | 
			
		||||
              </div>
 | 
			
		||||
              <p>
 | 
			
		||||
                Patrick focuses on backend development and system
 | 
			
		||||
                administration, managing our infrastructure and core application
 | 
			
		||||
                logic with expertise in server-side technologies and DevOps.
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="team-member">
 | 
			
		||||
              <div class="member-avatar">
 | 
			
		||||
                <i class="fas fa-palette"></i>
 | 
			
		||||
              </div>
 | 
			
		||||
              <h3>Sage</h3>
 | 
			
		||||
              <div class="team-role">Frontend Developer & Communications</div>
 | 
			
		||||
              <p>
 | 
			
		||||
                Sage focuses on frontend development and is in charge of
 | 
			
		||||
                communication and UI/UX design, ensuring our projects have
 | 
			
		||||
                intuitive interfaces and excellent user experiences.
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- About Section -->
 | 
			
		||||
      <section id="about" class="section">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="section-header">
 | 
			
		||||
            <h2 class="section-title">About Us</h2>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="about-content">
 | 
			
		||||
            <p>
 | 
			
		||||
              Interstellar Development is a small international team dedicated
 | 
			
		||||
              to advancing free and open source software. Our diverse
 | 
			
		||||
              backgrounds and shared commitment to user freedom drive our
 | 
			
		||||
              development philosophy.
 | 
			
		||||
            </p>
 | 
			
		||||
 | 
			
		||||
            <p>
 | 
			
		||||
              We began with a vision to create high-quality free software,
 | 
			
		||||
              particularly in gaming where such options are often limited. We
 | 
			
		||||
              recognized that many free software games either lack polish or
 | 
			
		||||
              simply don't exist, inspiring us to focus on this under-served
 | 
			
		||||
              area.
 | 
			
		||||
            </p>
 | 
			
		||||
 | 
			
		||||
            <p>
 | 
			
		||||
              At Interstellar Development, we believe true digital freedom can
 | 
			
		||||
              only be achieved through free software. All our projects are
 | 
			
		||||
              released under copyleft licenses, ensuring they remain free for
 | 
			
		||||
              everyone to use, modify, and distribute.
 | 
			
		||||
            </p>
 | 
			
		||||
 | 
			
		||||
            <p>
 | 
			
		||||
              While we don't currently accept donations, we welcome feedback and
 | 
			
		||||
              suggestions. If you wish to support the free software movement, we
 | 
			
		||||
              encourage donations to the Free Software Foundation, whose work
 | 
			
		||||
              continues to inspire our journey.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- Vision Section -->
 | 
			
		||||
      <section id="vision" class="section">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="section-header">
 | 
			
		||||
            <h2 class="section-title">Our Vision</h2>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="vision-content">
 | 
			
		||||
            <div class="vision-text">
 | 
			
		||||
              <p>
 | 
			
		||||
                Our mission is to create high-quality free software
 | 
			
		||||
                alternatives, particularly for games that are unavailable on
 | 
			
		||||
                free operating systems like GNU/Linux and FreeBSD. We aim to
 | 
			
		||||
                address gaps in the software ecosystem where free alternatives
 | 
			
		||||
                are lacking.
 | 
			
		||||
              </p>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="principles-grid">
 | 
			
		||||
              <div class="principle-card">
 | 
			
		||||
                <div class="principle-icon">
 | 
			
		||||
                  <i class="fas fa-unlock"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
                <h4>Software Freedom</h4>
 | 
			
		||||
                <p>
 | 
			
		||||
                  All our projects are released under copyleft licenses to
 | 
			
		||||
                  ensure user freedoms are protected and preserved.
 | 
			
		||||
                </p>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div class="principle-card">
 | 
			
		||||
                <div class="principle-icon">
 | 
			
		||||
                  <i class="fas fa-users"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
                <h4>Community Collaboration</h4>
 | 
			
		||||
                <p>
 | 
			
		||||
                  We believe in building software with and for the community,
 | 
			
		||||
                  welcoming contributions and feedback.
 | 
			
		||||
                </p>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div class="principle-card">
 | 
			
		||||
                <div class="principle-icon">
 | 
			
		||||
                  <i class="fas fa-star"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
                <h4>Quality Standards</h4>
 | 
			
		||||
                <p>
 | 
			
		||||
                  We strive to create software that matches or exceeds the
 | 
			
		||||
                  quality of proprietary alternatives.
 | 
			
		||||
                </p>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- Stats Section -->
 | 
			
		||||
      <section class="stats-section">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="stats-container">
 | 
			
		||||
            <div class="stat-item">
 | 
			
		||||
              <div class="stat-number" data-count="0">0</div>
 | 
			
		||||
              <div class="stat-label">Projects</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="stat-item">
 | 
			
		||||
              <div class="stat-number" data-count="2">0</div>
 | 
			
		||||
              <div class="stat-label">Team Members</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="stat-item">
 | 
			
		||||
              <div class="stat-number" data-count="3">0</div>
 | 
			
		||||
              <div class="stat-label">Years Active</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="stat-item">
 | 
			
		||||
              <div class="stat-number" data-count="100">0</div>
 | 
			
		||||
              <div class="stat-label">% Free Software</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- Contact Section -->
 | 
			
		||||
      <section id="contact" class="section">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="section-header">
 | 
			
		||||
            <h2 class="section-title">Get In Touch</h2>
 | 
			
		||||
            <p class="section-subtitle">
 | 
			
		||||
              Have questions about our projects or want to contribute? We'd love
 | 
			
		||||
              to hear from you.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="contact-container">
 | 
			
		||||
            <div class="contact-info">
 | 
			
		||||
              <div class="contact-methods">
 | 
			
		||||
                <div class="contact-item">
 | 
			
		||||
                  <div class="contact-icon">
 | 
			
		||||
                    <i class="fas fa-envelope"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="contact-details">
 | 
			
		||||
                    <h4>Email</h4>
 | 
			
		||||
                    <a href="mailto:info@interstellardevelopment.org"
 | 
			
		||||
                      >info@interstellardevelopment.org</a
 | 
			
		||||
                    >
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="contact-item">
 | 
			
		||||
                  <div class="contact-icon">
 | 
			
		||||
                    <i class="fas fa-code-branch"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="contact-details">
 | 
			
		||||
                    <h4>Source Code</h4>
 | 
			
		||||
                    <a
 | 
			
		||||
                      href="https://interstellardevelopment.org/code/interstellar_development"
 | 
			
		||||
                      target="_blank"
 | 
			
		||||
                      >View Our Projects</a
 | 
			
		||||
                    >
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <form class="contact-form" id="contact-form">
 | 
			
		||||
              <div class="form-group">
 | 
			
		||||
                <label for="name">Your Name</label>
 | 
			
		||||
                <input type="text" id="name" name="name" required />
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div class="form-group">
 | 
			
		||||
                <label for="email">Email Address</label>
 | 
			
		||||
                <input type="email" id="email" name="email" required />
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div class="form-group">
 | 
			
		||||
                <label for="subject">Subject</label>
 | 
			
		||||
                <select id="subject" name="subject" required>
 | 
			
		||||
                  <option value="">Select a subject</option>
 | 
			
		||||
                  <option value="contribution">Project Contribution</option>
 | 
			
		||||
                  <option value="feedback">Feedback</option>
 | 
			
		||||
                  <option value="inquiry">General Inquiry</option>
 | 
			
		||||
                  <option value="bug">Bug Report</option>
 | 
			
		||||
                  <option value="other">Other</option>
 | 
			
		||||
                </select>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div class="form-group">
 | 
			
		||||
                <label for="message">Message</label>
 | 
			
		||||
                <textarea
 | 
			
		||||
                  id="message"
 | 
			
		||||
                  name="message"
 | 
			
		||||
                  rows="5"
 | 
			
		||||
                  required
 | 
			
		||||
                ></textarea>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <button type="submit" class="btn btn-primary">
 | 
			
		||||
                <i class="fas fa-paper-plane"></i>
 | 
			
		||||
                Send Message
 | 
			
		||||
              </button>
 | 
			
		||||
            </form>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
 | 
			
		||||
      <!-- Footer -->
 | 
			
		||||
      <footer class="footer">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <div class="footer-content">
 | 
			
		||||
            <div class="footer-brand">
 | 
			
		||||
              <div class="logo">Interstellar Development</div>
 | 
			
		||||
              <p>
 | 
			
		||||
                Developing free and open source software with a focus on user
 | 
			
		||||
                freedom and quality.
 | 
			
		||||
              </p>
 | 
			
		||||
 | 
			
		||||
              <div class="footer-social">
 | 
			
		||||
                <a
 | 
			
		||||
                  href="https://interstellardevelopment.org/code/interstellar_development"
 | 
			
		||||
                  class="social-link"
 | 
			
		||||
                  target="_blank"
 | 
			
		||||
                  aria-label="Source Code"
 | 
			
		||||
                >
 | 
			
		||||
                  <i class="fas fa-code-branch"></i>
 | 
			
		||||
                </a>
 | 
			
		||||
                <a href="#" class="social-link" aria-label="Discord">
 | 
			
		||||
                  <i class="fab fa-discord"></i>
 | 
			
		||||
                </a>
 | 
			
		||||
                <a href="#" class="social-link" aria-label="Mastodon">
 | 
			
		||||
                  <i class="fab fa-mastodon"></i>
 | 
			
		||||
                </a>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="footer-links">
 | 
			
		||||
              <h4>Navigation</h4>
 | 
			
		||||
              <ul>
 | 
			
		||||
                <li><a href="#projects">Projects</a></li>
 | 
			
		||||
                <li><a href="#team">Team</a></li>
 | 
			
		||||
                <li><a href="#about">About</a></li>
 | 
			
		||||
                <li><a href="#vision">Vision</a></li>
 | 
			
		||||
                <li><a href="#contact">Contact</a></li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="footer-links">
 | 
			
		||||
              <h4>Projects</h4>
 | 
			
		||||
              <ul>
 | 
			
		||||
                <li>
 | 
			
		||||
                  <a href="" target="_blank">Coming soon</a>
 | 
			
		||||
                </li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="footer-links">
 | 
			
		||||
              <h4>Resources</h4>
 | 
			
		||||
              <ul>
 | 
			
		||||
                <li>
 | 
			
		||||
                  <a href="https://www.fsf.org/" target="_blank"
 | 
			
		||||
                    >Free Software Foundation</a
 | 
			
		||||
                  >
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>
 | 
			
		||||
                  <a href="https://www.gnu.org/" target="_blank">GNU Project</a>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>
 | 
			
		||||
                  <a
 | 
			
		||||
                    href="https://interstellardevelopment.org/code/interstellar_development"
 | 
			
		||||
                    target="_blank"
 | 
			
		||||
                    >Source Code</a
 | 
			
		||||
                  >
 | 
			
		||||
                </li>
 | 
			
		||||
                <li>
 | 
			
		||||
                  <a href="mailto:info@interstellardevelopment.org"
 | 
			
		||||
                    >Contact Us</a
 | 
			
		||||
                  >
 | 
			
		||||
                </li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="footer-bottom">
 | 
			
		||||
            <div class="footer-bottom-content">
 | 
			
		||||
              <p>© 2025 Interstellar Development. All rights reserved.</p>
 | 
			
		||||
              <div class="footer-badges">
 | 
			
		||||
                <span class="badge">
 | 
			
		||||
                  <i class="fas fa-heart"></i>
 | 
			
		||||
                  Committed to Free Software
 | 
			
		||||
                </span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </footer>
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <!-- General scripts -->
 | 
			
		||||
    <script src="src/js/main.js"></script>
 | 
			
		||||
    <script src="src/js/navigation.js"></script>
 | 
			
		||||
    <script src="src/js/form.js"></script>
 | 
			
		||||
    <script src="src/js/portfolioCard.js"></script>
 | 
			
		||||
    <script src="src/js/portfolioFilter.js"></script>
 | 
			
		||||
    <script src="src/js/overview.js"></script>
 | 
			
		||||
    <script src="src/js/sectionTracker.js"></script>
 | 
			
		||||
 | 
			
		||||
    <!-- Animation scripts -->
 | 
			
		||||
    <script type="module" src="src/js/animation/starBackground.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,47 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Game Landing Page</title>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div class="landing-page">
 | 
			
		||||
      <div class="game-background">
 | 
			
		||||
        <!-- Game is embedded or shown as background -->
 | 
			
		||||
        <canvas id="gameCanvas"></canvas>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="content">
 | 
			
		||||
        <h1>Welcome to the Asteroid Game!</h1>
 | 
			
		||||
        <p>
 | 
			
		||||
          In this game, you control a spaceship that shoots at asteroids to
 | 
			
		||||
          avoid destruction and collect items for power-ups.
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>Your goal is to survive as long as possible while scoring points!</p>
 | 
			
		||||
        <button onclick="window.location.href='secret.html'">Play Game</button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script src="app.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,414 +0,0 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
// Canvas setup
 | 
			
		||||
const canvas = document.getElementById("gameCanvas");
 | 
			
		||||
const ctx = canvas.getContext("2d");
 | 
			
		||||
const targetFPS = 60;
 | 
			
		||||
const targetFrameTime = 1000 / targetFPS;
 | 
			
		||||
canvas.width = window.innerWidth;
 | 
			
		||||
canvas.height = window.innerHeight;
 | 
			
		||||
 | 
			
		||||
let lastFrameTime = performance.now();
 | 
			
		||||
 | 
			
		||||
// Game elements
 | 
			
		||||
const player = {
 | 
			
		||||
  x: canvas.width / 2,
 | 
			
		||||
  y: canvas.height - 60,
 | 
			
		||||
  width: 40,
 | 
			
		||||
  height: 40,
 | 
			
		||||
  color: "white",
 | 
			
		||||
  speed: 5,
 | 
			
		||||
  dx: 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let bullets = [];
 | 
			
		||||
let asteroids = [];
 | 
			
		||||
let items = [];
 | 
			
		||||
let score = 0;
 | 
			
		||||
let totalBulletsFired = 0;
 | 
			
		||||
let isGameOver = false;
 | 
			
		||||
let lastBulletTime = 0;
 | 
			
		||||
let ammo = 100; // Ammo counter
 | 
			
		||||
 | 
			
		||||
// Difficulty control
 | 
			
		||||
let asteroidSpawnInterval = 800; // Faster spawn rate
 | 
			
		||||
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
 | 
			
		||||
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
 | 
			
		||||
 | 
			
		||||
// Controls
 | 
			
		||||
let canShoot = true; // Flag to control shooting
 | 
			
		||||
let rapidFireActive = false;
 | 
			
		||||
let shotgunActive = false;
 | 
			
		||||
let rainbowActive = false;
 | 
			
		||||
let lastShotgunTime = 0;
 | 
			
		||||
 | 
			
		||||
// Controls for sphere effects
 | 
			
		||||
let blueSphereCooldown = 0;
 | 
			
		||||
let yellowSphereCooldown = 0;
 | 
			
		||||
let greenSphereCooldown = 0;
 | 
			
		||||
let rainbowSphereCooldown = 0;
 | 
			
		||||
 | 
			
		||||
// Sphere types
 | 
			
		||||
const sphereTypes = ["blue", "yellow", "green", "rainbow"];
 | 
			
		||||
 | 
			
		||||
/// Control for left button press and release
 | 
			
		||||
function btnMoveLeft(isPressed) {
 | 
			
		||||
  if (isPressed) {
 | 
			
		||||
    player.dx = -player.speed; // Start moving left
 | 
			
		||||
  } else {
 | 
			
		||||
    player.dx = 0; // Stop moving when button is released
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Control for shoot button click (simulates spacebar press)
 | 
			
		||||
function btnShoot() {
 | 
			
		||||
  if (canShoot && !isGameOver) {
 | 
			
		||||
    shootBullet();
 | 
			
		||||
    canShoot = false; // Prevent shooting until the button is "released"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Control for right button press and release
 | 
			
		||||
function btnMoveRight(isPressed) {
 | 
			
		||||
  if (isPressed) {
 | 
			
		||||
    player.dx = player.speed; // Start moving right
 | 
			
		||||
  } else {
 | 
			
		||||
    player.dx = 0; // Stop moving when button is released
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.getElementById("shootBtn").addEventListener("mouseup", () => {
 | 
			
		||||
  canShoot = true; // Allow shooting again when button is released
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
window.addEventListener("keydown", (e) => {
 | 
			
		||||
  if (e.key === "ArrowLeft" || e.key === "a") player.dx = -player.speed;
 | 
			
		||||
  if (e.key === "ArrowRight" || e.key === "d") player.dx = player.speed;
 | 
			
		||||
 | 
			
		||||
  // Shoot only if it's not a hold, and we can shoot
 | 
			
		||||
  if (e.key === " " && canShoot && !isGameOver) {
 | 
			
		||||
    shootBullet();
 | 
			
		||||
    canShoot = false; // Prevent shooting until the key is released
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (e.key === "r" && isGameOver) restartGame();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
window.addEventListener("keyup", (e) => {
 | 
			
		||||
  // Stop moving when either the arrow keys or the 'a'/'d' keys are released
 | 
			
		||||
  if (
 | 
			
		||||
    e.key === "ArrowLeft" ||
 | 
			
		||||
    e.key === "ArrowRight" ||
 | 
			
		||||
    e.key === "a" ||
 | 
			
		||||
    e.key === "d"
 | 
			
		||||
  ) {
 | 
			
		||||
    player.dx = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Allow shooting again when the space key is released
 | 
			
		||||
  if (e.key === " ") {
 | 
			
		||||
    canShoot = true;
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Bullet mechanics with cooldown
 | 
			
		||||
function shootBullet() {
 | 
			
		||||
  const now = Date.now();
 | 
			
		||||
  if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
 | 
			
		||||
  if (ammo <= 0) return; // Prevent shooting if ammo is empty
 | 
			
		||||
  lastBulletTime = now;
 | 
			
		||||
  ammo--; // Decrease ammo
 | 
			
		||||
 | 
			
		||||
  totalBulletsFired++; // Increment total bullets fired
 | 
			
		||||
 | 
			
		||||
  if (rapidFireActive) {
 | 
			
		||||
    // If rapid fire is active, fire bullets continuously with a short delay
 | 
			
		||||
    for (let i = 0; i < 3; i++) {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        bullets.push({
 | 
			
		||||
          x: player.x + player.width / 2 - 2.5,
 | 
			
		||||
          y: player.y,
 | 
			
		||||
          width: 5,
 | 
			
		||||
          height: 10,
 | 
			
		||||
          color: "yellow",
 | 
			
		||||
          speed: 7,
 | 
			
		||||
        });
 | 
			
		||||
      }, i * 50); // Fire bullets with 50ms delay between shots
 | 
			
		||||
    }
 | 
			
		||||
  } else if (shotgunActive) {
 | 
			
		||||
    // Shotgun effect, firing 3 bullets with a spread
 | 
			
		||||
    for (let i = -1; i <= 1; i++) {
 | 
			
		||||
      bullets.push({
 | 
			
		||||
        x: player.x + player.width / 2 - 2.5,
 | 
			
		||||
        y: player.y,
 | 
			
		||||
        width: 5,
 | 
			
		||||
        height: 10,
 | 
			
		||||
        color: "yellow",
 | 
			
		||||
        speed: 7,
 | 
			
		||||
        angle: i * 10, // Spray the bullets at different angles
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // Normal bullet
 | 
			
		||||
    bullets.push({
 | 
			
		||||
      x: player.x + player.width / 2 - 2.5,
 | 
			
		||||
      y: player.y,
 | 
			
		||||
      width: 5,
 | 
			
		||||
      height: 10,
 | 
			
		||||
      color: "yellow",
 | 
			
		||||
      speed: 7,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Generate random color
 | 
			
		||||
function getRandomColor() {
 | 
			
		||||
  const colors = ["red", "blue", "green", "orange", "purple", "pink"];
 | 
			
		||||
  return colors[Math.floor(Math.random() * colors.length)];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Asteroid mechanics
 | 
			
		||||
function createAsteroid() {
 | 
			
		||||
  const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
 | 
			
		||||
  asteroids.push({
 | 
			
		||||
    x: Math.random() * canvas.width,
 | 
			
		||||
    y: -size,
 | 
			
		||||
    width: size,
 | 
			
		||||
    height: size,
 | 
			
		||||
    color: getRandomColor(),
 | 
			
		||||
    speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier, // Faster initial speed
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Item mechanics
 | 
			
		||||
function createItem() {
 | 
			
		||||
  const randomType =
 | 
			
		||||
    sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
 | 
			
		||||
  const size = 30;
 | 
			
		||||
  const x = Math.random() * canvas.width;
 | 
			
		||||
  items.push({
 | 
			
		||||
    x: x,
 | 
			
		||||
    y: -size,
 | 
			
		||||
    width: size,
 | 
			
		||||
    height: size,
 | 
			
		||||
    type: randomType,
 | 
			
		||||
    color:
 | 
			
		||||
      randomType === "blue"
 | 
			
		||||
        ? "blue"
 | 
			
		||||
        : randomType === "yellow"
 | 
			
		||||
        ? "yellow"
 | 
			
		||||
        : randomType === "green"
 | 
			
		||||
        ? "green"
 | 
			
		||||
        : "rainbow",
 | 
			
		||||
    speed: 3,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update game elements
 | 
			
		||||
function updatePlayer() {
 | 
			
		||||
  player.x += player.dx;
 | 
			
		||||
  if (player.x < 0) player.x = 0;
 | 
			
		||||
  if (player.x + player.width > canvas.width)
 | 
			
		||||
    player.x = canvas.width - player.width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateBullets() {
 | 
			
		||||
  bullets.forEach((bullet, index) => {
 | 
			
		||||
    bullet.y -= bullet.speed;
 | 
			
		||||
    if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateAsteroids() {
 | 
			
		||||
  asteroids.forEach((asteroid, index) => {
 | 
			
		||||
    asteroid.y += asteroid.speed;
 | 
			
		||||
    if (asteroid.y > canvas.height) asteroids.splice(index, 1);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateItems() {
 | 
			
		||||
  items.forEach((item, index) => {
 | 
			
		||||
    item.y += item.speed;
 | 
			
		||||
    if (item.y > canvas.height) items.splice(index, 1);
 | 
			
		||||
    // Check if player collects the item
 | 
			
		||||
    if (
 | 
			
		||||
      player.x < item.x + item.width &&
 | 
			
		||||
      player.x + player.width > item.x &&
 | 
			
		||||
      player.y < item.y + item.height &&
 | 
			
		||||
      player.y + player.height > item.y
 | 
			
		||||
    ) {
 | 
			
		||||
      applyItemEffect(item.type);
 | 
			
		||||
      items.splice(index, 1); // Remove the item after it is collected
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyItemEffect(type) {
 | 
			
		||||
  let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
 | 
			
		||||
  if (type === "blue") {
 | 
			
		||||
    rapidFireActive = true;
 | 
			
		||||
    setTimeout(() => (rapidFireActive = false), 15000); // 15 seconds of rapid fire
 | 
			
		||||
  } else if (type === "yellow") {
 | 
			
		||||
    shotgunActive = true;
 | 
			
		||||
    setTimeout(() => (shotgunActive = false), 30000); // 30 seconds of shotgun
 | 
			
		||||
  } else if (type === "green") {
 | 
			
		||||
    ammo = 100; // Refill ammo
 | 
			
		||||
  } else if (type === "rainbow") {
 | 
			
		||||
    rapidFireActive = true;
 | 
			
		||||
    shotgunActive = true;
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      rapidFireActive = false;
 | 
			
		||||
      shotgunActive = false;
 | 
			
		||||
    }, 15000); // 15 seconds with all effects
 | 
			
		||||
  }
 | 
			
		||||
  score += points; // Add points when an item is collected
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collision detection
 | 
			
		||||
function checkCollisions() {
 | 
			
		||||
  bullets.forEach((bullet, bIndex) => {
 | 
			
		||||
    asteroids.forEach((asteroid, aIndex) => {
 | 
			
		||||
      if (
 | 
			
		||||
        bullet.x < asteroid.x + asteroid.width &&
 | 
			
		||||
        bullet.x + bullet.width > asteroid.x &&
 | 
			
		||||
        bullet.y < asteroid.y + asteroid.height &&
 | 
			
		||||
        bullet.y + bullet.height > asteroid.y
 | 
			
		||||
      ) {
 | 
			
		||||
        bullets.splice(bIndex, 1);
 | 
			
		||||
        asteroids.splice(aIndex, 1);
 | 
			
		||||
        score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
 | 
			
		||||
        // Visual feedback for destroyed asteroid
 | 
			
		||||
        createExplosion(asteroid.x, asteroid.y);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  asteroids.forEach((asteroid) => {
 | 
			
		||||
    if (
 | 
			
		||||
      player.x < asteroid.x + asteroid.width &&
 | 
			
		||||
      player.x + player.width > asteroid.x &&
 | 
			
		||||
      player.y < asteroid.y + asteroid.height &&
 | 
			
		||||
      player.y + player.height > asteroid.y
 | 
			
		||||
    ) {
 | 
			
		||||
      isGameOver = true;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Explosion effect
 | 
			
		||||
function createExplosion(x, y) {
 | 
			
		||||
  ctx.fillStyle = "yellow";
 | 
			
		||||
  ctx.beginPath();
 | 
			
		||||
  ctx.arc(x, y, 20, 0, Math.PI * 2);
 | 
			
		||||
  ctx.fill();
 | 
			
		||||
  setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Draw elements
 | 
			
		||||
function drawPlayer() {
 | 
			
		||||
  ctx.fillStyle = player.color;
 | 
			
		||||
  ctx.fillRect(player.x, player.y, player.width, player.height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawBullets() {
 | 
			
		||||
  bullets.forEach((bullet) => {
 | 
			
		||||
    ctx.fillStyle = bullet.color;
 | 
			
		||||
    ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawAsteroids() {
 | 
			
		||||
  asteroids.forEach((asteroid) => {
 | 
			
		||||
    ctx.fillStyle = asteroid.color;
 | 
			
		||||
    ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawItems() {
 | 
			
		||||
  items.forEach((item) => {
 | 
			
		||||
    ctx.fillStyle = item.color;
 | 
			
		||||
    ctx.beginPath();
 | 
			
		||||
    ctx.arc(
 | 
			
		||||
      item.x + item.width / 2,
 | 
			
		||||
      item.y + item.height / 2,
 | 
			
		||||
      item.width / 2,
 | 
			
		||||
      0,
 | 
			
		||||
      Math.PI * 2
 | 
			
		||||
    );
 | 
			
		||||
    ctx.fill();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawScore() {
 | 
			
		||||
  ctx.fillStyle = "white";
 | 
			
		||||
  ctx.font = "24px Arial";
 | 
			
		||||
  ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawAmmo() {
 | 
			
		||||
  ctx.fillStyle = "white";
 | 
			
		||||
  ctx.font = "24px Arial";
 | 
			
		||||
  ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawGameOver() {
 | 
			
		||||
  if (isGameOver) {
 | 
			
		||||
    ctx.fillStyle = "white";
 | 
			
		||||
    ctx.font = "40px Arial";
 | 
			
		||||
    ctx.textAlign = "center";
 | 
			
		||||
    ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 40);
 | 
			
		||||
    ctx.font = "24px Arial";
 | 
			
		||||
    ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
 | 
			
		||||
    ctx.fillText(
 | 
			
		||||
      `Bullets Fired: ${totalBulletsFired}`,
 | 
			
		||||
      canvas.width / 2,
 | 
			
		||||
      canvas.height / 2 + 40
 | 
			
		||||
    );
 | 
			
		||||
    ctx.fillText(
 | 
			
		||||
      'Press "R" to Restart',
 | 
			
		||||
      canvas.width / 2,
 | 
			
		||||
      canvas.height / 2 + 80
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restart game
 | 
			
		||||
function restartGame() {
 | 
			
		||||
  window.location.reload();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Main game loop
 | 
			
		||||
function gameLoop() {
 | 
			
		||||
  const currentTime = performance.now();
 | 
			
		||||
  const elapsedTime = currentTime - lastFrameTime;
 | 
			
		||||
 | 
			
		||||
  if (elapsedTime >= targetFrameTime) {
 | 
			
		||||
    lastFrameTime = currentTime - (elapsedTime % targetFrameTime);
 | 
			
		||||
    ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
			
		||||
 | 
			
		||||
    if (!isGameOver) {
 | 
			
		||||
      updatePlayer();
 | 
			
		||||
      updateBullets();
 | 
			
		||||
      updateAsteroids();
 | 
			
		||||
      updateItems();
 | 
			
		||||
      checkCollisions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drawPlayer();
 | 
			
		||||
    drawBullets();
 | 
			
		||||
    drawAsteroids();
 | 
			
		||||
    drawItems();
 | 
			
		||||
    drawScore();
 | 
			
		||||
    drawAmmo();
 | 
			
		||||
    drawGameOver();
 | 
			
		||||
 | 
			
		||||
    if (!isGameOver) {
 | 
			
		||||
      if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
 | 
			
		||||
      if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  requestAnimationFrame(gameLoop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start game loop
 | 
			
		||||
requestAnimationFrame(gameLoop);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,41 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Asteroid Shooter</title>
 | 
			
		||||
    <link rel="stylesheet" href="style.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <canvas id="gameCanvas"></canvas>
 | 
			
		||||
 | 
			
		||||
    <!-- Virtual buttons for mobile -->
 | 
			
		||||
    <div class="controls">
 | 
			
		||||
      <button id="leftBtn" class="control-btn">Left</button>
 | 
			
		||||
      <button id="shootBtn" class="control-btn" onclick="btnShoot()">
 | 
			
		||||
        Shoot
 | 
			
		||||
      </button>
 | 
			
		||||
      <button id="rightBtn" class="control-btn">Right</button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script src="game.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,43 +0,0 @@
 | 
			
		|||
body {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
canvas {
 | 
			
		||||
  display: block;
 | 
			
		||||
  background: black;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.controls {
 | 
			
		||||
  display: none;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  bottom: 20px;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.control-btn {
 | 
			
		||||
  display: none;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  font-size: 18px;
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.6);
 | 
			
		||||
  color: white;
 | 
			
		||||
  border: 1px solid #fff;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  flex-grow: 1;
 | 
			
		||||
  margin: 0 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 600px) {
 | 
			
		||||
  .control-btn {
 | 
			
		||||
    display: block;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
    padding: 12px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,76 +0,0 @@
 | 
			
		|||
* {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body,
 | 
			
		||||
html {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  font-family: Arial, sans-serif;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  background-color: #000;
 | 
			
		||||
  color: white;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.landing-page {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.game-background {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.7); /* Dark overlay */
 | 
			
		||||
  backdrop-filter: blur(8px); /* Apply blur effect to the background */
 | 
			
		||||
  z-index: -1; /* Ensures it's in the background */
 | 
			
		||||
  pointer-events: none; /* Prevent interaction with the blurred background */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content {
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  z-index: 1; /* Ensure content appears above the game background */
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  max-width: 600px; /* Limit the width of the content */
 | 
			
		||||
  position: relative;
 | 
			
		||||
  color: white;
 | 
			
		||||
  backdrop-filter: blur(
 | 
			
		||||
    8px
 | 
			
		||||
  ); /* Ensure content has some blur as well for contrast */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
  font-size: 2rem;
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
  font-size: 1.2rem;
 | 
			
		||||
  margin-bottom: 30px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
  padding: 12px 24px;
 | 
			
		||||
  background-color: #ffcc00;
 | 
			
		||||
  color: black;
 | 
			
		||||
  border: none;
 | 
			
		||||
  font-size: 18px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  text-transform: uppercase;
 | 
			
		||||
  transition: background-color 0.3s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button:hover {
 | 
			
		||||
  background-color: #ff9900;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Endless runner</title>
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div class="game-container">
 | 
			
		||||
      <canvas id="gameCanvas"></canvas>
 | 
			
		||||
      <button id="restartBtn" onclick="restartGame()">Restart</button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script src="script.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,90 +0,0 @@
 | 
			
		|||
import { useEffect, useRef, useState } from "react";
 | 
			
		||||
 | 
			
		||||
export default function EndlessRunner() {
 | 
			
		||||
  const canvasRef = useRef(null);
 | 
			
		||||
  const [running, setRunning] = useState(true);
 | 
			
		||||
  const player = { x: 50, y: 150, width: 20, height: 20, dy: 0 };
 | 
			
		||||
  const gravity = 0.5;
 | 
			
		||||
  let obstacles = [];
 | 
			
		||||
  let score = 0;
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const canvas = canvasRef.current;
 | 
			
		||||
    const ctx = canvas.getContext("2d");
 | 
			
		||||
    canvas.width = window.innerWidth;
 | 
			
		||||
    canvas.height = 300;
 | 
			
		||||
 | 
			
		||||
    function update() {
 | 
			
		||||
      if (!running) return;
 | 
			
		||||
      ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
			
		||||
 | 
			
		||||
      // Player physics
 | 
			
		||||
      player.dy += gravity;
 | 
			
		||||
      player.y += player.dy;
 | 
			
		||||
      if (player.y > 150) {
 | 
			
		||||
        player.y = 150;
 | 
			
		||||
        player.dy = 0;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Draw player
 | 
			
		||||
      ctx.fillStyle = "blue";
 | 
			
		||||
      ctx.fillRect(player.x, player.y, player.width, player.height);
 | 
			
		||||
 | 
			
		||||
      // Obstacles
 | 
			
		||||
      if (Math.random() < 0.02) {
 | 
			
		||||
        obstacles.push({ x: canvas.width, y: 150, width: 20, height: 20 });
 | 
			
		||||
      }
 | 
			
		||||
      obstacles = obstacles.map((obstacle) => ({
 | 
			
		||||
        ...obstacle,
 | 
			
		||||
        x: obstacle.x - 5,
 | 
			
		||||
      }));
 | 
			
		||||
      obstacles = obstacles.filter(
 | 
			
		||||
        (obstacle) => obstacle.x + obstacle.width > 0
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      obstacles.forEach((obstacle) => {
 | 
			
		||||
        ctx.fillStyle = "red";
 | 
			
		||||
        ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
 | 
			
		||||
 | 
			
		||||
        // Collision detection
 | 
			
		||||
        if (
 | 
			
		||||
          player.x < obstacle.x + obstacle.width &&
 | 
			
		||||
          player.x + player.width > obstacle.x &&
 | 
			
		||||
          player.y < obstacle.y + obstacle.height &&
 | 
			
		||||
          player.y + player.height > obstacle.y
 | 
			
		||||
        ) {
 | 
			
		||||
          setRunning(false);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Score
 | 
			
		||||
      score++;
 | 
			
		||||
      ctx.fillStyle = "black";
 | 
			
		||||
      ctx.fillText("Score: " + score, 10, 20);
 | 
			
		||||
 | 
			
		||||
      requestAnimationFrame(update);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update();
 | 
			
		||||
  }, [running]);
 | 
			
		||||
 | 
			
		||||
  function jump() {
 | 
			
		||||
    if (player.y >= 150) {
 | 
			
		||||
      player.dy = -10;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col items-center">
 | 
			
		||||
      <canvas ref={canvasRef} className="border" onClick={jump}></canvas>
 | 
			
		||||
      {!running && (
 | 
			
		||||
        <button
 | 
			
		||||
          onClick={() => window.location.reload()}
 | 
			
		||||
          className="mt-4 bg-blue-500 text-white px-4 py-2 rounded"
 | 
			
		||||
        >
 | 
			
		||||
          Restart
 | 
			
		||||
        </button>
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,34 +0,0 @@
 | 
			
		|||
body {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  background-color: #f4f4f4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.game-container {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
canvas {
 | 
			
		||||
  border: 2px solid black;
 | 
			
		||||
  background-color: white;
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#restartBtn {
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
  padding: 10px 20px;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  background-color: #007bff;
 | 
			
		||||
  color: white;
 | 
			
		||||
  border: none;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#restartBtn:hover {
 | 
			
		||||
  background-color: #0056b3;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										404
									
								
								secret/game.js
									
										
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1,404 +0,0 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
// Canvas setup
 | 
			
		||||
const canvas = document.getElementById("gameCanvas");
 | 
			
		||||
const ctx = canvas.getContext("2d");
 | 
			
		||||
canvas.width = window.innerWidth;
 | 
			
		||||
canvas.height = window.innerHeight;
 | 
			
		||||
 | 
			
		||||
// Game elements
 | 
			
		||||
const player = {
 | 
			
		||||
  x: canvas.width / 2,
 | 
			
		||||
  y: canvas.height - 60,
 | 
			
		||||
  width: 40,
 | 
			
		||||
  height: 40,
 | 
			
		||||
  color: "white",
 | 
			
		||||
  speed: 5,
 | 
			
		||||
  dx: 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let bullets = [];
 | 
			
		||||
let asteroids = [];
 | 
			
		||||
let items = [];
 | 
			
		||||
let score = 0;
 | 
			
		||||
let totalBulletsFired = 0;
 | 
			
		||||
let isGameOver = false;
 | 
			
		||||
let lastBulletTime = 0;
 | 
			
		||||
let ammo = 100; // Ammo counter
 | 
			
		||||
 | 
			
		||||
// Difficulty control
 | 
			
		||||
let asteroidSpawnInterval = 800; // Faster spawn rate
 | 
			
		||||
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
 | 
			
		||||
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
 | 
			
		||||
 | 
			
		||||
// Controls
 | 
			
		||||
let canShoot = true; // Flag to control shooting
 | 
			
		||||
let rapidFireActive = false;
 | 
			
		||||
let shotgunActive = false;
 | 
			
		||||
let rainbowActive = false;
 | 
			
		||||
let lastShotgunTime = 0;
 | 
			
		||||
 | 
			
		||||
// Controls for sphere effects
 | 
			
		||||
let blueSphereCooldown = 0;
 | 
			
		||||
let yellowSphereCooldown = 0;
 | 
			
		||||
let greenSphereCooldown = 0;
 | 
			
		||||
let rainbowSphereCooldown = 0;
 | 
			
		||||
 | 
			
		||||
// Sphere types
 | 
			
		||||
const sphereTypes = ["blue", "yellow", "green", "rainbow"];
 | 
			
		||||
 | 
			
		||||
/// Control for left button press and release
 | 
			
		||||
function btnMoveLeft(isPressed) {
 | 
			
		||||
  if (isPressed) {
 | 
			
		||||
    player.dx = -player.speed; // Start moving left
 | 
			
		||||
  } else {
 | 
			
		||||
    player.dx = 0; // Stop moving when button is released
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Control for shoot button click (simulates spacebar press)
 | 
			
		||||
function btnShoot() {
 | 
			
		||||
  if (canShoot && !isGameOver) {
 | 
			
		||||
    shootBullet();
 | 
			
		||||
    canShoot = false; // Prevent shooting until the button is "released"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Control for right button press and release
 | 
			
		||||
function btnMoveRight(isPressed) {
 | 
			
		||||
  if (isPressed) {
 | 
			
		||||
    player.dx = player.speed; // Start moving right
 | 
			
		||||
  } else {
 | 
			
		||||
    player.dx = 0; // Stop moving when button is released
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.getElementById("shootBtn").addEventListener("mouseup", () => {
 | 
			
		||||
  canShoot = true; // Allow shooting again when button is released
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
window.addEventListener("keydown", (e) => {
 | 
			
		||||
  if (e.key === "ArrowLeft" || e.key === "a") player.dx = -player.speed;
 | 
			
		||||
  if (e.key === "ArrowRight" || e.key === "d") player.dx = player.speed;
 | 
			
		||||
 | 
			
		||||
  // Shoot only if it's not a hold, and we can shoot
 | 
			
		||||
  if (e.key === " " && canShoot && !isGameOver) {
 | 
			
		||||
    shootBullet();
 | 
			
		||||
    canShoot = false; // Prevent shooting until the key is released
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (e.key === "r" && isGameOver) restartGame();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
window.addEventListener("keyup", (e) => {
 | 
			
		||||
  // Stop moving when either the arrow keys or the 'a'/'d' keys are released
 | 
			
		||||
  if (
 | 
			
		||||
    e.key === "ArrowLeft" ||
 | 
			
		||||
    e.key === "ArrowRight" ||
 | 
			
		||||
    e.key === "a" ||
 | 
			
		||||
    e.key === "d"
 | 
			
		||||
  ) {
 | 
			
		||||
    player.dx = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Allow shooting again when the space key is released
 | 
			
		||||
  if (e.key === " ") {
 | 
			
		||||
    canShoot = true;
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Bullet mechanics with cooldown
 | 
			
		||||
function shootBullet() {
 | 
			
		||||
  const now = Date.now();
 | 
			
		||||
  if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
 | 
			
		||||
  if (ammo <= 0) return; // Prevent shooting if ammo is empty
 | 
			
		||||
  lastBulletTime = now;
 | 
			
		||||
  ammo--; // Decrease ammo
 | 
			
		||||
 | 
			
		||||
  totalBulletsFired++; // Increment total bullets fired
 | 
			
		||||
 | 
			
		||||
  if (rapidFireActive) {
 | 
			
		||||
    // If rapid fire is active, fire bullets continuously with a short delay
 | 
			
		||||
    for (let i = 0; i < 3; i++) {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        bullets.push({
 | 
			
		||||
          x: player.x + player.width / 2 - 2.5,
 | 
			
		||||
          y: player.y,
 | 
			
		||||
          width: 5,
 | 
			
		||||
          height: 10,
 | 
			
		||||
          color: "yellow",
 | 
			
		||||
          speed: 7,
 | 
			
		||||
        });
 | 
			
		||||
      }, i * 50); // Fire bullets with 50ms delay between shots
 | 
			
		||||
    }
 | 
			
		||||
  } else if (shotgunActive) {
 | 
			
		||||
    // Shotgun effect, firing 3 bullets with a spread
 | 
			
		||||
    for (let i = -1; i <= 1; i++) {
 | 
			
		||||
      bullets.push({
 | 
			
		||||
        x: player.x + player.width / 2 - 2.5,
 | 
			
		||||
        y: player.y,
 | 
			
		||||
        width: 5,
 | 
			
		||||
        height: 10,
 | 
			
		||||
        color: "yellow",
 | 
			
		||||
        speed: 7,
 | 
			
		||||
        angle: i * 10, // Spray the bullets at different angles
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // Normal bullet
 | 
			
		||||
    bullets.push({
 | 
			
		||||
      x: player.x + player.width / 2 - 2.5,
 | 
			
		||||
      y: player.y,
 | 
			
		||||
      width: 5,
 | 
			
		||||
      height: 10,
 | 
			
		||||
      color: "yellow",
 | 
			
		||||
      speed: 7,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Generate random color
 | 
			
		||||
function getRandomColor() {
 | 
			
		||||
  const colors = ["red", "blue", "green", "orange", "purple", "pink"];
 | 
			
		||||
  return colors[Math.floor(Math.random() * colors.length)];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Asteroid mechanics
 | 
			
		||||
function createAsteroid() {
 | 
			
		||||
  const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
 | 
			
		||||
  asteroids.push({
 | 
			
		||||
    x: Math.random() * canvas.width,
 | 
			
		||||
    y: -size,
 | 
			
		||||
    width: size,
 | 
			
		||||
    height: size,
 | 
			
		||||
    color: getRandomColor(),
 | 
			
		||||
    speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier, // Faster initial speed
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Item mechanics
 | 
			
		||||
function createItem() {
 | 
			
		||||
  const randomType =
 | 
			
		||||
    sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
 | 
			
		||||
  const size = 30;
 | 
			
		||||
  const x = Math.random() * canvas.width;
 | 
			
		||||
  items.push({
 | 
			
		||||
    x: x,
 | 
			
		||||
    y: -size,
 | 
			
		||||
    width: size,
 | 
			
		||||
    height: size,
 | 
			
		||||
    type: randomType,
 | 
			
		||||
    color:
 | 
			
		||||
      randomType === "blue"
 | 
			
		||||
        ? "blue"
 | 
			
		||||
        : randomType === "yellow"
 | 
			
		||||
        ? "yellow"
 | 
			
		||||
        : randomType === "green"
 | 
			
		||||
        ? "green"
 | 
			
		||||
        : "rainbow",
 | 
			
		||||
    speed: 3,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update game elements
 | 
			
		||||
function updatePlayer() {
 | 
			
		||||
  player.x += player.dx;
 | 
			
		||||
  if (player.x < 0) player.x = 0;
 | 
			
		||||
  if (player.x + player.width > canvas.width)
 | 
			
		||||
    player.x = canvas.width - player.width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateBullets() {
 | 
			
		||||
  bullets.forEach((bullet, index) => {
 | 
			
		||||
    bullet.y -= bullet.speed;
 | 
			
		||||
    if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateAsteroids() {
 | 
			
		||||
  asteroids.forEach((asteroid, index) => {
 | 
			
		||||
    asteroid.y += asteroid.speed;
 | 
			
		||||
    if (asteroid.y > canvas.height) asteroids.splice(index, 1);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateItems() {
 | 
			
		||||
  items.forEach((item, index) => {
 | 
			
		||||
    item.y += item.speed;
 | 
			
		||||
    if (item.y > canvas.height) items.splice(index, 1);
 | 
			
		||||
    // Check if player collects the item
 | 
			
		||||
    if (
 | 
			
		||||
      player.x < item.x + item.width &&
 | 
			
		||||
      player.x + player.width > item.x &&
 | 
			
		||||
      player.y < item.y + item.height &&
 | 
			
		||||
      player.y + player.height > item.y
 | 
			
		||||
    ) {
 | 
			
		||||
      applyItemEffect(item.type);
 | 
			
		||||
      items.splice(index, 1); // Remove the item after it is collected
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyItemEffect(type) {
 | 
			
		||||
  let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
 | 
			
		||||
  if (type === "blue") {
 | 
			
		||||
    rapidFireActive = true;
 | 
			
		||||
    setTimeout(() => (rapidFireActive = false), 15000); // 15 seconds of rapid fire
 | 
			
		||||
  } else if (type === "yellow") {
 | 
			
		||||
    shotgunActive = true;
 | 
			
		||||
    setTimeout(() => (shotgunActive = false), 30000); // 30 seconds of shotgun
 | 
			
		||||
  } else if (type === "green") {
 | 
			
		||||
    ammo = 100; // Refill ammo
 | 
			
		||||
  } else if (type === "rainbow") {
 | 
			
		||||
    rapidFireActive = true;
 | 
			
		||||
    shotgunActive = true;
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      rapidFireActive = false;
 | 
			
		||||
      shotgunActive = false;
 | 
			
		||||
    }, 15000); // 15 seconds with all effects
 | 
			
		||||
  }
 | 
			
		||||
  score += points; // Add points when an item is collected
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collision detection
 | 
			
		||||
function checkCollisions() {
 | 
			
		||||
  bullets.forEach((bullet, bIndex) => {
 | 
			
		||||
    asteroids.forEach((asteroid, aIndex) => {
 | 
			
		||||
      if (
 | 
			
		||||
        bullet.x < asteroid.x + asteroid.width &&
 | 
			
		||||
        bullet.x + bullet.width > asteroid.x &&
 | 
			
		||||
        bullet.y < asteroid.y + asteroid.height &&
 | 
			
		||||
        bullet.y + bullet.height > asteroid.y
 | 
			
		||||
      ) {
 | 
			
		||||
        bullets.splice(bIndex, 1);
 | 
			
		||||
        asteroids.splice(aIndex, 1);
 | 
			
		||||
        score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
 | 
			
		||||
        // Visual feedback for destroyed asteroid
 | 
			
		||||
        createExplosion(asteroid.x, asteroid.y);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  asteroids.forEach((asteroid) => {
 | 
			
		||||
    if (
 | 
			
		||||
      player.x < asteroid.x + asteroid.width &&
 | 
			
		||||
      player.x + player.width > asteroid.x &&
 | 
			
		||||
      player.y < asteroid.y + asteroid.height &&
 | 
			
		||||
      player.y + player.height > asteroid.y
 | 
			
		||||
    ) {
 | 
			
		||||
      isGameOver = true;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Explosion effect
 | 
			
		||||
function createExplosion(x, y) {
 | 
			
		||||
  ctx.fillStyle = "yellow";
 | 
			
		||||
  ctx.beginPath();
 | 
			
		||||
  ctx.arc(x, y, 20, 0, Math.PI * 2);
 | 
			
		||||
  ctx.fill();
 | 
			
		||||
  setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Draw elements
 | 
			
		||||
function drawPlayer() {
 | 
			
		||||
  ctx.fillStyle = player.color;
 | 
			
		||||
  ctx.fillRect(player.x, player.y, player.width, player.height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawBullets() {
 | 
			
		||||
  bullets.forEach((bullet) => {
 | 
			
		||||
    ctx.fillStyle = bullet.color;
 | 
			
		||||
    ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawAsteroids() {
 | 
			
		||||
  asteroids.forEach((asteroid) => {
 | 
			
		||||
    ctx.fillStyle = asteroid.color;
 | 
			
		||||
    ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawItems() {
 | 
			
		||||
  items.forEach((item) => {
 | 
			
		||||
    ctx.fillStyle = item.color;
 | 
			
		||||
    ctx.beginPath();
 | 
			
		||||
    ctx.arc(
 | 
			
		||||
      item.x + item.width / 2,
 | 
			
		||||
      item.y + item.height / 2,
 | 
			
		||||
      item.width / 2,
 | 
			
		||||
      0,
 | 
			
		||||
      Math.PI * 2
 | 
			
		||||
    );
 | 
			
		||||
    ctx.fill();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawScore() {
 | 
			
		||||
  ctx.fillStyle = "white";
 | 
			
		||||
  ctx.font = "24px Arial";
 | 
			
		||||
  ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawAmmo() {
 | 
			
		||||
  ctx.fillStyle = "white";
 | 
			
		||||
  ctx.font = "24px Arial";
 | 
			
		||||
  ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawGameOver() {
 | 
			
		||||
  if (isGameOver) {
 | 
			
		||||
    ctx.fillStyle = "white";
 | 
			
		||||
    ctx.font = "40px Arial";
 | 
			
		||||
    ctx.textAlign = "center";
 | 
			
		||||
    ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 40);
 | 
			
		||||
    ctx.font = "24px Arial";
 | 
			
		||||
    ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
 | 
			
		||||
    ctx.fillText(
 | 
			
		||||
      `Bullets Fired: ${totalBulletsFired}`,
 | 
			
		||||
      canvas.width / 2,
 | 
			
		||||
      canvas.height / 2 + 40
 | 
			
		||||
    );
 | 
			
		||||
    ctx.fillText(
 | 
			
		||||
      'Press "R" to Restart',
 | 
			
		||||
      canvas.width / 2,
 | 
			
		||||
      canvas.height / 2 + 80
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restart game
 | 
			
		||||
function restartGame() {
 | 
			
		||||
  window.location.reload();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Main game loop
 | 
			
		||||
function gameLoop() {
 | 
			
		||||
  ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
			
		||||
 | 
			
		||||
  if (!isGameOver) {
 | 
			
		||||
    updatePlayer();
 | 
			
		||||
    updateBullets();
 | 
			
		||||
    updateAsteroids();
 | 
			
		||||
    updateItems();
 | 
			
		||||
    checkCollisions();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawPlayer();
 | 
			
		||||
  drawBullets();
 | 
			
		||||
  drawAsteroids();
 | 
			
		||||
  drawItems();
 | 
			
		||||
  drawScore();
 | 
			
		||||
  drawAmmo();
 | 
			
		||||
  drawGameOver();
 | 
			
		||||
 | 
			
		||||
  if (!isGameOver) {
 | 
			
		||||
    if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
 | 
			
		||||
    if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  requestAnimationFrame(gameLoop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start game loop
 | 
			
		||||
gameLoop();
 | 
			
		||||
| 
						 | 
				
			
			@ -1,65 +0,0 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
 | 
			
		||||
const targetNum = Math.trunc(Math.random() * 20) + 1;
 | 
			
		||||
let highScore = Number(localStorage.getItem("highscore")) || 0;
 | 
			
		||||
let userGuess = 10; // Default guess
 | 
			
		||||
let currScore = 20;
 | 
			
		||||
 | 
			
		||||
const screenEl = document.querySelector(".screen");
 | 
			
		||||
const msgEl = document.querySelector(".message");
 | 
			
		||||
const guessInput = document.querySelector("#guess");
 | 
			
		||||
const scoreEl = document.querySelector(".score");
 | 
			
		||||
const highScoreEl = document.querySelector(".highScore");
 | 
			
		||||
const checkBtn = document.querySelector("#check");
 | 
			
		||||
const restartBtn = document.querySelector("#restart");
 | 
			
		||||
const incBtn = document.querySelector("#up");
 | 
			
		||||
const decBtn = document.querySelector("#down");
 | 
			
		||||
 | 
			
		||||
const setMsg = (msg) => (msgEl.textContent = msg);
 | 
			
		||||
const setScore = (score) =>
 | 
			
		||||
  (scoreEl.textContent = `Score: ${(currScore = score)}`);
 | 
			
		||||
const setHighScore = () => {
 | 
			
		||||
  highScoreEl.textContent = `Highscore: ${highScore}`;
 | 
			
		||||
  localStorage.setItem("highscore", highScore);
 | 
			
		||||
};
 | 
			
		||||
const changeColor = (color) => (screenEl.style.backgroundColor = color);
 | 
			
		||||
 | 
			
		||||
checkBtn.addEventListener("click", () => {
 | 
			
		||||
  userGuess = Number(guessInput.textContent);
 | 
			
		||||
  if (!userGuess || userGuess < 1 || userGuess > 20) {
 | 
			
		||||
    setMsg("Please enter a valid number between 1 and 20.");
 | 
			
		||||
  } else if (userGuess === targetNum) {
 | 
			
		||||
    highScore = Math.max(highScore, currScore);
 | 
			
		||||
    setHighScore();
 | 
			
		||||
    setMsg(
 | 
			
		||||
      currScore !== 20 ? "Correct Number!" : "Are you sure you didn't cheat?"
 | 
			
		||||
    );
 | 
			
		||||
    changeColor(currScore !== 20 ? "#1ba100" : "#FFC300");
 | 
			
		||||
  } else {
 | 
			
		||||
    setMsg(userGuess > targetNum ? "Too High!" : "Too Low!");
 | 
			
		||||
    if (currScore > 1) {
 | 
			
		||||
      setScore(currScore - 1);
 | 
			
		||||
    } else {
 | 
			
		||||
      setScore(1);
 | 
			
		||||
      setMsg("You lost the game!");
 | 
			
		||||
      changeColor("#a10000");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
restartBtn.addEventListener("click", () => location.reload());
 | 
			
		||||
incBtn.addEventListener(
 | 
			
		||||
  "click",
 | 
			
		||||
  () =>
 | 
			
		||||
    (guessInput.textContent = Math.min(Number(guessInput.textContent) + 1, 20))
 | 
			
		||||
);
 | 
			
		||||
decBtn.addEventListener(
 | 
			
		||||
  "click",
 | 
			
		||||
  () =>
 | 
			
		||||
    (guessInput.textContent = Math.max(Number(guessInput.textContent) - 1, 1))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
guessInput.textContent = userGuess;
 | 
			
		||||
setMsg("Guess a number");
 | 
			
		||||
setScore(currScore);
 | 
			
		||||
setHighScore();
 | 
			
		||||
| 
						 | 
				
			
			@ -1,72 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Guess My Number</title>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div class="gameboy">
 | 
			
		||||
      <!-- Game Boy Screen -->
 | 
			
		||||
      <div class="screen">
 | 
			
		||||
        <article class="game">
 | 
			
		||||
          <h1>Guess My Number - Game</h1>
 | 
			
		||||
          <p class="message"></p>
 | 
			
		||||
          <div class="guess-display">
 | 
			
		||||
            <span id="guess"></span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <p class="score"></p>
 | 
			
		||||
          <p class="highScore"></p>
 | 
			
		||||
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <h2>Description</h2>
 | 
			
		||||
            <p>Guess a number between 1 and 20</p>
 | 
			
		||||
            <p>A = check</p>
 | 
			
		||||
            <p>B = Reload</p>
 | 
			
		||||
            <p>▲ = increases guess by one</p>
 | 
			
		||||
            <p>▼ = decreases guess by one</p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </article>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <!-- Controls -->
 | 
			
		||||
      <div class="controls">
 | 
			
		||||
        <!-- D-Pad on the left -->
 | 
			
		||||
        <div class="dpad">
 | 
			
		||||
          <button class="dpad-btn up" id="up">▲</button>
 | 
			
		||||
          <button class="dpad-btn left" id="left">◀</button>
 | 
			
		||||
          <div class="dpad-center"></div>
 | 
			
		||||
          <button class="dpad-btn right" id="right">▶</button>
 | 
			
		||||
          <button class="dpad-btn down" id="down">▼</button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- A and B Buttons on the right -->
 | 
			
		||||
        <div class="action-buttons">
 | 
			
		||||
          <button class="btn" id="check">A</button>
 | 
			
		||||
          <button class="btn" id="restart">B</button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script src="game.js"></script>
 | 
			
		||||
    <script src="styles.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,189 +0,0 @@
 | 
			
		|||
/* Base Reset */
 | 
			
		||||
body,
 | 
			
		||||
html {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  font-family: monospace;
 | 
			
		||||
  background-color: #3a2d56;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* GameBoy Layout */
 | 
			
		||||
.gameboy {
 | 
			
		||||
  background-color: #5f4c82; /* Game Boy Color purple shell */
 | 
			
		||||
  width: 441px;
 | 
			
		||||
  height: 735px;
 | 
			
		||||
  border-radius: 20px;
 | 
			
		||||
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 768px) {
 | 
			
		||||
  .gameboy {
 | 
			
		||||
    width: 100vw;
 | 
			
		||||
    height: 100vh;
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Screen */
 | 
			
		||||
.screen {
 | 
			
		||||
  background-color: #306230; /* Game Boy green screen */
 | 
			
		||||
  border: 4px solid #0f380f;
 | 
			
		||||
  width: 90%;
 | 
			
		||||
  height: 55%;
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.game {
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  width: 90%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Titles */
 | 
			
		||||
h1 {
 | 
			
		||||
  font-size: 2rem; /* Increased font size */
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  text-transform: uppercase;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Guess Display */
 | 
			
		||||
.guess-display {
 | 
			
		||||
  background-color: #9bbc0f;
 | 
			
		||||
  color: #0f380f;
 | 
			
		||||
  border: 2px solid #0f380f;
 | 
			
		||||
  font-size: 2rem; /* Increased font size */
 | 
			
		||||
  width: 80px; /* Increased width */
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  margin: 10px auto;
 | 
			
		||||
  padding: 10px; /* Increased padding */
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Messages */
 | 
			
		||||
.message,
 | 
			
		||||
.score,
 | 
			
		||||
.highScore {
 | 
			
		||||
  font-size: 1.4rem; /* Increased font size */
 | 
			
		||||
  margin: 5px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.description,
 | 
			
		||||
.description p {
 | 
			
		||||
  font-size: 1.2rem;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  padding: 0 auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Controls Section */
 | 
			
		||||
.controls {
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* D-Pad */
 | 
			
		||||
.dpad {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 120px; /* Increased size */
 | 
			
		||||
  height: 120px; /* Increased size */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base Styling for D-Pad Buttons */
 | 
			
		||||
.dpad-btn {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  color: #9bbc0f;
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  width: 42px;
 | 
			
		||||
  height: 42px;
 | 
			
		||||
  font-size: 1.5rem; /* Increased size */
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.up {
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.down {
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.left {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.right {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* D-Pad Center to Connect Buttons */
 | 
			
		||||
.dpad-center {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translate(-50%, -50%);
 | 
			
		||||
  width: 40px;
 | 
			
		||||
  height: 40px;
 | 
			
		||||
  border: 2px solid #9cbc0f00;
 | 
			
		||||
  z-index: 0;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* A and B Buttons */
 | 
			
		||||
.action-buttons {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  height: 140px; /* Increased height */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  color: #9bbc0f;
 | 
			
		||||
  border: 2px solid #9bbc0f;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  width: 60px;
 | 
			
		||||
  height: 60px;
 | 
			
		||||
  font-size: 1.8rem; /* Increased font size */
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: transform 0.1s, background-color 0.2s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn:hover {
 | 
			
		||||
  background-color: #9bbc0f;
 | 
			
		||||
  color: #0f380f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn:active {
 | 
			
		||||
  transform: scale(0.9);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,148 +0,0 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
const left = document.querySelector("#left");
 | 
			
		||||
const right = document.querySelector("#right");
 | 
			
		||||
const gameboy = document.querySelector(".gameboy");
 | 
			
		||||
const html = document.documentElement;
 | 
			
		||||
const body = document.body;
 | 
			
		||||
const screen = document.querySelector(".screen");
 | 
			
		||||
const dpadButtons = document.querySelectorAll(".dpad-btn");
 | 
			
		||||
const dpadCenter = document.querySelector(".dpad-center"); // Darker variant
 | 
			
		||||
const actionButtons = document.querySelectorAll(".btn");
 | 
			
		||||
 | 
			
		||||
const colors = [
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#B39DDB",
 | 
			
		||||
    htmlColor: "#D1C4E9",
 | 
			
		||||
    screenColor: "#E1BEE7",
 | 
			
		||||
    buttonColor: "#673AB7",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#5E35B1",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FFC107",
 | 
			
		||||
    htmlColor: "#FFF9C4",
 | 
			
		||||
    screenColor: "#FFEB3B",
 | 
			
		||||
    buttonColor: "#FF9800",
 | 
			
		||||
    buttonTextColor: "#000000",
 | 
			
		||||
    dpadCenterColor: "#EF6C00",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#8BC34A",
 | 
			
		||||
    htmlColor: "#C5E1A5",
 | 
			
		||||
    screenColor: "#A5D6A7",
 | 
			
		||||
    buttonColor: "#FF5722",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#E64A19",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#F44336",
 | 
			
		||||
    htmlColor: "#FFCDD2",
 | 
			
		||||
    screenColor: "#EF9A9A",
 | 
			
		||||
    buttonColor: "#E91E63",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#C2185B",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#03A9F4",
 | 
			
		||||
    htmlColor: "#BBDEFB",
 | 
			
		||||
    screenColor: "#90CAF9",
 | 
			
		||||
    buttonColor: "#FFEB3B",
 | 
			
		||||
    buttonTextColor: "#000000",
 | 
			
		||||
    dpadCenterColor: "#0277BD",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FF7043",
 | 
			
		||||
    htmlColor: "#FFCCBC",
 | 
			
		||||
    screenColor: "#FFAB91",
 | 
			
		||||
    buttonColor: "#FF5722",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#D84315",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#9C27B0",
 | 
			
		||||
    htmlColor: "#E1BEE7",
 | 
			
		||||
    screenColor: "#D1C4E9",
 | 
			
		||||
    buttonColor: "#7B1FA2",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#6A1B9A",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FFD700",
 | 
			
		||||
    htmlColor: "#FFF9C4",
 | 
			
		||||
    screenColor: "#FFF59D",
 | 
			
		||||
    buttonColor: "#FF9800",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#F57F17",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#009688",
 | 
			
		||||
    htmlColor: "#B2DFDB",
 | 
			
		||||
    screenColor: "#80CBC4",
 | 
			
		||||
    buttonColor: "#4CAF50",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#00796B",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#795548",
 | 
			
		||||
    htmlColor: "#D7CCC8",
 | 
			
		||||
    screenColor: "#A1887F",
 | 
			
		||||
    buttonColor: "#9E9E9E",
 | 
			
		||||
    buttonTextColor: "#000000",
 | 
			
		||||
    dpadCenterColor: "#5D4037",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FF5733",
 | 
			
		||||
    htmlColor: "#FFCCCB",
 | 
			
		||||
    screenColor: "#FFABAB",
 | 
			
		||||
    buttonColor: "#C70039",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#B71C1C",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#00BCD4",
 | 
			
		||||
    htmlColor: "#B2EBF2",
 | 
			
		||||
    screenColor: "#80DEEA",
 | 
			
		||||
    buttonColor: "#00ACC1",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#00838F",
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
let currentColorIndex = localStorage.getItem("gameboyColorIndex")
 | 
			
		||||
  ? parseInt(localStorage.getItem("gameboyColorIndex"))
 | 
			
		||||
  : 0;
 | 
			
		||||
 | 
			
		||||
function updateGameBoyColor() {
 | 
			
		||||
  gameboy.style.backgroundColor = colors[currentColorIndex].gameboyColor;
 | 
			
		||||
  html.style.backgroundColor = colors[currentColorIndex].htmlColor;
 | 
			
		||||
  body.style.backgroundColor = colors[currentColorIndex].htmlColor;
 | 
			
		||||
  screen.style.backgroundColor = colors[currentColorIndex].screenColor;
 | 
			
		||||
 | 
			
		||||
  dpadButtons.forEach((button) => {
 | 
			
		||||
    button.style.backgroundColor = colors[currentColorIndex].buttonColor;
 | 
			
		||||
    button.style.color = colors[currentColorIndex].buttonTextColor;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Using darker dpad center color
 | 
			
		||||
  dpadCenter.style.backgroundColor = colors[currentColorIndex].dpadCenterColor;
 | 
			
		||||
  dpadCenter.style.color = colors[currentColorIndex].buttonTextColor;
 | 
			
		||||
 | 
			
		||||
  actionButtons.forEach((button) => {
 | 
			
		||||
    button.style.backgroundColor = colors[currentColorIndex].buttonColor;
 | 
			
		||||
    button.style.color = colors[currentColorIndex].buttonTextColor;
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
left.addEventListener("click", () => {
 | 
			
		||||
  currentColorIndex = (currentColorIndex - 1 + colors.length) % colors.length;
 | 
			
		||||
  localStorage.setItem("gameboyColorIndex", currentColorIndex);
 | 
			
		||||
  updateGameBoyColor();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
right.addEventListener("click", () => {
 | 
			
		||||
  currentColorIndex = (currentColorIndex + 1) % colors.length;
 | 
			
		||||
  localStorage.setItem("gameboyColorIndex", currentColorIndex);
 | 
			
		||||
  updateGameBoyColor();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
updateGameBoyColor();
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 42 KiB  | 
| 
		 Before Width: | Height: | Size: 949 KiB  | 
| 
		 Before Width: | Height: | Size: 9 KiB  | 
| 
		 Before Width: | Height: | Size: 573 KiB  | 
| 
		 Before Width: | Height: | Size: 455 KiB  | 
| 
		 Before Width: | Height: | Size: 55 KiB  | 
| 
		 Before Width: | Height: | Size: 73 KiB  | 
| 
		 Before Width: | Height: | Size: 14 KiB  | 
| 
		 Before Width: | Height: | Size: 176 KiB  | 
| 
						 | 
				
			
			@ -1,118 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Secret Game Collection</title>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <header>
 | 
			
		||||
      <h1>Secret Game Collection</h1>
 | 
			
		||||
    </header>
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
      <div class="grid-container">
 | 
			
		||||
        <a
 | 
			
		||||
          href="asteroidDestroyer/explenation.html"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
          class="item"
 | 
			
		||||
        >
 | 
			
		||||
          <img src="images/asteroid.png" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Secret Asteroid Shooter</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              In this game, you control a spaceship that shoots at asteroids to
 | 
			
		||||
              avoid destruction and collect items for power-ups.
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
              Your goal is to survive as long as possible while scoring points!
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="#" target="_blank" class="item">
 | 
			
		||||
          <img src="images/default.jpeg" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Secret Blackjack</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Try to beat the dealer by getting a hand value as close to 21 as
 | 
			
		||||
              possible without going over.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="#" target="_blank" class="item">
 | 
			
		||||
          <img src="images/default.jpeg" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Snake</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Guide the snake to eat food and grow longer while avoiding
 | 
			
		||||
              collisions with the walls and itself.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="#" target="_blank" class="item">
 | 
			
		||||
          <img src="images/default.jpeg" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Solitaire</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              A classic card game where the objective is to move all cards to
 | 
			
		||||
              foundation piles in ascending order.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="mineSweeper/index.html" target="_blank" class="item">
 | 
			
		||||
          <img src="images/minesweeper.png" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Minesweeper</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Uncover squares on a grid while avoiding hidden mines, using
 | 
			
		||||
              numbers to deduce safe spots.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="guessMyNumber/index.html" target="_blank" class="item">
 | 
			
		||||
          <img src="images/number.jpeg" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Guess My Number</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              A simple game where you try to guess a randomly chosen number
 | 
			
		||||
              within a certain range.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="#" target="_blank" class="item">
 | 
			
		||||
          <img src="images/default.jpeg" alt="Image can't be displayed" />
 | 
			
		||||
          <h2>Endless Runner</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Run through an endless landscape, avoiding obstacles and
 | 
			
		||||
              collecting items to score points.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <footer>
 | 
			
		||||
      <p>© 2025 Game Collection</p>
 | 
			
		||||
    </footer>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,60 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Minesweeper</title>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="settings">
 | 
			
		||||
      <h1>Minesweeper</h1>
 | 
			
		||||
      <label for="gridSize">Grid Size:</label>
 | 
			
		||||
      <input
 | 
			
		||||
        type="number"
 | 
			
		||||
        id="gridSize"
 | 
			
		||||
        min="6"
 | 
			
		||||
        max="25"
 | 
			
		||||
        value="9"
 | 
			
		||||
        aria-label="Grid Size"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <label for="bombs">Number of Bombs:</label>
 | 
			
		||||
      <input
 | 
			
		||||
        type="number"
 | 
			
		||||
        id="bombs"
 | 
			
		||||
        min="1"
 | 
			
		||||
        max="300"
 | 
			
		||||
        value="9"
 | 
			
		||||
        aria-label="Number of Bombs"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <button id="startGame">Start Game</button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
      <canvas id="game"></canvas>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script src="script.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,192 +0,0 @@
 | 
			
		|||
document.getElementById("startGame").addEventListener("click", function () {
 | 
			
		||||
  const gridSize = parseInt(document.getElementById("gridSize").value);
 | 
			
		||||
  const bombs = parseInt(document.getElementById("bombs").value);
 | 
			
		||||
 | 
			
		||||
  document.getElementById("settings").style.display = "none";
 | 
			
		||||
  document.getElementById("game").style.display = "block";
 | 
			
		||||
 | 
			
		||||
  renderGame(gridSize, bombs);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const canvas = document.getElementById("game");
 | 
			
		||||
const ctx = canvas.getContext("2d");
 | 
			
		||||
 | 
			
		||||
class Minesweeper {
 | 
			
		||||
  constructor(width, height, bombs) {
 | 
			
		||||
    this.size = 25;
 | 
			
		||||
    this.field = new Array(width);
 | 
			
		||||
    this.bombs = new Array(width);
 | 
			
		||||
    for (let x = 0; x < this.field.length; x++) {
 | 
			
		||||
      this.field[x] = new Array(height);
 | 
			
		||||
      this.bombs[x] = new Array(height);
 | 
			
		||||
      for (let y = 0; y < this.field[x].length; y++) {
 | 
			
		||||
        this.field[x][y] = 1;
 | 
			
		||||
        this.bombs[x][y] = false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.bombAmount =
 | 
			
		||||
      bombs > (width * height) / 2 ? (width * height) / 2 : bombs;
 | 
			
		||||
    this.width = width;
 | 
			
		||||
    this.height = height;
 | 
			
		||||
    this.firstClick = false;
 | 
			
		||||
    this.drawField();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateBombs() {
 | 
			
		||||
    for (let i = 0; i < this.bombAmount; i++) {
 | 
			
		||||
      let x = Math.floor(Math.random() * this.width);
 | 
			
		||||
      let y = Math.floor(Math.random() * this.height);
 | 
			
		||||
      this.bombs[x][y] || this.field[x][y] == 0
 | 
			
		||||
        ? i--
 | 
			
		||||
        : (this.bombs[x][y] = true);
 | 
			
		||||
    }
 | 
			
		||||
    this.firstClick = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getNearbyBombs(x, y) {
 | 
			
		||||
    let counter = 0;
 | 
			
		||||
    for (let newX = x - 1; newX <= x + 1; newX++) {
 | 
			
		||||
      for (let newY = y - 1; newY <= y + 1; newY++) {
 | 
			
		||||
        if (
 | 
			
		||||
          newX < this.field.length &&
 | 
			
		||||
          newX >= 0 &&
 | 
			
		||||
          newY < this.field[0].length &&
 | 
			
		||||
          newY >= 0
 | 
			
		||||
        ) {
 | 
			
		||||
          this.bombs[newX][newY] ? counter++ : {};
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return counter;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  checkWin() {
 | 
			
		||||
    for (let x = 0; x < this.field.length; x++) {
 | 
			
		||||
      for (let y = 0; y < this.field[x].length; y++) {
 | 
			
		||||
        if (this.field[x][y] != 0 && !this.bombs[x][y]) {
 | 
			
		||||
          return;
 | 
			
		||||
        } else if (this.field[x][y] == 0 && this.bombs[x][y]) {
 | 
			
		||||
          alert(`[ERROR]: Square (${x}|${y}) should not be a bomb!`);
 | 
			
		||||
          this.bombs[x][y] = false;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.drawField();
 | 
			
		||||
    alert("You won!");
 | 
			
		||||
    window.location.reload();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  markSquare(x, y) {
 | 
			
		||||
    if (x < this.field.length && y < this.field[0].length) {
 | 
			
		||||
      switch (this.field[x][y]) {
 | 
			
		||||
        case 1:
 | 
			
		||||
          this.field[x][y]++;
 | 
			
		||||
          break;
 | 
			
		||||
        case 2:
 | 
			
		||||
          this.field[x][y]++;
 | 
			
		||||
          break;
 | 
			
		||||
        case 3:
 | 
			
		||||
          this.field[x][y] = 1;
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      this.drawField();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uncoverSquare(x, y) {
 | 
			
		||||
    if (x < this.field.length && x >= 0 && y < this.field[0].length && y >= 0) {
 | 
			
		||||
      if (this.bombs[x][y] && this.field[x][y] == 1) {
 | 
			
		||||
        alert("You lost!");
 | 
			
		||||
        window.location.reload();
 | 
			
		||||
      } else if (this.field[x][y] == 1) {
 | 
			
		||||
        this.field[x][y] = 0;
 | 
			
		||||
        !this.firstClick ? this.generateBombs() : {};
 | 
			
		||||
        if (this.getNearbyBombs(x, y) == 0) {
 | 
			
		||||
          for (let newX = x - 1; newX <= x + 1; newX++) {
 | 
			
		||||
            for (let newY = y - 1; newY <= y + 1; newY++) {
 | 
			
		||||
              if (
 | 
			
		||||
                newX < this.field.length &&
 | 
			
		||||
                newX >= 0 &&
 | 
			
		||||
                newY < this.field[0].length &&
 | 
			
		||||
                newY >= 0
 | 
			
		||||
              ) {
 | 
			
		||||
                this.field[newX][newY] == 1
 | 
			
		||||
                  ? this.uncoverSquare(newX, newY)
 | 
			
		||||
                  : {};
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.checkWin();
 | 
			
		||||
      this.drawField();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawSquare(x, y, type) {
 | 
			
		||||
    ctx.lineWidth = 3;
 | 
			
		||||
    let uncovered = (x + y) % 2 === 0 ? "#D3D3D3" : "#A9A9A9";
 | 
			
		||||
    let covered = (x + y) % 2 === 0 ? "#4CAF50" : "#81C784";
 | 
			
		||||
    ctx.fillStyle = type != 0 ? covered : uncovered;
 | 
			
		||||
    ctx.fillRect(x * this.size, y * this.size, this.size, this.size);
 | 
			
		||||
    ctx.strokeStyle = "#000";
 | 
			
		||||
    ctx.strokeRect(x * this.size, y * this.size, this.size, this.size);
 | 
			
		||||
 | 
			
		||||
    if (type != 1) {
 | 
			
		||||
      const fontSize = this.size / 2;
 | 
			
		||||
      const number = this.getNearbyBombs(x, y);
 | 
			
		||||
      let finalPrint;
 | 
			
		||||
      ctx.font = `${fontSize}px sans-serif`;
 | 
			
		||||
      ctx.fillStyle = "#000";
 | 
			
		||||
      type == 0 ? (finalPrint = number ? number : " ") : {};
 | 
			
		||||
      type == 2 ? (finalPrint = "🚩") : {};
 | 
			
		||||
      type == 3 ? (finalPrint = "❓") : {};
 | 
			
		||||
      ctx.fillText(
 | 
			
		||||
        finalPrint,
 | 
			
		||||
        x * this.size + fontSize / (type == 0 ? 1.5 : 1.8),
 | 
			
		||||
        y * this.size + fontSize * 1.3
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  drawField() {
 | 
			
		||||
    if (window.innerWidth > window.innerHeight) {
 | 
			
		||||
      this.size = window.innerHeight / (this.field[0].length + 4);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.size = window.innerWidth / (this.field.length + 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    canvas.width = this.size * this.field.length;
 | 
			
		||||
    canvas.height = this.size * this.field[0].length;
 | 
			
		||||
 | 
			
		||||
    const offsetX = (canvas.width - this.field.length * this.size) / 2;
 | 
			
		||||
    const offsetY = (canvas.height - this.field[0].length * this.size) / 2;
 | 
			
		||||
 | 
			
		||||
    for (let x = 0; x < this.field.length; x++) {
 | 
			
		||||
      for (let y = 0; y < this.field[x].length; y++) {
 | 
			
		||||
        this.drawSquare(x, y, this.field[x][y], offsetX, offsetY);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const renderGame = (gridSize, bombs) => {
 | 
			
		||||
  let field = new Minesweeper(gridSize, gridSize, bombs);
 | 
			
		||||
  window.addEventListener("resize", () => field.drawField());
 | 
			
		||||
  canvas.addEventListener("click", (event) => {
 | 
			
		||||
    const rect = canvas.getBoundingClientRect();
 | 
			
		||||
    const x = Math.floor((event.clientX - rect.left) / field.size);
 | 
			
		||||
    const y = Math.floor((event.clientY - rect.top) / field.size);
 | 
			
		||||
    field.uncoverSquare(x, y);
 | 
			
		||||
  });
 | 
			
		||||
  canvas.addEventListener("contextmenu", (event) => {
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
    const rect = canvas.getBoundingClientRect();
 | 
			
		||||
    const x = Math.floor((event.clientX - rect.left) / field.size);
 | 
			
		||||
    const y = Math.floor((event.clientY - rect.top) / field.size);
 | 
			
		||||
    field.markSquare(x, y);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,104 +0,0 @@
 | 
			
		|||
* {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body,
 | 
			
		||||
html {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  background-color: #121212;
 | 
			
		||||
  color: #e0e0e0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#settings {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  margin: auto;
 | 
			
		||||
  background-color: #1e1e1e;
 | 
			
		||||
  padding: 40px;
 | 
			
		||||
  border-radius: 12px;
 | 
			
		||||
  box-shadow: 0 6px 15px rgba(0, 0, 0, 0.5);
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  max-width: 600px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
  font-size: 2.5em;
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
  color: #007bff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
label {
 | 
			
		||||
  margin-bottom: 12px;
 | 
			
		||||
  font-size: 20px;
 | 
			
		||||
  color: #d1d1d1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[type="number"],
 | 
			
		||||
input[type="range"],
 | 
			
		||||
span {
 | 
			
		||||
  padding: 12px;
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  max-width: 400px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  border: 1px solid #444;
 | 
			
		||||
  border-radius: 6px;
 | 
			
		||||
  background-color: #333;
 | 
			
		||||
  color: #e0e0e0;
 | 
			
		||||
  font-size: 18px;
 | 
			
		||||
  transition: border-color 0.3s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input[type="number"]:focus,
 | 
			
		||||
input[type="range"]:focus {
 | 
			
		||||
  border-color: #007bff;
 | 
			
		||||
  outline: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
  padding: 12px 24px;
 | 
			
		||||
  background-color: #007bff;
 | 
			
		||||
  color: white;
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-radius: 6px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: background-color 0.3s ease, transform 0.2s ease;
 | 
			
		||||
  font-size: 18px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button:hover {
 | 
			
		||||
  background-color: #0056b3;
 | 
			
		||||
  transform: translateY(-2px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button:active {
 | 
			
		||||
  transform: translateY(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
canvas {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 600px) {
 | 
			
		||||
  #settings {
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  input[type="number"],
 | 
			
		||||
  input[type="range"],
 | 
			
		||||
  button {
 | 
			
		||||
    width: 90%;
 | 
			
		||||
    max-width: none;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,95 +0,0 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
 | 
			
		||||
const cvs = document.getElementById("snake");
 | 
			
		||||
const ctx = cvs.getContext("2d");
 | 
			
		||||
const box = 20;
 | 
			
		||||
 | 
			
		||||
let snake = [{ x: 9 * box, y: 10 * box }];
 | 
			
		||||
let food = {
 | 
			
		||||
  x: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
  y: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
};
 | 
			
		||||
let score = 0;
 | 
			
		||||
let d;
 | 
			
		||||
let game;
 | 
			
		||||
 | 
			
		||||
document.addEventListener("keydown", direction);
 | 
			
		||||
function direction(event) {
 | 
			
		||||
  let key = event.keyCode;
 | 
			
		||||
  if ((key == 37 || key == 65) && d != "RIGHT") d = "LEFT";
 | 
			
		||||
  else if ((key == 38 || key == 87) && d != "DOWN") d = "UP";
 | 
			
		||||
  else if ((key == 39 || key == 68) && d != "LEFT") d = "RIGHT";
 | 
			
		||||
  else if ((key == 40 || key == 83) && d != "UP") d = "DOWN";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function collision(head, array) {
 | 
			
		||||
  return array.some((part) => head.x == part.x && head.y == part.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function draw() {
 | 
			
		||||
  ctx.fillStyle = "#0f380f";
 | 
			
		||||
  ctx.fillRect(0, 0, cvs.width, cvs.height);
 | 
			
		||||
 | 
			
		||||
  ctx.fillStyle = "red";
 | 
			
		||||
  ctx.fillRect(food.x, food.y, box, box);
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < snake.length; i++) {
 | 
			
		||||
    ctx.fillStyle = i == 0 ? "lime" : "white";
 | 
			
		||||
    ctx.fillRect(snake[i].x, snake[i].y, box, box);
 | 
			
		||||
    ctx.strokeStyle = "black";
 | 
			
		||||
    ctx.strokeRect(snake[i].x, snake[i].y, box, box);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let snakeX = snake[0].x;
 | 
			
		||||
  let snakeY = snake[0].y;
 | 
			
		||||
 | 
			
		||||
  if (d == "LEFT") snakeX -= box;
 | 
			
		||||
  if (d == "UP") snakeY -= box;
 | 
			
		||||
  if (d == "RIGHT") snakeX += box;
 | 
			
		||||
  if (d == "DOWN") snakeY += box;
 | 
			
		||||
 | 
			
		||||
  if (snakeX == food.x && snakeY == food.y) {
 | 
			
		||||
    score++;
 | 
			
		||||
    food = {
 | 
			
		||||
      x: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
      y: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
    };
 | 
			
		||||
  } else {
 | 
			
		||||
    snake.pop();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let newHead = { x: snakeX, y: snakeY };
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    snakeX < 0 ||
 | 
			
		||||
    snakeX >= cvs.width ||
 | 
			
		||||
    snakeY < 0 ||
 | 
			
		||||
    snakeY >= cvs.height ||
 | 
			
		||||
    collision(newHead, snake)
 | 
			
		||||
  ) {
 | 
			
		||||
    clearInterval(game);
 | 
			
		||||
    document.getElementById("restartBtn").style.display = "block";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  snake.unshift(newHead);
 | 
			
		||||
 | 
			
		||||
  ctx.fillStyle = "white";
 | 
			
		||||
  ctx.font = "20px Arial";
 | 
			
		||||
  ctx.fillText("Score: " + score, 10, 20);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function restartGame() {
 | 
			
		||||
  snake = [{ x: 9 * box, y: 10 * box }];
 | 
			
		||||
  food = {
 | 
			
		||||
    x: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
    y: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
  };
 | 
			
		||||
  score = 0;
 | 
			
		||||
  d = "";
 | 
			
		||||
  document.getElementById("restartBtn").style.display = "none";
 | 
			
		||||
  game = setInterval(draw, 150);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.getElementById("restartBtn").addEventListener("click", restartGame);
 | 
			
		||||
game = setInterval(draw, 150);
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 33 KiB  | 
| 
						 | 
				
			
			@ -1,67 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Snake - Game</title>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div class="gameboy">
 | 
			
		||||
      <!-- Game Boy Screen -->
 | 
			
		||||
      <div class="screen">
 | 
			
		||||
        <article class="game">
 | 
			
		||||
          <h1 class="title" id="title">Snake - Game</h1>
 | 
			
		||||
          <div class="description" id="description">
 | 
			
		||||
            <h2>Description</h2>
 | 
			
		||||
            <p>Eat as many apples and grow as much as possible</p>
 | 
			
		||||
            <p>◀ or A or arrow left = move left</p>
 | 
			
		||||
            <p>▶ or D or arrow right = move right</p>
 | 
			
		||||
            <p>▲ or W or arrow up = move up</p>
 | 
			
		||||
            <p>▼ or S or arrow down = move down</p>
 | 
			
		||||
          </div>
 | 
			
		||||
          <canvas id="snake" width="400" height="400"></canvas>
 | 
			
		||||
        </article>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <!-- Controls -->
 | 
			
		||||
      <div class="controls">
 | 
			
		||||
        <!-- D-Pad on the left -->
 | 
			
		||||
        <div class="dpad">
 | 
			
		||||
          <button class="dpad-btn up" id="up">▲</button>
 | 
			
		||||
          <button class="dpad-btn left" id="left">◀</button>
 | 
			
		||||
          <div class="dpad-center"></div>
 | 
			
		||||
          <button class="dpad-btn right" id="right">▶</button>
 | 
			
		||||
          <button class="dpad-btn down" id="down">▼</button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- A, B and start button on the right -->
 | 
			
		||||
        <div class="action-buttons">
 | 
			
		||||
          <button class="start-btn btn" id="start">Start</button>
 | 
			
		||||
          <button class="btn" id="a">A</button>
 | 
			
		||||
          <button class="btn" id="b">B</button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script src="game.js"></script>
 | 
			
		||||
    <script src="styles.js"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,238 +0,0 @@
 | 
			
		|||
/* Base Reset */
 | 
			
		||||
body,
 | 
			
		||||
html {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  font-family: monospace;
 | 
			
		||||
  background-color: #3a2d56;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* GameBoy Layout */
 | 
			
		||||
.gameboy {
 | 
			
		||||
  background-color: #5f4c82;
 | 
			
		||||
  width: 441px;
 | 
			
		||||
  height: 735px;
 | 
			
		||||
  border-radius: 20px;
 | 
			
		||||
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 768px) {
 | 
			
		||||
  .gameboy {
 | 
			
		||||
    width: 100vw;
 | 
			
		||||
    height: 100vh;
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Screen */
 | 
			
		||||
.screen {
 | 
			
		||||
  background-color: black;
 | 
			
		||||
  border: 4px solid #0f380f;
 | 
			
		||||
  width: 90%;
 | 
			
		||||
  height: 55%;
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.game {
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  width: 90%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Titles */
 | 
			
		||||
h1 {
 | 
			
		||||
  font-size: 2rem;
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  text-transform: uppercase;
 | 
			
		||||
  color: #9bbc0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.description,
 | 
			
		||||
.description p {
 | 
			
		||||
  font-size: 1.2rem;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  padding: 0 auto;
 | 
			
		||||
  color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Grid container */
 | 
			
		||||
#grid {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: repeat(10, 1fr); /* Adjust to match gridSize */
 | 
			
		||||
  grid-template-rows: repeat(10, 1fr); /* Adjust to match gridSize */
 | 
			
		||||
  width: 400px; /* Adjust as needed */
 | 
			
		||||
  height: 400px; /* Adjust as needed */
 | 
			
		||||
  border: 2px solid #0f380f;
 | 
			
		||||
  margin: 20px auto;
 | 
			
		||||
  /* initially hide */
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Individual cells */
 | 
			
		||||
.cell {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.cell.light-green {
 | 
			
		||||
  background-color: #9bbc0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.cell.dark-green {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Snake styling */
 | 
			
		||||
.snake {
 | 
			
		||||
  background-color: #e600ff; /* Snake color */
 | 
			
		||||
  z-index: 1000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Apple styling */
 | 
			
		||||
.apple {
 | 
			
		||||
  background-color: red; /* Apple color */
 | 
			
		||||
  z-index: 999;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Controls Section */
 | 
			
		||||
.controls {
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* D-Pad */
 | 
			
		||||
.dpad {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 120px;
 | 
			
		||||
  height: 120px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Base Styling for D-Pad Buttons */
 | 
			
		||||
.dpad-btn {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  color: #9bbc0f;
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  width: 42px;
 | 
			
		||||
  height: 42px;
 | 
			
		||||
  font-size: 1.5rem;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.up {
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.down {
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.left {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dpad-btn.right {
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* D-Pad Center to Connect Buttons */
 | 
			
		||||
.dpad-center {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translate(-50%, -50%);
 | 
			
		||||
  width: 40px;
 | 
			
		||||
  height: 40px;
 | 
			
		||||
  border: 2px solid transparent;
 | 
			
		||||
  z-index: 0;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* A and B Buttons */
 | 
			
		||||
.action-buttons {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  height: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  color: #9bbc0f;
 | 
			
		||||
  border: 2px solid #9bbc0f;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  width: 60px;
 | 
			
		||||
  height: 60px;
 | 
			
		||||
  font-size: 1.8rem;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: transform 0.1s, background-color 0.2s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn:hover {
 | 
			
		||||
  background-color: #9bbc0f;
 | 
			
		||||
  color: #0f380f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn:active {
 | 
			
		||||
  transform: scale(0.9);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Start Button */
 | 
			
		||||
.start-btn {
 | 
			
		||||
  background-color: #0f380f;
 | 
			
		||||
  color: #9bbc0f;
 | 
			
		||||
  border: 2px solid #9bbc0f;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  width: 100px;
 | 
			
		||||
  height: 40px;
 | 
			
		||||
  font-size: 1.2rem;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: transform 0.1s, background-color 0.2s;
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.start-btn:hover {
 | 
			
		||||
  background-color: #9bbc0f;
 | 
			
		||||
  color: #0f380f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.start-btn:active {
 | 
			
		||||
  transform: scale(0.9);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Hidden Canvas for Debugging or Fallback */
 | 
			
		||||
canvas {
 | 
			
		||||
  display: none;
 | 
			
		||||
  z-index: 1000;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,134 +0,0 @@
 | 
			
		|||
"use strict";
 | 
			
		||||
const aBtn = document.querySelector("#a");
 | 
			
		||||
const bBtn = document.querySelector("#b");
 | 
			
		||||
const gameboy = document.querySelector(".gameboy");
 | 
			
		||||
const html = document.documentElement;
 | 
			
		||||
const body = document.body;
 | 
			
		||||
const dpadButtons = document.querySelectorAll(".dpad-btn");
 | 
			
		||||
const dpadCenter = document.querySelector(".dpad-center"); // Darker variant
 | 
			
		||||
const actionButtons = document.querySelectorAll(".btn");
 | 
			
		||||
 | 
			
		||||
const colors = [
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#B39DDB",
 | 
			
		||||
    htmlColor: "#D1C4E9",
 | 
			
		||||
    buttonColor: "#673AB7",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#5E35B1",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FFC107",
 | 
			
		||||
    htmlColor: "#FFF9C4",
 | 
			
		||||
    buttonColor: "#FF9800",
 | 
			
		||||
    buttonTextColor: "#000000",
 | 
			
		||||
    dpadCenterColor: "#EF6C00",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#8BC34A",
 | 
			
		||||
    htmlColor: "#C5E1A5",
 | 
			
		||||
    buttonColor: "#FF5722",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#E64A19",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#F44336",
 | 
			
		||||
    htmlColor: "#FFCDD2",
 | 
			
		||||
    buttonColor: "#E91E63",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#C2185B",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#03A9F4",
 | 
			
		||||
    htmlColor: "#BBDEFB",
 | 
			
		||||
    buttonColor: "#FFEB3B",
 | 
			
		||||
    buttonTextColor: "#000000",
 | 
			
		||||
    dpadCenterColor: "#0277BD",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FF7043",
 | 
			
		||||
    htmlColor: "#FFCCBC",
 | 
			
		||||
    buttonColor: "#FF5722",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#D84315",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#9C27B0",
 | 
			
		||||
    htmlColor: "#E1BEE7",
 | 
			
		||||
    buttonColor: "#7B1FA2",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#6A1B9A",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FFD700",
 | 
			
		||||
    htmlColor: "#FFF9C4",
 | 
			
		||||
    buttonColor: "#FF9800",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#F57F17",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#009688",
 | 
			
		||||
    htmlColor: "#B2DFDB",
 | 
			
		||||
    buttonColor: "#4CAF50",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#00796B",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#795548",
 | 
			
		||||
    htmlColor: "#D7CCC8",
 | 
			
		||||
    buttonColor: "#9E9E9E",
 | 
			
		||||
    buttonTextColor: "#000000",
 | 
			
		||||
    dpadCenterColor: "#5D4037",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#FF5733",
 | 
			
		||||
    htmlColor: "#FFCCCB",
 | 
			
		||||
    buttonColor: "#C70039",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#B71C1C",
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    gameboyColor: "#00BCD4",
 | 
			
		||||
    htmlColor: "#B2EBF2",
 | 
			
		||||
    buttonColor: "#00ACC1",
 | 
			
		||||
    buttonTextColor: "#FFFFFF",
 | 
			
		||||
    dpadCenterColor: "#00838F",
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
let currentColorIndex = localStorage.getItem("gameboyColorIndex")
 | 
			
		||||
  ? parseInt(localStorage.getItem("gameboyColorIndex"))
 | 
			
		||||
  : 0;
 | 
			
		||||
 | 
			
		||||
function updateGameBoyColor() {
 | 
			
		||||
  gameboy.style.backgroundColor = colors[currentColorIndex].gameboyColor;
 | 
			
		||||
  html.style.backgroundColor = colors[currentColorIndex].htmlColor;
 | 
			
		||||
  body.style.backgroundColor = colors[currentColorIndex].htmlColor;
 | 
			
		||||
 | 
			
		||||
  dpadButtons.forEach((button) => {
 | 
			
		||||
    button.style.backgroundColor = colors[currentColorIndex].buttonColor;
 | 
			
		||||
    button.style.color = colors[currentColorIndex].buttonTextColor;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Using darker dpad center color
 | 
			
		||||
  dpadCenter.style.backgroundColor = colors[currentColorIndex].dpadCenterColor;
 | 
			
		||||
  dpadCenter.style.color = colors[currentColorIndex].buttonTextColor;
 | 
			
		||||
 | 
			
		||||
  actionButtons.forEach((button) => {
 | 
			
		||||
    button.style.backgroundColor = colors[currentColorIndex].buttonColor;
 | 
			
		||||
    button.style.color = colors[currentColorIndex].buttonTextColor;
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
aBtn.addEventListener("click", () => {
 | 
			
		||||
  currentColorIndex = (currentColorIndex - 1 + colors.length) % colors.length;
 | 
			
		||||
  localStorage.setItem("gameboyColorIndex", currentColorIndex);
 | 
			
		||||
  updateGameBoyColor();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
bBtn.addEventListener("click", () => {
 | 
			
		||||
  currentColorIndex = (currentColorIndex + 1) % colors.length;
 | 
			
		||||
  localStorage.setItem("gameboyColorIndex", currentColorIndex);
 | 
			
		||||
  updateGameBoyColor();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
updateGameBoyColor();
 | 
			
		||||
| 
						 | 
				
			
			@ -1,197 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Snake Game - GameBoy Style</title>
 | 
			
		||||
    <style>
 | 
			
		||||
      /* Base Reset */
 | 
			
		||||
      body,
 | 
			
		||||
      html {
 | 
			
		||||
        margin: 0;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        font-family: monospace;
 | 
			
		||||
        background-color: #3a2d56;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        height: 100vh;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* GameBoy Layout */
 | 
			
		||||
      .gameboy {
 | 
			
		||||
        background-color: #5f4c82;
 | 
			
		||||
        width: 441px;
 | 
			
		||||
        height: 735px;
 | 
			
		||||
        border-radius: 20px;
 | 
			
		||||
        box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        padding: 10px;
 | 
			
		||||
        position: relative;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @media (max-width: 768px) {
 | 
			
		||||
        .gameboy {
 | 
			
		||||
          width: 100vw;
 | 
			
		||||
          height: 100vh;
 | 
			
		||||
          border-radius: 0;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* Screen */
 | 
			
		||||
      .screen {
 | 
			
		||||
        background-color: black;
 | 
			
		||||
        border: 4px solid #0f380f;
 | 
			
		||||
        width: 90%;
 | 
			
		||||
        height: 55%;
 | 
			
		||||
        margin-top: 20px;
 | 
			
		||||
        border-radius: 10px;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      canvas {
 | 
			
		||||
        border: 1px solid black;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* Restart Button */
 | 
			
		||||
      #restartBtn {
 | 
			
		||||
        padding: 10px 20px;
 | 
			
		||||
        font-size: 16px;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
        margin-top: 20px;
 | 
			
		||||
        background-color: #0f380f;
 | 
			
		||||
        color: white;
 | 
			
		||||
        border: none;
 | 
			
		||||
        border-radius: 5px;
 | 
			
		||||
        display: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      #restartBtn:hover {
 | 
			
		||||
        background-color: #9bbc0f;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* Titles */
 | 
			
		||||
      h1 {
 | 
			
		||||
        font-size: 1.8rem;
 | 
			
		||||
        margin-bottom: 10px;
 | 
			
		||||
        text-transform: uppercase;
 | 
			
		||||
        color: #9bbc0f;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div class="gameboy">
 | 
			
		||||
      <h1>Snake Game</h1>
 | 
			
		||||
      <div class="screen">
 | 
			
		||||
        <canvas id="snake" width="400" height="400"></canvas>
 | 
			
		||||
      </div>
 | 
			
		||||
      <button id="restartBtn">Restart</button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script>
 | 
			
		||||
      "use strict";
 | 
			
		||||
 | 
			
		||||
      const cvs = document.getElementById("snake");
 | 
			
		||||
      const ctx = cvs.getContext("2d");
 | 
			
		||||
      const box = 20;
 | 
			
		||||
 | 
			
		||||
      let snake = [{ x: 9 * box, y: 10 * box }];
 | 
			
		||||
      let food = {
 | 
			
		||||
        x: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
        y: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
      };
 | 
			
		||||
      let score = 0;
 | 
			
		||||
      let d;
 | 
			
		||||
      let game;
 | 
			
		||||
 | 
			
		||||
      document.addEventListener("keydown", direction);
 | 
			
		||||
      function direction(event) {
 | 
			
		||||
        let key = event.keyCode;
 | 
			
		||||
        if ((key == 37 || key == 65) && d != "RIGHT") d = "LEFT";
 | 
			
		||||
        else if ((key == 38 || key == 87) && d != "DOWN") d = "UP";
 | 
			
		||||
        else if ((key == 39 || key == 68) && d != "LEFT") d = "RIGHT";
 | 
			
		||||
        else if ((key == 40 || key == 83) && d != "UP") d = "DOWN";
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function collision(head, array) {
 | 
			
		||||
        return array.some((part) => head.x == part.x && head.y == part.y);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function draw() {
 | 
			
		||||
        ctx.fillStyle = "#0f380f";
 | 
			
		||||
        ctx.fillRect(0, 0, cvs.width, cvs.height);
 | 
			
		||||
 | 
			
		||||
        ctx.fillStyle = "red";
 | 
			
		||||
        ctx.fillRect(food.x, food.y, box, box);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < snake.length; i++) {
 | 
			
		||||
          ctx.fillStyle = i == 0 ? "lime" : "white";
 | 
			
		||||
          ctx.fillRect(snake[i].x, snake[i].y, box, box);
 | 
			
		||||
          ctx.strokeStyle = "black";
 | 
			
		||||
          ctx.strokeRect(snake[i].x, snake[i].y, box, box);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let snakeX = snake[0].x;
 | 
			
		||||
        let snakeY = snake[0].y;
 | 
			
		||||
 | 
			
		||||
        if (d == "LEFT") snakeX -= box;
 | 
			
		||||
        if (d == "UP") snakeY -= box;
 | 
			
		||||
        if (d == "RIGHT") snakeX += box;
 | 
			
		||||
        if (d == "DOWN") snakeY += box;
 | 
			
		||||
 | 
			
		||||
        if (snakeX == food.x && snakeY == food.y) {
 | 
			
		||||
          score++;
 | 
			
		||||
          food = {
 | 
			
		||||
            x: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
            y: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
          };
 | 
			
		||||
        } else {
 | 
			
		||||
          snake.pop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let newHead = { x: snakeX, y: snakeY };
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
          snakeX < 0 ||
 | 
			
		||||
          snakeX >= cvs.width ||
 | 
			
		||||
          snakeY < 0 ||
 | 
			
		||||
          snakeY >= cvs.height ||
 | 
			
		||||
          collision(newHead, snake)
 | 
			
		||||
        ) {
 | 
			
		||||
          clearInterval(game);
 | 
			
		||||
          document.getElementById("restartBtn").style.display = "block";
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        snake.unshift(newHead);
 | 
			
		||||
 | 
			
		||||
        ctx.fillStyle = "white";
 | 
			
		||||
        ctx.font = "20px Arial";
 | 
			
		||||
        ctx.fillText("Score: " + score, 10, 20);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function restartGame() {
 | 
			
		||||
        snake = [{ x: 9 * box, y: 10 * box }];
 | 
			
		||||
        food = {
 | 
			
		||||
          x: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
          y: Math.floor(Math.random() * 19 + 1) * box,
 | 
			
		||||
        };
 | 
			
		||||
        score = 0;
 | 
			
		||||
        d = "";
 | 
			
		||||
        document.getElementById("restartBtn").style.display = "none";
 | 
			
		||||
        game = setInterval(draw, 150);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      document
 | 
			
		||||
        .getElementById("restartBtn")
 | 
			
		||||
        .addEventListener("click", restartGame);
 | 
			
		||||
      game = setInterval(draw, 150);
 | 
			
		||||
    </script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,145 +0,0 @@
 | 
			
		|||
* {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
  font-family: "Courier New", Courier, monospace;
 | 
			
		||||
  background-color: #0d0d0d;
 | 
			
		||||
  color: #b0b0b0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  line-height: 1.6;
 | 
			
		||||
  background-image: url("images/background.jpg");
 | 
			
		||||
  background-size: cover; /* Adjust size for tape appearance */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header {
 | 
			
		||||
  background-color: #222; /* Fully opaque background */
 | 
			
		||||
  color: #b0b0b0;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  padding: 1em 0;
 | 
			
		||||
  font-size: 2rem;
 | 
			
		||||
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7);
 | 
			
		||||
  animation: neonFlicker 1.5s infinite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create the flickering neon light effect */
 | 
			
		||||
@keyframes neonFlicker {
 | 
			
		||||
  0% {
 | 
			
		||||
    text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
 | 
			
		||||
      0 0 20px #ffcc00, 0 0 30px #ffcc00, 0 0 40px #ffcc00, 0 0 50px #ffcc00;
 | 
			
		||||
  }
 | 
			
		||||
  20% {
 | 
			
		||||
    text-shadow: 0 0 3px #ffcc00, 0 0 7px #ffcc00, 0 0 10px #ffcc00,
 | 
			
		||||
      0 0 15px #ffcc00, 0 0 20px #ffcc00;
 | 
			
		||||
  }
 | 
			
		||||
  40% {
 | 
			
		||||
    text-shadow: 0 0 5px #ffcc00, 0 0 15px #ffcc00, 0 0 25px #ffcc00;
 | 
			
		||||
  }
 | 
			
		||||
  60% {
 | 
			
		||||
    text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
 | 
			
		||||
      0 0 20px #ffcc00, 0 0 30px #ffcc00;
 | 
			
		||||
  }
 | 
			
		||||
  80% {
 | 
			
		||||
    text-shadow: 0 0 3px #ffcc00, 0 0 7px #ffcc00, 0 0 10px #ffcc00;
 | 
			
		||||
  }
 | 
			
		||||
  100% {
 | 
			
		||||
    text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
 | 
			
		||||
      0 0 20px #ffcc00, 0 0 30px #ffcc00, 0 0 40px #ffcc00;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer {
 | 
			
		||||
  background-color: #111;
 | 
			
		||||
  color: #b0b0b0;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  padding: 1em 0;
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grid-container {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: repeat(3, 1fr);
 | 
			
		||||
  gap: 20px;
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  background-color: #1a1a1a;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.8);
 | 
			
		||||
  transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 400px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item img {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  object-fit: cover;
 | 
			
		||||
  filter: brightness(0.6);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item .description {
 | 
			
		||||
  padding: 30px;
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
  color: #ccc;
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.8);
 | 
			
		||||
  border-radius: 0 0 10px 10px;
 | 
			
		||||
  flex-grow: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item:hover {
 | 
			
		||||
  transform: scale(1.05);
 | 
			
		||||
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.9);
 | 
			
		||||
  filter: brightness(1.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item:hover img {
 | 
			
		||||
  transform: scale(1.1);
 | 
			
		||||
  filter: brightness(1.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item h2 {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 10%;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
  color: #ffffff;
 | 
			
		||||
  font-size: 1.8rem;
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.9);
 | 
			
		||||
  padding: 5px 15px;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  transition: opacity 0.3s ease, transform 0.3s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item:hover h2 {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  transform: translateX(-50%) translateY(-10px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 800px) {
 | 
			
		||||
  header {
 | 
			
		||||
    font-size: 1.5rem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .item {
 | 
			
		||||
    height: auto;
 | 
			
		||||
    width: auto;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .grid-container {
 | 
			
		||||
    grid-template-columns: repeat(1, 1fr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/favicon/android-chrome-192x192.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/favicon/android-chrome-512x512.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 46 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/favicon/apple-touch-icon.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/favicon/favicon-16x16.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 795 B  | 
							
								
								
									
										
											BIN
										
									
								
								src/favicon/favicon-32x32.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/favicon/favicon.ico
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 15 KiB  | 
| 
		 Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB  | 
| 
		 Before Width: | Height: | Size: 385 KiB After Width: | Height: | Size: 385 KiB  | 
							
								
								
									
										311
									
								
								src/js/animation/Meteor.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,311 @@
 | 
			
		|||
class Meteor {
 | 
			
		||||
  constructor(container) {
 | 
			
		||||
    this.element = document.createElement("div");
 | 
			
		||||
    this.element.style.position = "absolute";
 | 
			
		||||
    this.container = container;
 | 
			
		||||
    this.speed = Math.random() * 3 + 4;
 | 
			
		||||
    this.life = 1.0;
 | 
			
		||||
    this.particles = [];
 | 
			
		||||
 | 
			
		||||
    this.colorType = Math.floor(Math.random() * 4);
 | 
			
		||||
    this.colors = this.getMeteorColors();
 | 
			
		||||
 | 
			
		||||
    this.containerRect = container.getBoundingClientRect();
 | 
			
		||||
 | 
			
		||||
    // Start from random edge with PIXEL-based positioning
 | 
			
		||||
    const edge = Math.floor(Math.random() * 4);
 | 
			
		||||
    switch (edge) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        // Top
 | 
			
		||||
        this.x = Math.random() * this.containerRect.width;
 | 
			
		||||
        this.y = -20;
 | 
			
		||||
      case 1:
 | 
			
		||||
        // Right
 | 
			
		||||
        this.x = this.containerRect.width + 20;
 | 
			
		||||
        this.y = Math.random() * this.containerRect.height;
 | 
			
		||||
      case 2:
 | 
			
		||||
        // Bottom
 | 
			
		||||
        this.x = Math.random() * this.containerRect.width;
 | 
			
		||||
        this.y = this.containerRect.height + 20;
 | 
			
		||||
      case 3:
 | 
			
		||||
        // Left
 | 
			
		||||
        this.x = -20;
 | 
			
		||||
        this.y = Math.random() * this.containerRect.height;
 | 
			
		||||
      default:
 | 
			
		||||
        console.log("Error creating Meteor direction");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const centerX = this.containerRect.width / 2 + (Math.random() - 0.5) * 200;
 | 
			
		||||
    const centerY = this.containerRect.height / 2 + (Math.random() - 0.5) * 200;
 | 
			
		||||
    this.angle =
 | 
			
		||||
      Math.atan2(centerY - this.y, centerX - this.x) +
 | 
			
		||||
      (Math.random() - 0.5) * 0.8;
 | 
			
		||||
    this.dx = Math.cos(this.angle) * this.speed;
 | 
			
		||||
    this.dy = Math.sin(this.angle) * this.speed;
 | 
			
		||||
 | 
			
		||||
    this.init();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getMeteorColors() {
 | 
			
		||||
    const colorSchemes = [
 | 
			
		||||
      {
 | 
			
		||||
        head: "rgba(255, 255, 255, 1)",
 | 
			
		||||
        core: "rgba(255, 230, 150, 0.9)",
 | 
			
		||||
        mid: "rgba(255, 180, 80, 0.7)",
 | 
			
		||||
        outer: "rgba(255, 100, 50, 0.5)",
 | 
			
		||||
        glow: "rgba(255, 200, 100, 0.9)",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        head: "rgba(255, 255, 255, 1)",
 | 
			
		||||
        core: "rgba(180, 220, 255, 0.9)",
 | 
			
		||||
        mid: "rgba(100, 180, 255, 0.7)",
 | 
			
		||||
        outer: "rgba(50, 100, 255, 0.5)",
 | 
			
		||||
        glow: "rgba(100, 180, 255, 0.9)",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        head: "rgba(255, 255, 255, 1)",
 | 
			
		||||
        core: "rgba(220, 180, 255, 0.9)",
 | 
			
		||||
        mid: "rgba(200, 100, 255, 0.7)",
 | 
			
		||||
        outer: "rgba(180, 50, 255, 0.5)",
 | 
			
		||||
        glow: "rgba(200, 150, 255, 0.9)",
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        head: "rgba(255, 255, 255, 1)",
 | 
			
		||||
        core: "rgba(180, 255, 200, 0.9)",
 | 
			
		||||
        mid: "rgba(80, 255, 150, 0.7)",
 | 
			
		||||
        outer: "rgba(50, 200, 100, 0.5)",
 | 
			
		||||
        glow: "rgba(150, 255, 180, 0.9)",
 | 
			
		||||
      },
 | 
			
		||||
    ];
 | 
			
		||||
    return colorSchemes[this.colorType];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init() {
 | 
			
		||||
    const size = Math.random() * 4 + 10;
 | 
			
		||||
    const colors = this.colors;
 | 
			
		||||
 | 
			
		||||
    const head = document.createElement("div");
 | 
			
		||||
    head.style.width = `${size}px`;
 | 
			
		||||
    head.style.height = `${size}px`;
 | 
			
		||||
    head.style.borderRadius = "50%";
 | 
			
		||||
    head.style.background = `
 | 
			
		||||
      radial-gradient(
 | 
			
		||||
        circle at 30% 30%,
 | 
			
		||||
        ${colors.head} 0%,
 | 
			
		||||
        ${colors.core} 30%,
 | 
			
		||||
        ${colors.mid} 60%,
 | 
			
		||||
        ${colors.outer} 100%
 | 
			
		||||
      )
 | 
			
		||||
    `;
 | 
			
		||||
    head.style.boxShadow = `
 | 
			
		||||
      0 0 ${size * 4}px ${colors.glow},
 | 
			
		||||
      0 0 ${size * 2}px rgba(255, 255, 255, 0.8),
 | 
			
		||||
      inset 0 0 ${size * 0.5}px rgba(255, 255, 255, 0.9)
 | 
			
		||||
    `;
 | 
			
		||||
    head.style.position = "absolute";
 | 
			
		||||
    head.style.left = "50%";
 | 
			
		||||
    head.style.top = "50%";
 | 
			
		||||
    head.style.transform = "translate(-50%, -50%)";
 | 
			
		||||
    head.style.zIndex = "3";
 | 
			
		||||
    head.style.filter = "blur(0.5px)";
 | 
			
		||||
 | 
			
		||||
    // Create multi-layer tail
 | 
			
		||||
    this.createTailLayer(size * 3, size * 1.2, colors.core, 1, "1px");
 | 
			
		||||
    this.createTailLayer(size * 5, size * 2, colors.mid, 0.6, "2px");
 | 
			
		||||
    this.createTailLayer(size * 8, size * 3, colors.outer, 0.3, "3px");
 | 
			
		||||
 | 
			
		||||
    // Assemble meteor
 | 
			
		||||
    this.element.appendChild(head);
 | 
			
		||||
    this.element.style.left = `${this.x}px`;
 | 
			
		||||
    this.element.style.top = `${this.y}px`;
 | 
			
		||||
    this.element.style.zIndex = "10";
 | 
			
		||||
    this.element.style.filter = "blur(0.5px)";
 | 
			
		||||
 | 
			
		||||
    this.head = head;
 | 
			
		||||
    this.size = size;
 | 
			
		||||
 | 
			
		||||
    this.container.appendChild(this.element);
 | 
			
		||||
 | 
			
		||||
    this.createTrailParticles();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTailLayer(length, width, color, opacity, blur) {
 | 
			
		||||
    const tail = document.createElement("div");
 | 
			
		||||
    const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI;
 | 
			
		||||
 | 
			
		||||
    tail.style.width = `${length}px`;
 | 
			
		||||
    tail.style.height = `${width}px`;
 | 
			
		||||
    tail.style.position = "absolute";
 | 
			
		||||
    tail.style.left = "50%";
 | 
			
		||||
    tail.style.top = "50%";
 | 
			
		||||
    tail.style.transformOrigin = "left center";
 | 
			
		||||
    tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`;
 | 
			
		||||
    tail.style.background = `linear-gradient(to right, 
 | 
			
		||||
      ${color} 0%, 
 | 
			
		||||
      ${color.replace(")", ", 0.8)").replace("rgba", "rgba")} 20%,
 | 
			
		||||
      ${color.replace(")", ", 0.4)").replace("rgba", "rgba")} 50%,
 | 
			
		||||
      ${color.replace(")", ", 0.1)").replace("rgba", "rgba")} 80%,
 | 
			
		||||
      transparent 100%)`;
 | 
			
		||||
    tail.style.opacity = opacity;
 | 
			
		||||
    tail.style.filter = `blur(${blur})`;
 | 
			
		||||
    tail.style.pointerEvents = "none";
 | 
			
		||||
    tail.style.zIndex = "2";
 | 
			
		||||
    tail.style.borderRadius = "0 50% 50% 0";
 | 
			
		||||
 | 
			
		||||
    this.element.appendChild(tail);
 | 
			
		||||
    this.tail = tail;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createTrailParticles() {
 | 
			
		||||
    for (let i = 0; i < 5; i++) {
 | 
			
		||||
      this.addTrailParticle();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addTrailParticle() {
 | 
			
		||||
    const particle = document.createElement("div");
 | 
			
		||||
    const size = Math.random() * 2 + 1;
 | 
			
		||||
    const colors = this.colors;
 | 
			
		||||
 | 
			
		||||
    particle.style.width = `${size}px`;
 | 
			
		||||
    particle.style.height = `${size}px`;
 | 
			
		||||
    particle.style.borderRadius = "50%";
 | 
			
		||||
    particle.style.background = colors.mid;
 | 
			
		||||
    particle.style.position = "absolute";
 | 
			
		||||
    particle.style.left = `${this.x}px`;
 | 
			
		||||
    particle.style.top = `${this.y}px`;
 | 
			
		||||
    particle.style.boxShadow = `0 0 ${size * 3}px ${colors.glow}`;
 | 
			
		||||
    particle.style.opacity = "0.7";
 | 
			
		||||
    particle.style.zIndex = "1";
 | 
			
		||||
    particle.style.pointerEvents = "none";
 | 
			
		||||
 | 
			
		||||
    this.container.appendChild(particle);
 | 
			
		||||
 | 
			
		||||
    this.particles.push({
 | 
			
		||||
      element: particle,
 | 
			
		||||
      life: 1.0,
 | 
			
		||||
      x: this.x,
 | 
			
		||||
      y: this.y,
 | 
			
		||||
      vx: (Math.random() - 0.5) * 0.5,
 | 
			
		||||
      vy: (Math.random() - 0.5) * 0.5,
 | 
			
		||||
      size: size,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update() {
 | 
			
		||||
    this.x += this.dx;
 | 
			
		||||
    this.y += this.dy;
 | 
			
		||||
    this.life -= 0.005;
 | 
			
		||||
 | 
			
		||||
    this.element.style.left = `${this.x}px`;
 | 
			
		||||
    this.element.style.top = `${this.y}px`;
 | 
			
		||||
    this.element.style.opacity = this.life;
 | 
			
		||||
 | 
			
		||||
    // Update tail direction
 | 
			
		||||
    const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI;
 | 
			
		||||
    const tails = this.element.querySelectorAll("div:not(.particle)");
 | 
			
		||||
    tails.forEach((tail) => {
 | 
			
		||||
      if (tail !== this.head) {
 | 
			
		||||
        tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`;
 | 
			
		||||
        tail.style.opacity = this.life;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (Math.random() < 0.3) {
 | 
			
		||||
      this.addTrailParticle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update existing particles with pixel positioning
 | 
			
		||||
    for (let i = this.particles.length - 1; i >= 0; i--) {
 | 
			
		||||
      const p = this.particles[i];
 | 
			
		||||
      p.life -= 0.02;
 | 
			
		||||
      p.x += p.vx;
 | 
			
		||||
      p.y += p.vy;
 | 
			
		||||
 | 
			
		||||
      p.element.style.left = `${p.x}px`;
 | 
			
		||||
      p.element.style.top = `${p.y}px`;
 | 
			
		||||
      p.element.style.opacity = p.life;
 | 
			
		||||
      p.element.style.transform = `scale(${p.life})`;
 | 
			
		||||
 | 
			
		||||
      if (p.life <= 0) {
 | 
			
		||||
        p.element.remove();
 | 
			
		||||
        this.particles.splice(i, 1);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      this.x < -50 ||
 | 
			
		||||
      this.x > this.containerRect.width + 50 ||
 | 
			
		||||
      this.y < -50 ||
 | 
			
		||||
      this.y > this.containerRect.height + 50 ||
 | 
			
		||||
      this.life <= 0
 | 
			
		||||
    ) {
 | 
			
		||||
      // Fade out particles
 | 
			
		||||
      this.particles.forEach((p) => {
 | 
			
		||||
        p.element.style.transition = "opacity 0.5s";
 | 
			
		||||
        p.element.style.opacity = "0";
 | 
			
		||||
        setTimeout(() => p.element.remove(), 500);
 | 
			
		||||
      });
 | 
			
		||||
      this.remove();
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  remove() {
 | 
			
		||||
    if (this.life > 0.3) {
 | 
			
		||||
      this.createExplosion();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      this.element.remove();
 | 
			
		||||
    }, 300);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createExplosion() {
 | 
			
		||||
    for (let i = 0; i < 8; i++) {
 | 
			
		||||
      const particle = document.createElement("div");
 | 
			
		||||
      const size = Math.random() * 3 + 2;
 | 
			
		||||
      const colors = this.colors;
 | 
			
		||||
 | 
			
		||||
      particle.style.width = `${size}px`;
 | 
			
		||||
      particle.style.height = `${size}px`;
 | 
			
		||||
      particle.style.borderRadius = "50%";
 | 
			
		||||
      particle.style.background = colors.core;
 | 
			
		||||
      particle.style.position = "absolute";
 | 
			
		||||
      particle.style.left = `${this.x}px`;
 | 
			
		||||
      particle.style.top = `${this.y}px`;
 | 
			
		||||
      particle.style.boxShadow = `0 0 ${size * 4}px ${colors.glow}`;
 | 
			
		||||
      particle.style.opacity = "0.8";
 | 
			
		||||
      particle.style.zIndex = "2";
 | 
			
		||||
 | 
			
		||||
      this.container.appendChild(particle);
 | 
			
		||||
 | 
			
		||||
      // Animate explosion
 | 
			
		||||
      const angle = (i / 8) * Math.PI * 2;
 | 
			
		||||
      const distance = Math.random() * 20 + 10;
 | 
			
		||||
      const duration = Math.random() * 500 + 500;
 | 
			
		||||
 | 
			
		||||
      particle.animate(
 | 
			
		||||
        [
 | 
			
		||||
          {
 | 
			
		||||
            transform: "translate(0, 0) scale(1)",
 | 
			
		||||
            opacity: 0.8,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            transform: `translate(${Math.cos(angle) * distance}px, ${
 | 
			
		||||
              Math.sin(angle) * distance
 | 
			
		||||
            }px) scale(0.1)`,
 | 
			
		||||
            opacity: 0,
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        {
 | 
			
		||||
          duration: duration,
 | 
			
		||||
          easing: "cubic-bezier(0.4, 0, 0.2, 1)",
 | 
			
		||||
        }
 | 
			
		||||
      ).onfinish = () => particle.remove();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Meteor;
 | 
			
		||||
							
								
								
									
										63
									
								
								src/js/animation/Star.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
class Star {
 | 
			
		||||
  constructor(container) {
 | 
			
		||||
    this.element = document.createElement("div");
 | 
			
		||||
    this.element.classList.add("star");
 | 
			
		||||
    this.container = container;
 | 
			
		||||
    this.speed = Math.random() * 0.5 + 0.1;
 | 
			
		||||
 | 
			
		||||
    // Random direction angle (0 to 360 degrees)
 | 
			
		||||
    this.angle = Math.random() * Math.PI * 2;
 | 
			
		||||
    this.dx = Math.cos(this.angle) * this.speed;
 | 
			
		||||
    this.dy = Math.sin(this.angle) * this.speed;
 | 
			
		||||
 | 
			
		||||
    this.x = Math.random() * 100;
 | 
			
		||||
    this.y = Math.random() * 100;
 | 
			
		||||
 | 
			
		||||
    this.color = this.getRandomStarColor();
 | 
			
		||||
    this.init();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getRandomStarColor() {
 | 
			
		||||
    const colors = [
 | 
			
		||||
      "rgba(255, 255, 255, 0.9)", // Pure white
 | 
			
		||||
      "rgba(255, 250, 200, 0.9)", // Warm white
 | 
			
		||||
      "rgba(200, 220, 255, 0.9)", // Cool blue-white
 | 
			
		||||
      "rgba(255, 220, 180, 0.9)", // Yellow-white
 | 
			
		||||
      "rgba(180, 200, 255, 0.9)", // Blue-white
 | 
			
		||||
    ];
 | 
			
		||||
    return colors[Math.floor(Math.random() * colors.length)];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init() {
 | 
			
		||||
    const size = Math.random() * 3 + 0.5;
 | 
			
		||||
    this.element.style.width = `${size}px`;
 | 
			
		||||
    this.element.style.height = `${size}px`;
 | 
			
		||||
    this.element.style.left = `${this.x}%`;
 | 
			
		||||
    this.element.style.top = `${this.y}%`;
 | 
			
		||||
    this.element.style.background = this.color;
 | 
			
		||||
    this.element.style.boxShadow = `0 0 ${size * 2}px ${this.color}`;
 | 
			
		||||
    this.element.style.animationDelay = `${Math.random() * 5}s`;
 | 
			
		||||
    this.element.style.animationDuration = `${3 + Math.random() * 4}s`;
 | 
			
		||||
    this.container.appendChild(this.element);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  update() {
 | 
			
		||||
    this.x += this.dx * 0.05;
 | 
			
		||||
    this.y += this.dy * 0.05;
 | 
			
		||||
 | 
			
		||||
    // Wrap around when star goes off screen
 | 
			
		||||
    if (this.y > 100) this.y = 0;
 | 
			
		||||
    if (this.y < 0) this.y = 100;
 | 
			
		||||
    if (this.x > 100) this.x = 0;
 | 
			
		||||
    if (this.x < 0) this.x = 100;
 | 
			
		||||
 | 
			
		||||
    this.element.style.left = `${this.x}%`;
 | 
			
		||||
    this.element.style.top = `${this.y}%`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  remove() {
 | 
			
		||||
    this.element.remove();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Star;
 | 
			
		||||
							
								
								
									
										81
									
								
								src/js/animation/starBackground.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
import Star from "./Star.js";
 | 
			
		||||
import Meteor from "./Meteor.js";
 | 
			
		||||
 | 
			
		||||
const starInstances = [];
 | 
			
		||||
const meteorInstances = [];
 | 
			
		||||
 | 
			
		||||
function createStars() {
 | 
			
		||||
  const stars = document.getElementById("stars");
 | 
			
		||||
  const count = 400;
 | 
			
		||||
  stars.innerHTML = "";
 | 
			
		||||
  starInstances.length = 0;
 | 
			
		||||
 | 
			
		||||
  for (let i = 0; i < count; i++) {
 | 
			
		||||
    const star = new Star(stars);
 | 
			
		||||
    starInstances.push(star);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function injectStarCSS() {
 | 
			
		||||
  const style = document.createElement("style");
 | 
			
		||||
  style.textContent = `
 | 
			
		||||
    .star {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      border-radius: 50%;
 | 
			
		||||
      animation: twinkle ease-in-out infinite;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @keyframes twinkle {
 | 
			
		||||
      0%, 100% { opacity: 1; }
 | 
			
		||||
      50% { opacity: 0.3; }
 | 
			
		||||
    }
 | 
			
		||||
  `;
 | 
			
		||||
  document.head.appendChild(style);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function trySpawnMeteor() {
 | 
			
		||||
  const spawnChance = 0.12;
 | 
			
		||||
  if (Math.random() < spawnChance) {
 | 
			
		||||
    const stars = document.getElementById("stars");
 | 
			
		||||
    const newMeteor = new Meteor(stars);
 | 
			
		||||
    meteorInstances.push(newMeteor);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function animateStars() {
 | 
			
		||||
  starInstances.forEach((star) => star.update());
 | 
			
		||||
 | 
			
		||||
  // Update meteors and remove dead ones
 | 
			
		||||
  for (let i = meteorInstances.length - 1; i >= 0; i--) {
 | 
			
		||||
    if (!meteorInstances[i].update()) {
 | 
			
		||||
      meteorInstances.splice(i, 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  requestAnimationFrame(animateStars);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createMeteorBurst() {
 | 
			
		||||
  for (let i = 0; i < 3; i++) {
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      const stars = document.getElementById("stars");
 | 
			
		||||
      const newMeteor = new Meteor(stars);
 | 
			
		||||
      meteorInstances.push(newMeteor);
 | 
			
		||||
    }, i * 200);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
  injectStarCSS();
 | 
			
		||||
  createStars();
 | 
			
		||||
  animateStars();
 | 
			
		||||
 | 
			
		||||
  setInterval(trySpawnMeteor, 2000);
 | 
			
		||||
  setInterval(createMeteorBurst, 15000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (document.readyState === "loading") {
 | 
			
		||||
  document.addEventListener("DOMContentLoaded", init);
 | 
			
		||||
} else {
 | 
			
		||||
  init();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/js/form.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
// Form submission
 | 
			
		||||
function setupForm() {
 | 
			
		||||
  const form = document.getElementById("contact-form");
 | 
			
		||||
  if (form) {
 | 
			
		||||
    form.addEventListener("submit", function (e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
 | 
			
		||||
      // Simple form validation
 | 
			
		||||
      const name = document.getElementById("name").value;
 | 
			
		||||
      const email = document.getElementById("email").value;
 | 
			
		||||
      const message = document.getElementById("message").value;
 | 
			
		||||
 | 
			
		||||
      if (name && email && message) {
 | 
			
		||||
        // In a real implementation, you would send the form data to a server
 | 
			
		||||
        alert("Thank you for your message! We will get back to you soon.");
 | 
			
		||||
        form.reset();
 | 
			
		||||
      } else {
 | 
			
		||||
        alert("Please fill in all required fields.");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/js/main.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
// Initialize everything when DOM is loaded
 | 
			
		||||
document.addEventListener("DOMContentLoaded", function () {
 | 
			
		||||
  // Initialize components
 | 
			
		||||
  if (typeof createStars === "function") createStars();
 | 
			
		||||
  if (typeof updateNavigation === "function") updateNavigation();
 | 
			
		||||
  if (typeof setupForm === "function") setupForm();
 | 
			
		||||
  if (typeof setupMobileNavigation === "function") setupMobileNavigation();
 | 
			
		||||
 | 
			
		||||
  // Set up event listeners
 | 
			
		||||
  window.addEventListener("scroll", updateNavigation);
 | 
			
		||||
 | 
			
		||||
  // Smooth scrolling for navigation links
 | 
			
		||||
  document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
 | 
			
		||||
    anchor.addEventListener("click", function (e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      const targetId = this.getAttribute("href");
 | 
			
		||||
      if (targetId === "#") return;
 | 
			
		||||
 | 
			
		||||
      const targetElement = document.querySelector(targetId);
 | 
			
		||||
      if (targetElement) {
 | 
			
		||||
        targetElement.scrollIntoView({
 | 
			
		||||
          behavior: "smooth",
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										106
									
								
								src/js/navigation.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
// Navigation JavaScript
 | 
			
		||||
let lastScrollY = window.scrollY;
 | 
			
		||||
let ticking = false;
 | 
			
		||||
 | 
			
		||||
function updateNavigation() {
 | 
			
		||||
  const navigation = document.getElementById("navigation");
 | 
			
		||||
  const scrolled = window.scrollY > 50;
 | 
			
		||||
 | 
			
		||||
  if (scrolled) {
 | 
			
		||||
    navigation.classList.add("scrolled");
 | 
			
		||||
 | 
			
		||||
    // Hide/show nav on scroll
 | 
			
		||||
    if (window.scrollY > lastScrollY && window.scrollY > 100) {
 | 
			
		||||
      navigation.classList.add("hidden");
 | 
			
		||||
    } else {
 | 
			
		||||
      navigation.classList.remove("hidden");
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    navigation.classList.remove("scrolled", "hidden");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lastScrollY = window.scrollY;
 | 
			
		||||
  ticking = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function requestTick() {
 | 
			
		||||
  if (!ticking) {
 | 
			
		||||
    requestAnimationFrame(updateNavigation);
 | 
			
		||||
    ticking = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mobile navigation functionality
 | 
			
		||||
function setupMobileNavigation() {
 | 
			
		||||
  const burgerMenu = document.getElementById("burger-menu");
 | 
			
		||||
  const mainNav = document.getElementById("main-nav");
 | 
			
		||||
  const body = document.body;
 | 
			
		||||
 | 
			
		||||
  // Create overlay for mobile menu
 | 
			
		||||
  const overlay = document.createElement("div");
 | 
			
		||||
  overlay.className = "nav-overlay";
 | 
			
		||||
  document.body.appendChild(overlay);
 | 
			
		||||
 | 
			
		||||
  if (burgerMenu && mainNav) {
 | 
			
		||||
    burgerMenu.addEventListener("click", function () {
 | 
			
		||||
      const isActive = mainNav.classList.contains("active");
 | 
			
		||||
 | 
			
		||||
      if (isActive) {
 | 
			
		||||
        // Close menu
 | 
			
		||||
        mainNav.classList.remove("active");
 | 
			
		||||
        burgerMenu.classList.remove("active");
 | 
			
		||||
        overlay.classList.remove("active");
 | 
			
		||||
        body.classList.remove("nav-open");
 | 
			
		||||
      } else {
 | 
			
		||||
        // Open menu
 | 
			
		||||
        mainNav.classList.add("active");
 | 
			
		||||
        burgerMenu.classList.add("active");
 | 
			
		||||
        overlay.classList.add("active");
 | 
			
		||||
        body.classList.add("nav-open");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Close menu when clicking on overlay
 | 
			
		||||
    overlay.addEventListener("click", function () {
 | 
			
		||||
      mainNav.classList.remove("active");
 | 
			
		||||
      burgerMenu.classList.remove("active");
 | 
			
		||||
      overlay.classList.remove("active");
 | 
			
		||||
      body.classList.remove("nav-open");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Close menu when clicking on a link
 | 
			
		||||
    const navLinks = mainNav.querySelectorAll("a");
 | 
			
		||||
    navLinks.forEach((link) => {
 | 
			
		||||
      link.addEventListener("click", function () {
 | 
			
		||||
        mainNav.classList.remove("active");
 | 
			
		||||
        burgerMenu.classList.remove("active");
 | 
			
		||||
        overlay.classList.remove("active");
 | 
			
		||||
        body.classList.remove("nav-open");
 | 
			
		||||
 | 
			
		||||
        // Update active state
 | 
			
		||||
        navLinks.forEach((l) => l.classList.remove("active"));
 | 
			
		||||
        this.classList.add("active");
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Set initial active state based on current hash
 | 
			
		||||
    function setActiveNavLink() {
 | 
			
		||||
      const currentHash = window.location.hash || "#home";
 | 
			
		||||
      navLinks.forEach((link) => {
 | 
			
		||||
        if (link.getAttribute("href") === currentHash) {
 | 
			
		||||
          link.classList.add("active");
 | 
			
		||||
        } else {
 | 
			
		||||
          link.classList.remove("active");
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update active state on hash change
 | 
			
		||||
    window.addEventListener("hashchange", setActiveNavLink);
 | 
			
		||||
    setActiveNavLink();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initialize navigation
 | 
			
		||||
window.addEventListener("scroll", requestTick);
 | 
			
		||||
window.addEventListener("load", updateNavigation);
 | 
			
		||||
							
								
								
									
										57
									
								
								src/js/overview.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
// Counter animation for stats
 | 
			
		||||
document.addEventListener("DOMContentLoaded", function () {
 | 
			
		||||
  const counters = document.querySelectorAll(".stat-number");
 | 
			
		||||
  let hasCounted = false;
 | 
			
		||||
 | 
			
		||||
  function animateCounters() {
 | 
			
		||||
    if (hasCounted) return;
 | 
			
		||||
 | 
			
		||||
    counters.forEach((counter) => {
 | 
			
		||||
      const target = parseInt(counter.getAttribute("data-count"));
 | 
			
		||||
      const duration = 2000; // 2 seconds
 | 
			
		||||
      const frameDuration = 1000 / 60; // 60 frames per second
 | 
			
		||||
      const totalFrames = Math.round(duration / frameDuration);
 | 
			
		||||
      let frame = 0;
 | 
			
		||||
 | 
			
		||||
      const counterInterval = setInterval(() => {
 | 
			
		||||
        frame++;
 | 
			
		||||
        const progress = frame / totalFrames;
 | 
			
		||||
        const currentCount = Math.round(target * progress);
 | 
			
		||||
 | 
			
		||||
        counter.textContent = currentCount;
 | 
			
		||||
 | 
			
		||||
        if (frame === totalFrames) {
 | 
			
		||||
          clearInterval(counterInterval);
 | 
			
		||||
        }
 | 
			
		||||
      }, frameDuration);
 | 
			
		||||
 | 
			
		||||
      counter.classList.add("animated");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    hasCounted = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check if element is in viewport
 | 
			
		||||
  function isInViewport(element) {
 | 
			
		||||
    const rect = element.getBoundingClientRect();
 | 
			
		||||
    return (
 | 
			
		||||
      rect.top >= 0 &&
 | 
			
		||||
      rect.left >= 0 &&
 | 
			
		||||
      rect.bottom <=
 | 
			
		||||
        (window.innerHeight || document.documentElement.clientHeight) &&
 | 
			
		||||
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check on scroll and on load
 | 
			
		||||
  function checkCounters() {
 | 
			
		||||
    const statsContainer = document.querySelector(".stats-container");
 | 
			
		||||
    if (statsContainer && isInViewport(statsContainer)) {
 | 
			
		||||
      animateCounters();
 | 
			
		||||
      window.removeEventListener("scroll", checkCounters);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  window.addEventListener("scroll", checkCounters);
 | 
			
		||||
  checkCounters(); // Check on page load
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										228
									
								
								src/js/portfolioCard.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,228 @@
 | 
			
		|||
class PortfolioCard extends HTMLElement {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    const shadow = this.attachShadow({ mode: "open" });
 | 
			
		||||
 | 
			
		||||
    const icon = this.getAttribute("icon") || "fas fa-code";
 | 
			
		||||
    const title = this.getAttribute("title") || "Project Title";
 | 
			
		||||
    const desc = this.getAttribute("desc") || "Project description goes here.";
 | 
			
		||||
    const tags = (this.getAttribute("tags") || "").split(",");
 | 
			
		||||
    const link = this.getAttribute("link") || "#";
 | 
			
		||||
    const collaboration = this.getAttribute("collaboration") || "";
 | 
			
		||||
 | 
			
		||||
    shadow.innerHTML = `
 | 
			
		||||
      <!-- Font Awesome inside Shadow DOM -->
 | 
			
		||||
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 | 
			
		||||
      <!-- Use global styles.css from index.html -->
 | 
			
		||||
      <link rel="stylesheet" href="src/styles/styles.css">
 | 
			
		||||
 | 
			
		||||
      <style>
 | 
			
		||||
        /* Collaboration badge styling */
 | 
			
		||||
        .collaboration-badge {
 | 
			
		||||
          display: inline-flex;
 | 
			
		||||
          align-items: center;
 | 
			
		||||
          gap: 0.4rem;
 | 
			
		||||
          background: linear-gradient(135deg, #3b82f6, #2563eb);
 | 
			
		||||
          color: white;
 | 
			
		||||
          padding: 0.3rem 0.8rem;
 | 
			
		||||
          border-radius: 20px;
 | 
			
		||||
          font-size: 0.75rem;
 | 
			
		||||
          margin-top: 0.5rem;
 | 
			
		||||
          margin-bottom: 1rem;
 | 
			
		||||
          animation: pulse 2s infinite;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @keyframes pulse {
 | 
			
		||||
          0% {
 | 
			
		||||
            box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
 | 
			
		||||
          }
 | 
			
		||||
          70% {
 | 
			
		||||
            box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
 | 
			
		||||
          }
 | 
			
		||||
          100% {
 | 
			
		||||
            box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /* Enhanced card animations */
 | 
			
		||||
        .portfolio-item {
 | 
			
		||||
          opacity: 0;
 | 
			
		||||
          transform: translateY(20px);
 | 
			
		||||
          animation: fadeInUp 0.6s ease forwards;
 | 
			
		||||
          display: flex;
 | 
			
		||||
          flex-direction: column;
 | 
			
		||||
          height: 100%;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @keyframes fadeInUp {
 | 
			
		||||
          from {
 | 
			
		||||
            opacity: 0;
 | 
			
		||||
            transform: translateY(20px);
 | 
			
		||||
          }
 | 
			
		||||
          to {
 | 
			
		||||
            opacity: 1;
 | 
			
		||||
            transform: translateY(0);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /* Portfolio title with icon */
 | 
			
		||||
        .portfolio-title {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          align-items: center;
 | 
			
		||||
          gap: 0.5rem;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-content {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          flex-direction: column;
 | 
			
		||||
          flex: 1;
 | 
			
		||||
          padding: 1.5rem;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-tags {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          flex-wrap: wrap;
 | 
			
		||||
          gap: 0.5rem;
 | 
			
		||||
          margin-bottom: 1.5rem;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /* Full-width Enhanced Button Styles */
 | 
			
		||||
        .portfolio-btn-container {
 | 
			
		||||
          margin-top: auto;
 | 
			
		||||
          padding: 0 1.5rem 1.5rem;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn {
 | 
			
		||||
          width: 100%;
 | 
			
		||||
          position: relative;
 | 
			
		||||
          overflow: hidden;
 | 
			
		||||
          z-index: 1;
 | 
			
		||||
          padding: 1rem 1.8rem;
 | 
			
		||||
          border-radius: 8px;
 | 
			
		||||
          font-weight: 600;
 | 
			
		||||
          letter-spacing: 0.5px;
 | 
			
		||||
          text-transform: uppercase;
 | 
			
		||||
          font-size: 0.9rem;
 | 
			
		||||
          transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
 | 
			
		||||
          border: 2px solid transparent;
 | 
			
		||||
          background: linear-gradient(135deg, var(--purple), var(--purple-dark));
 | 
			
		||||
          color: white;
 | 
			
		||||
          box-shadow: 0 4px 15px var(--shadow-purple);
 | 
			
		||||
          display: flex;
 | 
			
		||||
          justify-content: center;
 | 
			
		||||
          align-items: center;
 | 
			
		||||
          gap: 0.8rem;
 | 
			
		||||
          text-decoration: none;
 | 
			
		||||
          text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn::before {
 | 
			
		||||
          content: '';
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          top: 0;
 | 
			
		||||
          left: 0;
 | 
			
		||||
          width: 100%;
 | 
			
		||||
          height: 100%;
 | 
			
		||||
          background: linear-gradient(135deg, var(--purple-dark), var(--purple-muted));
 | 
			
		||||
          z-index: -1;
 | 
			
		||||
          transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
 | 
			
		||||
          transform: scaleX(0);
 | 
			
		||||
          transform-origin: right;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn:hover::before {
 | 
			
		||||
          transform: scaleX(1);
 | 
			
		||||
          transform-origin: left;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn:hover {
 | 
			
		||||
          transform: translateY(-3px);
 | 
			
		||||
          box-shadow: 0 8px 25px rgba(168, 85, 247, 0.4);
 | 
			
		||||
          border-color: var(--purple-light);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn:active {
 | 
			
		||||
          transform: translateY(0);
 | 
			
		||||
          box-shadow: 0 4px 15px var(--shadow-purple);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn i {
 | 
			
		||||
          font-size: 1rem;
 | 
			
		||||
          transition: transform 0.3s ease;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn:hover i {
 | 
			
		||||
          transform: translateX(4px);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn::after {
 | 
			
		||||
          content: '';
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          top: -50%;
 | 
			
		||||
          left: -50%;
 | 
			
		||||
          width: 200%;
 | 
			
		||||
          height: 200%;
 | 
			
		||||
          background: linear-gradient(
 | 
			
		||||
            to bottom right,
 | 
			
		||||
            rgba(255, 255, 255, 0.2),
 | 
			
		||||
            rgba(255, 255, 255, 0.1) 20%,
 | 
			
		||||
            rgba(255, 255, 255, 0) 50%,
 | 
			
		||||
            rgba(255, 255, 255, 0) 100%
 | 
			
		||||
          );
 | 
			
		||||
          transform: rotate(30deg) translateY(-150%);
 | 
			
		||||
          transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn:hover::after {
 | 
			
		||||
          transform: rotate(30deg) translateY(150%);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        .portfolio-btn:focus {
 | 
			
		||||
          outline: 2px solid var(--purple-light);
 | 
			
		||||
          outline-offset: 2px;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /* Responsive adjustments */
 | 
			
		||||
        @media (max-width: 768px) {
 | 
			
		||||
          .portfolio-btn {
 | 
			
		||||
            padding: 0.9rem 1.5rem;
 | 
			
		||||
            font-size: 0.85rem;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      </style>
 | 
			
		||||
 | 
			
		||||
      <div class="portfolio-item">
 | 
			
		||||
        <div class="portfolio-img">
 | 
			
		||||
          <i class="${icon}"></i>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="portfolio-content">
 | 
			
		||||
          <h3 class="portfolio-title">
 | 
			
		||||
            <i class="${icon}"></i> ${title}
 | 
			
		||||
          </h3>
 | 
			
		||||
          <p class="portfolio-desc">${desc}</p>
 | 
			
		||||
          ${
 | 
			
		||||
            collaboration
 | 
			
		||||
              ? `
 | 
			
		||||
            <div class="collaboration-badge">
 | 
			
		||||
              <i class="fas fa-users"></i> ${collaboration}
 | 
			
		||||
            </div>
 | 
			
		||||
          `
 | 
			
		||||
              : ""
 | 
			
		||||
          }
 | 
			
		||||
          <div class="portfolio-tags">
 | 
			
		||||
            ${tags
 | 
			
		||||
              .map((tag) => `<span class="tag">${tag.trim()}</span>`)
 | 
			
		||||
              .join("")}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="portfolio-btn-container">
 | 
			
		||||
          <a href="${link}" target="_blank" class="portfolio-btn">
 | 
			
		||||
            <i class="fas fa-arrow-right"></i> Explore Project
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
customElements.define("portfolio-card", PortfolioCard);
 | 
			
		||||
							
								
								
									
										95
									
								
								src/js/portfolioFilter.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
// Portfolio filtering functionality
 | 
			
		||||
document.addEventListener("DOMContentLoaded", function () {
 | 
			
		||||
  // Get all portfolio items
 | 
			
		||||
  const portfolioItems = document.querySelectorAll("portfolio-card");
 | 
			
		||||
  const filterButtons = document.querySelectorAll(".filter-btn");
 | 
			
		||||
  const searchInput = document.getElementById("project-search");
 | 
			
		||||
 | 
			
		||||
  // Create a container for no results message
 | 
			
		||||
  const noResults = document.createElement("div");
 | 
			
		||||
  noResults.className = "no-results";
 | 
			
		||||
  noResults.innerHTML = `
 | 
			
		||||
    <i class="fas fa-search"></i>
 | 
			
		||||
    <h3>No projects found</h3>
 | 
			
		||||
    <p>Try adjusting your search or filter criteria</p>
 | 
			
		||||
  `;
 | 
			
		||||
 | 
			
		||||
  // Function to filter portfolio items
 | 
			
		||||
  function filterPortfolio() {
 | 
			
		||||
    const activeFilter =
 | 
			
		||||
      document.querySelector(".filter-btn.active").dataset.filter;
 | 
			
		||||
    const searchTerm = searchInput.value.toLowerCase();
 | 
			
		||||
    let visibleItems = 0;
 | 
			
		||||
 | 
			
		||||
    portfolioItems.forEach((item) => {
 | 
			
		||||
      const tags = item.getAttribute("tags").toLowerCase();
 | 
			
		||||
      const title = item.getAttribute("title").toLowerCase();
 | 
			
		||||
      const desc = item.getAttribute("desc").toLowerCase();
 | 
			
		||||
 | 
			
		||||
      // Check if item matches the active filter
 | 
			
		||||
      const matchesFilter =
 | 
			
		||||
        activeFilter === "all" ||
 | 
			
		||||
        tags.includes(activeFilter) ||
 | 
			
		||||
        title.includes(activeFilter) ||
 | 
			
		||||
        desc.includes(activeFilter);
 | 
			
		||||
 | 
			
		||||
      // Check if item matches the search term
 | 
			
		||||
      const matchesSearch =
 | 
			
		||||
        searchTerm === "" ||
 | 
			
		||||
        title.includes(searchTerm) ||
 | 
			
		||||
        desc.includes(searchTerm) ||
 | 
			
		||||
        tags.includes(searchTerm);
 | 
			
		||||
 | 
			
		||||
      // Show or hide the item based on filters
 | 
			
		||||
      if (matchesFilter && matchesSearch) {
 | 
			
		||||
        item.style.display = "block";
 | 
			
		||||
        visibleItems++;
 | 
			
		||||
 | 
			
		||||
        // Add animation for appearing items
 | 
			
		||||
        item.style.animation = "fadeInUp 0.5s ease forwards";
 | 
			
		||||
      } else {
 | 
			
		||||
        item.style.display = "none";
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Show no results message if needed
 | 
			
		||||
    const portfolioGrid = document.querySelector(".portfolio-grid");
 | 
			
		||||
    const existingNoResults = portfolioGrid.querySelector(".no-results");
 | 
			
		||||
 | 
			
		||||
    if (visibleItems === 0) {
 | 
			
		||||
      if (!existingNoResults) {
 | 
			
		||||
        portfolioGrid.appendChild(noResults);
 | 
			
		||||
      }
 | 
			
		||||
    } else if (existingNoResults) {
 | 
			
		||||
      portfolioGrid.removeChild(existingNoResults);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Add click event listeners to filter buttons
 | 
			
		||||
  filterButtons.forEach((button) => {
 | 
			
		||||
    button.addEventListener("click", function () {
 | 
			
		||||
      // Remove active class from all buttons
 | 
			
		||||
      filterButtons.forEach((btn) => btn.classList.remove("active"));
 | 
			
		||||
 | 
			
		||||
      // Add active class to clicked button
 | 
			
		||||
      this.classList.add("active");
 | 
			
		||||
 | 
			
		||||
      // Filter portfolio items
 | 
			
		||||
      filterPortfolio();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Add input event listener to search field
 | 
			
		||||
  searchInput.addEventListener("input", filterPortfolio);
 | 
			
		||||
 | 
			
		||||
  // Add keyboard shortcut for search (Ctrl/Cmd + F)
 | 
			
		||||
  document.addEventListener("keydown", function (e) {
 | 
			
		||||
    if ((e.ctrlKey || e.metaKey) && e.key === "f") {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      searchInput.focus();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Initialize filter on page load
 | 
			
		||||
  filterPortfolio();
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										111
									
								
								src/js/sectionTracker.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
// Section tracking and navigation highlighting
 | 
			
		||||
document.addEventListener("DOMContentLoaded", function () {
 | 
			
		||||
  const sections = document.querySelectorAll("section[id]");
 | 
			
		||||
  const navLinks = document.querySelectorAll(".nav-links a");
 | 
			
		||||
 | 
			
		||||
  // Configuration
 | 
			
		||||
  const offset = 100; // Offset for when section becomes "active" (accounts for fixed nav)
 | 
			
		||||
  let isScrolling = false;
 | 
			
		||||
 | 
			
		||||
  function getCurrentSection() {
 | 
			
		||||
    let currentSection = "";
 | 
			
		||||
    const scrollPosition = window.scrollY + offset;
 | 
			
		||||
 | 
			
		||||
    // Find which section we're currently in
 | 
			
		||||
    sections.forEach((section) => {
 | 
			
		||||
      const sectionTop = section.offsetTop;
 | 
			
		||||
      const sectionHeight = section.offsetHeight;
 | 
			
		||||
      const sectionId = section.getAttribute("id");
 | 
			
		||||
 | 
			
		||||
      if (
 | 
			
		||||
        scrollPosition >= sectionTop &&
 | 
			
		||||
        scrollPosition < sectionTop + sectionHeight
 | 
			
		||||
      ) {
 | 
			
		||||
        currentSection = sectionId;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Special case: if we're at the very top, activate the first section
 | 
			
		||||
    if (window.scrollY < 100) {
 | 
			
		||||
      currentSection = sections[0]?.getAttribute("id") || "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return currentSection;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function updateActiveNavLink() {
 | 
			
		||||
    const currentSection = getCurrentSection();
 | 
			
		||||
 | 
			
		||||
    navLinks.forEach((link) => {
 | 
			
		||||
      const href = link.getAttribute("href");
 | 
			
		||||
 | 
			
		||||
      // Remove active class from all links
 | 
			
		||||
      link.classList.remove("active");
 | 
			
		||||
 | 
			
		||||
      // Add active class to matching link
 | 
			
		||||
      if (href === `#${currentSection}`) {
 | 
			
		||||
        link.classList.add("active");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Throttle scroll events for better performance
 | 
			
		||||
  function handleScroll() {
 | 
			
		||||
    if (!isScrolling) {
 | 
			
		||||
      window.requestAnimationFrame(() => {
 | 
			
		||||
        updateActiveNavLink();
 | 
			
		||||
        isScrolling = false;
 | 
			
		||||
      });
 | 
			
		||||
      isScrolling = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Listen for scroll events
 | 
			
		||||
  window.addEventListener("scroll", handleScroll);
 | 
			
		||||
 | 
			
		||||
  // Update on page load
 | 
			
		||||
  updateActiveNavLink();
 | 
			
		||||
 | 
			
		||||
  // Also update when clicking nav links (for smooth scroll)
 | 
			
		||||
  navLinks.forEach((link) => {
 | 
			
		||||
    link.addEventListener("click", function (e) {
 | 
			
		||||
      // Remove active from all
 | 
			
		||||
      navLinks.forEach((l) => l.classList.remove("active"));
 | 
			
		||||
      // Add active to clicked link
 | 
			
		||||
      this.classList.add("active");
 | 
			
		||||
 | 
			
		||||
      // Let the scroll handler update it properly after scroll completes
 | 
			
		||||
      setTimeout(updateActiveNavLink, 100);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Handle hash changes (browser back/forward)
 | 
			
		||||
  window.addEventListener("hashchange", function () {
 | 
			
		||||
    setTimeout(updateActiveNavLink, 100);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Intersection Observer for more precise tracking (progressive enhancement)
 | 
			
		||||
  if ("IntersectionObserver" in window) {
 | 
			
		||||
    const observerOptions = {
 | 
			
		||||
      rootMargin: "-20% 0px -70% 0px", // Trigger when section is roughly in the middle of viewport
 | 
			
		||||
      threshold: 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const observer = new IntersectionObserver((entries) => {
 | 
			
		||||
      entries.forEach((entry) => {
 | 
			
		||||
        if (entry.isIntersecting) {
 | 
			
		||||
          const sectionId = entry.target.getAttribute("id");
 | 
			
		||||
          navLinks.forEach((link) => {
 | 
			
		||||
            link.classList.remove("active");
 | 
			
		||||
            if (link.getAttribute("href") === `#${sectionId}`) {
 | 
			
		||||
              link.classList.add("active");
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }, observerOptions);
 | 
			
		||||
 | 
			
		||||
    // Observe all sections
 | 
			
		||||
    sections.forEach((section) => observer.observe(section));
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										1943
									
								
								src/styles/styles.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										432
									
								
								styles.css
									
										
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -1,432 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
        interstellar_development website
 | 
			
		||||
        Copyright (C) 2024 interstellar_development
 | 
			
		||||
 | 
			
		||||
        This program is free software: you can redistribute it and/or modify
 | 
			
		||||
        it under the terms of the GNU Affero General Public License as
 | 
			
		||||
        published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
        License, or (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
        This program is distributed in the hope that it will be useful,
 | 
			
		||||
        but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
        GNU Affero General Public License for more details.
 | 
			
		||||
 | 
			
		||||
        You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
        along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
:root {
 | 
			
		||||
  --background-color: #0b0c1d;
 | 
			
		||||
  --text-color: #c7d5e0;
 | 
			
		||||
  --accent-color: #ffdd55;
 | 
			
		||||
  --accent-hover-color: #ffd700;
 | 
			
		||||
  --dark-blue: rgba(31, 42, 64, 1);
 | 
			
		||||
  --dark-blue-translucent: rgba(31, 42, 64, 0.9);
 | 
			
		||||
  --light-blue: rgba(46, 58, 95, 0.8);
 | 
			
		||||
  --border-radius: 8px;
 | 
			
		||||
  --transition-speed: 0.3s;
 | 
			
		||||
  --box-shadow: 0 2px 15px rgba(0, 0, 0, 0.7);
 | 
			
		||||
  --font-size-large: 2.5em;
 | 
			
		||||
  --font-size-medium: 1.8em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Reset and normalize */
 | 
			
		||||
*,
 | 
			
		||||
*::before,
 | 
			
		||||
*::after {
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Body styling */
 | 
			
		||||
body {
 | 
			
		||||
  background-color: var(--background-color);
 | 
			
		||||
  color: var(--text-color);
 | 
			
		||||
  font-family: "Arial", sans-serif;
 | 
			
		||||
  line-height: 1.6;
 | 
			
		||||
  padding: 0 20px;
 | 
			
		||||
  background: url("images/star.jpg") no-repeat center center fixed;
 | 
			
		||||
  background-size: cover;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Header Styling */
 | 
			
		||||
/* Header Styling */
 | 
			
		||||
header {
 | 
			
		||||
  background-color: var(--dark-blue);
 | 
			
		||||
  height: 5em;
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  padding: 15px 20px;
 | 
			
		||||
  box-shadow: var(--box-shadow);
 | 
			
		||||
  backdrop-filter: blur(5px);
 | 
			
		||||
  z-index: 100;
 | 
			
		||||
  margin-bottom: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Burger Menu Styling */
 | 
			
		||||
.burger-menu {
 | 
			
		||||
  background: none;
 | 
			
		||||
  border: none;
 | 
			
		||||
  color: #ffffff;
 | 
			
		||||
  font-size: 1.8em;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  display: block;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  z-index: 110;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Dropdown Menu (Hidden by Default) */
 | 
			
		||||
.div-menu {
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
  background-color: var(--light-blue);
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin-top: 0px;
 | 
			
		||||
  height: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.div-menu li {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 1em;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.div-menu a {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  padding: 8px 0;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  color: #ffffff;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  transition: background-color 0.3s ease;
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.div-menu a:hover {
 | 
			
		||||
  background-color: #34495e;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Menu Animation and Styling */
 | 
			
		||||
.menu {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  background-color: var(--light-blue);
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: -50vh;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  z-index: 5;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  font-weight: bolder;
 | 
			
		||||
  font-size: large;
 | 
			
		||||
  transition: top 0.5s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.menu.active {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  top: 4em;
 | 
			
		||||
  z-index: 5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Header Content Container */
 | 
			
		||||
.header-content {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  max-width: 1200px;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  padding: 0 20px;
 | 
			
		||||
  z-index: 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Project Name Styling */
 | 
			
		||||
.project-name {
 | 
			
		||||
  font-size: 2em;
 | 
			
		||||
  color: var(--accent-color);
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  transition: color var(--transition-speed), text-shadow var(--transition-speed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.project-name:hover {
 | 
			
		||||
  color: var(--accent-hover-color);
 | 
			
		||||
  text-shadow: 0 0 10px var(--accent-hover-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Article styling */
 | 
			
		||||
article {
 | 
			
		||||
  max-width: 800px;
 | 
			
		||||
  margin: 6.25em auto;
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  background-color: var(--dark-blue-translucent);
 | 
			
		||||
  border-radius: var(--border-radius);
 | 
			
		||||
  box-shadow: var(--box-shadow);
 | 
			
		||||
  backdrop-filter: blur(5px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
article h1 {
 | 
			
		||||
  font-size: var(--font-size-large);
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
  color: var(--accent-color);
 | 
			
		||||
  text-shadow: 0 0 15px var(--accent-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
article p {
 | 
			
		||||
  color: var(--text-color);
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
  hyphens: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Download list */
 | 
			
		||||
article h2 {
 | 
			
		||||
  font-size: var(--font-size-medium);
 | 
			
		||||
  margin: 20px 0 10px;
 | 
			
		||||
  color: var(--accent-color);
 | 
			
		||||
  text-shadow: 0 0 10px var(--accent-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
article ul {
 | 
			
		||||
  list-style-type: none;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
article ul a li {
 | 
			
		||||
  background-color: var(--light-blue);
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  border-radius: var(--border-radius);
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  transition: background-color var(--transition-speed),
 | 
			
		||||
    box-shadow var(--transition-speed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
article ul a li:hover {
 | 
			
		||||
  background-color: rgba(68, 80, 124, 0.9);
 | 
			
		||||
  box-shadow: 0 0 10px var(--accent-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
article ul a li {
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  color: var(--accent-color);
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Footer styling */
 | 
			
		||||
footer {
 | 
			
		||||
  background-color: var(--dark-blue);
 | 
			
		||||
  padding: 10px 20px;
 | 
			
		||||
  color: var(--text-color);
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.7);
 | 
			
		||||
  backdrop-filter: blur(5px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.footer-content {
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  font-size: 0.9em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Card container styles */
 | 
			
		||||
.cards {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: repeat(3, 1fr); /* Display 3 cards per line */
 | 
			
		||||
  gap: 20px;
 | 
			
		||||
  margin-top: 50px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ensure the <a> tag covers the entire card */
 | 
			
		||||
section .card a {
 | 
			
		||||
  display: flex; /* Use flex to make <a> fill the card and align content */
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  justify-content: center; /* Vertically center the content */
 | 
			
		||||
  align-items: center; /* Horizontally center the content */
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  color: inherit;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Card styles */
 | 
			
		||||
section .card {
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  background: linear-gradient(
 | 
			
		||||
    180deg,
 | 
			
		||||
    rgba(0, 0, 50, 0.9),
 | 
			
		||||
    rgba(10, 10, 100, 0.9),
 | 
			
		||||
    rgba(30, 30, 150, 0.9)
 | 
			
		||||
  );
 | 
			
		||||
  border-radius: 12px;
 | 
			
		||||
  box-shadow: 0 5px 20px rgba(0, 0, 50, 0.8), 0 0 10px rgba(255, 255, 255, 0.1);
 | 
			
		||||
  border: 1px solid #2e3a60;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  transition: background 0.5s ease, transform 0.4s ease, box-shadow 0.5s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Hover effect */
 | 
			
		||||
section .card:hover {
 | 
			
		||||
  transform: translateY(-8px);
 | 
			
		||||
  background: linear-gradient(
 | 
			
		||||
    180deg,
 | 
			
		||||
    rgba(30, 30, 150, 0.9),
 | 
			
		||||
    rgba(40, 0, 100, 0.9),
 | 
			
		||||
    rgba(100, 0, 150, 0.9)
 | 
			
		||||
  );
 | 
			
		||||
  box-shadow: 0 10px 30px rgba(0, 0, 100, 0.7), 0 0 20px rgba(255, 221, 85, 0.8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section .card img {
 | 
			
		||||
  height: 80px;
 | 
			
		||||
  width: 80px;
 | 
			
		||||
  object-fit: cover;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  margin: 0 auto 15px;
 | 
			
		||||
  box-shadow: 0 0 15px rgba(255, 221, 85, 0.5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section .card h3 {
 | 
			
		||||
  margin: 10px 0;
 | 
			
		||||
  font-size: 1.4em;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  color: rgba(255, 221, 85, 1);
 | 
			
		||||
  text-shadow: 0 0 15px rgba(255, 221, 85, 0.9);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section .card p {
 | 
			
		||||
  flex-grow: 1;
 | 
			
		||||
  color: rgba(200, 220, 255, 0.8);
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  text-shadow: 0 0 8px rgba(255, 255, 255, 0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section .card::before {
 | 
			
		||||
  content: "";
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: -20px;
 | 
			
		||||
  right: -20px;
 | 
			
		||||
  width: 60px;
 | 
			
		||||
  height: 60px;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  background: radial-gradient(
 | 
			
		||||
    circle,
 | 
			
		||||
    rgba(255, 255, 255, 0.1),
 | 
			
		||||
    rgba(255, 255, 255, 0.02)
 | 
			
		||||
  );
 | 
			
		||||
  box-shadow: 0 0 50px rgba(255, 255, 255, 0.5);
 | 
			
		||||
  animation: spin 8s linear infinite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section .card .suit-icon {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  bottom: 10px;
 | 
			
		||||
  right: 10px;
 | 
			
		||||
  width: 24px;
 | 
			
		||||
  height: 24px;
 | 
			
		||||
  opacity: 0.7;
 | 
			
		||||
  transition: opacity var(--transition-speed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section .card:hover .suit-icon {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Keyframes for spinning element */
 | 
			
		||||
@keyframes spin {
 | 
			
		||||
  0% {
 | 
			
		||||
    transform: rotate(0deg);
 | 
			
		||||
  }
 | 
			
		||||
  100% {
 | 
			
		||||
    transform: rotate(360deg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Form styling */
 | 
			
		||||
form {
 | 
			
		||||
  max-width: 600px;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  background: var(--dark-blue-translucent);
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  border-radius: var(--border-radius);
 | 
			
		||||
  box-shadow: var(--box-shadow);
 | 
			
		||||
  backdrop-filter: blur(5px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form label,
 | 
			
		||||
form input,
 | 
			
		||||
form textarea {
 | 
			
		||||
  color: var(--text-color);
 | 
			
		||||
  background-color: var(--light-blue);
 | 
			
		||||
  border: 1px solid #3a4b7f;
 | 
			
		||||
  border-radius: var(--border-radius);
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  margin: 10px 0;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form input[type="submit"] {
 | 
			
		||||
  background-color: var(--accent-color);
 | 
			
		||||
  color: #fff;
 | 
			
		||||
  border: none;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: background-color var(--transition-speed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form input[type="submit"]:hover {
 | 
			
		||||
  background-color: var(--accent-hover-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Footer Styling */
 | 
			
		||||
footer {
 | 
			
		||||
  background-color: var(--dark-blue);
 | 
			
		||||
  padding: 10px 20px;
 | 
			
		||||
  color: var(--text-color);
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  font-size: 0.9em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 768px) {
 | 
			
		||||
  .cards {
 | 
			
		||||
    grid-template-columns: 1fr; /* 1 card per line on smaller screens */
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  header ul {
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    gap: 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  article {
 | 
			
		||||
    margin: 12em 10px;
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  section .card {
 | 
			
		||||
    padding: 12px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  section .card img {
 | 
			
		||||
    height: 60px;
 | 
			
		||||
    width: 60px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .project-name {
 | 
			
		||||
    font-size: 1.3em;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,110 +0,0 @@
 | 
			
		|||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Game Collection</title>
 | 
			
		||||
    <link rel="stylesheet" href="styles.css" />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="apple-touch-icon"
 | 
			
		||||
      sizes="180x180"
 | 
			
		||||
      href="../favicon_io/apple-touch-icon.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="32x32"
 | 
			
		||||
      href="../favicon_io/favicon-32x32.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link
 | 
			
		||||
      rel="icon"
 | 
			
		||||
      type="image/png"
 | 
			
		||||
      sizes="16x16"
 | 
			
		||||
      href="../favicon_io/favicon-16x16.png"
 | 
			
		||||
    />
 | 
			
		||||
    <link rel="manifest" href="../favicon_io/site.webmanifest" />
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <header>
 | 
			
		||||
      <h1>Game Collection</h1>
 | 
			
		||||
    </header>
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
      <div class="grid-container">
 | 
			
		||||
        <a href="" target="_blank" class="item">
 | 
			
		||||
          <img
 | 
			
		||||
            src="../secret/../secret/images/default.jpeg"
 | 
			
		||||
            alt="Image can't be displayed"
 | 
			
		||||
          />
 | 
			
		||||
          <h2>Snake</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Guide the snake to eat food and grow longer while avoiding
 | 
			
		||||
              collisions with the walls and itself.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="" target="_blank" class="item">
 | 
			
		||||
          <img
 | 
			
		||||
            src="../secret/images/default.jpeg"
 | 
			
		||||
            alt="Image can't be displayed"
 | 
			
		||||
          />
 | 
			
		||||
          <h2>Solitaire</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              A classic card game where the objective is to move all cards to
 | 
			
		||||
              foundation piles in ascending order.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="../secret/mineSweeper/index.html" target="_blank" class="item">
 | 
			
		||||
          <img
 | 
			
		||||
            src="../secret/images/minesweeper.png"
 | 
			
		||||
            alt="Image can't be displayed"
 | 
			
		||||
          />
 | 
			
		||||
          <h2>Minesweeper</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Uncover squares on a grid while avoiding hidden mines, using
 | 
			
		||||
              numbers to deduce safe spots.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a
 | 
			
		||||
          href="../secret/guessMyNumber/index.html"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
          class="item"
 | 
			
		||||
        >
 | 
			
		||||
          <img
 | 
			
		||||
            src="../secret/images/number.jpeg"
 | 
			
		||||
            alt="Image can't be displayed"
 | 
			
		||||
          />
 | 
			
		||||
          <h2>Guess My Number</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              A simple game where you try to guess a randomly chosen number
 | 
			
		||||
              within a certain range.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
        <a href="" target="_blank" class="item">
 | 
			
		||||
          <img
 | 
			
		||||
            src="../secret/images/default.jpeg"
 | 
			
		||||
            alt="Image can't be displayed"
 | 
			
		||||
          />
 | 
			
		||||
          <h2>Endless Runner</h2>
 | 
			
		||||
          <div class="description">
 | 
			
		||||
            <p>
 | 
			
		||||
              Run through an endless landscape, avoiding obstacles and
 | 
			
		||||
              collecting items to score points.
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <footer>
 | 
			
		||||
      <p>© 2025 Game Collection</p>
 | 
			
		||||
    </footer>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,120 +0,0 @@
 | 
			
		|||
/* Reset and box-sizing */
 | 
			
		||||
* {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* General Styles */
 | 
			
		||||
body {
 | 
			
		||||
  font-family: Arial, sans-serif;
 | 
			
		||||
  background-color: #282c34;
 | 
			
		||||
  color: #ffffff;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header {
 | 
			
		||||
  background-color: #4caf50;
 | 
			
		||||
  color: white;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  padding: 1em 0;
 | 
			
		||||
  font-size: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer {
 | 
			
		||||
  background-color: #333;
 | 
			
		||||
  color: white;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  padding: 1em 0;
 | 
			
		||||
  margin-top: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Grid Styles */
 | 
			
		||||
.grid-container {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: repeat(3, 1fr);
 | 
			
		||||
  gap: 20px; /* Space between items */
 | 
			
		||||
  padding: 20px; /* Space around the grid */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Game Item */
 | 
			
		||||
.item {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  background-color: #444;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
 | 
			
		||||
  transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
 | 
			
		||||
  width: 100%; /* Ensure it takes full width of the column */
 | 
			
		||||
  height: 400px; /* Set a fixed height for all items */
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column; /* Stack children vertically */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ensure the image takes the top part of the card */
 | 
			
		||||
.item img {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%; /* Set a height for the image */
 | 
			
		||||
  object-fit: cover;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item .description {
 | 
			
		||||
  padding: 30px;
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
  color: #ddd;
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.5);
 | 
			
		||||
  border-radius: 0 0 10px 10px;
 | 
			
		||||
  flex-grow: 1; /* Allow description to take remaining space */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Hover effect for scaling and glowing */
 | 
			
		||||
.item:hover {
 | 
			
		||||
  transform: scale(1.05);
 | 
			
		||||
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
 | 
			
		||||
  filter: brightness(1.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item:hover img {
 | 
			
		||||
  transform: scale(1.1); /* Slight zoom-in effect for the image */
 | 
			
		||||
  filter: brightness(1.1); /* Increase image brightness */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item h2 {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 10%;
 | 
			
		||||
  left: 50%;
 | 
			
		||||
  transform: translateX(-50%);
 | 
			
		||||
  color: white;
 | 
			
		||||
  font-size: 1.5rem;
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.6);
 | 
			
		||||
  padding: 5px 15px;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  transition: opacity 0.3s ease, transform 0.3s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item:hover h2 {
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  transform: translateX(-50%) translateY(-10px); /* Move the title upwards with hover */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mobile Optimization */
 | 
			
		||||
@media (max-width: 600px) {
 | 
			
		||||
  header {
 | 
			
		||||
    font-size: 1.2rem;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .item {
 | 
			
		||||
    height: auto; /* Allow auto height on mobile for better responsiveness */
 | 
			
		||||
    width: auto;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .grid-container {
 | 
			
		||||
    grid-template-columns: repeat(1, 1fr);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||