diff --git a/.eleventy.js b/.eleventy.js index 874f6688..4ac2994f 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -1,9 +1,8 @@ const yaml = require("js-yaml"); +const fs = require("node:fs/promises"); -module.exports = function (eleventyConfig) { - eleventyConfig.addShortcode("currentYear", function () { - return new Date().getFullYear(); - }); +module.exports = (eleventyConfig) => { + eleventyConfig.addShortcode("currentYear", () => new Date().getFullYear()); // Add this line to copy your external assets eleventyConfig.addPassthroughCopy("src/assets"); // 1. Recognize YAML as a template format @@ -20,19 +19,18 @@ module.exports = function (eleventyConfig) { }; }, getData: async (inputPath) => { - 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", (collectionApi) => { // Grab all yaml files from the users folder const people = collectionApi.getFilteredByGlob("src/users/*.yaml"); // Create a copy of the array to avoid mutating the original global collection - let shuffled = [...people]; + const shuffled = [...people]; // Fisher-Yates Shuffle for (let i = shuffled.length - 1; i > 0; i--) { diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e1aef321..25232c77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ default_stages: [pre-commit, pre-push] -minimum_prek_version: "0.2.22" +minimum_pre_commit_version: "0.2.22" default_language_version: python: python3 node: 24.13.0 @@ -118,17 +118,10 @@ repos: hooks: - id: biome-check name: run biome-check - description: Run Biome linter and formatter for JSON files + description: Run Biome linter and formatter for JS and JSON files + types_or: [javascript, json] additional_dependencies: ["@biomejs/biome"] - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v4.0.0-alpha.8 - hooks: - - id: prettier - name: run prettier - types_or: [css, html, javascript, json, markdown, yaml] - additional_dependencies: ["prettier@3.8.1"] - - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.47.0 hooks: diff --git a/biome.json b/biome.json index 115edc82..4594359b 100644 --- a/biome.json +++ b/biome.json @@ -1,8 +1,5 @@ { "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json", - "files": { - "includes": ["**/*.json"] - }, "linter": { "enabled": true, "rules": { @@ -13,5 +10,8 @@ "enabled": true, "indentStyle": "space", "indentWidth": 2 + }, + "files": { + "includes": ["**/*.js", "**/*.json"] } } diff --git a/src/_data/build.js b/src/_data/build.js index 7f753a25..bbb1c1da 100644 --- a/src/_data/build.js +++ b/src/_data/build.js @@ -1,4 +1,4 @@ -const { execSync } = require("child_process"); +const { execSync } = require("node:child_process"); module.exports = () => { const now = new Date(); @@ -13,7 +13,7 @@ module.exports = () => { try { // Get the short git hash (first 7 characters) gitHash = execSync("git rev-parse --short HEAD").toString().trim(); - } catch (e) { + } catch (_e) { console.warn("Could not fetch git hash, defaulting to 'development'"); } diff --git a/src/assets/css/style.css b/src/assets/css/style.css index 4381e013..947fb47d 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -5,93 +5,75 @@ --bg-page: #f0f4f8; --bg-card: #ffffff; --bg-footer: #e2e8f0; - --text-main: #1e293b; /* Default Light */ + --text-main: #1a202c; --text-muted: #4a5568; --border-color: #cbd5e0; - --accent: #94a3b8; /* Default */ + --accent: #2563eb; --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); - /* Default Rarity Colors */ - --rarity-common: #94a3b8; - --rarity-uncommon: #64748b; /* Adjusted from pure black for better theme compatibility */ - --rarity-rare: #3b82f6; - --rarity-epic: #a855f7; - --rarity-legendary: #fbbf24; - --rarity-mythic: #ef4444; - --rarity-absolute: #ffffff; + --card-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } .dark { --bg-page: #05070a; - --bg-card: #1e293b; + --bg-card: #0f172a; --bg-footer: #020617; - --text-main: #f8fafc; + --text-main: #f1f5f9; --text-muted: #94a3b8; --border-color: #1e293b; --accent: #38bdf8; --accent-light: rgba(56, 189, 248, 0.1); --danger: #f87171; --card-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5); - --rarity-absolute: #ffffff; - --rarity-uncommon: #cbd5e1; } -/* Light Mode specific overrides if needed */ -.light { - --rarity-absolute: #0f172a; /* Make absolute dark on light themes so it's visible */ - --rarity-uncommon: #1e293b; -} - -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 */ -/** - * DYNAMIC COLORING (NO ANIMATION) - */ -.user-card h2, -.profile-link, -.user-card a { +.user-card h2 { + /* Dynamic color from theme logic */ color: var(--accent) !important; 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.3s ease; /* Only animate the color fade, not movement */ + margin-bottom: 0.5rem; } -/* Static Hover: No movement */ +/* Hover state: No movement, just a slight brightness shift */ .user-card:hover h2 { - filter: brightness(1.2); + filter: brightness(1.1); +} + +/** + * 3. PROFILE LINKS - THEMED + */ +.profile-link, +.user-card a[href*="github"], +.user-card a[href*="linkedin"] { + color: var(--accent) !important; + font-weight: 700; + text-decoration: none; + transition: opacity 0.2s ease; + display: inline-flex; + align-items: center; + gap: 0.4rem; } .profile-link:hover, .user-card a:hover { text-decoration: underline; - opacity: 0.8; -} - -/* 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; + opacity: 0.8; /* Subtle visual feedback instead of moving */ } /* If your links look like tags/pills */ @@ -108,6 +90,8 @@ body { border-color: var(--accent); } + + /* If you have a specific button-style profile link */ .profile-btn { background-color: var(--accent-light) !important; @@ -174,9 +158,7 @@ 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); @@ -184,28 +166,19 @@ 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 @@ -233,20 +206,20 @@ 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; @@ -267,38 +240,22 @@ 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 { @@ -311,8 +268,7 @@ 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%); } @@ -320,609 +276,18 @@ 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); - } -} - -.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); - } -} -/* 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); + 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); } } -.border-trace { - 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; -} - -@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); - } -} -/* 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; -} - -/* 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; -} - -/* 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; -} - -@keyframes gem-shimmer { - 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; - } -} - -/* 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); -} - -.skill-item { - 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); -} - -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 */ -} - -/* Added via JS when XP increases */ -.xp-pulse { - 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 */ -#jump-lvl { - appearance: textfield; /* Removes default arrows */ - -webkit-appearance: none; - line-height: 30px !important; -} - -#jump-lvl::placeholder { - 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; -} -/* 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)); - } -} - -/* 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; -} - -/* 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; -} - -/* 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); - } -} - -#level-name { - 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; -} - -/* 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); -} - -#level-badge { - 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 */ -} - -/* Progress bar should always have a high-contrast background */ -.xp-bar-container { - background: rgba(0, 0, 0, 0.1); -} -.dark .xp-bar-container { - 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; -} - -@keyframes floatUp { - 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; - } -} - -.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); -} - -/* 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); -} - -/* 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); -} - -.skill-tag:hover { - 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; -} - -a:hover { - 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); - } -} - -.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 */ -} - -/* Optional: Add a glow to the level number too */ -#level-number { - transition: all 0.3s ease; -} -.animate-badge-pop #level-number { - 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; -} - -.toast-content { - display: flex; - align-items: center; - gap: 16px; -} - -.toast-emoji { - font-size: 2rem; -} - -.toast-title { - 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; -} - -@keyframes slideDownIn { - 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; -} -.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; -} - -@keyframes matrixSlideIn { - 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; -} - -/* Custom scrollbar for the terminal */ -#matrix-console-output::-webkit-scrollbar { - width: 3px; -} -#matrix-console-output::-webkit-scrollbar-thumb { - 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; -} - -@keyframes matrixLineFade { - 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; -} - -#matrix-console-output { - 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; -} -.cursor-grab:active { - cursor: grabbing; -} - -/* Prevents the terminal text from getting highlighted while you move the window */ -#matrix-console-container.is-dragging { - user-select: none; -} - -/* Full-screen Force Field */ -.force-field-overlay { - position: fixed !important; - inset: 0 !important; - z-index: 999999 !important; - background: radial-gradient( - circle, - rgba(0, 150, 255, 0.1) 0%, - rgba(0, 0, 50, 0.6) 100% - ); - pointer-events: none; - box-shadow: inset 0 0 150px rgba(0, 190, 255, 0.5); - backdrop-filter: blur(2px) contrast(1.2); - animation: force-throb 4s ease-in-out infinite; -} - -@keyframes force-throb { - 0%, - 100% { - opacity: 0.7; - } - 50% { - opacity: 1; - } -} - -/* Bright Lightning Arcs */ -.lightning-flash { - position: fixed; - inset: 0; - z-index: 1000000; - pointer-events: none; - background: white; - opacity: 0; -} - -@keyframes bolt { - 0% { - opacity: 0; - } - 10% { - opacity: 1; - background: #99f6ff; - } - 20% { - opacity: 0; - } - 25% { - opacity: 0.8; - } - 30% { - opacity: 0; - } -} -#phaser-container { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: 9999; /* Ensures it sits ABOVE your website content */ - pointer-events: none; /* Let's start with none so it doesn't block the heart */ -} - -/* Ensure the canvas itself fills the container */ -#phaser-container canvas { - display: block; -} +.konami-roll { animation: konami-barrel-roll 2s cubic-bezier(0.45, 0.05, 0.55, 0.95); } diff --git a/src/assets/js/eggs.js b/src/assets/js/eggs.js index 2ba15bb9..a4c18a6a 100644 --- a/src/assets/js/eggs.js +++ b/src/assets/js/eggs.js @@ -39,7 +39,7 @@ const emojiBurst = [ let heartClickCount = 0; let phaserStarted = false; -let gameInstance; +let _gameInstance; let player; let cursors; let aliens; @@ -110,7 +110,7 @@ function initPhaserGame() { }, }; - gameInstance = new Phaser.Game(config); + _gameInstance = new Phaser.Game(config); } // 4. PHASER SCENE FUNCTIONS @@ -187,23 +187,18 @@ function spawnExplosion(scene) { } function setupSpaceInvaders() { - const scene = this; - // Player Rocket - player = scene.add.text( - window.innerWidth / 2, - window.innerHeight - 80, - "🚀", - { fontSize: "50px" }, - ); - scene.physics.add.existing(player); + player = this.add.text(window.innerWidth / 2, window.innerHeight - 80, "🚀", { + fontSize: "50px", + }); + this.physics.add.existing(player); player.body.setCollideWorldBounds(true); // Bullets - bullets = scene.physics.add.group(); + bullets = this.physics.add.group(); // Aliens Grid - Adjusted for smaller size - aliens = scene.physics.add.group(); + aliens = this.physics.add.group(); const rows = 5; const cols = 10; const spacingX = 50; // Tighter horizontal spacing @@ -211,16 +206,16 @@ function setupSpaceInvaders() { for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { - let alienEmoji = ["👾", "👽", "🛸", "🐙", "👾"][y]; + const alienEmoji = ["👾", "👽", "🛸", "🐙", "👾"][y]; // Shrink from 35px to 24px - let alien = scene.add.text( + const alien = this.add.text( x * spacingX + 80, y * spacingY + 80, alienEmoji, { fontSize: "24px" }, ); - scene.physics.add.existing(alien); + this.physics.add.existing(alien); alien.body.setAllowGravity(false); // Shrink the collision box to match the smaller emoji alien.body.setSize(24, 24); @@ -230,16 +225,16 @@ function setupSpaceInvaders() { } // Alien Movement Timer - scene.alienDirection = 1; - scene.time.addEvent({ + this.alienDirection = 1; + this.time.addEvent({ delay: 800, callback: moveAliens, - callbackScope: scene, + callbackScope: this, loop: true, }); // Collisions - scene.physics.add.overlap(bullets, aliens, (bullet, alien) => { + this.physics.add.overlap(bullets, aliens, (bullet, alien) => { bullet.destroy(); alien.destroy(); if (aliens.countActive() === 0) { @@ -248,7 +243,7 @@ function setupSpaceInvaders() { } }); - cursors = scene.input.keyboard.createCursorKeys(); + cursors = this.input.keyboard.createCursorKeys(); } function moveAliens() { diff --git a/src/assets/js/script.js b/src/assets/js/script.js index a6d41a9a..f99dc6cb 100644 --- a/src/assets/js/script.js +++ b/src/assets/js/script.js @@ -17,7 +17,7 @@ const NUM_LEVELS = LEVELS.length; let currentLevel = Number(localStorage.getItem("userLevel")) || 0; // Load saved XP or start at 0 -let currentXP = parseInt(localStorage.getItem("userXP")) || 0; +let currentXP = parseInt(localStorage.getItem("userXP"), 10) || 0; let isSurging = false; @@ -104,7 +104,7 @@ function playSound(type) { } } -let unlockedEggs = JSON.parse(localStorage.getItem("unlockedEggs")) || []; +const unlockedEggs = JSON.parse(localStorage.getItem("unlockedEggs")) || []; let surpriseClickCount = 0; let matrixActive = false; let destructInterval; @@ -174,7 +174,7 @@ document.addEventListener("mouseup", () => { dragHeader.style.cursor = "grab"; }); -function minimizeConsole() { +function _minimizeConsole() { // Toggles the height of the output area if (consoleOutput.style.display === "none") { consoleOutput.style.display = "block"; @@ -185,7 +185,7 @@ function minimizeConsole() { } } -function maximizeConsole() { +function _maximizeConsole() { // Toggles a full-screen-ish mode consoleContainer.classList.toggle("console-maximized"); @@ -233,7 +233,7 @@ function reopenConsole() { let isProcessingXP = false; // Ensure this is in the GLOBAL scope (not hidden inside another function) -window.createFloatingXP = function (e) { +window.createFloatingXP = (e) => { // Prevent "spam" firing from high-speed mouse movement if (isProcessingXP) return; isProcessingXP = true; @@ -270,7 +270,7 @@ window.createFloatingXP = function (e) { setTimeout(() => popup.remove(), 800); }; -function handleLevelClick() { +function _handleLevelClick() { triggerSecretUnlock("badge_click"); } @@ -327,7 +327,9 @@ function applyTheme(theme) { "--accent", "--accent-light", ]; - props.forEach((p) => html.style.removeProperty(p)); + props.forEach((p) => { + html.style.removeProperty(p); + }); if (theme === "dark") { html.classList.add("dark"); @@ -362,7 +364,7 @@ function applyTheme(theme) { updateThemeIcon(theme); } -function toggleTheme() { +function _toggleTheme() { playSound("click"); const current = localStorage.getItem("theme") || "light"; const next = @@ -460,7 +462,7 @@ function triggerForceSurge() { }, 8000); } -function triggerMagicXP() { +function _triggerMagicXP() { initAudio(); addExperience(XP_MAGIC_BONUS); } @@ -692,7 +694,7 @@ function initMatrix() { ctx.fillStyle = "rgba(0, 0, 0, 0.05)"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "#0F0"; - ctx.font = fontSize + "px monospace"; + ctx.font = `${fontSize}px monospace`; for (let i = 0; i < rainDrops.length; i++) { const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length)); @@ -758,7 +760,7 @@ document /** * 7. SELF DESTRUCT ENGINE */ -window.startSelfDestruct = function () { +window.startSelfDestruct = () => { const btn = document.getElementById("self-destruct-btn"); const devPanel = document.getElementById("dev-tools"); @@ -835,7 +837,7 @@ window.startSelfDestruct = function () { }, 1000); }; -function scrollToRandomUser() { +function _scrollToRandomUser() { playSound("click"); surpriseClickCount++; @@ -886,7 +888,7 @@ function scrollToRandomUser() { /** * UTILITY: SCREENSHOT MODE */ -window.toggleScreenshotMode = function () { +window.toggleScreenshotMode = () => { const devPanel = document.getElementById("dev-tools"); const header = document.querySelector("header"); const footer = document.querySelector("footer"); @@ -1002,7 +1004,7 @@ document.addEventListener("keydown", (e) => { async function addExperience(amount) { // 1. Force strict numeric types to prevent "1" + "1" = "11" - let xpToAdd = Number(amount) || 0; + const xpToAdd = Number(amount) || 0; currentXP = Number(currentXP) || 0; currentLevel = Number(currentLevel) || 0; const XP_THRESHOLD = 45; @@ -1062,9 +1064,9 @@ function updateInventoryCounts(lvl) { // We use i <= lvl because currentLevel is the index reached for (let i = 0; i <= lvl; i++) { const levelEntry = LEVELS[i]; - if (levelEntry && levelEntry.rarity) { + if (levelEntry?.rarity) { const r = levelEntry.rarity.toLowerCase(); - if (counts.hasOwnProperty(r)) { + if (Object.hasOwn(counts, r)) { counts[r]++; } } @@ -1184,7 +1186,7 @@ function initProfileTracker() { // 3. UI Update Logic const refreshStats = () => { - const count = parseInt(localStorage.getItem("profile_view_count") || 0); + const count = parseInt(localStorage.getItem("profile_view_count") || 0, 10); statsContainer.innerHTML = ` VIEWS: ${count} `; @@ -1198,9 +1200,10 @@ function initProfileTracker() { const targetLink = e.target.closest("a"); // Only increment if the link text contains "Profile" - if (targetLink && targetLink.textContent.includes("Profile")) { - let currentCount = parseInt( + if (targetLink?.textContent.includes("Profile")) { + const currentCount = parseInt( localStorage.getItem("profile_view_count") || 0, + 10, ); localStorage.setItem("profile_view_count", currentCount + 1); refreshStats(); @@ -1249,11 +1252,11 @@ function addMaintenanceXP() { } } -function jumpToLevel() { +function _jumpToLevel() { const input = document.getElementById("jump-lvl"); if (!input || input.value === "") return; - let targetLvl = parseInt(input.value); + let targetLvl = parseInt(input.value, 10); // Clamp between 0 and NUM_LEVELS targetLvl = Math.max(0, Math.min(NUM_LEVELS, targetLvl)); @@ -1273,7 +1276,7 @@ function jumpToLevel() { showLevelUpNotification(rank); } -function handleFooterDotClick() { +function _handleFooterDotClick() { // 1. Get the current list of unlocked eggs const rawEggs = localStorage.getItem("unlockedEggs") || "[]"; const unlockedEggs = JSON.parse(rawEggs); @@ -1281,7 +1284,7 @@ function handleFooterDotClick() { // 2. Exit if already unlocked if (unlockedEggs.includes("footer_surge")) return; - let clicks = parseInt(localStorage.getItem("footerDotClicks")) || 0; + let clicks = parseInt(localStorage.getItem("footerDotClicks"), 10) || 0; clicks++; const core = document.getElementById("footer-dot-core");