From babe91fbbc99893f12456590df143a30cf1aceb1 Mon Sep 17 00:00:00 2001 From: Subham Sangwan Date: Mon, 26 Jan 2026 18:28:06 +0530 Subject: [PATCH 1/2] Fix pre-commit: Add .pre-commit-config.yaml - Add comprehensive pre-commit configuration file - Include hooks for file validation (trailing whitespace, end-of-file, etc.) - Add YAML validation for user profile files - Add Prettier formatting for JS, CSS, HTML, Markdown, YAML, JSON - Add Markdownlint for documentation quality - Exclude _site/, node_modules/, and package-lock.json Fixes #243 --- .pre-commit-config.yaml | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5869dfe --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,49 @@ +# Pre-commit configuration for NextCommunity.github.io +# See https://pre-commit.com for more information + +repos: + # Standard pre-commit hooks for basic file checks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + exclude: ^package-lock\.json$ + - id: end-of-file-fixer + exclude: ^package-lock\.json$ + - id: check-yaml + args: ["--unsafe"] + - id: check-added-large-files + args: ["--maxkb=1000"] + - id: check-json + exclude: ^package-lock\.json$ + - id: check-merge-conflict + - id: check-case-conflict + - id: mixed-line-ending + args: ["--fix=lf"] + + # Prettier for code formatting (JavaScript, CSS, HTML, Markdown, YAML, JSON) + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + types_or: [javascript, css, html, markdown, yaml, json] + exclude: ^(package-lock\.json|_site/) + additional_dependencies: + - prettier@3.1.0 + + # Markdownlint for markdown files + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.39.0 + hooks: + - id: markdownlint + args: ["--fix"] + exclude: ^node_modules/ + +# Exclude patterns applied to all hooks +exclude: | + (?x)( + ^_site/| + ^node_modules/| + ^\.git/| + package-lock\.json + ) From 875badccbd7e191c0982e85288457581e023f546 Mon Sep 17 00:00:00 2001 From: Subham Sangwan Date: Tue, 27 Jan 2026 07:22:50 +0530 Subject: [PATCH 2/2] Apply pre-commit formatting fixes - Apply Prettier formatting to YAML, JS, CSS files - Fix trailing whitespace and end-of-file issues - Fix markdownlint errors in README.md: * Add language specifiers to code blocks (MD040) * Add .markdownlint.yaml to disable MD013 (line-length) and MD036 (emphasis-as-heading) - Ensure all linting checks pass --- .eleventy.js | 10 +- .github/workflows/deploy.yml | 6 +- .markdownlint.yaml | 5 + README.md | 74 +- src/_data/build.js | 14 +- src/assets/css/style.css | 705 ++++++----- src/assets/js/script.js | 2009 ++++++++++++++++-------------- src/users/Anderson.yaml | 2 +- src/users/Ephraim.yaml | 2 +- src/users/GabrielTheophilo.yaml | 2 +- src/users/HunainAnis.yaml | 6 +- src/users/TinotendaMhedziso.yaml | 6 +- src/users/abdorah.yaml | 10 +- src/users/anekenonso.yaml | 2 +- src/users/anufdo.yaml | 2 +- src/users/ayush.yaml | 10 +- src/users/basemax.yaml | 2 +- src/users/enimiste.yaml | 2 +- src/users/eugene4545.yaml | 2 +- src/users/gakramx.yaml | 4 +- src/users/iabdr.yaml | 6 +- src/users/kenhorlador.yaml | 12 +- src/users/mahabub.yaml | 26 +- src/users/majalian.yaml | 14 +- src/users/mohdrash.yaml | 6 +- src/users/pratik-wadhai.yaml | 6 +- 26 files changed, 1579 insertions(+), 1366 deletions(-) create mode 100644 .markdownlint.yaml diff --git a/.eleventy.js b/.eleventy.js index b9d0e32..4f76cc1 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -1,6 +1,6 @@ const yaml = require("js-yaml"); -module.exports = function(eleventyConfig) { +module.exports = function (eleventyConfig) { // Add this line to copy your external assets eleventyConfig.addPassthroughCopy("src/assets"); // 1. Recognize YAML as a template format @@ -20,11 +20,11 @@ module.exports = function(eleventyConfig) { const fs = require("fs/promises"); const content = await fs.readFile(inputPath, "utf-8"); return yaml.load(content); - } + }, }); // 2. The Randomized Collection - eleventyConfig.addCollection("randomPeople", function(collectionApi) { + eleventyConfig.addCollection("randomPeople", function (collectionApi) { // Grab all yaml files from the users folder const people = collectionApi.getFilteredByGlob("src/users/*.yaml"); @@ -44,7 +44,7 @@ module.exports = function(eleventyConfig) { dir: { input: "src", output: "_site", - includes: "_includes" - } + includes: "_includes", + }, }; }; diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8fcccf1..fc6f05b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,8 +8,8 @@ on: jobs: deployment: permissions: - pages: write # to deploy to Pages - id-token: write # to verify the deployment originates from an appropriate source + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source runs-on: ubuntu-latest steps: - name: Checkout code @@ -25,7 +25,7 @@ jobs: - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: - path: '_site' # The directory that contains the deployable files + path: "_site" # The directory that contains the deployable files - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..f1d751b --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,5 @@ +# Markdownlint configuration +# Disable line length rule for long URLs and badges +MD013: false +# Allow emphasis for styled section labels +MD036: false diff --git a/README.md b/README.md index 08aede0..c7dad3e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ NextCommunity is a static site directory built with [Eleventy (11ty)](https://www.11ty.dev/) that celebrates the global developer community. Each developer gets their own profile page showcasing their skills, bio, and social links. ### ๐Ÿ› ๏ธ Tech Stack + - **Static Site Generator**: Eleventy (11ty) - **Templating**: Nunjucks - **Styling**: TailwindCSS @@ -33,6 +34,7 @@ NextCommunity is a static site directory built with [Eleventy (11ty)](https://ww - **Deployment**: GitHub Pages ### โœจ Features + - ๐ŸŽฒ Randomized display of developer profiles - ๐ŸŒ“ Dark/Light theme support - ๐Ÿ“ฑ Fully responsive design @@ -86,7 +88,7 @@ bio: | Write your professional bio here. You can use multiple lines. Share your experience, interests, and what you're passionate about. - + Add your skills, projects, or anything else you'd like to highlight! ``` @@ -123,11 +125,13 @@ git push origin main 2. Click the **"Contribute"** button, then **"Open Pull Request"** 3. Write a clear title: `Add [Your Name] to directory` 4. In the description, mention: - ``` + + ```markdown Fixes #213 - + Adding my profile to the NextCommunity developer directory. ``` + 5. Click **"Create Pull Request"** ### Step 8: Wait for Review โณ @@ -143,25 +147,25 @@ git push origin main ### Required Fields -| Field | Description | Example | -|-------|-------------|---------| -| `name` | Your full name | `John Bampton` | -| `github` | Your GitHub username (without @) | `jbampton` | -| `country` | Your country | `Australia` | -| `location` | Your city | `Brisbane` | -| `role` | Your professional title | `Frontend Developer` | +| Field | Description | Example | +| ----------- | ------------------------------------ | -------------------------- | +| `name` | Your full name | `John Bampton` | +| `github` | Your GitHub username (without @) | `jbampton` | +| `country` | Your country | `Australia` | +| `location` | Your city | `Brisbane` | +| `role` | Your professional title | `Frontend Developer` | | `languages` | Space-separated list of technologies | `JavaScript React Node.js` | -| `bio` | Multi-line biography | See template above | +| `bio` | Multi-line biography | See template above | ### Optional Fields -| Field | Description | Example | -|-------|-------------|---------| -| `website` | Your personal website URL | `https://yoursite.com` | -| `email` | Your email address | `you@example.com` | -| `instagram` | Full Instagram profile URL | `https://instagram.com/username` | -| `twitter` | Full Twitter/X profile URL | `https://twitter.com/username` | -| `linkedin` | Full LinkedIn profile URL | `https://linkedin.com/in/username` | +| Field | Description | Example | +| ----------- | -------------------------- | ---------------------------------- | +| `website` | Your personal website URL | `https://yoursite.com` | +| `email` | Your email address | `you@example.com` | +| `instagram` | Full Instagram profile URL | `https://instagram.com/username` | +| `twitter` | Full Twitter/X profile URL | `https://twitter.com/username` | +| `linkedin` | Full LinkedIn profile URL | `https://linkedin.com/in/username` | ### Field Guidelines @@ -174,6 +178,7 @@ git push origin main ### ๐Ÿ“š Real Examples **Example 1: Minimal Profile** + ```yaml name: Jane Smith github: janesmith @@ -187,6 +192,7 @@ bio: | ``` **Example 2: Complete Profile** + ```yaml name: Carlos Rodriguez github: carlosr @@ -200,7 +206,7 @@ role: Full Stack Engineer languages: TypeScript React Node.js AWS bio: | Full-stack engineer specializing in modern web technologies. - + Currently building cloud-native applications and contributing to open source. Passionate about clean code, testing, and developer experience. ``` @@ -245,7 +251,7 @@ npm run build ### Project Structure -``` +```text NextCommunity.github.io/ โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ _data/ # Site-wide data files @@ -281,12 +287,14 @@ We're committed to providing a welcoming and inclusive environment. Please be re ### What Gets Approved? โœ… **Yes:** + - Complete, valid YAML files - Professional bios and appropriate content - Real GitHub profiles - Accurate information โŒ **No:** + - Spam or promotional content - Offensive or inappropriate material - Fake or duplicate profiles @@ -295,6 +303,7 @@ We're committed to providing a welcoming and inclusive environment. Please be re ### CI/CD Checks Every pull request runs automated checks: + - **Linting**: Ensures YAML syntax is correct - **Build Test**: Verifies the site builds successfully - **Pre-commit Hooks**: Checks code quality @@ -311,7 +320,8 @@ If checks fail, you'll see error messages in the PR. Fix the issues and push aga **Problem**: Your YAML file has syntax errors. -**Solution**: +**Solution**: + - Check for proper indentation (use spaces, not tabs) - Ensure colons have a space after them (`name: John`, not `name:John`) - Use `|` for multi-line bio text @@ -321,7 +331,8 @@ If checks fail, you'll see error messages in the PR. Fix the issues and push aga **Problem**: The `github` field doesn't match a real GitHub profile. -**Solution**: +**Solution**: + - Ensure you're using your exact GitHub username - Check for typos - Username is case-sensitive in this field @@ -331,6 +342,7 @@ If checks fail, you'll see error messages in the PR. Fix the issues and push aga **Problem**: File naming or format issue. **Solution**: + - File must be in `src/users/` directory - File must be named `username.yaml` (lowercase, with `.yaml` extension) - All required fields must be filled in @@ -340,6 +352,7 @@ If checks fail, you'll see error messages in the PR. Fix the issues and push aga **Problem**: Code quality checks didn't pass. **Solution**: + ```bash # Install pre-commit pip install pre-commit @@ -350,22 +363,22 @@ pre-commit run --all-files ### FAQ -**Q: Can I update my profile after it's merged?** +**Q: Can I update my profile after it's merged?** A: Yes! Just create a new PR with updates to your YAML file. -**Q: How long does review take?** +**Q: How long does review take?** A: Usually within 24-48 hours, depending on maintainer availability. -**Q: Can I add multiple social links?** +**Q: Can I add multiple social links?** A: Yes, all social fields (twitter, linkedin, instagram) are optional and independent. -**Q: What if I don't have a personal website?** +**Q: What if I don't have a personal website?** A: No problem! Just omit the `website` field or set it to your GitHub profile. -**Q: Can I use emojis in my profile?** +**Q: Can I use emojis in my profile?** A: Yes, emojis are supported in the `name` and `bio` fields! ๐ŸŽ‰ -**Q: Is there a character limit for the bio?** +**Q: Is there a character limit for the bio?** A: No hard limit, but keep it concise (2-4 paragraphs recommended). --- @@ -374,7 +387,8 @@ A: No hard limit, but keep it concise (2-4 paragraphs recommended). This project is licensed under the **GNU General Public License v3.0** - see the [LICENSE](LICENSE) file for details. -### What this means: +### What this means + - โœ… You can freely use, modify, and distribute this code - โœ… You must disclose source code when distributing - โœ… Changes must also be GPL-3.0 licensed @@ -396,5 +410,3 @@ This project is licensed under the **GNU General Public License v3.0** - see the - **Report bugs**: [Open an issue](https://github.com/jbampton/NextCommunity.github.io/issues) - **Ask questions**: [Start a discussion](https://github.com/jbampton/NextCommunity.github.io/discussions) - **Maintainer**: [@jbampton](https://github.com/jbampton) - - diff --git a/src/_data/build.js b/src/_data/build.js index 55cf1ba..7f753a2 100644 --- a/src/_data/build.js +++ b/src/_data/build.js @@ -1,18 +1,18 @@ -const { execSync } = require('child_process'); +const { execSync } = require("child_process"); module.exports = () => { const now = new Date(); // Format the date: "Jan 26, 2026, 07:51 AM" - const timestamp = now.toLocaleString('en-US', { - dateStyle: 'medium', - timeStyle: 'short', + const timestamp = now.toLocaleString("en-US", { + dateStyle: "medium", + timeStyle: "short", }); - let gitHash = 'development'; + let gitHash = "development"; try { // Get the short git hash (first 7 characters) - gitHash = execSync('git rev-parse --short HEAD').toString().trim(); + gitHash = execSync("git rev-parse --short HEAD").toString().trim(); } catch (e) { console.warn("Could not fetch git hash, defaulting to 'development'"); } @@ -20,6 +20,6 @@ module.exports = () => { return { timestamp, hash: gitHash, - repoUrl: "https://github.com/NextCommunity/NextCommunity.github.io" + repoUrl: "https://github.com/NextCommunity/NextCommunity.github.io", }; }; diff --git a/src/assets/css/style.css b/src/assets/css/style.css index a5ad7be..b4d534c 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -12,7 +12,8 @@ --accent-light: #eff6ff; --accent-rgb: 37, 99, 235; --danger: #dc2626; - --card-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --card-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); } .dark { @@ -28,17 +29,18 @@ --card-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5); } -html, body { +html, +body { overflow-x: clip; min-height: 100%; background-color: var(--bg-page); color: var(--text-main); - transition: background-color 0.4s ease, color 0.4s ease; + transition: + background-color 0.4s ease, + color 0.4s ease; scroll-behavior: smooth; } - - /** * 2. USER CARDS - DYNAMIC & STATIC */ @@ -52,7 +54,9 @@ html, body { font-weight: 800; text-decoration: none; /* Animate ONLY the color property when theme changes */ - transition: color 0.4s ease, opacity 0.2s ease; + transition: + color 0.4s ease, + opacity 0.2s ease; } /* Static Hover: No movement */ @@ -68,10 +72,10 @@ html, body { /* Button to trigger Screenshot Mode in the Dev Panel */ .screenshot-btn { - border-color: var(--text-muted) !important; - color: var(--text-muted) !important; - font-size: 0.6rem !important; - margin-top: 10px; + border-color: var(--text-muted) !important; + color: var(--text-muted) !important; + font-size: 0.6rem !important; + margin-top: 10px; } /* If your links look like tags/pills */ @@ -88,8 +92,6 @@ html, body { border-color: var(--accent); } - - /* If you have a specific button-style profile link */ .profile-btn { background-color: var(--accent-light) !important; @@ -156,7 +158,9 @@ html, body { overflow: hidden; } -.dark .surprise-btn { background-color: var(--accent) !important; } +.dark .surprise-btn { + background-color: var(--accent) !important; +} .surprise-btn:hover { transform: translateY(-2px); @@ -164,19 +168,28 @@ html, body { filter: brightness(1.1); } -.surprise-btn::after, .skill-item::after { - content: ''; +.surprise-btn::after, +.skill-item::after { + content: ""; position: absolute; top: 0; left: -150%; width: 100%; height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.4), + transparent + ); transform: skewX(-20deg); transition: 0.6s cubic-bezier(0.19, 1, 0.22, 1); } -.surprise-btn:hover::after, .skill-item:hover::after { left: 150%; } +.surprise-btn:hover::after, +.skill-item:hover::after { + left: 150%; +} /** * 4. DEVELOPER TOOLS - INDESTRUCTIBLE VIEWPORT PANEL @@ -204,20 +217,20 @@ html, body { /* NUCLEAR LOCK: Forces stability during page-wide glitches */ #dev-tools[data-lock="true"], html body #dev-tools[data-lock="true"] { - display: block !important; - visibility: visible !important; - opacity: 1 !important; - position: fixed !important; - top: 1rem !important; - right: 1rem !important; - transform: none !important; - animation: none !important; - border-left-color: #ef4444 !important; - box-shadow: 0 0 40px rgba(239, 68, 68, 0.6) !important; + display: block !important; + visibility: visible !important; + opacity: 1 !important; + position: fixed !important; + top: 1rem !important; + right: 1rem !important; + transform: none !important; + animation: none !important; + border-left-color: #ef4444 !important; + box-shadow: 0 0 40px rgba(239, 68, 68, 0.6) !important; } #dev-tools button { - font-family: 'JetBrains Mono', monospace; + font-family: "JetBrains Mono", monospace; font-size: 0.75rem; background: rgba(255, 255, 255, 0.03); color: #e2e8f0; @@ -238,22 +251,38 @@ html body #dev-tools[data-lock="true"] { } /* Console Neon Overrides */ -#dev-tools button[onclick*="matrix"] { color: #00ff41 !important; } -#dev-tools button[onclick*="konami"] { color: #ffcc00 !important; } -#dev-tools button[onclick*="gravity"] { color: #ff3333 !important; } -#dev-tools button[onclick*="badge_click"] { color: #bc13fe !important; } +#dev-tools button[onclick*="matrix"] { + color: #00ff41 !important; +} +#dev-tools button[onclick*="konami"] { + color: #ffcc00 !important; +} +#dev-tools button[onclick*="gravity"] { + color: #ff3333 !important; +} +#dev-tools button[onclick*="badge_click"] { + color: #bc13fe !important; +} /** * 5. SELF DESTRUCT & REPAIR */ #destruct-bar-container { - width: 100%; height: 14px; background: #000; - border: 2px solid #333; margin-top: 15px; border-radius: 4px; + width: 100%; + height: 14px; + background: #000; + border: 2px solid #333; + margin-top: 15px; + border-radius: 4px; } #destruct-bar-progress { - height: 100%; width: 0%; background: #22c55e; - transition: width 1s linear, background-color 0.3s; + height: 100%; + width: 0%; + background: #22c55e; + transition: + width 1s linear, + background-color 0.3s; } #repair-btn { @@ -266,7 +295,8 @@ html body #dev-tools[data-lock="true"] { box-shadow: 0 0 60px rgba(37, 99, 235, 0.6); z-index: 10001; position: fixed; - top: 50%; left: 50%; + top: 50%; + left: 50%; transform: translate(-50%, -50%); } @@ -274,467 +304,558 @@ html body #dev-tools[data-lock="true"] { * 6. ANIMATIONS */ @keyframes shake-anim { - 0% { transform: translate(0, 0); } - 25% { transform: translate(3px, -3px); } - 50% { transform: translate(-3px, 3px); } - 100% { transform: translate(0, 0); } + 0% { + transform: translate(0, 0); + } + 25% { + transform: translate(3px, -3px); + } + 50% { + transform: translate(-3px, 3px); + } + 100% { + transform: translate(0, 0); + } +} + +.glitch-shake { + animation: shake-anim 0.1s infinite; + overflow: hidden; } -.glitch-shake { animation: shake-anim 0.1s infinite; overflow: hidden; } - @keyframes konami-barrel-roll { - 0% { transform: rotate(0deg) scale(1); filter: hue-rotate(0deg); } - 50% { transform: rotate(180deg) scale(0.8); filter: hue-rotate(180deg); } - 100% { transform: rotate(360deg) scale(1); filter: hue-rotate(360deg); } + 0% { + transform: rotate(0deg) scale(1); + filter: hue-rotate(0deg); + } + 50% { + transform: rotate(180deg) scale(0.8); + filter: hue-rotate(180deg); + } + 100% { + transform: rotate(360deg) scale(1); + filter: hue-rotate(360deg); + } } -.konami-roll { animation: konami-barrel-roll 2s cubic-bezier(0.45, 0.05, 0.55, 0.95); } +.konami-roll { + animation: konami-barrel-roll 2s cubic-bezier(0.45, 0.05, 0.55, 0.95); +} /** * FANCY USER SELECTION EFFECT */ @keyframes fancy-ping { - 0% { - box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.7); - } - 70% { - box-shadow: 0 0 0 20px rgba(var(--accent-rgb), 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); - } + 0% { + box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.7); + } + 70% { + box-shadow: 0 0 0 20px rgba(var(--accent-rgb), 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); + } } /* Ensure the card can show the trace glow */ .user-card.selected-fancy { - z-index: 50; - overflow: visible !important; - transform: scale(1.02); - transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + z-index: 50; + overflow: visible !important; + transform: scale(1.02); + transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .border-trace { - position: absolute; - inset: -2px; /* Slightly outside the card border */ - width: calc(100% + 4px); - height: calc(100% + 4px); - pointer-events: none; - fill: none; + position: absolute; + inset: -2px; /* Slightly outside the card border */ + width: calc(100% + 4px); + height: calc(100% + 4px); + pointer-events: none; + fill: none; } .border-trace rect { - stroke-width: 4px; - stroke-linecap: round; - /* Perimeter length - 2500px is a safe bet for these cards */ - stroke-dasharray: 2500; - stroke-dashoffset: 2500; - animation: retrace-sequence 7.5s linear forwards; + stroke-width: 4px; + stroke-linecap: round; + /* Perimeter length - 2500px is a safe bet for these cards */ + stroke-dasharray: 2500; + stroke-dashoffset: 2500; + animation: retrace-sequence 7.5s linear forwards; } @keyframes retrace-sequence { - 0% { - stroke-dashoffset: 2500; - stroke: var(--accent); - filter: drop-shadow(0 0 8px var(--accent)); - } - 100% { - stroke-dashoffset: 0; - /* Cycle through colors while drawing */ - stroke: var(--accent); - filter: drop-shadow(0 0 12px var(--accent)) hue-rotate(360deg); - } + 0% { + stroke-dashoffset: 2500; + stroke: var(--accent); + filter: drop-shadow(0 0 8px var(--accent)); + } + 100% { + stroke-dashoffset: 0; + /* Cycle through colors while drawing */ + stroke: var(--accent); + filter: drop-shadow(0 0 12px var(--accent)) hue-rotate(360deg); + } } /* Visual feedback when hovering a skill to gain XP */ .mining-xp { - filter: brightness(1.5); - box-shadow: 0 0 15px var(--accent); - transition: all 0.2s ease; + filter: brightness(1.5); + box-shadow: 0 0 15px var(--accent); + transition: all 0.2s ease; } /* Special styling for those who have reached Level 5 (Data Miner) */ body[data-level="5"] .skill-item { - border-color: #06b6d4 !important; /* Data Miner Cyan */ - background: rgba(6, 182, 212, 0.1) !important; - position: relative; - overflow: hidden; + border-color: #06b6d4 !important; /* Data Miner Cyan */ + background: rgba(6, 182, 212, 0.1) !important; + position: relative; + overflow: hidden; } /* Add a gem-like sparkle to skills at Level 5 */ body[data-level="5"] .skill-item::before { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: linear-gradient(45deg, transparent, rgba(255,255,255,0.3), transparent); - transform: rotate(45deg); - animation: gem-shimmer 3s infinite; + content: ""; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + 45deg, + transparent, + rgba(255, 255, 255, 0.3), + transparent + ); + transform: rotate(45deg); + animation: gem-shimmer 3s infinite; } @keyframes gem-shimmer { - 0% { transform: translateX(-100%) rotate(45deg); } - 100% { transform: translateX(100%) rotate(45deg); } + 0% { + transform: translateX(-100%) rotate(45deg); + } + 100% { + transform: translateX(100%) rotate(45deg); + } } @keyframes float-up { - 0% { transform: translate(-50%, 0); opacity: 1; } - 100% { transform: translate(-50%, -50px); opacity: 0; } + 0% { + transform: translate(-50%, 0); + opacity: 1; + } + 100% { + transform: translate(-50%, -50px); + opacity: 0; + } } /* Level 5 Specific Visual Perk */ .level-architect .skill-item, body[data-level="5"] .skill-item { - border-color: #06b6d4 !important; - background: rgba(6, 182, 212, 0.1) !important; - box-shadow: 0 0 10px rgba(6, 182, 212, 0.2); + border-color: #06b6d4 !important; + background: rgba(6, 182, 212, 0.1) !important; + box-shadow: 0 0 10px rgba(6, 182, 212, 0.2); } .skill-item { - cursor: crosshair; /* Makes it feel like you are "mining" */ + cursor: crosshair; /* Makes it feel like you are "mining" */ } /* Level 6 Visuals */ body[data-level="6"] .user-card { - border-color: rgba(236, 72, 153, 0.3); + border-color: rgba(236, 72, 153, 0.3); } body[data-level="6"]::after { - content: ""; - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: linear-gradient( - rgba(18, 16, 16, 0) 50%, - rgba(0, 0, 0, 0.1) 50% - ); - background-size: 100% 4px; - z-index: 9999; - pointer-events: none; - opacity: 0.2; /* Very subtle scanlines */ + content: ""; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.1) 50%); + background-size: 100% 4px; + z-index: 9999; + pointer-events: none; + opacity: 0.2; /* Very subtle scanlines */ } /* Added via JS when XP increases */ .xp-pulse { - transform: scale(1.3); - color: white !important; + transform: scale(1.3); + color: white !important; } #jump-lvl { - font-family: 'Courier New', monospace; - text-shadow: 0 0 5px var(--accent); -}/* Ensure the text inside the jump box is always visible */ + font-family: "Courier New", monospace; + text-shadow: 0 0 5px var(--accent); +} /* Ensure the text inside the jump box is always visible */ #jump-lvl { - appearance: textfield; /* Removes default arrows */ - -webkit-appearance: none; - line-height: 30px !important; + appearance: textfield; /* Removes default arrows */ + -webkit-appearance: none; + line-height: 30px !important; } #jump-lvl::placeholder { - color: rgba(0, 255, 204, 0.3) !important; + color: rgba(0, 255, 204, 0.3) !important; } /* Force standard text color even on focus */ #jump-lvl:focus { - outline: 2px solid #00ffcc !important; - background-color: #000 !important; - color: #00ffcc !important; + outline: 2px solid #00ffcc !important; + background-color: #000 !important; + color: #00ffcc !important; } /* Base Force Aura Animation */ @keyframes force-pulse { - 0% { transform: scale(1); filter: brightness(1) drop-shadow(0 0 5px var(--glow-color)); } - 50% { transform: scale(1.1); filter: brightness(1.4) drop-shadow(0 0 20px var(--glow-color)); } - 100% { transform: scale(1); filter: brightness(1) drop-shadow(0 0 5px var(--glow-color)); } + 0% { + transform: scale(1); + filter: brightness(1) drop-shadow(0 0 5px var(--glow-color)); + } + 50% { + transform: scale(1.1); + filter: brightness(1.4) drop-shadow(0 0 20px var(--glow-color)); + } + 100% { + transform: scale(1); + filter: brightness(1) drop-shadow(0 0 5px var(--glow-color)); + } } /* The Active Class */ .force-glow { - --glow-color: #38bdf8; /* Default Jedi Blue */ - position: relative; - z-index: 10; - transition: all 0.3s ease; - animation: force-pulse 5s infinite ease-in-out; + --glow-color: #38bdf8; /* Default Jedi Blue */ + position: relative; + z-index: 10; + transition: all 0.3s ease; + animation: force-pulse 5s infinite ease-in-out; } /* Sith Variant (Red Glow) */ -[data-level^="13"], [data-level^="14"], [data-level^="15"] .force-glow { - --glow-color: #ef4444; /* Sith Red */ +[data-level^="13"], +[data-level^="14"], +[data-level^="15"] .force-glow { + --glow-color: #ef4444; /* Sith Red */ } /* Ensure the parent container has a background so you can see the 'empty' part */ #game-stats .bg-black\/10 { - background-color: rgba(0, 0, 0, 0.1) !important; - min-width: 80px; /* Ensure it hasn't collapsed to 0 width */ - height: 6px; + background-color: rgba(0, 0, 0, 0.1) !important; + min-width: 80px; /* Ensure it hasn't collapsed to 0 width */ + height: 6px; } /* Ensure the progress bar itself has a height and a visible color */ #level-progress { - height: 100%; - background-color: var(--accent); /* This is the level color */ - transition: width 0.3s ease-in-out !important; /* Make it smooth */ - display: block !important; + height: 100%; + background-color: var(--accent); /* This is the level color */ + transition: width 0.3s ease-in-out !important; /* Make it smooth */ + display: block !important; } /* Force Glow for the Badge */ @keyframes force-pulse { - 0% { filter: drop-shadow(0 0 2px var(--accent)); transform: scale(1); } - 50% { filter: drop-shadow(0 0 15px var(--accent)); transform: scale(1.1); } - 100% { filter: drop-shadow(0 0 2px var(--accent)); transform: scale(1); } + 0% { + filter: drop-shadow(0 0 2px var(--accent)); + transform: scale(1); + } + 50% { + filter: drop-shadow(0 0 15px var(--accent)); + transform: scale(1.1); + } + 100% { + filter: drop-shadow(0 0 2px var(--accent)); + transform: scale(1); + } } .force-glow { - animation: force-pulse 1.5s infinite ease-in-out; + animation: force-pulse 1.5s infinite ease-in-out; } #level-name { - font-weight: 900; - text-transform: uppercase; - letter-spacing: 0.02em; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 0.02em; - /* Remove any existing filters that cause fuzziness */ - filter: none !important; - transition: color 0.3s ease; + /* Remove any existing filters that cause fuzziness */ + filter: none !important; + transition: color 0.3s ease; } /* Adjust the stroke for Dark/Random themes to keep it crisp */ .dark #level-name { - text-shadow: 1px 1px 0px rgba(0,0,0,0.5); + text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.5); } #level-badge { - transition: background-color 0.4s ease, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); + transition: + background-color 0.4s ease, + transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); } /* Ensure the XP numbers are always legible */ #total-xp-display { - color: var(--text-main); - filter: brightness(1.2); /* Pops slightly more in dark mode */ + color: var(--text-main); + filter: brightness(1.2); /* Pops slightly more in dark mode */ } /* Progress bar should always have a high-contrast background */ .xp-bar-container { - background: rgba(0, 0, 0, 0.1); + background: rgba(0, 0, 0, 0.1); } .dark .xp-bar-container { - background: rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.1); } .xp-popup { - position: absolute; - pointer-events: none; - z-index: 9999; - animation: floatUp 1s ease-out forwards; - color: var(--accent); /* This uses our dynamic level color! */ - font-size: 0.75rem; + position: absolute; + pointer-events: none; + z-index: 9999; + animation: floatUp 1s ease-out forwards; + color: var(--accent); /* This uses our dynamic level color! */ + font-size: 0.75rem; } @keyframes floatUp { - 0% { - transform: translateY(0); - opacity: 1; - } - 100% { - transform: translateY(-50px); - opacity: 0; - } + 0% { + transform: translateY(0); + opacity: 1; + } + 100% { + transform: translateY(-50px); + opacity: 0; + } } @keyframes xpFloat { - 0% { - transform: translateY(0) scale(1); - opacity: 1; - } - 100% { - transform: translateY(-40px) scale(1.2); - opacity: 0; - } + 0% { + transform: translateY(0) scale(1); + opacity: 1; + } + 100% { + transform: translateY(-40px) scale(1.2); + opacity: 0; + } } .animate-xp-float { - animation: xpFloat 1s ease-out forwards; - /* This makes sure the text is crisp on light/dark themes */ - text-shadow: 0 0 4px rgba(0,0,0,0.3); + animation: xpFloat 1s ease-out forwards; + /* This makes sure the text is crisp on light/dark themes */ + text-shadow: 0 0 4px rgba(0, 0, 0, 0.3); } - - /* Username: Big, bold, and using the random accent */ -.developer-name, h1 { - color: var(--accent); - text-shadow: 0 0 20px hsla(var(--accent-hue), 90%, 65%, 0.3); +.developer-name, +h1 { + color: var(--accent); + text-shadow: 0 0 20px hsla(var(--accent-hue), 90%, 65%, 0.3); } /* Skills: Give them a 'glass' look using the random background */ .skill-tag { - background: var(--bg-card); - border: 1px solid var(--border-color); - color: var(--text-main); + background: var(--bg-card); + border: 1px solid var(--border-color); + color: var(--text-main); } .skill-tag:hover { - border-color: var(--accent); - color: var(--accent); - box-shadow: 0 0 15px var(--accent); + border-color: var(--accent); + color: var(--accent); + box-shadow: 0 0 15px var(--accent); } /* Links: Make them pop! */ a { - color: var(--accent); - transition: all 0.2s ease; + color: var(--accent); + transition: all 0.2s ease; } a:hover { - filter: brightness(1.2); - text-decoration: underline; + filter: brightness(1.2); + text-decoration: underline; } @keyframes badgePop { - 0% { transform: scale(1); filter: brightness(1); } - 50% { transform: scale(1.4); filter: brightness(1.5); } - 100% { transform: scale(1); filter: brightness(1); } + 0% { + transform: scale(1); + filter: brightness(1); + } + 50% { + transform: scale(1.4); + filter: brightness(1.5); + } + 100% { + transform: scale(1); + filter: brightness(1); + } } .animate-badge-pop { - animation: badgePop 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; - z-index: 101; /* Ensure it pops above the header bar */ + animation: badgePop 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; + z-index: 101; /* Ensure it pops above the header bar */ } /* Optional: Add a glow to the level number too */ #level-number { - transition: all 0.3s ease; + transition: all 0.3s ease; } .animate-badge-pop #level-number { - color: white; - text-shadow: 0 0 10px rgba(255,255,255,1); + color: white; + text-shadow: 0 0 10px rgba(255, 255, 255, 1); } - - .level-up-toast { - position: fixed; - top: 20px; - left: 50%; - transform: translateX(-50%); - background: rgba(15, 23, 42, 0.9); /* Dark background for contrast */ - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 16px 24px; - border-radius: 12px; - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), - 0 0 15px var(--accent); /* Glow matches current level */ - z-index: 1000; - animation: slideDownIn 0.5s cubic-bezier(0.18, 0.89, 0.32, 1.28) forwards; + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + background: rgba(15, 23, 42, 0.9); /* Dark background for contrast */ + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); + padding: 16px 24px; + border-radius: 12px; + box-shadow: + 0 10px 25px -5px rgba(0, 0, 0, 0.5), + 0 0 15px var(--accent); /* Glow matches current level */ + z-index: 1000; + animation: slideDownIn 0.5s cubic-bezier(0.18, 0.89, 0.32, 1.28) forwards; } .toast-content { - display: flex; - align-items: center; - gap: 16px; + display: flex; + align-items: center; + gap: 16px; } .toast-emoji { - font-size: 2rem; + font-size: 2rem; } .toast-title { - color: #94a3b8; - font-size: 0.7rem; - font-weight: 800; - letter-spacing: 0.1rem; - margin: 0; + color: #94a3b8; + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.1rem; + margin: 0; } .toast-rank { - font-size: 1.25rem; - font-weight: 900; - margin: 0; - text-transform: uppercase; + font-size: 1.25rem; + font-weight: 900; + margin: 0; + text-transform: uppercase; } @keyframes slideDownIn { - from { transform: translate(-50%, -100px); opacity: 0; } - to { transform: translate(-50%, 0); opacity: 1; } + from { + transform: translate(-50%, -100px); + opacity: 0; + } + to { + transform: translate(-50%, 0); + opacity: 1; + } } .fade-out { - opacity: 0; - transform: translate(-50%, -20px); - transition: all 0.5s ease; + opacity: 0; + transform: translate(-50%, -20px); + transition: all 0.5s ease; } .matrix-alert { - position: fixed; - bottom: 20%; - right: 20px; - background: rgba(0, 0, 0, 0.9); - border-left: 4px solid #10b981; - color: #10b981; - padding: 15px; - font-family: 'Courier New', monospace; - z-index: 2000; - box-shadow: 0 0 20px rgba(16, 185, 129, 0.2); - animation: matrixSlideIn 0.3s ease-out; + position: fixed; + bottom: 20%; + right: 20px; + background: rgba(0, 0, 0, 0.9); + border-left: 4px solid #10b981; + color: #10b981; + padding: 15px; + font-family: "Courier New", monospace; + z-index: 2000; + box-shadow: 0 0 20px rgba(16, 185, 129, 0.2); + animation: matrixSlideIn 0.3s ease-out; } @keyframes matrixSlideIn { - from { transform: translateX(100%); opacity: 0; } - to { transform: translateX(0); opacity: 1; } + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } } #matrix-console-output { - /* Subtle green glow on text */ - text-shadow: 0 0 5px rgba(16, 185, 129, 0.3); - scroll-behavior: smooth; - display: flex; - flex-direction: column; + /* Subtle green glow on text */ + text-shadow: 0 0 5px rgba(16, 185, 129, 0.3); + scroll-behavior: smooth; + display: flex; + flex-direction: column; } /* Custom scrollbar for the terminal */ #matrix-console-output::-webkit-scrollbar { - width: 3px; + width: 3px; } #matrix-console-output::-webkit-scrollbar-thumb { - background: rgba(16, 185, 129, 0.3); + background: rgba(16, 185, 129, 0.3); } /* The Matrix Line Animation */ .matrix-line { - border-left: 2px solid #10b981; - padding-left: 8px; - margin-bottom: 4px; - animation: matrixLineFade 0.3s ease-out; + border-left: 2px solid #10b981; + padding-left: 8px; + margin-bottom: 4px; + animation: matrixLineFade 0.3s ease-out; } @keyframes matrixLineFade { - from { opacity: 0; transform: translateX(-10px); } - to { opacity: 1; transform: translateX(0); } + from { + opacity: 0; + transform: translateX(-10px); + } + to { + opacity: 1; + transform: translateX(0); + } } /* Maximize State */ .console-maximized { - width: 90vw !important; - height: auto !important; - bottom: 5vh !important; - right: 5vw !important; - left: 5vw !important; - z-index: 2000 !important; + width: 90vw !important; + height: auto !important; + bottom: 5vh !important; + right: 5vw !important; + left: 5vw !important; + z-index: 2000 !important; } #matrix-console-output { - scrollbar-width: thin; - scrollbar-color: #10b981 transparent; + scrollbar-width: thin; + scrollbar-color: #10b981 transparent; } /* Add a scanline effect for that retro terminal feel */ #matrix-console-output::before { - content: " "; - position: absolute; - top: 0; left: 0; bottom: 0; right: 0; - background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), - linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); - z-index: 10; - background-size: 100% 2px, 3px 100%; - pointer-events: none; + content: " "; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), + linear-gradient( + 90deg, + rgba(255, 0, 0, 0.06), + rgba(0, 255, 0, 0.02), + rgba(0, 0, 255, 0.06) + ); + z-index: 10; + background-size: + 100% 2px, + 3px 100%; + pointer-events: none; } .cursor-grab:active { - cursor: grabbing; + cursor: grabbing; } /* Prevents the terminal text from getting highlighted while you move the window */ #matrix-console-container.is-dragging { - user-select: none; + user-select: none; } diff --git a/src/assets/js/script.js b/src/assets/js/script.js index b7043d4..aa5e945 100644 --- a/src/assets/js/script.js +++ b/src/assets/js/script.js @@ -1,260 +1,283 @@ - /** * 1. LEVELS DATA (0-100) */ const LEVELS = [ - // 0-10: Original Ranks - { level: 0, name: "Newbie", emoji: "๐Ÿฃ", color: "#94a3b8" }, - { level: 1, name: "Script Kid", emoji: "๐Ÿ›น", color: "#10b981" }, - { level: 2, name: "Code Breaker", emoji: "๐Ÿ•ต๏ธโ€โ™‚๏ธ", color: "#f59e0b" }, - { level: 3, name: "Void Walker", emoji: "๐ŸŒŒ", color: "#6366f1" }, - { level: 4, name: "Bug Hunter", emoji: "๐Ÿ›", color: "#84cc16" }, - { level: 5, name: "Data Miner", emoji: "๐Ÿ’Ž", color: "#06b6d4" }, - { level: 6, name: "Sys Admin", emoji: "๐Ÿ› ๏ธ", color: "#ec4899" }, - { level: 7, name: "Terminal Pro", emoji: "โŒจ๏ธ", color: "#7c3aed" }, - { level: 8, name: "Cloud Expert", emoji: "โ˜๏ธ", color: "#3b82f6" }, - { level: 9, name: "Full Stack", emoji: "๐Ÿฅž", color: "#f97316" }, - { level: 10, name: "Architect", emoji: "๐Ÿ‘‘", color: "#ef4444" }, - - // 11-30: Magic the Gathering (Creatures & Keywords) - { level: 11, name: "Llanowar Elf", emoji: "๐Ÿน", color: "#2d5a27" }, - { level: 12, name: "Scryer", emoji: "๐Ÿ”ฎ", color: "#1d4ed8" }, - { level: 13, name: "Trampler", emoji: "๐Ÿ˜", color: "#15803d" }, - { level: 14, name: "Flying Menace", emoji: "๐Ÿฆ‡", color: "#4a044e" }, - { level: 15, name: "Mana Leech", emoji: "๐Ÿ’ง", color: "#0ea5e9" }, - { level: 16, name: "Spellcounter", emoji: "๐Ÿšซ", color: "#2563eb" }, - { level: 17, name: "Goblin Guide", emoji: "๐Ÿ‘บ", color: "#dc2626" }, - { level: 18, name: "Serum Visionary", emoji: "๐Ÿงช", color: "#6366f1" }, - { level: 19, name: "Mythic Rare", emoji: "๐ŸŸ ", color: "#f97316" }, - { level: 20, name: "Planeswalker", emoji: "โœจ", color: "#fbbf24" }, - - // 21-40: Game of Thrones (Houses & Heroes) - { level: 21, name: "Night's Watch", emoji: "๐Ÿฆ…", color: "#1e293b" }, - { level: 22, name: "Wildling Scout", emoji: "โ„๏ธ", color: "#94a3b8" }, - { level: 23, name: "Ironborn", emoji: "โš“", color: "#475569" }, - { level: 24, name: "Dothraki Rider", emoji: "๐ŸŽ", color: "#b45309" }, - { level: 25, name: "Kingslayer", emoji: "๐Ÿ—ก๏ธ", color: "#facc15" }, - { level: 26, name: "Winterfell Warden", emoji: "๐Ÿบ", color: "#cbd5e1" }, - { level: 27, name: "Dragonstone Guard", emoji: "๐Ÿ‰", color: "#991b1b" }, - { level: 28, name: "Faceless Man", emoji: "๐ŸŽญ", color: "#4b5563" }, - { level: 29, name: "Hand of the King", emoji: "๐Ÿ–๏ธ", color: "#d97706" }, - { level: 30, name: "Iron Throne Heir", emoji: "โš”๏ธ", color: "#111827" }, - - // 31-50: Wheel of Time (The Tiers of Power) - { level: 31, name: "Two Rivers Archer", emoji: "๐Ÿน", color: "#166534" }, - { level: 32, name: "Gleeman", emoji: "๐ŸŽถ", color: "#be185d" }, - { level: 33, name: "Borderlander", emoji: "๐Ÿ›ก๏ธ", color: "#991b1b" }, - { level: 34, name: "Warders Bond", emoji: "๐Ÿ”—", color: "#1e293b" }, - { level: 35, name: "Aes Sedai Novice", emoji: "๐Ÿ•ฏ๏ธ", color: "#f8fafc" }, - { level: 36, name: "Accepted", emoji: "๐Ÿ’", color: "#e2e8f0" }, - { level: 37, name: "Aiel Dreamwalker", emoji: "๐Ÿœ๏ธ", color: "#d97706" }, - { level: 38, name: "Asha'man", emoji: "โšก", color: "#000000" }, - { level: 39, name: "Amyrlin Seat", emoji: "๐Ÿ“œ", color: "#ffffff" }, - { level: 40, name: "Ta'veren", emoji: "๐ŸŒ€", color: "#6366f1" }, - - // 41-60: Lord of the Rings (Fellowship & Foes) - { level: 41, name: "Hobbit Adventurer", emoji: "๐Ÿบ", color: "#15803d" }, - { level: 42, name: "Bree Strider", emoji: "๐Ÿ‘ข", color: "#451a03" }, - { level: 43, name: "Riddermark Lord", emoji: "๐Ÿ‡", color: "#166534" }, - { level: 44, name: "Gondor Soldier", emoji: "๐Ÿ›ก๏ธ", color: "#94a3b8" }, - { level: 45, name: "Uruk-hai Berserker", emoji: "โœ‹", color: "#450a0a" }, - { level: 46, name: "Elven Archer", emoji: "๐Ÿƒ", color: "#4ade80" }, - { level: 47, name: "Dwarf Warrior", emoji: "โ›๏ธ", color: "#78350f" }, - { level: 48, name: "Nazgรปl Rider", emoji: "๐ŸŽ", color: "#020617" }, - { level: 49, name: "Istari Pupil", emoji: "๐Ÿง™", color: "#3b82f6" }, - { level: 50, name: "Ring-bearer", emoji: "๐Ÿ’", color: "#fbbf24" }, - - // NEW LEVELS 51-60 - { level: 51, name: "White Wizard", emoji: "๐Ÿง™โ€โ™‚๏ธ", color: "#f8fafc" }, - { level: 52, name: "Silmaril Seeker", emoji: "๐Ÿ’Ž", color: "#7dd3fc" }, - { level: 53, name: "Dune Walker", emoji: "โณ", color: "#fcd34d" }, - { level: 54, name: "Shadowfax Rider", emoji: "๐ŸŽ", color: "#e2e8f0" }, - { level: 55, name: "Master of Coin", emoji: "๐Ÿช™", color: "#fbbf24" }, - { level: 56, name: "Kingsguard", emoji: "๐Ÿ›ก๏ธ", color: "#94a3b8" }, - { level: 57, name: "Valyrian Smith", emoji: "โš’๏ธ", color: "#475569" }, - { level: 58, name: "Night Watcher", emoji: "๐Ÿฆ‰", color: "#312e81" }, - { level: 59, name: "Obsidian Blade", emoji: "๐Ÿ—ก๏ธ", color: "#1e293b" }, - { level: 60, name: "Citadel Maester", emoji: "๐Ÿ“œ", color: "#8b5e3c" }, - - // 61-80: High Magic & Artifacts - { level: 61, name: "Mox Emerald", emoji: "๐Ÿ’š", color: "#10b981" }, - { level: 62, name: "Mox Sapphire", emoji: "๐Ÿ’™", color: "#3b82f6" }, - { level: 63, name: "Mox Ruby", emoji: "โค๏ธ", color: "#ef4444" }, - { level: 64, name: "Mox Jet", emoji: "๐Ÿ–ค", color: "#18181b" }, - { level: 65, name: "Mox Pearl", emoji: "๐Ÿค", color: "#f8fafc" }, - { level: 66, name: "Black Lotus", emoji: "๐ŸŒบ", color: "#000000" }, - { level: 67, name: "Balrog Slayer", emoji: "๐Ÿ”ฅ", color: "#f97316" }, - { level: 68, name: "Witch-king", emoji: "๐Ÿ‘‘", color: "#334155" }, - { level: 69, name: "Shelob's Kin", emoji: "๐Ÿ•ท๏ธ", color: "#0f172a" }, - { level: 70, name: "Dragon-friend", emoji: "๐Ÿฒ", color: "#dc2626" }, - - // NEW LEVELS 71-80 - { level: 71, name: "Neon Ghost", emoji: "๐Ÿ‘ป", color: "#22d3ee" }, - { level: 72, name: "Dragon's Greed", emoji: "๐Ÿช™", color: "#fbbf24" }, - { level: 73, name: "Mistborn", emoji: "๐ŸŒซ๏ธ", color: "#94a3b8" }, - { level: 74, name: "Cinder Soul", emoji: "๐Ÿ”ฅ", color: "#f87171" }, - { level: 75, name: "High Council", emoji: "๐Ÿ›๏ธ", color: "#6366f1" }, - { level: 76, name: "Valyrian Steel", emoji: "๐Ÿ—ก๏ธ", color: "#cbd5e1" }, - { level: 77, name: "Golden Snitch", emoji: "โœจ", color: "#facc15" }, - { level: 78, name: "Ether Weaver", emoji: "๐Ÿ•ธ๏ธ", color: "#a855f7" }, - { level: 79, name: "Star Forge", emoji: "๐Ÿ”จ", color: "#38bdf8" }, - { level: 80, name: "Mithril Guard", emoji: "๐Ÿ›ก๏ธ", color: "#e2e8f0" }, - - // 81-90: Wheel of Time (The Forsaken & Dragons) - { level: 81, name: "Lan Mandragoran", emoji: "๐Ÿ—ก๏ธ", color: "#1e293b" }, - { level: 82, name: "Moiraine Damodred", emoji: "๐Ÿ’ง", color: "#1d4ed8" }, - { level: 83, name: "Ishamael", emoji: "๐Ÿ‘๏ธ", color: "#450a0a" }, - { level: 84, name: "Callandor Wielder", emoji: "๐Ÿ’Ž", color: "#22d3ee" }, - { level: 85, name: "Lewes Therin", emoji: "โ˜€๏ธ", color: "#fde047" }, - { level: 86, name: "Dragon Reborn", emoji: "๐Ÿ‰", color: "#ef4444" }, - - // NEW LEVELS 87-90 - { level: 87, name: "Phoenix Down", emoji: "๐Ÿชถ", color: "#fb7185" }, - { level: 88, name: "Void Sentinel", emoji: "๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ", color: "#4ade80" }, - { level: 89, name: "Elder Wand", emoji: "๐Ÿช„", color: "#94a3b8" }, - { level: 90, name: "Balrog's Whip", emoji: "๐Ÿ”ฅ", color: "#b91c1c" }, - - // 91-100: Cosmic Legends - { level: 91, name: "Sauron's Shadow", emoji: "๐Ÿ‘๏ธ", color: "#000000" }, - { level: 92, name: "Galadriel's Light", emoji: "๐ŸŒŸ", color: "#e2e8f0" }, - { level: 93, name: "Eldrazi Titan", emoji: "๐Ÿ™", color: "#a855f7" }, - { level: 94, name: "Tom Bombadil", emoji: "๐Ÿ„", color: "#fbbf24" }, - { level: 95, name: "Sauron Unleashed", emoji: "๐ŸŒ‹", color: "#7f1d1d" }, - { level: 96, name: "Saruman the White", emoji: "โœ‹", color: "#cbd5e1" }, - { level: 97, name: "Gandalf the Grey", emoji: "๐ŸŽ†", color: "#64748b" }, - { level: 98, name: "Gandalf the White", emoji: "๐Ÿง™โ€โ™‚๏ธ", color: "#ffffff" }, - { level: 99, name: "The Creator", emoji: "๐ŸŒŒ", color: "#6366f1" }, - { level: 100, name: "Eru Ilรบvatar", emoji: "โœจ", color: "#ffffff" }, - - { level: 101, name: "Padawan Learner", emoji: "๐Ÿง˜", color: "#60a5fa" }, - { level: 102, name: "Moisture Farmer", emoji: "๐Ÿšœ", color: "#d97706" }, - { level: 103, name: "Scrappy Scavenger", emoji: "๐Ÿ”ง", color: "#fcd34d" }, - { level: 104, name: "Cantina Regular", emoji: "๐Ÿน", color: "#9333ea" }, - { level: 105, name: "Holocron Finder", emoji: "๐ŸงŠ", color: "#38bdf8" }, - { level: 106, name: "Speeder Racer", emoji: "๐ŸŽ๏ธ", color: "#fb923c" }, - { level: 107, name: "Droid Mechanic", emoji: "๐Ÿค–", color: "#94a3b8" }, - { level: 108, name: "Kyber Seeker", emoji: "๐Ÿ’Ž", color: "#ffffff" }, - { level: 109, name: "Outer Rim Nomad", emoji: "๐Ÿœ๏ธ", color: "#a8a29e" }, - { level: 110, name: "Rebel Pilot", emoji: "๐Ÿš€", color: "#ef4444" }, - { level: 111, name: "Squadron Leader", emoji: "๐Ÿ“ก", color: "#f87171" }, - { level: 112, name: "Astromech Specialist", emoji: "๐Ÿ”Œ", color: "#3b82f6" }, - { level: 113, name: "Smuggler Associate", emoji: "๐Ÿ“ฆ", color: "#b45309" }, - { level: 114, name: "Sabacc Champion", emoji: "๐Ÿƒ", color: "#fbbf24" }, - { level: 115, name: "Jedi Knight", emoji: "โš”๏ธ", color: "#60a5fa" }, - { level: 116, name: "Lightsaber Smith", emoji: "๐Ÿ› ๏ธ", color: "#2dd4bf" }, - { level: 117, name: "Force Sensitive", emoji: "๐ŸŒ€", color: "#818cf8" }, - { level: 118, name: "Temple Guardian", emoji: "๐Ÿฐ", color: "#facc15" }, - { level: 119, name: "Wayseeker", emoji: "๐Ÿงญ", color: "#94a3b8" }, - { level: 120, name: "Mandalorian Initiate", emoji: "๐Ÿ›ก๏ธ", color: "#64748b" }, - { level: 121, name: "Foundling", emoji: "๐Ÿ‘ถ", color: "#cbd5e1" }, - { level: 122, name: "Beskar Bearer", emoji: "๐ŸŒ‘", color: "#475569" }, - { level: 123, name: "Clan Defender", emoji: "โš”๏ธ", color: "#1e293b" }, - { level: 124, name: "Jetpack Ace", emoji: "๐Ÿ”ฅ", color: "#f97316" }, - { level: 125, name: "Jedi Guardian", emoji: "๐Ÿ›ก๏ธ", color: "#22c55e" }, - - // --- IMPERIAL MIGHT: THE SHADOW (126-150) --- - { level: 126, name: "Stormtrooper Recruit", emoji: "โšช", color: "#ffffff" }, - { level: 127, name: "Scout Trooper", emoji: "๐Ÿ๏ธ", color: "#e2e8f0" }, - { level: 128, name: "TIE Pilot", emoji: "๐Ÿฆ‡", color: "#1f2937" }, - { level: 129, name: "Imperial Officer", emoji: "๐Ÿ’‚", color: "#334155" }, - { level: 130, name: "Millennium Captain", emoji: "๐ŸŒŒ", color: "#fbbf24" }, - { level: 131, name: "Sith Acolyte", emoji: "๐Ÿ’€", color: "#991b1b" }, - { level: 132, name: "Shadow Guard", emoji: "๐Ÿ‘ค", color: "#000000" }, - { level: 133, name: "Inquisitor Trainee", emoji: "๐Ÿ”ด", color: "#dc2626" }, - { level: 134, name: "Purge Trooper", emoji: "๐Ÿงจ", color: "#450a0a" }, - { level: 135, name: "Sith Stalker", emoji: "๐Ÿ•ต๏ธ", color: "#7f1d1d" }, - { level: 136, name: "Red Guard", emoji: "๐Ÿ›ก๏ธ", color: "#b91c1c" }, - { level: 137, name: "Dark Side Adept", emoji: "๐ŸŒ‘", color: "#450606" }, - { level: 138, name: "Sith Alchemist", emoji: "๐Ÿงช", color: "#6d28d9" }, - { level: 139, name: "Holocron Corruptor", emoji: "๐Ÿ”ป", color: "#be123c" }, - { level: 140, name: "Imperial Inquisitor", emoji: "๐Ÿคบ", color: "#991b1b" }, - { level: 141, name: "Grand Inquisitor", emoji: "๐Ÿ‘น", color: "#7f1d1d" }, - { level: 142, name: "Star Destroyer Commander", emoji: "๐Ÿšข", color: "#94a3b8" }, - { level: 143, name: "Super Star Destroyer Admin", emoji: "๐Ÿ“", color: "#64748b" }, - { level: 144, name: "Kyber Crystal Corruptor", emoji: "๐Ÿฉธ", color: "#f43f5e" }, - { level: 145, name: "Death Star Architect", emoji: "๐Ÿ›ฐ๏ธ", color: "#475569" }, - { level: 146, name: "Planetary Governor", emoji: "๐Ÿช", color: "#ca8a04" }, - { level: 147, name: "Imperial Advisor", emoji: "๐Ÿง ", color: "#a855f7" }, - { level: 148, name: "Sith Warrior", emoji: "โš”๏ธ", color: "#991b1b" }, - { level: 149, name: "Dark Disciple", emoji: "๐Ÿ”ฅ", color: "#dc2626" }, - { level: 150, name: "Sith Lord", emoji: "๐Ÿ˜ˆ", color: "#000000" }, - - // --- BOUNTY HUNTERS & MERCENARIES (151-175) --- - { level: 151, name: "Guild Member", emoji: "๐Ÿ“œ", color: "#8b5e3c" }, - { level: 152, name: "Tracker", emoji: "๐Ÿ‘ฃ", color: "#a8a29e" }, - { level: 153, name: "Sniper Specialist", emoji: "๐ŸŽฏ", color: "#ef4444" }, - { level: 154, name: "Thermal Detonator Expert", emoji: "๐Ÿ’ฃ", color: "#f97316" }, - { level: 155, name: "Bounty Hunter Prime", emoji: "๐ŸŽ–๏ธ", color: "#4ade80" }, - { level: 156, name: "Carbonite Freezer", emoji: "๐ŸงŠ", color: "#bae6fd" }, - { level: 157, name: "Underworld Kingpin", emoji: "๐Ÿ‘‘", color: "#7c3aed" }, - { level: 158, name: "Hutt Enforcer", emoji: "๐ŸŒ", color: "#65a30d" }, - { level: 159, name: "Crime Syndicate Boss", emoji: "๐Ÿ’ผ", color: "#1e1b4b" }, - { level: 160, name: "Grand Moff", emoji: "๐Ÿ…", color: "#334155" }, - { level: 161, name: "Imperial Regent", emoji: "๐Ÿ›๏ธ", color: "#94a3b8" }, - { level: 162, name: "Hand of Justice", emoji: "โš–๏ธ", color: "#facc15" }, - { level: 163, name: "Ancient Archivist", emoji: "๐Ÿ“š", color: "#d97706" }, - { level: 164, name: "Dathomir Witch", emoji: "๐Ÿ”ฎ", color: "#db2777" }, - { level: 165, name: "Grey Jedi", emoji: "โ˜ฏ๏ธ", color: "#64748b" }, - { level: 166, name: "Balance Seeker", emoji: "๐ŸŒ“", color: "#94a3b8" }, - { level: 167, name: "Jedi Master", emoji: "๐Ÿง˜", color: "#60a5fa" }, - { level: 168, name: "Council Member", emoji: "๐Ÿช‘", color: "#3b82f6" }, - { level: 169, name: "Temple Overseer", emoji: "๐Ÿ›๏ธ", color: "#2563eb" }, - { level: 170, name: "Jedi Sage", emoji: "๐Ÿ“œ", color: "#93c5fd" }, - { level: 171, name: "High Republic Hero", emoji: "๐ŸŒŸ", color: "#fbbf24" }, - { level: 172, name: "Living Force Vessel", emoji: "๐Ÿƒ", color: "#4ade80" }, - { level: 173, name: "Chosen One Initiate", emoji: "โšก", color: "#ffffff" }, - { level: 174, name: "Master of the Order", emoji: "๐Ÿ‘‘", color: "#1d4ed8" }, - { level: 175, name: "Jedi Grand Master", emoji: "๐Ÿง˜โ€โ™‚๏ธ", color: "#10b981" }, - - // --- THE FORCE SUPREME (176-200) --- - { level: 176, name: "Dark Side Entity", emoji: "๐ŸŒ‘", color: "#111827" }, - { level: 177, name: "Life Force Leecher", emoji: "๐Ÿฉธ", color: "#7f1d1d" }, - { level: 178, name: "Sith Emperor", emoji: "๐Ÿ‘‘", color: "#000000" }, - { level: 179, name: "World Eater", emoji: "๐ŸŒŽ", color: "#4c0519" }, - { level: 180, name: "Darth Vader's Wrath", emoji: "๐Ÿ‘บ", color: "#b91c1c" }, - { level: 181, name: "Unstoppable Force", emoji: "๐ŸŒช๏ธ", color: "#ffffff" }, - { level: 182, name: "Immovable Object", emoji: "๐Ÿ—ฟ", color: "#475569" }, - { level: 183, name: "Force Projection", emoji: "โœจ", color: "#7dd3fc" }, - { level: 184, name: "Voice of the Force", emoji: "๐Ÿ—ฃ๏ธ", color: "#e2e8f0" }, - { level: 185, name: "Emperor's Hand", emoji: "โšก", color: "#a855f7" }, - { level: 186, name: "Force Stormbringer", emoji: "๐ŸŒฉ๏ธ", color: "#38bdf8" }, - { level: 187, name: "Space-Time Weaver", emoji: "๐Ÿ•ธ๏ธ", color: "#c084fc" }, - { level: 188, name: "World Between Worlds Explorer", emoji: "๐ŸŒŒ", color: "#a5b4fc" }, - { level: 189, name: "Cosmic Sentinel", emoji: "๐Ÿ‘๏ธ", color: "#2dd4bf" }, - { level: 190, name: "Force Ghost", emoji: "๐Ÿ‘ป", color: "#bae6fd" }, - { level: 191, name: "Ethereal Guide", emoji: "๐Ÿ•ฏ๏ธ", color: "#ffffff" }, - { level: 192, name: "Midi-chlorian Master", emoji: "๐Ÿงฌ", color: "#4ade80" }, - { level: 193, name: "Force Nexus", emoji: "๐Ÿ”†", color: "#facc15" }, - { level: 194, name: "Ancient One", emoji: "โณ", color: "#d97706" }, - { level: 195, name: "The Son & The Daughter", emoji: "โ˜ฏ๏ธ", color: "#000000" }, - { level: 196, name: "Avatar of Mortis", emoji: "๐Ÿ›๏ธ", color: "#f8fafc" }, - { level: 197, name: "Bendu's Wisdom", emoji: "๐Ÿ‚", color: "#78350f" }, - { level: 198, name: "The Father", emoji: "โš–๏ธ", color: "#fbbf24" }, - { level: 199, name: "The Whills", emoji: "๐Ÿ‘๏ธ", color: "#5eead4" }, - { level: 200, name: "One With The Force", emoji: "๐ŸŒŒ", color: "#ffffff" } + // 0-10: Original Ranks + { level: 0, name: "Newbie", emoji: "๐Ÿฃ", color: "#94a3b8" }, + { level: 1, name: "Script Kid", emoji: "๐Ÿ›น", color: "#10b981" }, + { level: 2, name: "Code Breaker", emoji: "๐Ÿ•ต๏ธโ€โ™‚๏ธ", color: "#f59e0b" }, + { level: 3, name: "Void Walker", emoji: "๐ŸŒŒ", color: "#6366f1" }, + { level: 4, name: "Bug Hunter", emoji: "๐Ÿ›", color: "#84cc16" }, + { level: 5, name: "Data Miner", emoji: "๐Ÿ’Ž", color: "#06b6d4" }, + { level: 6, name: "Sys Admin", emoji: "๐Ÿ› ๏ธ", color: "#ec4899" }, + { level: 7, name: "Terminal Pro", emoji: "โŒจ๏ธ", color: "#7c3aed" }, + { level: 8, name: "Cloud Expert", emoji: "โ˜๏ธ", color: "#3b82f6" }, + { level: 9, name: "Full Stack", emoji: "๐Ÿฅž", color: "#f97316" }, + { level: 10, name: "Architect", emoji: "๐Ÿ‘‘", color: "#ef4444" }, + + // 11-30: Magic the Gathering (Creatures & Keywords) + { level: 11, name: "Llanowar Elf", emoji: "๐Ÿน", color: "#2d5a27" }, + { level: 12, name: "Scryer", emoji: "๐Ÿ”ฎ", color: "#1d4ed8" }, + { level: 13, name: "Trampler", emoji: "๐Ÿ˜", color: "#15803d" }, + { level: 14, name: "Flying Menace", emoji: "๐Ÿฆ‡", color: "#4a044e" }, + { level: 15, name: "Mana Leech", emoji: "๐Ÿ’ง", color: "#0ea5e9" }, + { level: 16, name: "Spellcounter", emoji: "๐Ÿšซ", color: "#2563eb" }, + { level: 17, name: "Goblin Guide", emoji: "๐Ÿ‘บ", color: "#dc2626" }, + { level: 18, name: "Serum Visionary", emoji: "๐Ÿงช", color: "#6366f1" }, + { level: 19, name: "Mythic Rare", emoji: "๐ŸŸ ", color: "#f97316" }, + { level: 20, name: "Planeswalker", emoji: "โœจ", color: "#fbbf24" }, + + // 21-40: Game of Thrones (Houses & Heroes) + { level: 21, name: "Night's Watch", emoji: "๐Ÿฆ…", color: "#1e293b" }, + { level: 22, name: "Wildling Scout", emoji: "โ„๏ธ", color: "#94a3b8" }, + { level: 23, name: "Ironborn", emoji: "โš“", color: "#475569" }, + { level: 24, name: "Dothraki Rider", emoji: "๐ŸŽ", color: "#b45309" }, + { level: 25, name: "Kingslayer", emoji: "๐Ÿ—ก๏ธ", color: "#facc15" }, + { level: 26, name: "Winterfell Warden", emoji: "๐Ÿบ", color: "#cbd5e1" }, + { level: 27, name: "Dragonstone Guard", emoji: "๐Ÿ‰", color: "#991b1b" }, + { level: 28, name: "Faceless Man", emoji: "๐ŸŽญ", color: "#4b5563" }, + { level: 29, name: "Hand of the King", emoji: "๐Ÿ–๏ธ", color: "#d97706" }, + { level: 30, name: "Iron Throne Heir", emoji: "โš”๏ธ", color: "#111827" }, + + // 31-50: Wheel of Time (The Tiers of Power) + { level: 31, name: "Two Rivers Archer", emoji: "๐Ÿน", color: "#166534" }, + { level: 32, name: "Gleeman", emoji: "๐ŸŽถ", color: "#be185d" }, + { level: 33, name: "Borderlander", emoji: "๐Ÿ›ก๏ธ", color: "#991b1b" }, + { level: 34, name: "Warders Bond", emoji: "๐Ÿ”—", color: "#1e293b" }, + { level: 35, name: "Aes Sedai Novice", emoji: "๐Ÿ•ฏ๏ธ", color: "#f8fafc" }, + { level: 36, name: "Accepted", emoji: "๐Ÿ’", color: "#e2e8f0" }, + { level: 37, name: "Aiel Dreamwalker", emoji: "๐Ÿœ๏ธ", color: "#d97706" }, + { level: 38, name: "Asha'man", emoji: "โšก", color: "#000000" }, + { level: 39, name: "Amyrlin Seat", emoji: "๐Ÿ“œ", color: "#ffffff" }, + { level: 40, name: "Ta'veren", emoji: "๐ŸŒ€", color: "#6366f1" }, + + // 41-60: Lord of the Rings (Fellowship & Foes) + { level: 41, name: "Hobbit Adventurer", emoji: "๐Ÿบ", color: "#15803d" }, + { level: 42, name: "Bree Strider", emoji: "๐Ÿ‘ข", color: "#451a03" }, + { level: 43, name: "Riddermark Lord", emoji: "๐Ÿ‡", color: "#166534" }, + { level: 44, name: "Gondor Soldier", emoji: "๐Ÿ›ก๏ธ", color: "#94a3b8" }, + { level: 45, name: "Uruk-hai Berserker", emoji: "โœ‹", color: "#450a0a" }, + { level: 46, name: "Elven Archer", emoji: "๐Ÿƒ", color: "#4ade80" }, + { level: 47, name: "Dwarf Warrior", emoji: "โ›๏ธ", color: "#78350f" }, + { level: 48, name: "Nazgรปl Rider", emoji: "๐ŸŽ", color: "#020617" }, + { level: 49, name: "Istari Pupil", emoji: "๐Ÿง™", color: "#3b82f6" }, + { level: 50, name: "Ring-bearer", emoji: "๐Ÿ’", color: "#fbbf24" }, + + // NEW LEVELS 51-60 + { level: 51, name: "White Wizard", emoji: "๐Ÿง™โ€โ™‚๏ธ", color: "#f8fafc" }, + { level: 52, name: "Silmaril Seeker", emoji: "๐Ÿ’Ž", color: "#7dd3fc" }, + { level: 53, name: "Dune Walker", emoji: "โณ", color: "#fcd34d" }, + { level: 54, name: "Shadowfax Rider", emoji: "๐ŸŽ", color: "#e2e8f0" }, + { level: 55, name: "Master of Coin", emoji: "๐Ÿช™", color: "#fbbf24" }, + { level: 56, name: "Kingsguard", emoji: "๐Ÿ›ก๏ธ", color: "#94a3b8" }, + { level: 57, name: "Valyrian Smith", emoji: "โš’๏ธ", color: "#475569" }, + { level: 58, name: "Night Watcher", emoji: "๐Ÿฆ‰", color: "#312e81" }, + { level: 59, name: "Obsidian Blade", emoji: "๐Ÿ—ก๏ธ", color: "#1e293b" }, + { level: 60, name: "Citadel Maester", emoji: "๐Ÿ“œ", color: "#8b5e3c" }, + + // 61-80: High Magic & Artifacts + { level: 61, name: "Mox Emerald", emoji: "๐Ÿ’š", color: "#10b981" }, + { level: 62, name: "Mox Sapphire", emoji: "๐Ÿ’™", color: "#3b82f6" }, + { level: 63, name: "Mox Ruby", emoji: "โค๏ธ", color: "#ef4444" }, + { level: 64, name: "Mox Jet", emoji: "๐Ÿ–ค", color: "#18181b" }, + { level: 65, name: "Mox Pearl", emoji: "๐Ÿค", color: "#f8fafc" }, + { level: 66, name: "Black Lotus", emoji: "๐ŸŒบ", color: "#000000" }, + { level: 67, name: "Balrog Slayer", emoji: "๐Ÿ”ฅ", color: "#f97316" }, + { level: 68, name: "Witch-king", emoji: "๐Ÿ‘‘", color: "#334155" }, + { level: 69, name: "Shelob's Kin", emoji: "๐Ÿ•ท๏ธ", color: "#0f172a" }, + { level: 70, name: "Dragon-friend", emoji: "๐Ÿฒ", color: "#dc2626" }, + + // NEW LEVELS 71-80 + { level: 71, name: "Neon Ghost", emoji: "๐Ÿ‘ป", color: "#22d3ee" }, + { level: 72, name: "Dragon's Greed", emoji: "๐Ÿช™", color: "#fbbf24" }, + { level: 73, name: "Mistborn", emoji: "๐ŸŒซ๏ธ", color: "#94a3b8" }, + { level: 74, name: "Cinder Soul", emoji: "๐Ÿ”ฅ", color: "#f87171" }, + { level: 75, name: "High Council", emoji: "๐Ÿ›๏ธ", color: "#6366f1" }, + { level: 76, name: "Valyrian Steel", emoji: "๐Ÿ—ก๏ธ", color: "#cbd5e1" }, + { level: 77, name: "Golden Snitch", emoji: "โœจ", color: "#facc15" }, + { level: 78, name: "Ether Weaver", emoji: "๐Ÿ•ธ๏ธ", color: "#a855f7" }, + { level: 79, name: "Star Forge", emoji: "๐Ÿ”จ", color: "#38bdf8" }, + { level: 80, name: "Mithril Guard", emoji: "๐Ÿ›ก๏ธ", color: "#e2e8f0" }, + + // 81-90: Wheel of Time (The Forsaken & Dragons) + { level: 81, name: "Lan Mandragoran", emoji: "๐Ÿ—ก๏ธ", color: "#1e293b" }, + { level: 82, name: "Moiraine Damodred", emoji: "๐Ÿ’ง", color: "#1d4ed8" }, + { level: 83, name: "Ishamael", emoji: "๐Ÿ‘๏ธ", color: "#450a0a" }, + { level: 84, name: "Callandor Wielder", emoji: "๐Ÿ’Ž", color: "#22d3ee" }, + { level: 85, name: "Lewes Therin", emoji: "โ˜€๏ธ", color: "#fde047" }, + { level: 86, name: "Dragon Reborn", emoji: "๐Ÿ‰", color: "#ef4444" }, + + // NEW LEVELS 87-90 + { level: 87, name: "Phoenix Down", emoji: "๐Ÿชถ", color: "#fb7185" }, + { level: 88, name: "Void Sentinel", emoji: "๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธ", color: "#4ade80" }, + { level: 89, name: "Elder Wand", emoji: "๐Ÿช„", color: "#94a3b8" }, + { level: 90, name: "Balrog's Whip", emoji: "๐Ÿ”ฅ", color: "#b91c1c" }, + + // 91-100: Cosmic Legends + { level: 91, name: "Sauron's Shadow", emoji: "๐Ÿ‘๏ธ", color: "#000000" }, + { level: 92, name: "Galadriel's Light", emoji: "๐ŸŒŸ", color: "#e2e8f0" }, + { level: 93, name: "Eldrazi Titan", emoji: "๐Ÿ™", color: "#a855f7" }, + { level: 94, name: "Tom Bombadil", emoji: "๐Ÿ„", color: "#fbbf24" }, + { level: 95, name: "Sauron Unleashed", emoji: "๐ŸŒ‹", color: "#7f1d1d" }, + { level: 96, name: "Saruman the White", emoji: "โœ‹", color: "#cbd5e1" }, + { level: 97, name: "Gandalf the Grey", emoji: "๐ŸŽ†", color: "#64748b" }, + { level: 98, name: "Gandalf the White", emoji: "๐Ÿง™โ€โ™‚๏ธ", color: "#ffffff" }, + { level: 99, name: "The Creator", emoji: "๐ŸŒŒ", color: "#6366f1" }, + { level: 100, name: "Eru Ilรบvatar", emoji: "โœจ", color: "#ffffff" }, + + { level: 101, name: "Padawan Learner", emoji: "๐Ÿง˜", color: "#60a5fa" }, + { level: 102, name: "Moisture Farmer", emoji: "๐Ÿšœ", color: "#d97706" }, + { level: 103, name: "Scrappy Scavenger", emoji: "๐Ÿ”ง", color: "#fcd34d" }, + { level: 104, name: "Cantina Regular", emoji: "๐Ÿน", color: "#9333ea" }, + { level: 105, name: "Holocron Finder", emoji: "๐ŸงŠ", color: "#38bdf8" }, + { level: 106, name: "Speeder Racer", emoji: "๐ŸŽ๏ธ", color: "#fb923c" }, + { level: 107, name: "Droid Mechanic", emoji: "๐Ÿค–", color: "#94a3b8" }, + { level: 108, name: "Kyber Seeker", emoji: "๐Ÿ’Ž", color: "#ffffff" }, + { level: 109, name: "Outer Rim Nomad", emoji: "๐Ÿœ๏ธ", color: "#a8a29e" }, + { level: 110, name: "Rebel Pilot", emoji: "๐Ÿš€", color: "#ef4444" }, + { level: 111, name: "Squadron Leader", emoji: "๐Ÿ“ก", color: "#f87171" }, + { level: 112, name: "Astromech Specialist", emoji: "๐Ÿ”Œ", color: "#3b82f6" }, + { level: 113, name: "Smuggler Associate", emoji: "๐Ÿ“ฆ", color: "#b45309" }, + { level: 114, name: "Sabacc Champion", emoji: "๐Ÿƒ", color: "#fbbf24" }, + { level: 115, name: "Jedi Knight", emoji: "โš”๏ธ", color: "#60a5fa" }, + { level: 116, name: "Lightsaber Smith", emoji: "๐Ÿ› ๏ธ", color: "#2dd4bf" }, + { level: 117, name: "Force Sensitive", emoji: "๐ŸŒ€", color: "#818cf8" }, + { level: 118, name: "Temple Guardian", emoji: "๐Ÿฐ", color: "#facc15" }, + { level: 119, name: "Wayseeker", emoji: "๐Ÿงญ", color: "#94a3b8" }, + { level: 120, name: "Mandalorian Initiate", emoji: "๐Ÿ›ก๏ธ", color: "#64748b" }, + { level: 121, name: "Foundling", emoji: "๐Ÿ‘ถ", color: "#cbd5e1" }, + { level: 122, name: "Beskar Bearer", emoji: "๐ŸŒ‘", color: "#475569" }, + { level: 123, name: "Clan Defender", emoji: "โš”๏ธ", color: "#1e293b" }, + { level: 124, name: "Jetpack Ace", emoji: "๐Ÿ”ฅ", color: "#f97316" }, + { level: 125, name: "Jedi Guardian", emoji: "๐Ÿ›ก๏ธ", color: "#22c55e" }, + + // --- IMPERIAL MIGHT: THE SHADOW (126-150) --- + { level: 126, name: "Stormtrooper Recruit", emoji: "โšช", color: "#ffffff" }, + { level: 127, name: "Scout Trooper", emoji: "๐Ÿ๏ธ", color: "#e2e8f0" }, + { level: 128, name: "TIE Pilot", emoji: "๐Ÿฆ‡", color: "#1f2937" }, + { level: 129, name: "Imperial Officer", emoji: "๐Ÿ’‚", color: "#334155" }, + { level: 130, name: "Millennium Captain", emoji: "๐ŸŒŒ", color: "#fbbf24" }, + { level: 131, name: "Sith Acolyte", emoji: "๐Ÿ’€", color: "#991b1b" }, + { level: 132, name: "Shadow Guard", emoji: "๐Ÿ‘ค", color: "#000000" }, + { level: 133, name: "Inquisitor Trainee", emoji: "๐Ÿ”ด", color: "#dc2626" }, + { level: 134, name: "Purge Trooper", emoji: "๐Ÿงจ", color: "#450a0a" }, + { level: 135, name: "Sith Stalker", emoji: "๐Ÿ•ต๏ธ", color: "#7f1d1d" }, + { level: 136, name: "Red Guard", emoji: "๐Ÿ›ก๏ธ", color: "#b91c1c" }, + { level: 137, name: "Dark Side Adept", emoji: "๐ŸŒ‘", color: "#450606" }, + { level: 138, name: "Sith Alchemist", emoji: "๐Ÿงช", color: "#6d28d9" }, + { level: 139, name: "Holocron Corruptor", emoji: "๐Ÿ”ป", color: "#be123c" }, + { level: 140, name: "Imperial Inquisitor", emoji: "๐Ÿคบ", color: "#991b1b" }, + { level: 141, name: "Grand Inquisitor", emoji: "๐Ÿ‘น", color: "#7f1d1d" }, + { + level: 142, + name: "Star Destroyer Commander", + emoji: "๐Ÿšข", + color: "#94a3b8", + }, + { + level: 143, + name: "Super Star Destroyer Admin", + emoji: "๐Ÿ“", + color: "#64748b", + }, + { + level: 144, + name: "Kyber Crystal Corruptor", + emoji: "๐Ÿฉธ", + color: "#f43f5e", + }, + { level: 145, name: "Death Star Architect", emoji: "๐Ÿ›ฐ๏ธ", color: "#475569" }, + { level: 146, name: "Planetary Governor", emoji: "๐Ÿช", color: "#ca8a04" }, + { level: 147, name: "Imperial Advisor", emoji: "๐Ÿง ", color: "#a855f7" }, + { level: 148, name: "Sith Warrior", emoji: "โš”๏ธ", color: "#991b1b" }, + { level: 149, name: "Dark Disciple", emoji: "๐Ÿ”ฅ", color: "#dc2626" }, + { level: 150, name: "Sith Lord", emoji: "๐Ÿ˜ˆ", color: "#000000" }, + + // --- BOUNTY HUNTERS & MERCENARIES (151-175) --- + { level: 151, name: "Guild Member", emoji: "๐Ÿ“œ", color: "#8b5e3c" }, + { level: 152, name: "Tracker", emoji: "๐Ÿ‘ฃ", color: "#a8a29e" }, + { level: 153, name: "Sniper Specialist", emoji: "๐ŸŽฏ", color: "#ef4444" }, + { + level: 154, + name: "Thermal Detonator Expert", + emoji: "๐Ÿ’ฃ", + color: "#f97316", + }, + { level: 155, name: "Bounty Hunter Prime", emoji: "๐ŸŽ–๏ธ", color: "#4ade80" }, + { level: 156, name: "Carbonite Freezer", emoji: "๐ŸงŠ", color: "#bae6fd" }, + { level: 157, name: "Underworld Kingpin", emoji: "๐Ÿ‘‘", color: "#7c3aed" }, + { level: 158, name: "Hutt Enforcer", emoji: "๐ŸŒ", color: "#65a30d" }, + { level: 159, name: "Crime Syndicate Boss", emoji: "๐Ÿ’ผ", color: "#1e1b4b" }, + { level: 160, name: "Grand Moff", emoji: "๐Ÿ…", color: "#334155" }, + { level: 161, name: "Imperial Regent", emoji: "๐Ÿ›๏ธ", color: "#94a3b8" }, + { level: 162, name: "Hand of Justice", emoji: "โš–๏ธ", color: "#facc15" }, + { level: 163, name: "Ancient Archivist", emoji: "๐Ÿ“š", color: "#d97706" }, + { level: 164, name: "Dathomir Witch", emoji: "๐Ÿ”ฎ", color: "#db2777" }, + { level: 165, name: "Grey Jedi", emoji: "โ˜ฏ๏ธ", color: "#64748b" }, + { level: 166, name: "Balance Seeker", emoji: "๐ŸŒ“", color: "#94a3b8" }, + { level: 167, name: "Jedi Master", emoji: "๐Ÿง˜", color: "#60a5fa" }, + { level: 168, name: "Council Member", emoji: "๐Ÿช‘", color: "#3b82f6" }, + { level: 169, name: "Temple Overseer", emoji: "๐Ÿ›๏ธ", color: "#2563eb" }, + { level: 170, name: "Jedi Sage", emoji: "๐Ÿ“œ", color: "#93c5fd" }, + { level: 171, name: "High Republic Hero", emoji: "๐ŸŒŸ", color: "#fbbf24" }, + { level: 172, name: "Living Force Vessel", emoji: "๐Ÿƒ", color: "#4ade80" }, + { level: 173, name: "Chosen One Initiate", emoji: "โšก", color: "#ffffff" }, + { level: 174, name: "Master of the Order", emoji: "๐Ÿ‘‘", color: "#1d4ed8" }, + { level: 175, name: "Jedi Grand Master", emoji: "๐Ÿง˜โ€โ™‚๏ธ", color: "#10b981" }, + + // --- THE FORCE SUPREME (176-200) --- + { level: 176, name: "Dark Side Entity", emoji: "๐ŸŒ‘", color: "#111827" }, + { level: 177, name: "Life Force Leecher", emoji: "๐Ÿฉธ", color: "#7f1d1d" }, + { level: 178, name: "Sith Emperor", emoji: "๐Ÿ‘‘", color: "#000000" }, + { level: 179, name: "World Eater", emoji: "๐ŸŒŽ", color: "#4c0519" }, + { level: 180, name: "Darth Vader's Wrath", emoji: "๐Ÿ‘บ", color: "#b91c1c" }, + { level: 181, name: "Unstoppable Force", emoji: "๐ŸŒช๏ธ", color: "#ffffff" }, + { level: 182, name: "Immovable Object", emoji: "๐Ÿ—ฟ", color: "#475569" }, + { level: 183, name: "Force Projection", emoji: "โœจ", color: "#7dd3fc" }, + { level: 184, name: "Voice of the Force", emoji: "๐Ÿ—ฃ๏ธ", color: "#e2e8f0" }, + { level: 185, name: "Emperor's Hand", emoji: "โšก", color: "#a855f7" }, + { level: 186, name: "Force Stormbringer", emoji: "๐ŸŒฉ๏ธ", color: "#38bdf8" }, + { level: 187, name: "Space-Time Weaver", emoji: "๐Ÿ•ธ๏ธ", color: "#c084fc" }, + { + level: 188, + name: "World Between Worlds Explorer", + emoji: "๐ŸŒŒ", + color: "#a5b4fc", + }, + { level: 189, name: "Cosmic Sentinel", emoji: "๐Ÿ‘๏ธ", color: "#2dd4bf" }, + { level: 190, name: "Force Ghost", emoji: "๐Ÿ‘ป", color: "#bae6fd" }, + { level: 191, name: "Ethereal Guide", emoji: "๐Ÿ•ฏ๏ธ", color: "#ffffff" }, + { level: 192, name: "Midi-chlorian Master", emoji: "๐Ÿงฌ", color: "#4ade80" }, + { level: 193, name: "Force Nexus", emoji: "๐Ÿ”†", color: "#facc15" }, + { level: 194, name: "Ancient One", emoji: "โณ", color: "#d97706" }, + { level: 195, name: "The Son & The Daughter", emoji: "โ˜ฏ๏ธ", color: "#000000" }, + { level: 196, name: "Avatar of Mortis", emoji: "๐Ÿ›๏ธ", color: "#f8fafc" }, + { level: 197, name: "Bendu's Wisdom", emoji: "๐Ÿ‚", color: "#78350f" }, + { level: 198, name: "The Father", emoji: "โš–๏ธ", color: "#fbbf24" }, + { level: 199, name: "The Whills", emoji: "๐Ÿ‘๏ธ", color: "#5eead4" }, + { level: 200, name: "One With The Force", emoji: "๐ŸŒŒ", color: "#ffffff" }, ]; const XP_PER_LEVEL = 45; // Load saved level or start at 0 -let currentLevel = Number(localStorage.getItem('userLevel')) || 0; +let currentLevel = Number(localStorage.getItem("userLevel")) || 0; // Load saved XP or start at 0 -let currentXP = parseInt(localStorage.getItem('userXP')) || 0; - -function getContrastYIQ(hexcolor){ - hexcolor = hexcolor.replace("#", ""); - var r = parseInt(hexcolor.substr(0,2),16); - var g = parseInt(hexcolor.substr(2,2),16); - var b = parseInt(hexcolor.substr(4,2),16); - var yiq = ((r*299)+(g*587)+(b*114))/1000; - return (yiq >= 128) ? 'black' : 'white'; +let currentXP = parseInt(localStorage.getItem("userXP")) || 0; + +function getContrastYIQ(hexcolor) { + hexcolor = hexcolor.replace("#", ""); + var r = parseInt(hexcolor.substr(0, 2), 16); + var g = parseInt(hexcolor.substr(2, 2), 16); + var b = parseInt(hexcolor.substr(4, 2), 16); + var yiq = (r * 299 + g * 587 + b * 114) / 1000; + return yiq >= 128 ? "black" : "white"; } - function isEggUnlocked(eggId) { - // Returns true if the ID exists in the array, false otherwise - return unlockedEggs.includes(eggId); + // Returns true if the ID exists in the array, false otherwise + return unlockedEggs.includes(eggId); } /** @@ -263,491 +286,522 @@ function isEggUnlocked(eggId) { let audioCtx; function initAudio() { - try { - if (!audioCtx) { - audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - } - if (audioCtx.state === 'suspended') { - audioCtx.resume(); - } - } catch (e) { - console.error("AudioContext failed to initialize:", e); + try { + if (!audioCtx) { + audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } + if (audioCtx.state === "suspended") { + audioCtx.resume(); + } + } catch (e) { + console.error("AudioContext failed to initialize:", e); + } } -window.addEventListener('click', initAudio, { once: true }); -window.addEventListener('keydown', initAudio, { once: true }); +window.addEventListener("click", initAudio, { once: true }); +window.addEventListener("keydown", initAudio, { once: true }); function playSound(type) { - initAudio(); - if (!audioCtx || audioCtx.state !== 'running') return; - - const osc = audioCtx.createOscillator(); - const gain = audioCtx.createGain(); - osc.connect(gain); - gain.connect(audioCtx.destination); - const now = audioCtx.currentTime; - - if (type === 'click') { - osc.type = 'sine'; - osc.frequency.setValueAtTime(880, now); - gain.gain.setValueAtTime(0.1, now); - gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1); - osc.start(now); - osc.stop(now + 0.1); - } - else if (type === 'levelUp') { - osc.type = 'square'; - osc.frequency.setValueAtTime(440, now); - osc.frequency.exponentialRampToValueAtTime(880, now + 0.4); - gain.gain.setValueAtTime(0.15, now); - gain.gain.exponentialRampToValueAtTime(0.01, now + 1.0); - osc.start(now); - osc.stop(now + 1.5); - } - else if (type === 'secret') { - osc.type = 'triangle'; - [523.25, 659.25, 783.99, 1046.50].forEach((freq, i) => { - const s = audioCtx.createOscillator(); - const g = audioCtx.createGain(); - s.connect(g); g.connect(audioCtx.destination); - s.frequency.setValueAtTime(freq, now + i * 0.3); - g.gain.setValueAtTime(0.07, now + i * 0.3); - g.gain.exponentialRampToValueAtTime(0.01, now + i * 0.3 + 0.3); - s.start(now + i * 0.3); - s.stop(now + i * 0.3 + 0.3); - }); - } - else if (type === 'restore') { - osc.type = 'sine'; - [220, 440, 880, 1760].forEach((freq, i) => { - const s = audioCtx.createOscillator(); - const g = audioCtx.createGain(); - s.connect(g); g.connect(audioCtx.destination); - s.frequency.setValueAtTime(freq, now + i * 0.05); - g.gain.setValueAtTime(0.1, now + i * 0.05); - g.gain.exponentialRampToValueAtTime(0.01, now + i * 0.05 + 0.1); - s.start(now + i * 0.05); - s.stop(now + i * 0.05 + 0.1); - }); - } + initAudio(); + if (!audioCtx || audioCtx.state !== "running") return; + + const osc = audioCtx.createOscillator(); + const gain = audioCtx.createGain(); + osc.connect(gain); + gain.connect(audioCtx.destination); + const now = audioCtx.currentTime; + + if (type === "click") { + osc.type = "sine"; + osc.frequency.setValueAtTime(880, now); + gain.gain.setValueAtTime(0.1, now); + gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1); + osc.start(now); + osc.stop(now + 0.1); + } else if (type === "levelUp") { + osc.type = "square"; + osc.frequency.setValueAtTime(440, now); + osc.frequency.exponentialRampToValueAtTime(880, now + 0.4); + gain.gain.setValueAtTime(0.15, now); + gain.gain.exponentialRampToValueAtTime(0.01, now + 1.0); + osc.start(now); + osc.stop(now + 1.5); + } else if (type === "secret") { + osc.type = "triangle"; + [523.25, 659.25, 783.99, 1046.5].forEach((freq, i) => { + const s = audioCtx.createOscillator(); + const g = audioCtx.createGain(); + s.connect(g); + g.connect(audioCtx.destination); + s.frequency.setValueAtTime(freq, now + i * 0.3); + g.gain.setValueAtTime(0.07, now + i * 0.3); + g.gain.exponentialRampToValueAtTime(0.01, now + i * 0.3 + 0.3); + s.start(now + i * 0.3); + s.stop(now + i * 0.3 + 0.3); + }); + } else if (type === "restore") { + osc.type = "sine"; + [220, 440, 880, 1760].forEach((freq, i) => { + const s = audioCtx.createOscillator(); + const g = audioCtx.createGain(); + s.connect(g); + g.connect(audioCtx.destination); + s.frequency.setValueAtTime(freq, now + i * 0.05); + g.gain.setValueAtTime(0.1, now + i * 0.05); + g.gain.exponentialRampToValueAtTime(0.01, now + i * 0.05 + 0.1); + s.start(now + i * 0.05); + s.stop(now + i * 0.05 + 0.1); + }); + } } - -let unlockedEggs = JSON.parse(localStorage.getItem('unlockedEggs')) || []; +let unlockedEggs = JSON.parse(localStorage.getItem("unlockedEggs")) || []; let surpriseClickCount = 0; let matrixActive = false; let destructInterval; function getRank(lvl) { - const numericLevel = Number(lvl) || 0; + const numericLevel = Number(lvl) || 0; - // IMPORTANT: .slice().reverse() creates a temporary reversed list - // so we find the HIGHEST level match first. - const rank = LEVELS.slice().reverse().find(r => numericLevel >= r.level); + // IMPORTANT: .slice().reverse() creates a temporary reversed list + // so we find the HIGHEST level match first. + const rank = LEVELS.slice() + .reverse() + .find((r) => numericLevel >= r.level); - if (!rank) { - console.warn("Rank not found, defaulting to Newbie"); - return LEVELS[0]; - } + if (!rank) { + console.warn("Rank not found, defaulting to Newbie"); + return LEVELS[0]; + } - return rank; + return rank; } +const consoleContainer = document.getElementById("matrix-console-container"); +const consoleOutput = document.getElementById("matrix-console-output"); -const consoleContainer = document.getElementById('matrix-console-container'); -const consoleOutput = document.getElementById('matrix-console-output'); - -const dragContainer = document.getElementById('matrix-console-container'); -const dragHeader = dragContainer.querySelector('.bg-green-500\\/10'); // Selects the header bar +const dragContainer = document.getElementById("matrix-console-container"); +const dragHeader = dragContainer.querySelector(".bg-green-500\\/10"); // Selects the header bar let isDragging = false; let offsetLeft = 0; let offsetTop = 0; -dragHeader.addEventListener('mousedown', (e) => { - // Prevent dragging when clicking the minimize/close buttons - if (e.target.tagName === 'BUTTON') return; +dragHeader.addEventListener("mousedown", (e) => { + // Prevent dragging when clicking the minimize/close buttons + if (e.target.tagName === "BUTTON") return; - isDragging = true; + isDragging = true; - // Calculate where the mouse is relative to the top-left of the console - const rect = dragContainer.getBoundingClientRect(); - offsetLeft = e.clientX - rect.left; - offsetTop = e.clientY - rect.top; + // Calculate where the mouse is relative to the top-left of the console + const rect = dragContainer.getBoundingClientRect(); + offsetLeft = e.clientX - rect.left; + offsetTop = e.clientY - rect.top; - // Change cursor to indicate moving - dragHeader.style.cursor = 'grabbing'; + // Change cursor to indicate moving + dragHeader.style.cursor = "grabbing"; }); -document.addEventListener('mousemove', (e) => { - if (!isDragging) return; +document.addEventListener("mousemove", (e) => { + if (!isDragging) return; - // Calculate new position - let x = e.clientX - offsetLeft; - let y = e.clientY - offsetTop; + // Calculate new position + let x = e.clientX - offsetLeft; + let y = e.clientY - offsetTop; - // Boundary Check (Optional: keeps it inside the screen) - x = Math.max(0, Math.min(x, window.innerWidth - dragContainer.offsetWidth)); - y = Math.max(0, Math.min(y, window.innerHeight - dragContainer.offsetHeight)); + // Boundary Check (Optional: keeps it inside the screen) + x = Math.max(0, Math.min(x, window.innerWidth - dragContainer.offsetWidth)); + y = Math.max(0, Math.min(y, window.innerHeight - dragContainer.offsetHeight)); - // Apply position and remove Tailwind's 'bottom' and 'right' so they don't fight the 'top'/'left' - dragContainer.style.bottom = 'auto'; - dragContainer.style.right = 'auto'; - dragContainer.style.left = `${x}px`; - dragContainer.style.top = `${y}px`; + // Apply position and remove Tailwind's 'bottom' and 'right' so they don't fight the 'top'/'left' + dragContainer.style.bottom = "auto"; + dragContainer.style.right = "auto"; + dragContainer.style.left = `${x}px`; + dragContainer.style.top = `${y}px`; }); -document.addEventListener('mouseup', () => { - isDragging = false; - dragHeader.style.cursor = 'grab'; +document.addEventListener("mouseup", () => { + isDragging = false; + dragHeader.style.cursor = "grab"; }); function minimizeConsole() { - // Toggles the height of the output area - if (consoleOutput.style.display === 'none') { - consoleOutput.style.display = 'block'; - consoleContainer.style.width = '20rem'; // w-80 - } else { - consoleOutput.style.display = 'none'; - consoleContainer.style.width = '150px'; // Compact view - } + // Toggles the height of the output area + if (consoleOutput.style.display === "none") { + consoleOutput.style.display = "block"; + consoleContainer.style.width = "20rem"; // w-80 + } else { + consoleOutput.style.display = "none"; + consoleContainer.style.width = "150px"; // Compact view + } } function maximizeConsole() { - // Toggles a full-screen-ish mode - consoleContainer.classList.toggle('console-maximized'); - - // Adjust height when maximized - if (consoleContainer.classList.contains('console-maximized')) { - consoleOutput.style.height = '70vh'; - consoleOutput.style.display = 'block'; - } else { - consoleOutput.style.height = '12rem'; // h-48 - } + // Toggles a full-screen-ish mode + consoleContainer.classList.toggle("console-maximized"); + + // Adjust height when maximized + if (consoleContainer.classList.contains("console-maximized")) { + consoleOutput.style.height = "70vh"; + consoleOutput.style.display = "block"; + } else { + consoleOutput.style.height = "12rem"; // h-48 + } } function closeConsole() { - const container = document.getElementById('matrix-console-container'); - const reopenBtn = document.getElementById('reopen-console-btn'); - - // Hide the console - container.style.opacity = '0'; - container.style.transform = 'translateY(20px)'; - - setTimeout(() => { - container.classList.add('hidden'); - // Show the small reopen button - if (reopenBtn) reopenBtn.classList.remove('hidden'); - }, 300); + const container = document.getElementById("matrix-console-container"); + const reopenBtn = document.getElementById("reopen-console-btn"); + + // Hide the console + container.style.opacity = "0"; + container.style.transform = "translateY(20px)"; + + setTimeout(() => { + container.classList.add("hidden"); + // Show the small reopen button + if (reopenBtn) reopenBtn.classList.remove("hidden"); + }, 300); } function reopenConsole() { - const container = document.getElementById('matrix-console-container'); - const reopenBtn = document.getElementById('reopen-console-btn'); + const container = document.getElementById("matrix-console-container"); + const reopenBtn = document.getElementById("reopen-console-btn"); - // Show the console - container.classList.remove('hidden'); + // Show the console + container.classList.remove("hidden"); - // Trigger reflow for animation - void container.offsetWidth; + // Trigger reflow for animation + void container.offsetWidth; - container.style.opacity = '1'; - container.style.transform = 'translateY(0)'; + container.style.opacity = "1"; + container.style.transform = "translateY(0)"; - // Hide the reopen button - if (reopenBtn) reopenBtn.classList.add('hidden'); + // Hide the reopen button + if (reopenBtn) reopenBtn.classList.add("hidden"); } - let isProcessingXP = false; // Ensure this is in the GLOBAL scope (not hidden inside another function) -window.createFloatingXP = function(e) { - // Prevent "spam" firing from high-speed mouse movement - if (isProcessingXP) return; - isProcessingXP = true; +window.createFloatingXP = function (e) { + // Prevent "spam" firing from high-speed mouse movement + if (isProcessingXP) return; + isProcessingXP = true; - // Release the lock after 50ms - setTimeout(() => { isProcessingXP = false; }, 50); + // Release the lock after 50ms + setTimeout(() => { + isProcessingXP = false; + }, 50); - // 1. Create the XP element - const popup = document.createElement('div'); + // 1. Create the XP element + const popup = document.createElement("div"); - // 2. Styling (Tailwind classes + Inline for positioning) - popup.className = 'fixed pointer-events-none z-[999] font-black text-sm tracking-tighter animate-xp-float'; - popup.innerText = '+1 XP'; + // 2. Styling (Tailwind classes + Inline for positioning) + popup.className = + "fixed pointer-events-none z-[999] font-black text-sm tracking-tighter animate-xp-float"; + popup.innerText = "+1 XP"; - // 3. Get current Rank color for the "Pop" - const rank = getRank(currentLevel); - popup.style.color = rank.color; + // 3. Get current Rank color for the "Pop" + const rank = getRank(currentLevel); + popup.style.color = rank.color; - // 4. Position at mouse (using clientX/Y for fixed positioning) - popup.style.left = `${e.clientX}px`; - popup.style.top = `${e.clientY}px`; + // 4. Position at mouse (using clientX/Y for fixed positioning) + popup.style.left = `${e.clientX}px`; + popup.style.top = `${e.clientY}px`; - document.body.appendChild(popup); + document.body.appendChild(popup); - // 5. Award XP and update that "Newbie" header - if (typeof addExperience === 'function') { - addExperience(1); - } + // 5. Award XP and update that "Newbie" header + if (typeof addExperience === "function") { + addExperience(1); + } - // 6. Cleanup - setTimeout(() => popup.remove(), 800); + // 6. Cleanup + setTimeout(() => popup.remove(), 800); }; // Re-attach listeners to your skill tags function attachSkillListeners() { - const skillTags = document.querySelectorAll('.skill-tag'); // Use your actual class name - skillTags.forEach(tag => { - // Use 'mouseenter' for a clean single-pop on hover - tag.addEventListener('mouseenter', createXPPopup); - }); + const skillTags = document.querySelectorAll(".skill-tag"); // Use your actual class name + skillTags.forEach((tag) => { + // Use 'mouseenter' for a clean single-pop on hover + tag.addEventListener("mouseenter", createXPPopup); + }); } function unlockEgg(eggId) { - if (!unlockedEggs.includes(eggId)) { - unlockedEggs.push(eggId); - localStorage.setItem('unlockedEggs', JSON.stringify(unlockedEggs)); - playSound('levelUp'); - showLevelUpNotification(unlockedEggs.length); - updateGameUI(); - } + if (!unlockedEggs.includes(eggId)) { + unlockedEggs.push(eggId); + localStorage.setItem("unlockedEggs", JSON.stringify(unlockedEggs)); + playSound("levelUp"); + showLevelUpNotification(unlockedEggs.length); + updateGameUI(); + } } function handleLevelClick() { - triggerSecretUnlock('badge_click'); + triggerSecretUnlock("badge_click"); } function showLevelUpNotification(input) { - // Determine if input is a rank object or a level number - let rank; - if (typeof input === 'object' && input !== null) { - rank = input; - } else { - rank = getRank(input); // Convert number to rank object - } + // Determine if input is a rank object or a level number + let rank; + if (typeof input === "object" && input !== null) { + rank = input; + } else { + rank = getRank(input); // Convert number to rank object + } - // Safety fallback to prevent the "undefined" crash - if (!rank) rank = LEVELS[0]; + // Safety fallback to prevent the "undefined" crash + if (!rank) rank = LEVELS[0]; - const notify = document.createElement('div'); - notify.className = "fixed top-24 left-1/2 -translate-x-1/2 z-[2000] px-8 py-4 bg-white dark:bg-slate-900 border-4 rounded-full shadow-2xl flex items-center gap-4 animate-bounce"; + const notify = document.createElement("div"); + notify.className = + "fixed top-24 left-1/2 -translate-x-1/2 z-[2000] px-8 py-4 bg-white dark:bg-slate-900 border-4 rounded-full shadow-2xl flex items-center gap-4 animate-bounce"; - // Now rank.color is guaranteed to exist - notify.style.borderColor = rank.color; + // Now rank.color is guaranteed to exist + notify.style.borderColor = rank.color; - notify.innerHTML = ` + notify.innerHTML = ` ${rank.emoji}

