diff --git a/package-lock.json b/package-lock.json
index 889d019..381103d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"@monaco-editor/react": "^4.7.0",
"@uiw/codemirror-theme-vscode": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9",
+ "framer-motion": "^12.9.7",
"monaco-editor": "^0.52.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -2676,6 +2677,33 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/framer-motion": {
+ "version": "12.9.7",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.9.7.tgz",
+ "integrity": "sha512-Eo5TYU6sEPPy82GDx32PJm++G+AkBCrzxtEQOWLnpQX896Q3LFrsYhMZ5YO5ct4wL7wyHU6hqlrpYXeexKAevg==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.9.6",
+ "motion-utils": "^12.9.4",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2977,6 +3005,21 @@
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
"license": "MIT"
},
+ "node_modules/motion-dom": {
+ "version": "12.9.6",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.9.6.tgz",
+ "integrity": "sha512-IK9pm5zU8BIp3FCoUGF3T7AHVLVOlXxlwco/bIbcnpBtyYb2gDQhdOzUh2KSDJVjYl1MZ9vdq8tnFTTahX2lfg==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.9.4"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.9.4",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.9.4.tgz",
+ "integrity": "sha512-BW3I65zeM76CMsfh3kHid9ansEJk9Qvl+K5cu4DVHKGsI52n76OJ4z2CUJUV+Mn3uEP9k1JJA3tClG0ggSrRcg==",
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3427,6 +3470,12 @@
"node": ">=8.0"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/package.json b/package.json
index a8ec0eb..964af04 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"@monaco-editor/react": "^4.7.0",
"@uiw/codemirror-theme-vscode": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9",
+ "framer-motion": "^12.9.7",
"monaco-editor": "^0.52.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
diff --git a/public/images/battlesnake.png b/public/images/battlesnake.png
new file mode 100644
index 0000000..1f3206a
Binary files /dev/null and b/public/images/battlesnake.png differ
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
index f507995..48226b0 100644
--- a/src/components/Navbar.jsx
+++ b/src/components/Navbar.jsx
@@ -87,7 +87,7 @@ const Navbar = () => {
diff --git a/src/components/Services.jsx b/src/components/Services.jsx
index c4e59ce..bc25437 100644
--- a/src/components/Services.jsx
+++ b/src/components/Services.jsx
@@ -1,17 +1,29 @@
-import React from 'react';
-import '../scss/components/Services.scss';
-import ServiceCard from '../components/ServicesCard.jsx';
+import React from "react";
+import "../scss/components/_services.scss";
+import ServiceCard from "../components/ServicesCard.jsx";
+import battlesnakeLogo from "../../public/images/battlesnake.png";
function Services() {
return (
- Develop your own algorithm to find food, stay alive, and eliminate others. Battlesnakes are controlled by a web server you deploy, running the code you write.
+
+ Develop your own algorithm to find food, stay alive, and eliminate
+ others. Battlesnakes are controlled by a web server you deploy, running
+ the code you write.
+
+ }
title="What is Battlesnake?"
desc="Battlesnake is a competitive game where your code is the controller. All you need is a web server that responds to the Battlesnake API."
- cta="What how it works"
+ cta="How it works?"
+ link="https://docs.battlesnake.com/"
/>
{
+ if (isInView) {
+ controls.start("visible");
+ }
+ }, [isInView, controls]);
+
+ // Different animation variants based on the card index for a staggered effect
+ const getVariants = () => {
+ // Alternate between slide-in directions based on index
+ const direction = index % 2 === 0 ? 1 : -1;
+
+ return {
+ hidden: {
+ opacity: 0,
+ x: 60 * direction,
+ y: 20,
+ },
+ visible: {
+ opacity: 1,
+ x: 0,
+ y: 0,
+ transition: {
+ type: "spring",
+ duration: 0.8,
+ delay: index * 0.2, // Stagger effect based on index
+ damping: 15,
+ stiffness: 100,
+ },
+ },
+ };
+ };
-function ServiceCard({ icon, title, desc, cta }) {
return (
-
-
{icon}
-
{title}
+
+
+
+
+
+ {icon}
+
+
+
+ {title}
+
+
{desc}
- {cta} →
-
+
+
+ {cta} →
+
+
);
}
diff --git a/src/scss/components/Services.scss b/src/scss/components/Services.scss
deleted file mode 100644
index 563a6aa..0000000
--- a/src/scss/components/Services.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-.services {
- padding: 5rem 3rem;
- // background-image: url("../../../public/images/grid-background-3.png");
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- background-attachment: fixed;
-
- h2 {
- font-size: 2.5rem;
- font-weight: 900;
- text-align: center;
- margin-bottom: 3rem;
- color: #fff;
- text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7);
- background-color: rgba(0, 0, 0, 0.5);
- padding: 1.5rem;
- border-radius: 10px;
- }
-
- .cards {
- display: flex;
- justify-content: space-between;
- gap: 2rem;
- }
-}
diff --git a/src/scss/components/ServicesCard.scss b/src/scss/components/ServicesCard.scss
deleted file mode 100644
index f6a5848..0000000
--- a/src/scss/components/ServicesCard.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-.service-card {
- background-color: #fff;
- border: 2px solid #000;
- border-radius: 1rem;
- padding: 2rem;
- box-shadow: 6px 6px 0 #000;
- flex: 1;
-
- .icon {
- font-size: 2.5rem;
- margin-bottom: 1rem;
- }
-
- h3 {
- font-size: 1.5rem;
- font-weight: 700;
- margin-bottom: 1rem;
- }
-
- p {
- font-size: 1rem;
- line-height: 1.6;
- margin-bottom: 1.5rem;
- }
-
- .link {
- font-weight: bold;
- text-decoration: none;
- color: #202020;
- }
- }
-
\ No newline at end of file
diff --git a/src/scss/components/_navbar.scss b/src/scss/components/_navbar.scss
index 117bb35..6c687a6 100644
--- a/src/scss/components/_navbar.scss
+++ b/src/scss/components/_navbar.scss
@@ -202,7 +202,7 @@ $mobile-breakpoint: 1160px;
background-color: rgba($dark-bg, 0.7);
&-text {
- font-size: 2rem;
+ font-size: 1.2rem;
color: white;
letter-spacing: 2px;
animation: neon-flicker 5s infinite alternate;
diff --git a/src/scss/components/_services.scss b/src/scss/components/_services.scss
new file mode 100644
index 0000000..84e78f8
--- /dev/null
+++ b/src/scss/components/_services.scss
@@ -0,0 +1,77 @@
+// Services.scss
+$neon-pink: #ff2a6d;
+$neon-blue: #05d9e8;
+$neon-purple: #d300c5;
+$neon-yellow: #f7f500;
+$dark-bg: #0d0221;
+$cyber-black: #1a1a1a;
+
+@keyframes gridMovement {
+ 0% {
+ background-position: 0 0;
+ }
+ 100% {
+ background-position: 0 25px;
+ }
+}
+
+.services {
+ padding: 5rem 3rem;
+ background-color: $dark-bg;
+ position: relative;
+ overflow: hidden;
+
+ // Grid background effect
+ .grid-background {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-image: linear-gradient(
+ rgba($neon-blue, 0.1) 1px,
+ transparent 1px
+ ),
+ linear-gradient(90deg, rgba($neon-blue, 0.1) 1px, transparent 1px);
+ background-size: 25px 25px;
+ z-index: 0;
+ opacity: 0.3;
+ pointer-events: none;
+ animation: gridMovement 15s linear infinite;
+ }
+
+ .services-heading {
+ font-size: 2rem;
+ font-weight: 700;
+ text-align: center;
+ margin-bottom: 4rem;
+ color: white;
+ text-shadow: 0 0 10px rgba($neon-pink, 0.5);
+ position: relative;
+ z-index: 1;
+ max-width: 900px;
+ margin-left: auto;
+ margin-right: auto;
+
+ // Responsive font size
+ @media (max-width: 768px) {
+ font-size: 1.5rem;
+ margin-bottom: 2.5rem;
+ }
+ }
+
+ .cards-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 2rem;
+ position: relative;
+ z-index: 1;
+ max-width: 1200px;
+ margin: 0 auto;
+
+ // Stack cards on small screens
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
+ }
+ }
+}
diff --git a/src/scss/components/_servicesCard.scss b/src/scss/components/_servicesCard.scss
new file mode 100644
index 0000000..8bb5ed5
--- /dev/null
+++ b/src/scss/components/_servicesCard.scss
@@ -0,0 +1,115 @@
+// ServicesCard.scss
+$neon-pink: #ff2a6d;
+$neon-blue: #05d9e8;
+$neon-purple: #d300c5;
+$neon-yellow: #f7f500;
+$dark-bg: #0d0221;
+$cyber-black: #1a1a1a;
+
+@keyframes scanline {
+ 0% {
+ transform: translateY(-100%);
+ }
+ 100% {
+ transform: translateY(100%);
+ }
+}
+
+@keyframes borderGlow {
+ 0% {
+ box-shadow: 0 0 5px $neon-pink;
+ }
+ 50% {
+ box-shadow: 0 0 15px $neon-pink, 0 0 25px $neon-pink;
+ }
+ 100% {
+ box-shadow: 0 0 5px $neon-pink;
+ }
+}
+
+.service-card {
+ position: relative;
+ background-color: $cyber-black;
+ border-radius: 4px;
+ padding: 2rem;
+ color: white;
+ margin-bottom: 20px;
+ overflow: hidden;
+ border: 1px solid rgba($neon-pink, 0.5);
+ box-shadow: 0 0 10px rgba($neon-pink, 0.3);
+ width: 100%;
+
+ // The card border effect
+ .card-border {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: -1;
+ opacity: 0;
+ transition: opacity 0.5s ease;
+ }
+
+ // Scanline effect
+ .card-glow {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ background: linear-gradient(90deg, transparent, $neon-pink, transparent);
+ animation: scanline 2s linear infinite;
+ opacity: 0;
+ transition: opacity 0.5s ease;
+ }
+
+ &:hover {
+ animation: borderGlow 1.5s infinite;
+ border-color: $neon-pink;
+
+ .card-border {
+ opacity: 1;
+ }
+
+ .card-glow {
+ opacity: 0.7;
+ }
+ }
+
+ .icon {
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
+ transition: color 0.3s ease, text-shadow 0.3s ease;
+ }
+
+ h3 {
+ font-size: 1.5rem;
+ font-weight: 700;
+ margin-bottom: 1rem;
+ letter-spacing: 1px;
+ color: white;
+ transition: color 0.3s ease, text-shadow 0.3s ease;
+ }
+
+ p {
+ font-size: 1rem;
+ line-height: 1.6;
+ margin-bottom: 1.5rem;
+ color: #aaa;
+ }
+
+ .link {
+ font-weight: bold;
+ text-decoration: none;
+ color: white;
+ transition: color 0.3s ease;
+ display: inline-block;
+ letter-spacing: 0.5px;
+ position: relative;
+
+ &:hover {
+ color: $neon-pink;
+ }
+ }
+}