Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/components/announcementBanner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const BANNERS = [
url: 'https://www.hangingpiece.com?utm_source=acode_announcement_banner',
alt: 'Hanging Piece',
badge: 'NEW',
title: 'Hanging Piece - AI Chess Coach',
subtitle: 'Understand WHY you blundered. Stop repeating mistakes!',
title: 'Hanging Piece - Claude Code for Chess',
subtitle: 'Understand WHY you blundered and HOW to improve!',
cta: 'Try Now',
theme: 'hangingpiece',
},
Expand Down
57 changes: 48 additions & 9 deletions client/lib/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,39 @@ class Particle {
* @param {number} dx
* @param {number} dy
* @param {string} color
* @param {number} opacity
*/
constructor(canvas, radius, x, y, dx, dy, color) {
constructor(canvas, radius, x, y, dx, dy, color, opacity) {
this.radius = radius;
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.color = color;
this.opacity = opacity;
this.canvas = canvas;
}

draw(ctx) {
// Draw glow effect
const gradient = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius * 3);
gradient.addColorStop(0, this.color);
gradient.addColorStop(0.5, `${this.color}40`); // Add transparency to hex
gradient.addColorStop(1, `${this.color}00`);
Comment on lines +27 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string concatenation with hex colors doesn't create transparency properly - ${this.color}40 produces strings like #00d4ff40 which is invalid CSS

Suggested change
gradient.addColorStop(0.5, `${this.color}40`); // Add transparency to hex
gradient.addColorStop(1, `${this.color}00`);
gradient.addColorStop(0.5, `${this.color}66`); // 40% opacity in hex
gradient.addColorStop(1, `${this.color}00`);
Prompt To Fix With AI
This is a comment left during a code review.
Path: client/lib/background.js
Line: 27:28

Comment:
string concatenation with hex colors doesn't create transparency properly - `${this.color}40` produces strings like `#00d4ff40` which is invalid CSS

```suggestion
    gradient.addColorStop(0.5, `${this.color}66`); // 40% opacity in hex
    gradient.addColorStop(1, `${this.color}00`);
```

How can I resolve this? If you propose a fix, please make it concise.


ctx.globalAlpha = this.opacity * 0.3;
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.arc(this.x, this.y, this.radius * 3, 0, 2 * Math.PI);
ctx.fill();

// Draw main particle
ctx.globalAlpha = this.opacity;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
ctx.globalAlpha = 1;
}

update() {
Expand Down Expand Up @@ -67,14 +84,26 @@ export default function background(canvas) {

particles = [];

// Dramatic color palette: electric blues, cyans, and deep purples
const colors = [
'#00d4ff', // Electric cyan
'#0099ff', // Bright blue
'#3366ff', // Deep blue
'#6b5bff', // Purple-blue
'#00ffcc', // Aqua
'#1a8fff', // Ocean blue
];

for (let i = 0; i < numParticles; ++i) {
const r = Math.random() * 3 + 1;
const r = Math.random() * 2.5 + 1.5; // Slightly larger particles
const x = Math.random() * (canvas.width - r) + r;
const y = Math.random() * (canvas.height - r) + r;
const dx = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.5;
const dy = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.5;
const dx = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.2;
const dy = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 1.2;
const color = colors[Math.floor(Math.random() * colors.length)];
const opacity = Math.random() * 0.5 + 0.4; // 0.4 to 0.9 - more visible

particles.push(new Particle(canvas, r, x, y, dx, dy, '#606060'));
particles.push(new Particle(canvas, r, x, y, dx, dy, color, opacity));
}

if (reqId != null) cancelAnimationFrame(reqId);
Expand Down Expand Up @@ -106,13 +135,23 @@ export default function background(canvas) {
for (const p2 of particles) {
const distance = Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);

if (distance < 100) {
if (distance < 140) {
// Create gradient line from p1 to p2
const gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y);
gradient.addColorStop(0, p1.color);
gradient.addColorStop(0.5, '#00aaff'); // Electric blue middle
gradient.addColorStop(1, p2.color);

ctx.strokeStyle = gradient;
// More visible connections with better opacity curve
const opacityFactor = 1 - distance / 140;
ctx.globalAlpha = opacityFactor * opacityFactor * 0.5; // Quadratic falloff
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.strokeStyle = '#606060';
ctx.lineWidth = 1;
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
ctx.globalAlpha = 1;
}
}
}
Expand Down
225 changes: 200 additions & 25 deletions client/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ html {
width: 100%;
overflow: hidden;
margin: 0;
background: linear-gradient(to bottom right,
var(--secondary-color),
var(--primary-color),
var(--primary-color),
var(--primary-color));
background: linear-gradient(
135deg,
#1a1f2e 0%,
#2a2f3e 25%,
var(--primary-color) 50%,
#252a38 75%,
var(--secondary-color) 100%
);
color: var(--primary-text-color);
font-family: 'Montserrat', sans-serif;
}
Expand Down Expand Up @@ -88,7 +91,7 @@ header {
align-items: center;
transition: all 1s ease-in-out;

>.icon {
> .icon {
height: 60px;
width: 60px;
display: flex;
Expand Down Expand Up @@ -118,7 +121,7 @@ header {
justify-content: center;
}

#menu-toggler~.mask {
#menu-toggler ~ .mask {
position: fixed;
top: 0;
left: 0;
Expand All @@ -129,11 +132,11 @@ header {
transition: all 0.3s ease-in-out;
}

#menu-toggler:checked~nav:nth-of-type(2) {
#menu-toggler:checked ~ nav:nth-of-type(2) {
transform: translateX(0);
}

#menu-toggler:checked~.mask {
#menu-toggler:checked ~ .mask {
pointer-events: all;
background-color: rgba($color: #000000, $alpha: 0.5);
}
Expand Down Expand Up @@ -424,27 +427,199 @@ code {
width: fit-content;
}

.footer-nav {
display: flex;
gap: 14px;
justify-content: center;
padding: 10px;
flex-wrap: wrap;
footer {
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0.05) 0%,
rgba(0, 0, 0, 0.3) 100%
);
backdrop-filter: blur(30px);
border-top: 1px solid rgba(51, 153, 255, 0.15);
position: relative;
z-index: 0;
padding: 60px 20px 40px;
margin-top: 100px;

.footer-content {
max-width: 1000px;
margin: 0 auto;
display: grid;
grid-template-columns: 2fr 1fr 1fr 1.2fr;
gap: 40px;
margin-bottom: 40px;

@media screen and (max-width: 768px) {
grid-template-columns: 1fr 1fr;
gap: 32px;
}

@media screen and (max-width: 480px) {
grid-template-columns: 1fr;
text-align: center;
}
}

a {
color: white;
.footer-section {
display: flex;
flex-direction: column;
align-items: flex-start;

@media screen and (max-width: 480px) {
align-items: center;
}

h4 {
font-family: 'Lexend', sans-serif;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #fff;
margin-bottom: 16px;
}

&.footer-brand {
.footer-description {
font-size: 0.85rem;
line-height: 1.5;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 20px;
max-width: 240px;

@media screen and (max-width: 480px) {
margin-left: auto;
margin-right: auto;
}
}

.footer-logo {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;

@media screen and (max-width: 768px) {
justify-content: center;
}

img {
height: 24px;
}

span {
font-family: 'Lexend', sans-serif;
font-size: 1.2rem;
font-weight: 700;
background: linear-gradient(135deg, #3399ff, #66b3ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}

.footer-social {
display: flex;
gap: 12px;

@media screen and (max-width: 768px) {
justify-content: center;
}

a {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.6);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

&:hover {
background: rgba(51, 153, 255, 0.1);
border-color: rgba(51, 153, 255, 0.4);
color: #3399ff;
transform: translateY(-2px);
}
}
}
}
}

.footer-links {
display: flex;
flex-direction: column;
gap: 10px;

a {
color: rgba(255, 255, 255, 0.5);
text-decoration: none;
font-size: 0.85rem;
transition: all 0.2s ease;
margin: 0;

&:hover {
color: #fff;
padding-left: 2px;
}
}
}

.digitalocean-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: all 0.2s ease;
font-size: 0.8rem;
margin: 0;
white-space: nowrap;

&:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.2);
color: #fff;
}

img {
opacity: 0.8;
}

span {
font-weight: 500;
}
}
}

footer {
background-color: rgba(0, 0, 0, 0.1);
backdrop-filter: blur(20px);
position: relative;
z-index: 0;
.footer-bottom {
max-width: 1000px;
margin: 0 auto;
padding-top: 32px;
border-top: 1px solid rgba(255, 255, 255, 0.05);
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;

@media screen and (max-width: 768px) {
flex-direction: column;
text-align: center;
padding-top: 24px;
}

p {
color: rgba(255, 255, 255, 0.4);
font-size: 0.8rem;
margin: 0;
}
}
}

.text-center {
text-align: center;
}
}
Loading