ParthSadaria commited on
Commit
63dc499
·
verified ·
1 Parent(s): 7b61a04

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +263 -84
index.html CHANGED
@@ -49,7 +49,7 @@
49
  position: relative;
50
  overflow: hidden;
51
  cursor: none; /* Hide default cursor */
52
- perspective: 1000px;
53
  }
54
 
55
  /* Subtle Animated Background Gradient */
@@ -82,26 +82,57 @@
82
  padding: 2rem;
83
  max-width: 900px;
84
  width: 90%;
85
- animation: slideInUp 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.5s forwards;
 
86
  opacity: 0;
87
- transform: translateY(30px);
 
88
  }
89
 
 
90
  @keyframes slideInUp {
91
- to {
 
 
 
 
 
92
  opacity: 1;
93
- transform: translateY(0);
 
94
  }
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  h1 {
98
  font-size: clamp(3rem, 10vw, 6rem);
99
  font-weight: 700;
100
  letter-spacing: -0.05em;
101
- margin-bottom: 0.2rem;
102
  color: var(--text-color);
103
  text-shadow: 0 0 15px rgba(255, 255, 255, 0.1);
104
  transition: color var(--transition-speed) ease, text-shadow var(--transition-speed) ease;
 
 
105
  }
106
 
107
  .subtitle {
@@ -111,12 +142,16 @@
111
  margin-bottom: 2.5rem;
112
  letter-spacing: 0.05em;
113
  transition: color var(--transition-speed) ease;
 
 
114
  }
115
 
116
  .models-section {
117
  margin-bottom: 3rem;
118
  opacity: 0;
119
  animation: fadeIn 1s ease 1s forwards;
 
 
120
  }
121
 
122
  .models-section h2 {
@@ -146,15 +181,18 @@
146
  border-radius: 15px;
147
  border: 1px solid var(--border-color);
148
  transition: all var(--transition-speed) cubic-bezier(0.25, 0.46, 0.45, 0.94);
149
- transform: scale(1);
 
 
150
  }
151
 
152
  .model-badge:hover {
153
  background-color: var(--hover-bg);
154
  color: var(--accent-color);
155
  border-color: var(--accent-color);
156
- transform: scale(1.05) translateY(-2px);
157
- box-shadow: 0 4px 15px rgba(0, 255, 255, 0.2);
 
158
  }
159
 
160
  .playgrounds-section {
@@ -164,6 +202,8 @@
164
  flex-direction: column; /* Stack links vertically on mobile */
165
  align-items: center;
166
  gap: 1rem;
 
 
167
  }
168
 
169
  @media (min-width: 600px) {
@@ -187,6 +227,7 @@
187
  overflow: hidden;
188
  transition: all var(--transition-speed) ease;
189
  z-index: 1;
 
190
  }
191
 
192
  .playground-link::before {
@@ -204,8 +245,9 @@
204
  .playground-link:hover {
205
  color: var(--bg-color);
206
  border-color: var(--accent-color);
207
- transform: translateY(-3px);
208
- box-shadow: 0 5px 20px rgba(0, 255, 255, 0.3);
 
209
  }
210
 
211
  .playground-link:hover::before {
@@ -225,20 +267,18 @@
225
 
226
  .glow {
227
  position: fixed;
228
- /* Position set by JS */
229
  left: 0;
230
  top: 0;
231
  width: 800px;
232
  height: 800px;
233
  background: radial-gradient(circle at center, var(--glow-color) 0%, rgba(0, 0, 0, 0) 60%);
234
- border-radius: 50%; /* Ensure it's circular for centering */
235
  pointer-events: none;
236
  opacity: 0;
237
- transition: opacity 0.4s ease; /* Only transition opacity */
238
- /* transform: translate(-50%, -50%); NO - JS handles transform */
239
  z-index: 1;
240
  filter: blur(10px);
241
- will-change: transform, opacity; /* Optimize animation */
242
  }
243
 
244
  body:hover .glow {
@@ -247,7 +287,6 @@
247
 
248
  .cursor {
249
  position: fixed;
250
- /* Position set by JS */
251
  left: 0;
252
  top: 0;
253
  width: 25px;
@@ -255,29 +294,24 @@
255
  border: 2px solid var(--accent-color);
256
  border-radius: 50%;
257
  pointer-events: none;
258
- transition: background-color 0.2s ease, border-color 0.2s ease, border-width 0.2s ease, width 0.2s ease, height 0.2s ease; /* Smooth style changes, transform handled by JS loop */
259
- /* transform: translate(-50%, -50%); NO - JS handles transform */
260
  z-index: 9999;
261
  mix-blend-mode: difference;
262
  background-color: transparent;
263
- will-change: transform; /* Optimize animation */
264
  }
265
 
266
- /* Cursor interaction states (applied via JS) */
267
  .cursor.hover-link {
268
- /* Scale handled by JS */
269
  background-color: var(--accent-color);
270
  border-color: transparent;
271
  }
272
 
273
  .cursor.hover-text {
274
- /* Scale handled by JS */
275
  border-width: 4px;
276
  border-color: rgba(255,255,255, 0.5);
277
  }
278
 
279
  .cursor.clicking {
280
- /* Scale handled by JS */
281
  background-color: rgba(255,255,255,0.3);
282
  }
283
 
@@ -287,6 +321,13 @@
287
 
288
  /* Responsive adjustments */
289
  @media (max-width: 768px) {
 
 
 
 
 
 
 
290
  h1 {
291
  letter-spacing: -0.03em;
292
  }
@@ -302,12 +343,21 @@
302
  .playground-link {
303
  padding: 0.7em 1.5em;
304
  }
305
- /* Reduce glow size on smaller screens */
306
  .glow {
307
  width: 500px;
308
  height: 500px;
309
  }
310
  }
 
 
 
 
 
 
 
 
 
 
311
 
312
  </style>
313
  </head>
@@ -318,7 +368,13 @@
318
 
319
  <!-- Main Content -->
320
  <div class="container">
321
- <h1>Loki.AI</h1>
 
 
 
 
 
 
322
  <p class="subtitle">By Parth Sadaria</p>
323
 
324
  <div class="models-section">
@@ -347,7 +403,11 @@
347
  </div>
348
  </div>
349
 
 
 
 
350
  <script>
 
351
  const cursor = document.querySelector('.cursor');
352
  const glow = document.querySelector('.glow');
353
  const hoverables = document.querySelectorAll('a, .model-badge');
@@ -360,51 +420,61 @@
360
  let glowX = 0;
361
  let glowY = 0;
362
 
363
- // Adjust speed for desired smoothness (lower = smoother, more 'lag')
364
  const cursorSpeed = 0.15;
365
  const glowSpeed = 0.1;
366
 
367
- // Store current scale factor for smooth transitions
368
  let currentCursorScale = 1;
369
  const baseCursorScale = 1;
370
  const hoverLinkCursorScale = 1.5;
371
  const hoverTextCursorScale = 0.7;
372
  const clickCursorScale = 0.6;
373
 
374
- // Pre-calculate half-widths/heights ONCE (assuming they don't change)
375
- // Note: If elements resize dynamically, might need recalculation
376
- let cursorHalfWidth = cursor.offsetWidth / 2;
377
- let cursorHalfHeight = cursor.offsetHeight / 2;
378
- let glowHalfWidth = glow.offsetWidth / 2;
379
- let glowHalfHeight = glow.offsetHeight / 2;
380
-
381
- // Recalculate on resize just in case
382
- window.addEventListener('resize', () => {
383
- cursorHalfWidth = cursor.offsetWidth / 2;
384
- cursorHalfHeight = cursor.offsetHeight / 2;
385
- glowHalfWidth = glow.offsetWidth / 2;
386
- glowHalfHeight = glow.offsetHeight / 2;
387
- });
 
 
 
 
388
 
 
 
 
 
389
 
390
- function animate() {
391
- // Lerp for smooth following
392
  cursorX += (mouseX - cursorX) * cursorSpeed;
393
  cursorY += (mouseY - cursorY) * cursorSpeed;
394
  glowX += (mouseX - glowX) * glowSpeed;
395
  glowY += (mouseY - glowY) * glowSpeed;
396
 
397
- // Apply transform for positioning and scaling
398
- // Calculate the top-left position needed to center the element
399
  const cursorTranslateX = cursorX - cursorHalfWidth;
400
  const cursorTranslateY = cursorY - cursorHalfHeight;
401
  const glowTranslateX = glowX - glowHalfWidth;
402
  const glowTranslateY = glowY - glowHalfHeight;
403
 
404
- cursor.style.transform = `translate(${cursorTranslateX}px, ${cursorTranslateY}px) scale(${currentCursorScale})`;
405
- glow.style.transform = `translate(${glowTranslateX}px, ${glowTranslateY}px)`; // Glow doesn't scale dynamically here
 
 
 
 
 
 
406
 
407
- requestAnimationFrame(animate);
408
  }
409
 
410
  document.addEventListener('mousemove', (e) => {
@@ -412,25 +482,23 @@
412
  mouseY = e.clientY;
413
  });
414
 
415
- // Start the animation loop
416
- animate();
417
-
418
  function updateCursorState() {
419
  let targetScale = baseCursorScale;
420
  let isHoveringLink = false;
421
  let isHoveringText = false;
422
 
423
- // Check hover states directly (more robust than relying on mouseenter/leave timing with lerp)
424
  hoverables.forEach(el => {
425
- const rect = el.getBoundingClientRect();
426
- if (mouseX >= rect.left && mouseX <= rect.right && mouseY >= rect.top && mouseY <= rect.bottom) {
427
- isHoveringLink = true;
 
 
 
428
  }
429
  });
430
 
431
  textElements.forEach(el => {
432
- // Avoid text hover if already hovering a link defined in hoverables
433
- if (!isHoveringLink) {
434
  const rect = el.getBoundingClientRect();
435
  if (mouseX >= rect.left && mouseX <= rect.right && mouseY >= rect.top && mouseY <= rect.bottom) {
436
  isHoveringText = true;
@@ -438,46 +506,157 @@
438
  }
439
  });
440
 
441
- // Apply styles and target scale based on state
442
- cursor.classList.toggle('hover-link', isHoveringLink);
443
- cursor.classList.toggle('hover-text', isHoveringText && !isHoveringLink); // Only text hover if not link hover
 
444
 
445
- if (isHoveringLink) {
446
- targetScale = hoverLinkCursorScale;
447
- } else if (isHoveringText) {
448
- targetScale = hoverTextCursorScale;
449
- }
450
 
451
- // Handle clicking state separately
452
- if (cursor.classList.contains('clicking')) {
453
- // Optionally combine scales or prioritize click scale
454
- targetScale = clickCursorScale; // Click scale overrides others
455
- }
456
 
457
- // Smoothly transition scale (optional, can also rely on CSS transition if preferred)
458
- // currentCursorScale += (targetScale - currentCursorScale) * 0.2; // Lerp scale
459
- // Simpler: Just set the target scale and let JS/CSS handle visual update
460
  currentCursorScale = targetScale;
461
-
462
  }
463
 
464
- // Update state on mouse move for accuracy
465
  document.addEventListener('mousemove', updateCursorState);
466
 
467
-
468
- // Add/Remove clicking class
469
  document.addEventListener('mousedown', () => {
470
- cursor.classList.add('clicking');
471
- updateCursorState(); // Re-evaluate state immediately on click
472
  });
473
 
474
  document.addEventListener('mouseup', () => {
475
- cursor.classList.remove('clicking');
476
- updateCursorState(); // Re-evaluate state immediately on release
477
  });
478
 
479
- // Initial state check in case mouse enters window over an element
480
- updateCursorState();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481
 
482
  </script>
483
  </body>
 
49
  position: relative;
50
  overflow: hidden;
51
  cursor: none; /* Hide default cursor */
52
+ perspective: 1000px; /* Enable 3D transforms for children */
53
  }
54
 
55
  /* Subtle Animated Background Gradient */
 
82
  padding: 2rem;
83
  max-width: 900px;
84
  width: 90%;
85
+ /* Updated animation with 3D rotation */
86
+ animation: slideInUp 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.5s forwards;
87
  opacity: 0;
88
+ transform-style: preserve-3d; /* Ensure children respect 3D space */
89
+ /* Initial transform moved to keyframe */
90
  }
91
 
92
+ /* Enhanced Entry Animation */
93
  @keyframes slideInUp {
94
+ 0% {
95
+ opacity: 0;
96
+ /* Start further down, slightly rotated back */
97
+ transform: translateY(60px) rotateX(-15deg) translateZ(-50px);
98
+ }
99
+ 100% {
100
  opacity: 1;
101
+ /* End in the final position with no rotation */
102
+ transform: translateY(0) rotateX(0deg) translateZ(0);
103
  }
104
  }
105
 
106
+ .title-container {
107
+ position: relative; /* Needed for absolute positioning of canvas */
108
+ display: inline-block; /* Keep it tight around the h1 */
109
+ margin-bottom: 0.2rem;
110
+ }
111
+
112
+ /* Canvas for the 3D Glass Ball */
113
+ #glass-ball-canvas {
114
+ position: absolute;
115
+ top: 50%; /* Center vertically */
116
+ left: 50%; /* Center horizontally */
117
+ width: 180px; /* Adjust size as needed */
118
+ height: 180px; /* Adjust size as needed */
119
+ transform: translate(-50%, -65%) translateZ(20px); /* Center and bring slightly forward, adjust Y offset */
120
+ z-index: 1; /* Place it behind the text (adjust if needed) */
121
+ pointer-events: none; /* Allow clicks to pass through */
122
+ opacity: 0; /* Start hidden */
123
+ animation: fadeIn 2s ease 1.5s forwards; /* Fade in after main animation */
124
+ }
125
+
126
  h1 {
127
  font-size: clamp(3rem, 10vw, 6rem);
128
  font-weight: 700;
129
  letter-spacing: -0.05em;
130
+ /* margin-bottom: 0.2rem; Removed, handled by title-container */
131
  color: var(--text-color);
132
  text-shadow: 0 0 15px rgba(255, 255, 255, 0.1);
133
  transition: color var(--transition-speed) ease, text-shadow var(--transition-speed) ease;
134
+ position: relative; /* Needed for z-index stacking */
135
+ z-index: 2; /* Ensure text is above the canvas */
136
  }
137
 
138
  .subtitle {
 
142
  margin-bottom: 2.5rem;
143
  letter-spacing: 0.05em;
144
  transition: color var(--transition-speed) ease;
145
+ position: relative; /* Stacking context */
146
+ z-index: 2;
147
  }
148
 
149
  .models-section {
150
  margin-bottom: 3rem;
151
  opacity: 0;
152
  animation: fadeIn 1s ease 1s forwards;
153
+ position: relative; /* Stacking context */
154
+ z-index: 2;
155
  }
156
 
157
  .models-section h2 {
 
181
  border-radius: 15px;
182
  border: 1px solid var(--border-color);
183
  transition: all var(--transition-speed) cubic-bezier(0.25, 0.46, 0.45, 0.94);
184
+ transform: scale(1) translateZ(0); /* Prepare for 3D hover */
185
+ position: relative; /* Stacking context */
186
+ z-index: 2;
187
  }
188
 
189
  .model-badge:hover {
190
  background-color: var(--hover-bg);
191
  color: var(--accent-color);
192
  border-color: var(--accent-color);
193
+ /* Subtle 3D lift effect */
194
+ transform: scale(1.05) translateY(-2px) translateZ(10px);
195
+ box-shadow: 0 6px 20px rgba(0, 255, 255, 0.25);
196
  }
197
 
198
  .playgrounds-section {
 
202
  flex-direction: column; /* Stack links vertically on mobile */
203
  align-items: center;
204
  gap: 1rem;
205
+ position: relative; /* Stacking context */
206
+ z-index: 2;
207
  }
208
 
209
  @media (min-width: 600px) {
 
227
  overflow: hidden;
228
  transition: all var(--transition-speed) ease;
229
  z-index: 1;
230
+ transform: translateZ(0); /* Prepare for 3D hover */
231
  }
232
 
233
  .playground-link::before {
 
245
  .playground-link:hover {
246
  color: var(--bg-color);
247
  border-color: var(--accent-color);
248
+ /* Subtle 3D lift */
249
+ transform: translateY(-3px) translateZ(15px);
250
+ box-shadow: 0 8px 25px rgba(0, 255, 255, 0.35);
251
  }
252
 
253
  .playground-link:hover::before {
 
267
 
268
  .glow {
269
  position: fixed;
 
270
  left: 0;
271
  top: 0;
272
  width: 800px;
273
  height: 800px;
274
  background: radial-gradient(circle at center, var(--glow-color) 0%, rgba(0, 0, 0, 0) 60%);
275
+ border-radius: 50%;
276
  pointer-events: none;
277
  opacity: 0;
278
+ transition: opacity 0.4s ease;
 
279
  z-index: 1;
280
  filter: blur(10px);
281
+ will-change: transform, opacity;
282
  }
283
 
284
  body:hover .glow {
 
287
 
288
  .cursor {
289
  position: fixed;
 
290
  left: 0;
291
  top: 0;
292
  width: 25px;
 
294
  border: 2px solid var(--accent-color);
295
  border-radius: 50%;
296
  pointer-events: none;
297
+ transition: background-color 0.2s ease, border-color 0.2s ease, border-width 0.2s ease, width 0.2s ease, height 0.2s ease;
 
298
  z-index: 9999;
299
  mix-blend-mode: difference;
300
  background-color: transparent;
301
+ will-change: transform;
302
  }
303
 
 
304
  .cursor.hover-link {
 
305
  background-color: var(--accent-color);
306
  border-color: transparent;
307
  }
308
 
309
  .cursor.hover-text {
 
310
  border-width: 4px;
311
  border-color: rgba(255,255,255, 0.5);
312
  }
313
 
314
  .cursor.clicking {
 
315
  background-color: rgba(255,255,255,0.3);
316
  }
317
 
 
321
 
322
  /* Responsive adjustments */
323
  @media (max-width: 768px) {
324
+ /* Adjust canvas size/position on smaller screens if needed */
325
+ #glass-ball-canvas {
326
+ width: 120px;
327
+ height: 120px;
328
+ transform: translate(-50%, -60%) translateZ(15px); /* Adjust Y offset and Z */
329
+ }
330
+
331
  h1 {
332
  letter-spacing: -0.03em;
333
  }
 
343
  .playground-link {
344
  padding: 0.7em 1.5em;
345
  }
 
346
  .glow {
347
  width: 500px;
348
  height: 500px;
349
  }
350
  }
351
+ @media (max-width: 480px) {
352
+ #glass-ball-canvas {
353
+ width: 100px;
354
+ height: 100px;
355
+ transform: translate(-50%, -55%) translateZ(10px); /* Further adjust */
356
+ }
357
+ h1 {
358
+ font-size: clamp(2.5rem, 10vw, 4rem); /* Slightly smaller H1 on very small screens */
359
+ }
360
+ }
361
 
362
  </style>
363
  </head>
 
368
 
369
  <!-- Main Content -->
370
  <div class="container">
371
+
372
+ <!-- Title container for positioning canvas -->
373
+ <div class="title-container">
374
+ <!-- Canvas for 3D Glass Ball -->
375
+ <canvas id="glass-ball-canvas"></canvas>
376
+ <h1>Loki.AI</h1>
377
+ </div>
378
  <p class="subtitle">By Parth Sadaria</p>
379
 
380
  <div class="models-section">
 
403
  </div>
404
  </div>
405
 
406
+ <!-- Include Three.js Library -->
407
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
408
+
409
  <script>
410
+ // --- Existing Cursor Logic ---
411
  const cursor = document.querySelector('.cursor');
412
  const glow = document.querySelector('.glow');
413
  const hoverables = document.querySelectorAll('a, .model-badge');
 
420
  let glowX = 0;
421
  let glowY = 0;
422
 
 
423
  const cursorSpeed = 0.15;
424
  const glowSpeed = 0.1;
425
 
 
426
  let currentCursorScale = 1;
427
  const baseCursorScale = 1;
428
  const hoverLinkCursorScale = 1.5;
429
  const hoverTextCursorScale = 0.7;
430
  const clickCursorScale = 0.6;
431
 
432
+ let cursorHalfWidth = 12.5; // Initial guess, will be updated
433
+ let cursorHalfHeight = 12.5;
434
+ let glowHalfWidth = 400; // Initial guess
435
+ let glowHalfHeight = 400;
436
+
437
+ function updateElementSizes() {
438
+ cursorHalfWidth = cursor.offsetWidth / 2;
439
+ cursorHalfHeight = cursor.offsetHeight / 2;
440
+ // Only update glow size if the element exists and is rendered
441
+ if (glow && glow.offsetWidth > 0) {
442
+ glowHalfWidth = glow.offsetWidth / 2;
443
+ glowHalfHeight = glow.offsetHeight / 2;
444
+ } else {
445
+ // Fallback or default if not rendered yet (might happen on first frame)
446
+ glowHalfWidth = window.innerWidth > 768 ? 400 : 250;
447
+ glowHalfHeight = window.innerWidth > 768 ? 400 : 250;
448
+ }
449
+ }
450
 
451
+ window.addEventListener('resize', updateElementSizes);
452
+ // Initial size calculation after elements are potentially rendered
453
+ window.addEventListener('load', updateElementSizes);
454
+ updateElementSizes(); // Call once early
455
 
456
+
457
+ function animateCursor() {
458
  cursorX += (mouseX - cursorX) * cursorSpeed;
459
  cursorY += (mouseY - cursorY) * cursorSpeed;
460
  glowX += (mouseX - glowX) * glowSpeed;
461
  glowY += (mouseY - glowY) * glowSpeed;
462
 
 
 
463
  const cursorTranslateX = cursorX - cursorHalfWidth;
464
  const cursorTranslateY = cursorY - cursorHalfHeight;
465
  const glowTranslateX = glowX - glowHalfWidth;
466
  const glowTranslateY = glowY - glowHalfHeight;
467
 
468
+ // Use requestAnimationFrame for cursor/glow as well for consistency
469
+ // Check if elements exist before trying to style them
470
+ if (cursor) {
471
+ cursor.style.transform = `translate(${cursorTranslateX}px, ${cursorTranslateY}px) scale(${currentCursorScale})`;
472
+ }
473
+ if (glow) {
474
+ glow.style.transform = `translate(${glowTranslateX}px, ${glowTranslateY}px)`;
475
+ }
476
 
477
+ // requestAnimationFrame(animateCursor); // This will be called by the main loop
478
  }
479
 
480
  document.addEventListener('mousemove', (e) => {
 
482
  mouseY = e.clientY;
483
  });
484
 
 
 
 
485
  function updateCursorState() {
486
  let targetScale = baseCursorScale;
487
  let isHoveringLink = false;
488
  let isHoveringText = false;
489
 
 
490
  hoverables.forEach(el => {
491
+ // Ensure element is visible before getting rect
492
+ if (el.offsetParent !== null) {
493
+ const rect = el.getBoundingClientRect();
494
+ if (mouseX >= rect.left && mouseX <= rect.right && mouseY >= rect.top && mouseY <= rect.bottom) {
495
+ isHoveringLink = true;
496
+ }
497
  }
498
  });
499
 
500
  textElements.forEach(el => {
501
+ if (!isHoveringLink && el.offsetParent !== null) {
 
502
  const rect = el.getBoundingClientRect();
503
  if (mouseX >= rect.left && mouseX <= rect.right && mouseY >= rect.top && mouseY <= rect.bottom) {
504
  isHoveringText = true;
 
506
  }
507
  });
508
 
509
+ // Check if cursor exists before toggling classes
510
+ if (cursor) {
511
+ cursor.classList.toggle('hover-link', isHoveringLink);
512
+ cursor.classList.toggle('hover-text', isHoveringText && !isHoveringLink);
513
 
514
+ if (isHoveringLink) {
515
+ targetScale = hoverLinkCursorScale;
516
+ } else if (isHoveringText) {
517
+ targetScale = hoverTextCursorScale;
518
+ }
519
 
520
+ if (cursor.classList.contains('clicking')) {
521
+ targetScale = clickCursorScale;
522
+ }
523
+ }
 
524
 
 
 
 
525
  currentCursorScale = targetScale;
 
526
  }
527
 
 
528
  document.addEventListener('mousemove', updateCursorState);
529
 
 
 
530
  document.addEventListener('mousedown', () => {
531
+ if(cursor) cursor.classList.add('clicking');
532
+ updateCursorState();
533
  });
534
 
535
  document.addEventListener('mouseup', () => {
536
+ if(cursor) cursor.classList.remove('clicking');
537
+ updateCursorState();
538
  });
539
 
540
+ updateCursorState(); // Initial check
541
+
542
+ // --- New Three.js Logic for Glass Ball ---
543
+ let scene, camera, renderer, sphere, pointLight;
544
+
545
+ function initThreeJS() {
546
+ const canvas = document.getElementById('glass-ball-canvas');
547
+ if (!canvas || !THREE) return; // Ensure canvas and THREE exist
548
+
549
+ // Scene
550
+ scene = new THREE.Scene();
551
+
552
+ // Camera
553
+ const canvasRect = canvas.getBoundingClientRect();
554
+ // Adjust FOV based on desired sphere appearance size within canvas
555
+ camera = new THREE.PerspectiveCamera(50, canvasRect.width / canvasRect.height, 0.1, 100);
556
+ // Position camera to view the sphere centered at (0,0,0)
557
+ camera.position.z = 3;
558
+
559
+ // Renderer
560
+ renderer = new THREE.WebGLRenderer({
561
+ canvas: canvas,
562
+ alpha: true, // Transparent background
563
+ antialias: true
564
+ });
565
+ renderer.setSize(canvasRect.width, canvasRect.height);
566
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Optimize for high DPI displays
567
+
568
+ // Geometry
569
+ const geometry = new THREE.SphereGeometry(1, 32, 32); // Radius 1, detail 32x32
570
+
571
+ // Material - Physical material for glass effect
572
+ const material = new THREE.MeshPhysicalMaterial({
573
+ color: 0xffffff, // Base color (can be white for clear glass)
574
+ metalness: 0.1, // Low metalness for non-metals
575
+ roughness: 0.05, // Very low roughness for smooth glass
576
+ transmission: 0.95, // High transmission (opacity/translucency)
577
+ transparent: true,
578
+ opacity: 0.9, // Overall opacity (can be slightly less than 1)
579
+ ior: 1.52, // Index of Refraction for glass (~1.52)
580
+ thickness: 0.5, // Simulates thickness for refraction effects
581
+ // clearcoat: 1.0, // Optional: add a clear coat layer
582
+ // clearcoatRoughness: 0.1, // Roughness of the clear coat
583
+ envMapIntensity: 1 // How much environment affects reflection (needs env map for best results, but works ok without)
584
+ });
585
+
586
+ // Mesh
587
+ sphere = new THREE.Mesh(geometry, material);
588
+ scene.add(sphere);
589
+
590
+ // Lighting
591
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // Soft white light
592
+ scene.add(ambientLight);
593
+
594
+ pointLight = new THREE.PointLight(var(--accent-color), 2, 10); // Cyan light, intensity 2, distance 10
595
+ pointLight.position.set(1.5, 2, 2); // Position the light
596
+ scene.add(pointLight);
597
+
598
+ // Optional: Add another subtle light for more dimension
599
+ const pointLight2 = new THREE.PointLight(0xffffff, 0.7, 10);
600
+ pointLight2.position.set(-2, -1, 1.5);
601
+ scene.add(pointLight2);
602
+
603
+ // Start animation loop (combined with cursor loop)
604
+ animateCombined();
605
+
606
+ // Handle Resize
607
+ window.addEventListener('resize', onWindowResize, false);
608
+ }
609
+
610
+ function onWindowResize() {
611
+ const canvas = document.getElementById('glass-ball-canvas');
612
+ if (!canvas || !camera || !renderer) return;
613
+
614
+ const canvasRect = canvas.getBoundingClientRect(); // Use current rect
615
+ const newWidth = canvasRect.width;
616
+ const newHeight = canvasRect.height;
617
+
618
+ // Update camera aspect ratio
619
+ camera.aspect = newWidth / newHeight;
620
+ camera.updateProjectionMatrix();
621
+
622
+ // Update renderer size
623
+ renderer.setSize(newWidth, newHeight);
624
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
625
+
626
+ // Update cursor/glow element sizes too
627
+ updateElementSizes();
628
+ }
629
+
630
+ // Combined Animation Loop
631
+ function animateCombined() {
632
+ requestAnimationFrame(animateCombined);
633
+
634
+ // Update Cursor & Glow position/state
635
+ animateCursor(); // Updates cursor/glow positions via lerp
636
+ updateCursorState(); // Updates cursor style/scale based on hover
637
+
638
+ // Update Three.js Scene (if initialized)
639
+ if (sphere && scene && camera && renderer) {
640
+ // Subtle rotation
641
+ sphere.rotation.y += 0.003;
642
+ sphere.rotation.x += 0.001;
643
+
644
+ // Maybe subtle light movement or reaction to mouse (more complex)
645
+ // Example: light follows mouse slightly on x-axis
646
+ // pointLight.position.x = 1.5 + (mouseX / window.innerWidth - 0.5) * 2;
647
+
648
+ // Render the 3D scene
649
+ renderer.render(scene, camera);
650
+ }
651
+ }
652
+
653
+ // Initialize Three.js after the DOM is ready
654
+ // Use setTimeout to ensure layout is somewhat stable, especially for canvas size
655
+ // Alternatively, use window.onload if dependencies are complex
656
+ document.addEventListener('DOMContentLoaded', () => {
657
+ // Small delay to help ensure canvas has dimensions
658
+ setTimeout(initThreeJS, 100);
659
+ });
660
 
661
  </script>
662
  </body>