Rank Update

${rank.name}

`; - document.body.appendChild(notify); - setTimeout(() => { - notify.style.opacity = '0'; - setTimeout(() => notify.remove(), 500); - }, 4000); + document.body.appendChild(notify); + setTimeout(() => { + notify.style.opacity = "0"; + setTimeout(() => notify.remove(), 500); + }, 4000); } /** * 4. THEME SYSTEM */ function applyTheme(theme) { - const html = document.documentElement; - const heart = document.getElementById('footer-heart'); - localStorage.setItem('theme', theme); - - // Reset classes - html.classList.remove('dark'); - const props = ['--bg-page', '--bg-card', '--bg-footer', '--text-main', '--text-muted', '--border-color', '--accent', '--accent-light']; - props.forEach(p => html.style.removeProperty(p)); - - if (theme === 'dark') { - html.classList.add('dark'); - if (heart) heart.innerText = '๐Ÿ’œ'; - } - else if (theme === 'random') { - const h = Math.floor(Math.random() * 360); - // The "Secret Sauce": A second hue for accents (180 degrees away or +40) - const accentHue = (h + 160) % 360; - - // Backgrounds (Consistent and Dark) - html.style.setProperty('--bg-page', `hsl(${h}, 45%, 7%)`); - html.style.setProperty('--bg-card', `hsl(${h}, 35%, 12%)`); - html.style.setProperty('--bg-footer', `hsl(${h}, 40%, 5%)`); - - // Typography - html.style.setProperty('--text-main', `hsl(${h}, 10%, 98%)`); // Crisp White - html.style.setProperty('--text-muted', `hsl(${h}, 20%, 70%)`); // Readable Muted - - // The Pop Colors (The ones you were missing!) - html.style.setProperty('--accent', `hsl(${accentHue}, 90%, 65%)`); - html.style.setProperty('--accent-light', `hsl(${accentHue}, 90%, 40%)`); - html.style.setProperty('--border-color', `hsl(${h}, 30%, 20%)`); - - if (heart) { - const symbols = ['๐Ÿ’Ž', '๐ŸŒˆ', 'โœจ', '๐Ÿ”ฅ', '๐ŸŒ€', '๐Ÿงฌ']; - heart.innerText = symbols[Math.floor(Math.random() * symbols.length)]; - } - } - else { - if (heart) heart.innerText = 'โค๏ธ'; + const html = document.documentElement; + const heart = document.getElementById("footer-heart"); + localStorage.setItem("theme", theme); + + // Reset classes + html.classList.remove("dark"); + const props = [ + "--bg-page", + "--bg-card", + "--bg-footer", + "--text-main", + "--text-muted", + "--border-color", + "--accent", + "--accent-light", + ]; + props.forEach((p) => html.style.removeProperty(p)); + + if (theme === "dark") { + html.classList.add("dark"); + if (heart) heart.innerText = "๐Ÿ’œ"; + } else if (theme === "random") { + const h = Math.floor(Math.random() * 360); + // The "Secret Sauce": A second hue for accents (180 degrees away or +40) + const accentHue = (h + 160) % 360; + + // Backgrounds (Consistent and Dark) + html.style.setProperty("--bg-page", `hsl(${h}, 45%, 7%)`); + html.style.setProperty("--bg-card", `hsl(${h}, 35%, 12%)`); + html.style.setProperty("--bg-footer", `hsl(${h}, 40%, 5%)`); + + // Typography + html.style.setProperty("--text-main", `hsl(${h}, 10%, 98%)`); // Crisp White + html.style.setProperty("--text-muted", `hsl(${h}, 20%, 70%)`); // Readable Muted + + // The Pop Colors (The ones you were missing!) + html.style.setProperty("--accent", `hsl(${accentHue}, 90%, 65%)`); + html.style.setProperty("--accent-light", `hsl(${accentHue}, 90%, 40%)`); + html.style.setProperty("--border-color", `hsl(${h}, 30%, 20%)`); + + if (heart) { + const symbols = ["๐Ÿ’Ž", "๐ŸŒˆ", "โœจ", "๐Ÿ”ฅ", "๐ŸŒ€", "๐Ÿงฌ"]; + heart.innerText = symbols[Math.floor(Math.random() * symbols.length)]; } + } else { + if (heart) heart.innerText = "โค๏ธ"; + } - updateThemeIcon(theme); + updateThemeIcon(theme); } function toggleTheme() { - playSound('click'); - const current = localStorage.getItem('theme') || 'light'; - const next = current === 'light' ? 'dark' : (current === 'dark' ? 'random' : 'light'); - applyTheme(next); - - // Maintenance XP Trigger - addMaintenanceXP(); + playSound("click"); + const current = localStorage.getItem("theme") || "light"; + const next = + current === "light" ? "dark" : current === "dark" ? "random" : "light"; + applyTheme(next); + + // Maintenance XP Trigger + addMaintenanceXP(); } function updateThemeIcon(theme) { - const icon = document.getElementById('theme-icon'); - if (icon) icon.innerText = theme === 'light' ? '๐ŸŒ™' : (theme === 'dark' ? '๐ŸŽฒ' : 'โ˜€๏ธ'); + const icon = document.getElementById("theme-icon"); + if (icon) + icon.innerText = theme === "light" ? "๐ŸŒ™" : theme === "dark" ? "๐ŸŽฒ" : "โ˜€๏ธ"; } /** * 5. EASTER EGG LOGIC & TRIGGERS */ function triggerForceSurge() { - initAudio(); - addExperience(100); // This now handles UI updates, sounds, and bar filling + initAudio(); + addExperience(100); // This now handles UI updates, sounds, and bar filling } function triggerMagicXP() { - initAudio(); - addExperience(50); + initAudio(); + addExperience(50); } // Visual Effect for Level 101+ function triggerForceEffects(lvl) { - const badge = document.getElementById('level-badge'); - if (badge) { - badge.classList.add('force-glow'); - // Remove after 2 seconds unless it's a persistent rank - setTimeout(() => badge.classList.remove('force-glow'), 2000); - } + const badge = document.getElementById("level-badge"); + if (badge) { + badge.classList.add("force-glow"); + // Remove after 2 seconds unless it's a persistent rank + setTimeout(() => badge.classList.remove("force-glow"), 2000); + } } function triggerSecretUnlock(type) { - const eggId = `secret_${type}`; - - // 1. Check if this is a NEW discovery - const isNewUnlock = !unlockedEggs.includes(eggId); - - // 2. Trigger the Visual Effects (Always trigger these) - if (type === 'gravity') { - activateGravityEffect(); - } else if (type === 'matrix') { - initMatrix(); - } else if (type === 'konami') { - activateKonami(); - } - - // 3. Only process XP and Save if it's the first time - if (isNewUnlock) { - playSound('secret'); - // Update the array and save to localStorage - unlockedEggs.push(eggId); - localStorage.setItem('unlockedEggs', JSON.stringify(unlockedEggs)); - - // Assign XP based on difficulty - if (type === 'konami') { - addExperience(500); // Massive bonus for the long code - } else if (type === 'gravity' || type === 'matrix') { - addExperience(45); // 1 full level - } else { - addExperience(75); // 2 full levels - } - - console.log(`โœจ Secret Unlocked: ${eggId}`); + const eggId = `secret_${type}`; + + // 1. Check if this is a NEW discovery + const isNewUnlock = !unlockedEggs.includes(eggId); + + // 2. Trigger the Visual Effects (Always trigger these) + if (type === "gravity") { + activateGravityEffect(); + } else if (type === "matrix") { + initMatrix(); + } else if (type === "konami") { + activateKonami(); + } + + // 3. Only process XP and Save if it's the first time + if (isNewUnlock) { + playSound("secret"); + // Update the array and save to localStorage + unlockedEggs.push(eggId); + localStorage.setItem("unlockedEggs", JSON.stringify(unlockedEggs)); + + // Assign XP based on difficulty + if (type === "konami") { + addExperience(500); // Massive bonus for the long code + } else if (type === "gravity" || type === "matrix") { + addExperience(45); // 1 full level } else { - playSound('click'); - console.log(`Secret ${eggId} already discovered. No extra XP granted.`); + addExperience(75); // 2 full levels } + + console.log(`โœจ Secret Unlocked: ${eggId}`); + } else { + playSound("click"); + console.log(`Secret ${eggId} already discovered. No extra XP granted.`); + } } -const konamiCode = ['arrowup', 'arrowup', 'arrowdown', 'arrowdown', 'arrowleft', 'arrowright', 'arrowleft', 'arrowright', 'b', 'a']; +const konamiCode = [ + "arrowup", + "arrowup", + "arrowdown", + "arrowdown", + "arrowleft", + "arrowright", + "arrowleft", + "arrowright", + "b", + "a", +]; let konamiPosition = 0; -window.addEventListener('keydown', (e) => { - const key = e.key.toLowerCase(); - if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; - - if (key === 'd') { - e.preventDefault(); +window.addEventListener("keydown", (e) => { + const key = e.key.toLowerCase(); + if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return; - const systemDash = document.getElementById('dev-tools'); // Adjust ID as needed - const isOpening = systemDash.classList.contains('hidden'); + if (key === "d") { + e.preventDefault(); - localStorage.setItem('devToolsVisible', !isOpening); - playSound(isOpening ? 'secret' : 'click'); + const systemDash = document.getElementById("dev-tools"); // Adjust ID as needed + const isOpening = systemDash.classList.contains("hidden"); - if (isOpening) { + localStorage.setItem("devToolsVisible", !isOpening); + playSound(isOpening ? "secret" : "click"); - document.getElementById('matrix-console-container').classList.add('hidden'); - systemDash.classList.remove('hidden'); + if (isOpening) { + document + .getElementById("matrix-console-container") + .classList.add("hidden"); + systemDash.classList.remove("hidden"); - // Wait 100-150ms for the animation/display to settle - setTimeout(() => { - // Find the first interactive element (button or link) - const firstControl = systemDash.querySelector('button, a, input'); - if (firstControl) { - firstControl.focus(); - } - }, 150); - } else { - systemDash.classList.add('hidden'); - document.body.focus(); // Return focus to the page + // Wait 100-150ms for the animation/display to settle + setTimeout(() => { + // Find the first interactive element (button or link) + const firstControl = systemDash.querySelector("button, a, input"); + if (firstControl) { + firstControl.focus(); } + }, 150); + } else { + systemDash.classList.add("hidden"); + document.body.focus(); // Return focus to the page } + } - if (key === konamiCode[konamiPosition]) { - konamiPosition++; - if (konamiPosition === konamiCode.length) { - triggerSecretUnlock('konami'); - konamiPosition = 0; - } - } else { - konamiPosition = (key === 'arrowup') ? 1 : 0; + if (key === konamiCode[konamiPosition]) { + konamiPosition++; + if (konamiPosition === konamiCode.length) { + triggerSecretUnlock("konami"); + konamiPosition = 0; } + } else { + konamiPosition = key === "arrowup" ? 1 : 0; + } }); function activateKonami() { - document.documentElement.classList.add('konami-roll'); - setTimeout(() => document.documentElement.classList.remove('konami-roll'), 2000); + document.documentElement.classList.add("konami-roll"); + setTimeout( + () => document.documentElement.classList.remove("konami-roll"), + 2000, + ); } function activateGravityEffect() { + document.body.classList.add("glitch-shake"); - document.body.classList.add('glitch-shake'); + setTimeout(() => { + document.body.classList.remove("glitch-shake"); + const targets = document.querySelectorAll( + ".user-card, header, footer, main, h1, #game-stats", + ); + targets.forEach((el) => { + const dist = window.innerHeight + 1000; + el.style.transition = `transform ${ + 1 + Math.random() + }s ease-in, opacity 1s`; + el.style.transform = `translateY(${dist}px) rotate(${ + Math.random() * 60 - 30 + }deg)`; + el.style.opacity = "0"; + el.style.pointerEvents = "none"; + }); setTimeout(() => { - document.body.classList.remove('glitch-shake'); - const targets = document.querySelectorAll('.user-card, header, footer, main, h1, #game-stats'); - targets.forEach(el => { - const dist = window.innerHeight + 1000; - el.style.transition = `transform ${1 + Math.random()}s ease-in, opacity 1s`; - el.style.transform = `translateY(${dist}px) rotate(${Math.random() * 60 - 30}deg)`; - el.style.opacity = "0"; - el.style.pointerEvents = "none"; - }); - - setTimeout(() => { - if (!document.getElementById('repair-btn')) { - const btn = document.createElement('button'); - btn.id = 'repair-btn'; - btn.innerHTML = "REPAIR CORE SYSTEM"; - btn.style.cssText = ` + if (!document.getElementById("repair-btn")) { + const btn = document.createElement("button"); + btn.id = "repair-btn"; + btn.innerHTML = "REPAIR CORE SYSTEM"; + btn.style.cssText = ` position: fixed !important; top: 50% !important; left: 50% !important; @@ -762,296 +816,310 @@ function activateGravityEffect() { cursor: pointer; box-shadow: 0 0 50px rgba(37, 99, 235, 0.8); `; - btn.onclick = () => { - playSound('restore'); - btn.innerHTML = "SYSTEM RESTORED"; - btn.style.pointerEvents = "none"; - setTimeout(() => window.location.reload(), 1000); - }; - document.body.appendChild(btn); - } - }, 1500); - }, 500); + btn.onclick = () => { + playSound("restore"); + btn.innerHTML = "SYSTEM RESTORED"; + btn.style.pointerEvents = "none"; + setTimeout(() => window.location.reload(), 1000); + }; + document.body.appendChild(btn); + } + }, 1500); + }, 500); } /** * 6. MATRIX ENGINE */ function initMatrix() { - matrixActive = true; - const overlay = document.getElementById('matrix-overlay'); - const canvas = document.getElementById('matrix-canvas'); - if (!overlay || !canvas) return; - - overlay.classList.remove('hidden'); - overlay.style.display = 'block'; - - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - const ctx = canvas.getContext('2d'); - const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ใ‚ขใ‚กใ‚ซใ‚ตใ‚ฟใƒŠใƒใƒžใƒคใƒฃ'; - const fontSize = 16; - const columns = Math.floor(canvas.width / fontSize); - const rainDrops = Array.from({ length: columns }).fill(1); - - const render = () => { - if (!matrixActive) return; - ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = '#0F0'; - ctx.font = fontSize + 'px monospace'; - - for (let i = 0; i < rainDrops.length; i++) { - const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length)); - ctx.fillText(text, i * fontSize, rainDrops[i] * fontSize); - if (rainDrops[i] * fontSize > canvas.height && Math.random() > 0.975) rainDrops[i] = 0; - rainDrops[i]++; - } - requestAnimationFrame(render); - }; - render(); - window.addEventListener('keydown', handleMatrixEsc); + matrixActive = true; + const overlay = document.getElementById("matrix-overlay"); + const canvas = document.getElementById("matrix-canvas"); + if (!overlay || !canvas) return; + + overlay.classList.remove("hidden"); + overlay.style.display = "block"; + + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + const ctx = canvas.getContext("2d"); + const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ใ‚ขใ‚กใ‚ซใ‚ตใ‚ฟใƒŠใƒใƒžใƒคใƒฃ"; + const fontSize = 16; + const columns = Math.floor(canvas.width / fontSize); + const rainDrops = Array.from({ length: columns }).fill(1); + + const render = () => { + if (!matrixActive) return; + ctx.fillStyle = "rgba(0, 0, 0, 0.05)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "#0F0"; + ctx.font = fontSize + "px monospace"; + + for (let i = 0; i < rainDrops.length; i++) { + const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length)); + ctx.fillText(text, i * fontSize, rainDrops[i] * fontSize); + if (rainDrops[i] * fontSize > canvas.height && Math.random() > 0.975) + rainDrops[i] = 0; + rainDrops[i]++; + } + requestAnimationFrame(render); + }; + render(); + window.addEventListener("keydown", handleMatrixEsc); } -function handleMatrixEsc(e) { if (e.key === 'Escape') closeMatrix(); } +function handleMatrixEsc(e) { + if (e.key === "Escape") closeMatrix(); +} function closeMatrix() { - matrixActive = false; - const overlay = document.getElementById('matrix-overlay'); - if (overlay) { overlay.classList.add('hidden'); overlay.style.display = 'none'; } - window.removeEventListener('keydown', handleMatrixEsc); + matrixActive = false; + const overlay = document.getElementById("matrix-overlay"); + if (overlay) { + overlay.classList.add("hidden"); + overlay.style.display = "none"; + } + window.removeEventListener("keydown", handleMatrixEsc); } - let hasTriggeredFirstLevel = false; // Prevents the sound from spamming every click function triggerBadgeLevelUp() { - const badge = document.getElementById('level-badge'); + const badge = document.getElementById("level-badge"); - // 1. Visual Pop Animation - if (badge) { - badge.classList.remove('animate-badge-pop'); - void badge.offsetWidth; // Force reflow to restart animation - badge.classList.add('animate-badge-pop'); - } + // 1. Visual Pop Animation + if (badge) { + badge.classList.remove("animate-badge-pop"); + void badge.offsetWidth; // Force reflow to restart animation + badge.classList.add("animate-badge-pop"); + } - // 2. Secret Sound & Level Logic - if (!hasTriggeredFirstLevel) { - // Play your secret sound - playSound('secret'); + // 2. Secret Sound & Level Logic + if (!hasTriggeredFirstLevel) { + // Play your secret sound + playSound("secret"); - // Force a level up for the "first time" experience - addExperience(45); // Assuming 45 XP = 1 Level + // Force a level up for the "first time" experience + addExperience(45); // Assuming 45 XP = 1 Level - hasTriggeredFirstLevel = true; + hasTriggeredFirstLevel = true; - // Push a special "Easter Egg" message to the Matrix Console - if (typeof matrixConsoleLog === 'function') { - matrixConsoleLog(currentLevel); - } + // Push a special "Easter Egg" message to the Matrix Console + if (typeof matrixConsoleLog === "function") { + matrixConsoleLog(currentLevel); } + } } // Attach to the badge click -document.getElementById('level-badge').addEventListener('click', triggerBadgeLevelUp); - - +document + .getElementById("level-badge") + .addEventListener("click", triggerBadgeLevelUp); /** * 7. SELF DESTRUCT ENGINE */ -window.startSelfDestruct = function() { - const btn = document.getElementById('self-destruct-btn'); - const devPanel = document.getElementById('dev-tools'); - - if (destructInterval) return; +window.startSelfDestruct = function () { + const btn = document.getElementById("self-destruct-btn"); + const devPanel = document.getElementById("dev-tools"); - initAudio(); + if (destructInterval) return; - // Move to HTML root to ignore scroll position and Body transforms - document.documentElement.appendChild(devPanel); - devPanel.setAttribute('data-lock', 'true'); - devPanel.classList.remove('hidden'); + initAudio(); - btn.classList.add('is-destructing'); + // Move to HTML root to ignore scroll position and Body transforms + document.documentElement.appendChild(devPanel); + devPanel.setAttribute("data-lock", "true"); + devPanel.classList.remove("hidden"); - let timeLeft = 10; + btn.classList.add("is-destructing"); - destructInterval = setInterval(() => { - timeLeft--; + let timeLeft = 10; - // Re-locate elements in the new DOM position - const timerDisplay = document.getElementById('destruct-timer'); - const progressBar = document.getElementById('destruct-bar'); - const statusText = document.getElementById('destruct-text'); + destructInterval = setInterval(() => { + timeLeft--; - if (timerDisplay) timerDisplay.innerText = `${timeLeft}s`; + // Re-locate elements in the new DOM position + const timerDisplay = document.getElementById("destruct-timer"); + const progressBar = document.getElementById("destruct-bar"); + const statusText = document.getElementById("destruct-text"); - if (progressBar) { - const percent = ((10 - timeLeft) / 10) * 100; - progressBar.style.width = `${percent}%`; + if (timerDisplay) timerDisplay.innerText = `${timeLeft}s`; - // Color Logic - if (timeLeft > 5) progressBar.style.backgroundColor = "#22c55e"; - else if (timeLeft > 2) progressBar.style.backgroundColor = "#eab308"; - else progressBar.style.backgroundColor = "#ef4444"; - } + if (progressBar) { + const percent = ((10 - timeLeft) / 10) * 100; + progressBar.style.width = `${percent}%`; - // Rising Audio Frequency - if (audioCtx) { - const osc = audioCtx.createOscillator(); - const g = audioCtx.createGain(); - osc.connect(g); g.connect(audioCtx.destination); - osc.frequency.setValueAtTime(400 + (10 - timeLeft) * 80, audioCtx.currentTime); - g.gain.setValueAtTime(0.1, audioCtx.currentTime); - g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1); - osc.start(); osc.stop(audioCtx.currentTime + 0.1); - } - - // CRITICAL STATE: 3 Seconds Left - if (timeLeft <= 3) { - document.body.classList.add('glitch-shake'); - // Flash the console background red - devPanel.style.backgroundColor = (timeLeft % 2 === 0) ? "rgba(239, 68, 68, 0.9)" : "rgba(15, 23, 42, 0.95)"; - devPanel.style.borderColor = "#ffffff"; - if (statusText) statusText.innerText = "OVERHEAT_CRITICAL"; - } - - if (timeLeft <= 0) { - clearInterval(destructInterval); - destructInterval = null; - if (timerDisplay) timerDisplay.innerText = "0s"; - - // Clean up and trigger the fall - devPanel.style.backgroundColor = "rgba(15, 23, 42, 0.95)"; - triggerSecretUnlock('gravity'); - } - }, 1000); -} - -function scrollToRandomUser() { - playSound('click'); + // Color Logic + if (timeLeft > 5) progressBar.style.backgroundColor = "#22c55e"; + else if (timeLeft > 2) progressBar.style.backgroundColor = "#eab308"; + else progressBar.style.backgroundColor = "#ef4444"; + } - surpriseClickCount++; - if (surpriseClickCount >= 5) { - surpriseClickCount = 0; - triggerSecretUnlock('matrix'); - return; + // Rising Audio Frequency + if (audioCtx) { + const osc = audioCtx.createOscillator(); + const g = audioCtx.createGain(); + osc.connect(g); + g.connect(audioCtx.destination); + osc.frequency.setValueAtTime( + 400 + (10 - timeLeft) * 80, + audioCtx.currentTime, + ); + g.gain.setValueAtTime(0.1, audioCtx.currentTime); + g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1); + osc.start(); + osc.stop(audioCtx.currentTime + 0.1); } - const cards = document.querySelectorAll('.user-card'); - if (cards.length === 0) return; + // CRITICAL STATE: 3 Seconds Left + if (timeLeft <= 3) { + document.body.classList.add("glitch-shake"); + // Flash the console background red + devPanel.style.backgroundColor = + timeLeft % 2 === 0 + ? "rgba(239, 68, 68, 0.9)" + : "rgba(15, 23, 42, 0.95)"; + devPanel.style.borderColor = "#ffffff"; + if (statusText) statusText.innerText = "OVERHEAT_CRITICAL"; + } - // Clean up previous selection - cards.forEach(c => { - c.classList.remove('selected-fancy'); - const oldTrace = c.querySelector('.border-trace'); - if (oldTrace) oldTrace.remove(); - }); + if (timeLeft <= 0) { + clearInterval(destructInterval); + destructInterval = null; + if (timerDisplay) timerDisplay.innerText = "0s"; - const randomCard = cards[Math.floor(Math.random() * cards.length)]; - randomCard.scrollIntoView({ behavior: 'smooth', block: 'center' }); + // Clean up and trigger the fall + devPanel.style.backgroundColor = "rgba(15, 23, 42, 0.95)"; + triggerSecretUnlock("gravity"); + } + }, 1000); +}; +function scrollToRandomUser() { + playSound("click"); + + surpriseClickCount++; + if (surpriseClickCount >= 5) { + surpriseClickCount = 0; + triggerSecretUnlock("matrix"); + return; + } + + const cards = document.querySelectorAll(".user-card"); + if (cards.length === 0) return; + + // Clean up previous selection + cards.forEach((c) => { + c.classList.remove("selected-fancy"); + const oldTrace = c.querySelector(".border-trace"); + if (oldTrace) oldTrace.remove(); + }); + + const randomCard = cards[Math.floor(Math.random() * cards.length)]; + randomCard.scrollIntoView({ behavior: "smooth", block: "center" }); + + setTimeout(() => { + playSound("levelUp"); + randomCard.classList.add("selected-fancy"); + + // Inject the Tracing SVG + const svgNamespace = "http://www.w3.org/2000/svg"; + const svg = document.createElementNS(svgNamespace, "svg"); + const rect = document.createElementNS(svgNamespace, "rect"); + + svg.setAttribute("class", "border-trace"); + rect.setAttribute("fill", "none"); + rect.setAttribute("width", "100%"); + rect.setAttribute("height", "100%"); + + svg.appendChild(rect); + randomCard.appendChild(svg); + + // Remove trace and fancy class after the 7.5s animation ends setTimeout(() => { - playSound('levelUp'); - randomCard.classList.add('selected-fancy'); - - // Inject the Tracing SVG - const svgNamespace = "http://www.w3.org/2000/svg"; - const svg = document.createElementNS(svgNamespace, "svg"); - const rect = document.createElementNS(svgNamespace, "rect"); - - svg.setAttribute("class", "border-trace"); - rect.setAttribute("fill", "none"); - rect.setAttribute("width", "100%"); - rect.setAttribute("height", "100%"); - - svg.appendChild(rect); - randomCard.appendChild(svg); - - // Remove trace and fancy class after the 7.5s animation ends - setTimeout(() => { - randomCard.classList.remove('selected-fancy'); - svg.remove(); - }, 7500); - }, 400); + randomCard.classList.remove("selected-fancy"); + svg.remove(); + }, 7500); + }, 400); } /** * UTILITY: SCREENSHOT MODE */ -window.toggleScreenshotMode = function() { - const devPanel = document.getElementById('dev-tools'); - const header = document.querySelector('header'); - const footer = document.querySelector('footer'); - const gameStats = document.getElementById('game-stats'); - - // Hide everything - [devPanel, header, footer, gameStats].forEach(el => { - if(el) el.style.opacity = '0'; - if(el) el.style.pointerEvents = 'none'; +window.toggleScreenshotMode = function () { + const devPanel = document.getElementById("dev-tools"); + const header = document.querySelector("header"); + const footer = document.querySelector("footer"); + const gameStats = document.getElementById("game-stats"); + + // Hide everything + [devPanel, header, footer, gameStats].forEach((el) => { + if (el) el.style.opacity = "0"; + if (el) el.style.pointerEvents = "none"; + }); + + // Show a tiny notification that it's active + const toast = document.createElement("div"); + toast.style.cssText = + "position:fixed; bottom:20px; left:50%; transform:translateX(-50%); color:var(--text-muted); font-family:monospace; font-size:10px; z-index:9999;"; + toast.innerText = "SCREENSHOT MODE ACTIVE - RESTORING IN 5S"; + document.body.appendChild(toast); + + setTimeout(() => { + [devPanel, header, footer, gameStats].forEach((el) => { + if (el) el.style.opacity = "1"; + if (el) el.style.pointerEvents = "auto"; }); - - // Show a tiny notification that it's active - const toast = document.createElement('div'); - toast.style.cssText = "position:fixed; bottom:20px; left:50%; transform:translateX(-50%); color:var(--text-muted); font-family:monospace; font-size:10px; z-index:9999;"; - toast.innerText = "SCREENSHOT MODE ACTIVE - RESTORING IN 5S"; - document.body.appendChild(toast); - - setTimeout(() => { - [devPanel, header, footer, gameStats].forEach(el => { - if(el) el.style.opacity = '1'; - if(el) el.style.pointerEvents = 'auto'; - }); - toast.remove(); - }, 5000); + toast.remove(); + }, 5000); }; /** * 8. INITIALIZATION */ -document.addEventListener('DOMContentLoaded', () => { - const devToolsVisible = localStorage.getItem('devToolsVisible') === 'true'; - const devPanel = document.getElementById('dev-tools'); - // Add this to your initialization script - let skillHoverCount = 0; - - if (devToolsVisible && devPanel) { - devPanel.classList.remove('hidden'); - } - - applyTheme(localStorage.getItem('theme') || 'light'); - updateGameUI(); +document.addEventListener("DOMContentLoaded", () => { + const devToolsVisible = localStorage.getItem("devToolsVisible") === "true"; + const devPanel = document.getElementById("dev-tools"); + // Add this to your initialization script + let skillHoverCount = 0; + + if (devToolsVisible && devPanel) { + devPanel.classList.remove("hidden"); + } + + applyTheme(localStorage.getItem("theme") || "light"); + updateGameUI(); }); /** * 9. ENHANCED XP & SKILL MINING SYSTEM */ function renderXP(value) { - const pb = document.getElementById('level-progress'); - if (!pb) return; + const pb = document.getElementById("level-progress"); + if (!pb) return; - // 1. Ensure 'value' is a clean number - const currentXPNum = Number(value) || 0; + // 1. Ensure 'value' is a clean number + const currentXPNum = Number(value) || 0; - // 2. Calculate percentage (current / 45 * 100) - const percentage = Math.min((currentXPNum / 45) * 100, 100); + // 2. Calculate percentage (current / 45 * 100) + const percentage = Math.min((currentXPNum / 45) * 100, 100); - // 3. Apply to style - pb.style.width = `${percentage}%`; + // 3. Apply to style + pb.style.width = `${percentage}%`; - // Debugging: uncomment this to see the math in your console - // console.log(`XP: ${currentXPNum}, Percent: ${percentage}%`); + // Debugging: uncomment this to see the math in your console + // console.log(`XP: ${currentXPNum}, Percent: ${percentage}%`); } function showLevelUpToast(rank) { - // 1. Create the container - const toast = document.createElement('div'); - toast.className = 'level-up-toast'; + // 1. Create the container + const toast = document.createElement("div"); + toast.className = "level-up-toast"; - // 2. Build the inner content - // We use the rank color for the name and emoji to make it feel custom - toast.innerHTML = ` + // 2. Build the inner content + // We use the rank color for the name and emoji to make it feel custom + toast.innerHTML = `
${rank.emoji}
@@ -1061,208 +1129,215 @@ function showLevelUpToast(rank) {
`; - document.body.appendChild(toast); + document.body.appendChild(toast); - // 3. Auto-remove after animation - setTimeout(() => { - toast.classList.add('fade-out'); - setTimeout(() => toast.remove(), 500); - }, 2500); + // 3. Auto-remove after animation + setTimeout(() => { + toast.classList.add("fade-out"); + setTimeout(() => toast.remove(), 500); + }, 2500); } - function matrixConsoleLog(level) { - const rank = getRank(level); - - // This looks awesome in the F12 Dev Console - console.log( - `%c [SYSTEM] %c LEVEL UP: %c ${rank.name.toUpperCase()} %c [LVL ${level}] `, - "color: #10b981; font-weight: bold; background: #064e3b; padding: 2px;", - "color: #ffffff; background: #1e293b; padding: 2px;", - `color: ${rank.color}; font-weight: 900; background: #1e293b; padding: 2px;`, - "color: #94a3b8; background: #1e293b; padding: 2px;" - ); - - // 3. If you have an on-screen Matrix Console element, push there too: - const matrixConsole = document.getElementById('matrix-console-output'); - if (matrixConsole) { - const line = document.createElement('p'); - line.className = 'matrix-line text-xs font-mono mb-1'; - line.innerHTML = `>> Rank Updated: ${rank.name}`; - matrixConsole.appendChild(line); - // Auto-scroll to bottom - matrixConsole.scrollTop = matrixConsole.scrollHeight; - } + const rank = getRank(level); + + // This looks awesome in the F12 Dev Console + console.log( + `%c [SYSTEM] %c LEVEL UP: %c ${rank.name.toUpperCase()} %c [LVL ${level}] `, + "color: #10b981; font-weight: bold; background: #064e3b; padding: 2px;", + "color: #ffffff; background: #1e293b; padding: 2px;", + `color: ${rank.color}; font-weight: 900; background: #1e293b; padding: 2px;`, + "color: #94a3b8; background: #1e293b; padding: 2px;", + ); + + // 3. If you have an on-screen Matrix Console element, push there too: + const matrixConsole = document.getElementById("matrix-console-output"); + if (matrixConsole) { + const line = document.createElement("p"); + line.className = "matrix-line text-xs font-mono mb-1"; + line.innerHTML = `>> Rank Updated: ${rank.name}`; + matrixConsole.appendChild(line); + // Auto-scroll to bottom + matrixConsole.scrollTop = matrixConsole.scrollHeight; + } } -document.addEventListener('keydown', (e) => { - // Check if user pressed 'L' (for Log) and isn't typing in an input field - if (e.key.toLowerCase() === 'l' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') { - const container = document.getElementById('matrix-console-container'); - if (container.classList.contains('hidden')) { - reopenConsole(); - } else { - closeConsole(); - } +document.addEventListener("keydown", (e) => { + // Check if user pressed 'L' (for Log) and isn't typing in an input field + if ( + e.key.toLowerCase() === "l" && + e.target.tagName !== "INPUT" && + e.target.tagName !== "TEXTAREA" + ) { + const container = document.getElementById("matrix-console-container"); + if (container.classList.contains("hidden")) { + reopenConsole(); + } else { + closeConsole(); } + } }); async function addExperience(amount) { - // 1. Force strict numeric types to prevent "1" + "1" = "11" - let xpToAdd = Number(amount) || 0; - currentXP = Number(currentXP) || 0; - currentLevel = Number(currentLevel) || 0; - const XP_THRESHOLD = 45; - - // 2. Add the new XP - currentXP += xpToAdd; - - // 3. Process Level Ups one by one - // Using a while loop ensures that if you gain 100 XP, - // it processes Level 1, then Level 2, with the remainder left over. - while (currentXP >= XP_THRESHOLD && currentLevel < 200) { - currentXP -= XP_THRESHOLD; - currentLevel++; - // 1. Trigger the Visual Toast (Top of screen) - if (typeof showLevelUpToast === 'function') { - showLevelUpToast(getRank(currentLevel)); - } - - // 2. Trigger the "Matrix" Console Log - matrixConsoleLog(currentLevel); - - // --- THE POPUP TRIGGER --- - const badge = document.getElementById('level-badge'); - if (badge) { - // Remove the class if it exists (to reset animation) - badge.classList.remove('animate-badge-pop'); - // Trigger a "reflow" (magic trick to allow re-animation) - void badge.offsetWidth; - // Re-add the class - badge.classList.add('animate-badge-pop'); - } - // -------------------------- - - console.log(`Leveled Up to ${currentLevel}!`); + // 1. Force strict numeric types to prevent "1" + "1" = "11" + let xpToAdd = Number(amount) || 0; + currentXP = Number(currentXP) || 0; + currentLevel = Number(currentLevel) || 0; + const XP_THRESHOLD = 45; + + // 2. Add the new XP + currentXP += xpToAdd; + + // 3. Process Level Ups one by one + // Using a while loop ensures that if you gain 100 XP, + // it processes Level 1, then Level 2, with the remainder left over. + while (currentXP >= XP_THRESHOLD && currentLevel < 200) { + currentXP -= XP_THRESHOLD; + currentLevel++; + // 1. Trigger the Visual Toast (Top of screen) + if (typeof showLevelUpToast === "function") { + showLevelUpToast(getRank(currentLevel)); } - // 4. Persistence: Save clean numbers - localStorage.setItem('userLevel', currentLevel.toString()); - localStorage.setItem('userXP', currentXP.toString()); + // 2. Trigger the "Matrix" Console Log + matrixConsoleLog(currentLevel); - // 5. Update UI - updateGameUI(); -} - -function updateGameUI() { - const lvl = Number(currentLevel) || 0; - const rank = getRank(lvl); - - // Update the Name and its Color - const nameLabel = document.getElementById('level-name'); - if (nameLabel) { - nameLabel.innerText = rank.name; - nameLabel.style.color = rank.color; // This applies the array color - } - - // Update the Badge Background - const badge = document.getElementById('level-badge'); + // --- THE POPUP TRIGGER --- + const badge = document.getElementById("level-badge"); if (badge) { - badge.innerText = rank.emoji; - badge.style.backgroundColor = rank.color; + // Remove the class if it exists (to reset animation) + badge.classList.remove("animate-badge-pop"); + // Trigger a "reflow" (magic trick to allow re-animation) + void badge.offsetWidth; + // Re-add the class + badge.classList.add("animate-badge-pop"); } + // -------------------------- - // Update the Number and XP - if (document.getElementById('level-number')) { - document.getElementById('level-number').innerText = lvl; - } + console.log(`Leveled Up to ${currentLevel}!`); + } - if (document.getElementById('total-xp-display')) { - document.getElementById('total-xp-display').innerText = `${currentXP} / ${XP_PER_LEVEL}`; - } + // 4. Persistence: Save clean numbers + localStorage.setItem("userLevel", currentLevel.toString()); + localStorage.setItem("userXP", currentXP.toString()); - renderXP(currentXP); + // 5. Update UI + updateGameUI(); } -function initSkillMining() { - // Select all your skill badges/tags - const skillTags = document.querySelectorAll('.skill-tag, .experience-badge'); +function updateGameUI() { + const lvl = Number(currentLevel) || 0; + const rank = getRank(lvl); + + // Update the Name and its Color + const nameLabel = document.getElementById("level-name"); + if (nameLabel) { + nameLabel.innerText = rank.name; + nameLabel.style.color = rank.color; // This applies the array color + } + + // Update the Badge Background + const badge = document.getElementById("level-badge"); + if (badge) { + badge.innerText = rank.emoji; + badge.style.backgroundColor = rank.color; + } + + // Update the Number and XP + if (document.getElementById("level-number")) { + document.getElementById("level-number").innerText = lvl; + } + + if (document.getElementById("total-xp-display")) { + document.getElementById("total-xp-display").innerText = + `${currentXP} / ${XP_PER_LEVEL}`; + } + + renderXP(currentXP); +} - skillTags.forEach(tag => { - // Remove old listeners to prevent double-firing - tag.removeEventListener('mouseenter', createFloatingXP); - tag.addEventListener('mouseenter', createFloatingXP); - }); +function initSkillMining() { + // Select all your skill badges/tags + const skillTags = document.querySelectorAll(".skill-tag, .experience-badge"); + + skillTags.forEach((tag) => { + // Remove old listeners to prevent double-firing + tag.removeEventListener("mouseenter", createFloatingXP); + tag.addEventListener("mouseenter", createFloatingXP); + }); } // Run this on page load -document.addEventListener('DOMContentLoaded', initSkillMining); +document.addEventListener("DOMContentLoaded", initSkillMining); function initSkillXP() { - const skills = document.querySelectorAll('.skill-item'); - skills.forEach(skill => { - skill.addEventListener('mouseenter', (e) => { - const isLocked = document.getElementById('dev-tools')?.hasAttribute('data-lock'); - if (!isLocked) { - addExperience(1); - createFloatingXP(e); - - // Fancy scale-up on hover - skill.style.transform = "scale(1.1) translateY(-2px)"; - skill.style.transition = "transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275)"; - } - }); - - skill.addEventListener('mouseleave', () => { - skill.style.transform = "scale(1) translateY(0)"; - }); + const skills = document.querySelectorAll(".skill-item"); + skills.forEach((skill) => { + skill.addEventListener("mouseenter", (e) => { + const isLocked = document + .getElementById("dev-tools") + ?.hasAttribute("data-lock"); + if (!isLocked) { + addExperience(1); + createFloatingXP(e); + + // Fancy scale-up on hover + skill.style.transform = "scale(1.1) translateY(-2px)"; + skill.style.transition = + "transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275)"; + } }); + + skill.addEventListener("mouseleave", () => { + skill.style.transform = "scale(1) translateY(0)"; + }); + }); } // Re-initialize skills after Surprise scroll or any DOM changes -window.addEventListener('DOMContentLoaded', initSkillXP); +window.addEventListener("DOMContentLoaded", initSkillXP); /** * SYS ADMIN XP (Level 6 Mechanic) */ function addMaintenanceXP() { - // Sys Admins get more XP for system-level interactions - const bonus = unlockedEggs.length >= 6 ? 5 : 2; - addExperience(bonus); + // Sys Admins get more XP for system-level interactions + const bonus = unlockedEggs.length >= 6 ? 5 : 2; + addExperience(bonus); - // Console log for that "hacker" feel - if (unlockedEggs.length >= 6) { - console.log("%c [SYS_ADMIN] System optimized: +5 XP", "color: #ec4899; font-weight: bold;"); - } + // Console log for that "hacker" feel + if (unlockedEggs.length >= 6) { + console.log( + "%c [SYS_ADMIN] System optimized: +5 XP", + "color: #ec4899; font-weight: bold;", + ); + } } - - - function jumpToLevel() { - const input = document.getElementById('jump-lvl'); - if (!input || input.value === "") return; + const input = document.getElementById("jump-lvl"); + if (!input || input.value === "") return; - let targetLvl = parseInt(input.value); + let targetLvl = parseInt(input.value); - // Clamp between 0 and 200 - targetLvl = Math.max(0, Math.min(200, targetLvl)); + // Clamp between 0 and 200 + targetLvl = Math.max(0, Math.min(200, targetLvl)); - // Update the GLOBAL variables - currentLevel = targetLvl; - currentXP = 0; + // Update the GLOBAL variables + currentLevel = targetLvl; + currentXP = 0; - // Save to LocalStorage - localStorage.setItem('userLevel', currentLevel); - localStorage.setItem('userXP', currentXP); + // Save to LocalStorage + localStorage.setItem("userLevel", currentLevel); + localStorage.setItem("userXP", currentXP); - // Refresh everything - updateGameUI(); + // Refresh everything + updateGameUI(); - const rank = getRank(currentLevel); - showLevelUpNotification(rank); + const rank = getRank(currentLevel); + showLevelUpNotification(rank); - if (currentLevel >= 101) { - triggerForceSurge(); - } + if (currentLevel >= 101) { + triggerForceSurge(); + } } diff --git a/src/users/Anderson.yaml b/src/users/Anderson.yaml index 3a214a0..0c2a058 100644 --- a/src/users/Anderson.yaml +++ b/src/users/Anderson.yaml @@ -7,4 +7,4 @@ location: Valencia role: Translator, psychologist, text editor. languages: N/A bio: | - English Spanish Translator / Psychologist / Academic writer + English Spanish Translator / Psychologist / Academic writer diff --git a/src/users/Ephraim.yaml b/src/users/Ephraim.yaml index dfbcdfe..e76fdbe 100644 --- a/src/users/Ephraim.yaml +++ b/src/users/Ephraim.yaml @@ -11,4 +11,4 @@ location: Ebonyi languages: N/A role: Mobile Developer bio: | - I have good experience in building mobile apps with authentication integration using firebase, payment methods, NFC and optimizations. + I have good experience in building mobile apps with authentication integration using firebase, payment methods, NFC and optimizations. diff --git a/src/users/GabrielTheophilo.yaml b/src/users/GabrielTheophilo.yaml index 9809394..a0cf6f9 100644 --- a/src/users/GabrielTheophilo.yaml +++ b/src/users/GabrielTheophilo.yaml @@ -11,4 +11,4 @@ location: Rio de Janeiro role: Web Developer languages: c c++ java python postgresql js css3 html5 bio: | - Web developer learning Java/Js and postgresql for the web, C++ in software development and automation with Python + Web developer learning Java/Js and postgresql for the web, C++ in software development and automation with Python diff --git a/src/users/HunainAnis.yaml b/src/users/HunainAnis.yaml index fd0e1b4..0bd63a3 100644 --- a/src/users/HunainAnis.yaml +++ b/src/users/HunainAnis.yaml @@ -11,6 +11,6 @@ location: Karachi role: React Frontend Developer languages: HTML CSS JavaScript Python bio: | - A Frontend ReactJS developer, who is pationate about Mastering Javascript. - I am a Freelancer making Dynamic webapps using ReactJS. - I will start learning NodeJS soon! + A Frontend ReactJS developer, who is pationate about Mastering Javascript. + I am a Freelancer making Dynamic webapps using ReactJS. + I will start learning NodeJS soon! diff --git a/src/users/TinotendaMhedziso.yaml b/src/users/TinotendaMhedziso.yaml index fbc3d69..609c88e 100644 --- a/src/users/TinotendaMhedziso.yaml +++ b/src/users/TinotendaMhedziso.yaml @@ -11,6 +11,6 @@ location: Gqeberha role: Software Developer languages: C# CSS3 HTML5 JS Python MySQL bio: | - Final year Bachelor of Information Technology student who has some - experience with Full-stack web development in vanilla JS๐Ÿ’ป, building - desktop applications using C#๐Ÿ”Ž and advanced chatbot building using Botpress๐Ÿ‘ท๐Ÿฟโ€. + Final year Bachelor of Information Technology student who has some + experience with Full-stack web development in vanilla JS๐Ÿ’ป, building + desktop applications using C#๐Ÿ”Ž and advanced chatbot building using Botpress๐Ÿ‘ท๐Ÿฟโ€. diff --git a/src/users/abdorah.yaml b/src/users/abdorah.yaml index 4b06a23..6b51b57 100644 --- a/src/users/abdorah.yaml +++ b/src/users/abdorah.yaml @@ -11,10 +11,10 @@ location: Rabat role: Web Developer languages: c java js css3 html5 bio: | - Java Spring developer. + Java Spring developer. - _Who Am I?_ + _Who Am I?_ - - I am a curious software engineering student at the national higher school of computer science and system analysis, โœจENSIASโœจ. - - I love coding and contributing in open source projects. Also, every time I learn about a new topic or technology, I like to โœจ write articles and tutorials โœจ about it. - - I like to work also on โœจ competitive programming problems โœจ and learn from genius solutions. + - I am a curious software engineering student at the national higher school of computer science and system analysis, โœจENSIASโœจ. + - I love coding and contributing in open source projects. Also, every time I learn about a new topic or technology, I like to โœจ write articles and tutorials โœจ about it. + - I like to work also on โœจ competitive programming problems โœจ and learn from genius solutions. diff --git a/src/users/anekenonso.yaml b/src/users/anekenonso.yaml index bed14cd..c854282 100644 --- a/src/users/anekenonso.yaml +++ b/src/users/anekenonso.yaml @@ -7,4 +7,4 @@ location: Enugu role: Web Developer languages: html5 CSS3 JavaScript bio: | - I'm a web developer from Nigeria and my focus is on the Front-end. + I'm a web developer from Nigeria and my focus is on the Front-end. diff --git a/src/users/anufdo.yaml b/src/users/anufdo.yaml index 60a92f2..c211289 100644 --- a/src/users/anufdo.yaml +++ b/src/users/anufdo.yaml @@ -11,4 +11,4 @@ location: Minuwangoda role: Full Stack Developer languages: JavaScript TypeScript C# Java bio: | - Exploring new technologies and developing software solutions. + Exploring new technologies and developing software solutions. diff --git a/src/users/ayush.yaml b/src/users/ayush.yaml index c99093a..59a011e 100644 --- a/src/users/ayush.yaml +++ b/src/users/ayush.yaml @@ -10,10 +10,10 @@ location: Kathmandu role: Front End Engineer languages: JS React TS GraphQL bio: | - 2+ years of experience. + 2+ years of experience. - # _SKILLS_ + # _SKILLS_ - - NodeJS, Express - - Javascript, HTML, CSS, MySQL, - - Design - Photoshop, Illustrator, Figma + - NodeJS, Express + - Javascript, HTML, CSS, MySQL, + - Design - Photoshop, Illustrator, Figma diff --git a/src/users/basemax.yaml b/src/users/basemax.yaml index 82a79bb..2c7085d 100644 --- a/src/users/basemax.yaml +++ b/src/users/basemax.yaml @@ -7,4 +7,4 @@ location: Remote role: Software engineer languages: c php js css3 html5 bio: | - @github developer maintainer. Full-Time Open-Sourcerer. Full-stack programmer. In the path of a real computer engineer... (Compiler enthusiast) + @github developer maintainer. Full-Time Open-Sourcerer. Full-stack programmer. In the path of a real computer engineer... (Compiler enthusiast) diff --git a/src/users/enimiste.yaml b/src/users/enimiste.yaml index d497148..cdabe5f 100644 --- a/src/users/enimiste.yaml +++ b/src/users/enimiste.yaml @@ -7,4 +7,4 @@ location: Internet role: Software Engineer languages: java python php go scala js css3 html5 bio: | - Algorithms, programming, backend dev, devops, ... + Algorithms, programming, backend dev, devops, ... diff --git a/src/users/eugene4545.yaml b/src/users/eugene4545.yaml index 834bf81..8cbaf46 100644 --- a/src/users/eugene4545.yaml +++ b/src/users/eugene4545.yaml @@ -11,4 +11,4 @@ location: Lagos role: Web Developer languages: javascript typescript python bio: | - Med student and aspiring Full Stack developer, open source contributor + Med student and aspiring Full Stack developer, open source contributor diff --git a/src/users/gakramx.yaml b/src/users/gakramx.yaml index c06db09..6a90f7d 100644 --- a/src/users/gakramx.yaml +++ b/src/users/gakramx.yaml @@ -12,5 +12,5 @@ role: Systems programmer languages: c c++ assembly python scheme bash bio: | - I'm a software engineer & Electronic Technician, I have programming experience System Programming and Desktop Software, - Also in embedded systems such as Arduino and Raspberry Pi .. etc. + I'm a software engineer & Electronic Technician, I have programming experience System Programming and Desktop Software, + Also in embedded systems such as Arduino and Raspberry Pi .. etc. diff --git a/src/users/iabdr.yaml b/src/users/iabdr.yaml index aa5d4cd..dc5253e 100644 --- a/src/users/iabdr.yaml +++ b/src/users/iabdr.yaml @@ -8,6 +8,6 @@ location: ATD role: Web and App Developer languages: js css3 html5 bio: | - I'm a Student, Developer, Learner and Graphic Designer. - Iโ€™m interested in Web & App Development. - Iโ€™m currently learning Flutter, React Native & Python. + I'm a Student, Developer, Learner and Graphic Designer. + Iโ€™m interested in Web & App Development. + Iโ€™m currently learning Flutter, React Native & Python. diff --git a/src/users/kenhorlador.yaml b/src/users/kenhorlador.yaml index 74de214..f29299f 100644 --- a/src/users/kenhorlador.yaml +++ b/src/users/kenhorlador.yaml @@ -12,14 +12,14 @@ role: Software Engineer / UI/UX Designer / Layout Artist languages: c c++ js css3 html5 python3.82+ golang scss typescript bio: | - I'm Ken Horlador from Camarines Sur, Philippines. I enjoy learning programming languages and libraries/frameworks like ReactJS with GatsbyJS or NextJS. I also enjoy wireframing, developing user flows, user interface, user experience and design in general. + I'm Ken Horlador from Camarines Sur, Philippines. I enjoy learning programming languages and libraries/frameworks like ReactJS with GatsbyJS or NextJS. I also enjoy wireframing, developing user flows, user interface, user experience and design in general. - Currently taking Bachelor of Science in Computer Science at Camarines Sur Polytechnic Colleges. + Currently taking Bachelor of Science in Computer Science at Camarines Sur Polytechnic Colleges. - Former Layout Artist at The USI-Faรงade | The Official Student Publication of Universidad de Sta. Isabel [ 2019 - 2020 ] + Former Layout Artist at The USI-Faรงade | The Official Student Publication of Universidad de Sta. Isabel [ 2019 - 2020 ] - Currently working as a Layout Artist at TheSpark | The Official Student-Community Publication of Camarines Sur Polytechnic Colleges [ 2020 - Present ] + Currently working as a Layout Artist at TheSpark | The Official Student-Community Publication of Camarines Sur Polytechnic Colleges [ 2020 - Present ] - Personality type: INTJ ( Introverted, Intuitive, Thinking, Judging ) + Personality type: INTJ ( Introverted, Intuitive, Thinking, Judging ) - Pronouns: He/Him/His + Pronouns: He/Him/His diff --git a/src/users/mahabub.yaml b/src/users/mahabub.yaml index cbd7e3a..3f42788 100644 --- a/src/users/mahabub.yaml +++ b/src/users/mahabub.yaml @@ -12,18 +12,18 @@ role: Web Developer languages: ruby elixir js python3 css3 html5 bio: | - Professional Web Developer and Web technology enthusiast. Working with web technology with passion for around 5 years. Here is some key skills I can point out about me, + Professional Web Developer and Web technology enthusiast. Working with web technology with passion for around 5 years. Here is some key skills I can point out about me, - 1. Test Driven Development - 2. Through On Documentation - 3. Code Review - 4. UI+UX lover - 5. Latest tools usage to make web app/mobile app optimization more comfortable - 6. Linux Lover - 7. Git - 8. Ruby, Ruby On Rails - 9. Elixir Lang, Phoenix - 10. React JS, JavaScript - 11. Flutter / Dart + 1. Test Driven Development + 2. Through On Documentation + 3. Code Review + 4. UI+UX lover + 5. Latest tools usage to make web app/mobile app optimization more comfortable + 6. Linux Lover + 7. Git + 8. Ruby, Ruby On Rails + 9. Elixir Lang, Phoenix + 10. React JS, JavaScript + 11. Flutter / Dart - I am moreover into latest technology and good parts of anything out there always learning. I make open source contributions frequently on GitHub. + I am moreover into latest technology and good parts of anything out there always learning. I make open source contributions frequently on GitHub. diff --git a/src/users/majalian.yaml b/src/users/majalian.yaml index fa0d238..ae29cf7 100644 --- a/src/users/majalian.yaml +++ b/src/users/majalian.yaml @@ -11,16 +11,16 @@ languages: N/A role: Copywriter bio: | - I have been a technical writer for more than eight years in several different settings. I learn remarkably quickly, and my researching skills are outstanding. + I have been a technical writer for more than eight years in several different settings. I learn remarkably quickly, and my researching skills are outstanding. - The majority of my career has been in advertising and technical copywriting. + The majority of my career has been in advertising and technical copywriting. - Four years were spent working with the United States Agency for International Development, where I specialized in Business Development, which has undoubtedly been my favorite field. This position had the most variety, from official letters to courses and news writing and proposals. + Four years were spent working with the United States Agency for International Development, where I specialized in Business Development, which has undoubtedly been my favorite field. This position had the most variety, from official letters to courses and news writing and proposals. - I am here to help you achieve your goals, and I am passionate about my belief that together, we can achieve anything. + I am here to help you achieve your goals, and I am passionate about my belief that together, we can achieve anything. - I have joined Upwork in April 2021, gained two clients from day one who I still work with to this day, as we are enjoying our collaboration together. + I have joined Upwork in April 2021, gained two clients from day one who I still work with to this day, as we are enjoying our collaboration together. - Although I thoroughly enjoy technical writing, I am a competent, creative writer and would like to have more opportunities to employ my imagination. I write best about topics that interest me or that I feel passionate about. My sense of humor is generally decent, but I also have a satirical streak in my writing when appropriate. And I have to admit; I am most interested in creative writing for my freelance work, Beside my Arabic-English Translation gig. + Although I thoroughly enjoy technical writing, I am a competent, creative writer and would like to have more opportunities to employ my imagination. I write best about topics that interest me or that I feel passionate about. My sense of humor is generally decent, but I also have a satirical streak in my writing when appropriate. And I have to admit; I am most interested in creative writing for my freelance work, Beside my Arabic-English Translation gig. - My writing style is not for everyone. With customer satisfaction as my primary focus, I am more interested in quality. With that in mind, I want to make sure that the product meets or exceeds expectations, even if it means doing extra work upfront or toward the end. Writing has always been something I do for fun, and I love the idea that it can help others while keeping me happily occupied. + My writing style is not for everyone. With customer satisfaction as my primary focus, I am more interested in quality. With that in mind, I want to make sure that the product meets or exceeds expectations, even if it means doing extra work upfront or toward the end. Writing has always been something I do for fun, and I love the idea that it can help others while keeping me happily occupied. diff --git a/src/users/mohdrash.yaml b/src/users/mohdrash.yaml index 1f07348..bb93f9e 100644 --- a/src/users/mohdrash.yaml +++ b/src/users/mohdrash.yaml @@ -12,7 +12,7 @@ role: Software Engineer languages: c js css3 html5 c++ nodejs reactjs angularjs solidity moralis nextjs django python bio: | - I'm a Keralite. From a place of beauty and nature. Since my childhood, i love sports (like football and MMA), hiking mountains, cooking, eating and drawing. I always try to draw stuff with my unique point of view. I also love to create things that can be useful to others. - I started coding since I was in high school. I just got volunteered with a game developers community called 'Gametop' and was part in the development of two games 'sudden strike, sudden strike Iwo Jima' Coding is also an art for me like I love to hike mountains. I was absolute noob when it comes to code html, css, and js during my College time, I'm only familiar with C++ and Arduino. That I even tried to be like broken. It becomes my favourite part when I was joined in Talrop, an Engineers world and later joined with a cyber security team under the mentorship of 'Mr.Gautam Kumawat'. I love it and now I have the opportunity to design along with the coding. My career become more stronger when I joined in Osperb Innovations that I learned a lot and still working. I find it really interesting and I enjoyed the process a lot. + I'm a Keralite. From a place of beauty and nature. Since my childhood, i love sports (like football and MMA), hiking mountains, cooking, eating and drawing. I always try to draw stuff with my unique point of view. I also love to create things that can be useful to others. + I started coding since I was in high school. I just got volunteered with a game developers community called 'Gametop' and was part in the development of two games 'sudden strike, sudden strike Iwo Jima' Coding is also an art for me like I love to hike mountains. I was absolute noob when it comes to code html, css, and js during my College time, I'm only familiar with C++ and Arduino. That I even tried to be like broken. It becomes my favourite part when I was joined in Talrop, an Engineers world and later joined with a cyber security team under the mentorship of 'Mr.Gautam Kumawat'. I love it and now I have the opportunity to design along with the coding. My career become more stronger when I joined in Osperb Innovations that I learned a lot and still working. I find it really interesting and I enjoyed the process a lot. - My vision is to make the world a better place. My abundant energy fuels me in the pursuit of many interests, hobbies, areas of study and artistic endeavors. Iโ€™m a fast learner, able to pick up new skills and juggle different projects and roles with relative ease. + My vision is to make the world a better place. My abundant energy fuels me in the pursuit of many interests, hobbies, areas of study and artistic endeavors. Iโ€™m a fast learner, able to pick up new skills and juggle different projects and roles with relative ease. diff --git a/src/users/pratik-wadhai.yaml b/src/users/pratik-wadhai.yaml index e50ed70..27a97ed 100644 --- a/src/users/pratik-wadhai.yaml +++ b/src/users/pratik-wadhai.yaml @@ -11,8 +11,8 @@ location: Nagpur role: Frontend Developer languages: javascript python sql html css bio: | - As a recent graduate with a passion for web development, I have acquired skills in HTML, CSS, and JavaScript. My knowledge in UI design and web development can be leveraged to help develop compelling, high-performance user experiences. With a solid foundation in coding, I am eager to bring my knowledge and experience to an exciting opportunity in the field of Frontend Development. + As a recent graduate with a passion for web development, I have acquired skills in HTML, CSS, and JavaScript. My knowledge in UI design and web development can be leveraged to help develop compelling, high-performance user experiences. With a solid foundation in coding, I am eager to bring my knowledge and experience to an exciting opportunity in the field of Frontend Development. - As a frontend engineer, I'm skilled in ReactJS, Python, NodeJS, and JavaScript, as well as HTML/CSS. I'm passionate about contributing to projects and dedicated to delivering high-quality work. With a strong work ethic and a thirst for knowledge, I'm always looking for ways to learn and grow as a developer. + As a frontend engineer, I'm skilled in ReactJS, Python, NodeJS, and JavaScript, as well as HTML/CSS. I'm passionate about contributing to projects and dedicated to delivering high-quality work. With a strong work ethic and a thirst for knowledge, I'm always looking for ways to learn and grow as a developer. - Ultimately, my goal as a frontend engineer is to use my technical expertise and my love of learning to create user-friendly, engaging web experiences that help businesses grow and thrive. I'm eager to tackle new challenges, collaborate with like-minded professionals, and make a positive impact on the world of web development. + Ultimately, my goal as a frontend engineer is to use my technical expertise and my love of learning to create user-friendly, engaging web experiences that help businesses grow and thrive. I'm eager to tackle new challenges, collaborate with like-minded professionals, and make a positive impact on the world of web development.