Animation Added and Gif file updated

This commit is contained in:
2026-01-22 18:33:46 +05:30
parent 5cd7d95904
commit 3aabb142b9
24 changed files with 2036 additions and 826 deletions

View File

@@ -0,0 +1,192 @@
"use client";
import { useMemo, useRef, useState } from "react";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import { MeshDistortMaterial, Sphere, Preload, Float } from "@react-three/drei";
import * as THREE from "three";
// --- CONFIGURATION ---
const NEURON_COUNT = 18; // Fewer, but more detailed neurons
const CONNECTION_DISTANCE = 5.5; // Distance to form a synapse
const PULSE_SPEED = 2.5; // Speed of the electric signal
// 1. SINGLE NEURON CELL (The Soma)
// We use a distorted sphere to make it look like organic tissue
function NeuronCell({ position }: { position: [number, number, number] }) {
const meshRef = useRef<THREE.Mesh>(null!);
const [hovered, setHover] = useState(false);
// Randomize size slightly for variety
const scale = useMemo(() => 0.6 + Math.random() * 0.4, []);
useFrame((state) => {
// Gentle heartbeat pulsation
const t = state.clock.getElapsedTime();
const pulse = Math.sin(t * 3) * 0.05 + 1;
if (meshRef.current) {
meshRef.current.scale.set(scale * pulse, scale * pulse, scale * pulse);
}
});
return (
<Float speed={2} rotationIntensity={0.5} floatIntensity={0.5}>
<Sphere
ref={meshRef}
args={[1, 32, 32]} // Geometry args
position={position}
onPointerOver={() => setHover(true)}
onPointerOut={() => setHover(false)}
>
<MeshDistortMaterial
color={hovered ? "#06b6d4" : "#22d3ee"} // Cyan 500 to 400
emissive={hovered ? "#0891b2" : "#0e7490"}
emissiveIntensity={0.6}
roughness={0.2}
metalness={0.1}
distort={0.4} // The "wobble" amount (0-1)
speed={2} // Speed of the wobble
/>
<pointLight distance={3} intensity={2} color="#22d3ee" />
</Sphere>
</Float>
);
}
// 2. SYNAPSE (The Connection & The Impulse)
// Draws a curved organic line and a traveling light pulse
function Synapse({ start, end }: { start: THREE.Vector3; end: THREE.Vector3 }) {
const curve = useMemo(() => {
// Create a quadratic bezier curve for a natural "tendon" look
// Control point is midway but offset randomly to create the curve
const mid = new THREE.Vector3().lerpVectors(start, end, 0.5);
mid.x += (Math.random() - 0.5) * 2;
mid.y += (Math.random() - 0.5) * 2;
mid.z += (Math.random() - 0.5) * 2;
return new THREE.QuadraticBezierCurve3(start, mid, end);
}, [start, end]);
// Create points for the line geometry
const points = useMemo(() => curve.getPoints(20), [curve]);
// The traveling impulse ref
const impulseRef = useRef<THREE.Mesh>(null!);
useFrame((state) => {
// Move the impulse along the curve
const t = (state.clock.getElapsedTime() * PULSE_SPEED) % 1; // 0 to 1 loop
if (impulseRef.current) {
const pos = curve.getPointAt(t);
impulseRef.current.position.copy(pos);
// Scale impulse based on position (fade in/out at ends)
const scale = Math.sin(t * Math.PI);
impulseRef.current.scale.setScalar(scale * 0.15);
}
});
return (
<group>
{/* The Axon (Line) */}
<line>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={points.length}
array={new Float32Array(points.flatMap((p) => [p.x, p.y, p.z]))}
itemSize={3}
/>
</bufferGeometry>
<lineBasicMaterial
color="#155e75" // Dark Cyan
transparent
opacity={0.3}
linewidth={1}
/>
</line>
{/* The Electric Impulse (Glowing Dot) */}
<mesh ref={impulseRef}>
<sphereGeometry args={[1, 8, 8]} />
<meshBasicMaterial color="#ecfeff" toneMapped={false} />
<pointLight distance={2} intensity={2} color="#22d3ee" decay={2} />
</mesh>
</group>
);
}
// 3. MAIN SCENE CONTROLLER
function NeuralNetworkScene() {
const { viewport } = useThree();
const mouse = useRef(new THREE.Vector2());
// Generate Neurons
const neurons = useMemo(() => {
const temp = [];
for (let i = 0; i < NEURON_COUNT; i++) {
temp.push({
position: new THREE.Vector3(
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 10
),
id: i,
});
}
return temp;
}, []);
// Generate Connections (Proximity based)
const connections = useMemo(() => {
const conns = [];
for (let i = 0; i < neurons.length; i++) {
for (let j = i + 1; j < neurons.length; j++) {
const dist = neurons[i].position.distanceTo(neurons[j].position);
if (dist < CONNECTION_DISTANCE) {
conns.push({ start: neurons[i].position, end: neurons[j].position, key: `${i}-${j}` });
}
}
}
return conns;
}, [neurons]);
useFrame((state) => {
// Soft camera movement based on mouse
const x = (state.pointer.x * viewport.width) / 10;
const y = (state.pointer.y * viewport.height) / 10;
state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, x, 0.05);
state.camera.position.y = THREE.MathUtils.lerp(state.camera.position.y, y, 0.05);
state.camera.lookAt(0, 0, 0);
});
return (
<group>
{/* Draw Neurons */}
{neurons.map((n) => (
<NeuronCell key={n.id} position={[n.position.x, n.position.y, n.position.z]} />
))}
{/* Draw Synapses */}
{connections.map((c) => (
<Synapse key={c.key} start={c.start} end={c.end} />
))}
</group>
);
}
export default function BioNeurons() {
return (
<div className="absolute inset-0 z-0 h-full w-full">
<Canvas
camera={{ position: [0, 0, 14], fov: 45 }}
gl={{ alpha: true, antialias: true }}
dpr={[1, 2]}
>
<ambientLight intensity={0.2} />
<pointLight position={[10, 10, 10]} intensity={1} color="#06b6d4" />
<NeuralNetworkScene />
<Preload all />
</Canvas>
</div>
);
}

View File

@@ -0,0 +1,110 @@
"use client";
import React, { useEffect, useRef } from "react";
interface Props {
className?: string;
}
const DNAHelix: React.FC<Props> = ({ className }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Dimensions based on parent container
let width = canvas.width = canvas.parentElement?.clientWidth || window.innerWidth;
let height = canvas.height = canvas.parentElement?.clientHeight || window.innerHeight;
let animationFrameId: number;
let time = 0;
const handleResize = () => {
if (canvas.parentElement) {
width = canvas.width = canvas.parentElement.clientWidth;
height = canvas.height = canvas.parentElement.clientHeight;
}
};
window.addEventListener("resize", handleResize);
const render = () => {
ctx.clearRect(0, 0, width, height);
time += 0.02; // Rotation speed
const strandGap = 40; // Vertical distance between "base pairs"
const amplitude = 50; // Width of the helix
const frequency = 0.015; // Tightness of the loops
// Position helix on the right side (85% of width) to keep content clear
const centerX = width * 0.85;
ctx.lineWidth = 2;
// Calculate number of points based on container height
const numPoints = Math.ceil(height / strandGap) + 5;
for (let i = 0; i < numPoints; i++) {
// Scroll the helix slowly downward
const y = i * strandGap - (time * 10) % strandGap;
// Calculate rotation phase
const phase = y * frequency + time;
// Strand 1 (Cyan)
const x1 = centerX + Math.sin(phase) * amplitude;
// Strand 2 (Emerald) - Offset by PI
const x2 = centerX + Math.sin(phase + Math.PI) * amplitude;
// Depth calculation for 3D effect (fade when "back")
const depth1 = Math.cos(phase);
const depth2 = Math.cos(phase + Math.PI);
// Map depth (-1 to 1) to opacity (0.2 to 0.8)
const alpha1 = (depth1 + 1) / 2 * 0.6 + 0.2;
const alpha2 = (depth2 + 1) / 2 * 0.6 + 0.2;
// Draw Connector Line (Base Pair)
ctx.beginPath();
ctx.moveTo(x1, y);
ctx.lineTo(x2, y);
ctx.strokeStyle = `rgba(34, 211, 238, 0.05)`; // Very faint connector
ctx.stroke();
// Draw Strand 1 Particle
ctx.beginPath();
ctx.arc(x1, y, 4 + depth1, 0, Math.PI * 2);
ctx.fillStyle = `rgba(34, 211, 238, ${alpha1})`; // Cyan
ctx.fill();
// Draw Strand 2 Particle
ctx.beginPath();
ctx.arc(x2, y, 4 + depth2, 0, Math.PI * 2);
ctx.fillStyle = `rgba(52, 211, 153, ${alpha2})`; // Emerald
ctx.fill();
}
animationFrameId = requestAnimationFrame(render);
};
render();
return () => {
window.removeEventListener("resize", handleResize);
cancelAnimationFrame(animationFrameId);
};
}, []);
return (
<canvas
ref={canvasRef}
className={`block w-full h-full ${className}`}
/>
);
};
export default DNAHelix;

View File

@@ -0,0 +1,123 @@
"use client";
import React, { useEffect, useRef } from "react";
interface Props {
className?: string;
}
const GravityWave: React.FC<Props> = ({ className }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Initialize dimensions based on parent container
let width = canvas.width = canvas.parentElement?.clientWidth || window.innerWidth;
let height = canvas.height = canvas.parentElement?.clientHeight || window.innerHeight;
let animationFrameId: number;
let time = 0;
// Configuration
const gap = 50;
const rows = Math.ceil(height / gap) + 4;
const cols = Math.ceil(width / gap) + 4;
// Mouse state
const mouse = { x: -500, y: -500 };
const handleResize = () => {
// Resize based on parent element
if (canvas.parentElement) {
width = canvas.width = canvas.parentElement.clientWidth;
height = canvas.height = canvas.parentElement.clientHeight;
}
};
const handleMouseMove = (e: MouseEvent) => {
const rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left;
mouse.y = e.clientY - rect.top;
};
window.addEventListener("resize", handleResize);
// Attach mouse move to the specific canvas/container, not window, if you want localized interaction
// But keeping it on window usually feels smoother for "approaching" the section
window.addEventListener("mousemove", handleMouseMove);
const render = () => {
ctx.clearRect(0, 0, width, height);
time += 0.02;
ctx.shadowBlur = 4;
ctx.shadowColor = "rgba(34, 211, 238, 0.5)";
const gradient = ctx.createLinearGradient(0, 0, width, height);
gradient.addColorStop(0, "rgba(34, 211, 238, 0.4)");
gradient.addColorStop(1, "rgba(52, 211, 153, 0.4)");
ctx.strokeStyle = gradient;
ctx.lineWidth = 1.5;
// Re-calculate rows/cols in case of resize
const currentRows = Math.ceil(height / gap) + 4;
const currentCols = Math.ceil(width / gap) + 4;
for (let iy = 0; iy < currentRows; iy++) {
const yBase = iy * gap;
ctx.beginPath();
for (let ix = 0; ix < currentCols; ix++) {
const xBase = ix * gap;
const waveOffset = Math.sin(ix * 0.2 + time) * 8 + Math.cos(iy * 0.3 + time) * 8;
const dx = xBase - mouse.x;
const dy = yBase - mouse.y;
const dist = Math.sqrt(dx * dx + dy * dy);
const maxDist = 400;
let gravityX = 0;
let gravityY = 0;
if (dist < maxDist) {
const force = (maxDist - dist) / maxDist;
const power = force * 60;
const angle = Math.atan2(dy, dx);
gravityX = Math.cos(angle) * power;
gravityY = Math.sin(angle) * power;
}
const xFinal = xBase + gravityX;
const yFinal = yBase + waveOffset + gravityY;
if (ix === 0) ctx.moveTo(xFinal, yFinal);
else ctx.lineTo(xFinal, yFinal);
}
ctx.stroke();
}
animationFrameId = requestAnimationFrame(render);
};
render();
return () => {
window.removeEventListener("resize", handleResize);
window.removeEventListener("mousemove", handleMouseMove);
cancelAnimationFrame(animationFrameId);
};
}, []);
return (
<canvas
ref={canvasRef}
className={`block w-full h-full ${className}`}
style={{ touchAction: "none" }}
/>
);
};
export default GravityWave;

View File

@@ -0,0 +1,65 @@
"use client";
import { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { Points, PointMaterial, Preload } from "@react-three/drei";
import * as THREE from "three";
function Particles(props: any) {
const ref = useRef<THREE.Points>(null!);
// Generate 5000 random points inside a sphere
const sphere = useMemo(() => {
const coords = new Float32Array(5000 * 3);
for (let i = 0; i < 5000; i++) {
const r = 1.2 * Math.cbrt(Math.random()); // Radius
const theta = Math.random() * 2 * Math.PI; // Theta
const phi = Math.acos(2 * Math.random() - 1); // Phi
const x = r * Math.sin(phi) * Math.cos(theta);
const y = r * Math.sin(phi) * Math.sin(theta);
const z = r * Math.cos(phi);
coords[i * 3] = x;
coords[i * 3 + 1] = y;
coords[i * 3 + 2] = z;
}
return coords;
}, []);
useFrame((state, delta) => {
if (ref.current) {
// Rotate the entire cloud slowly
ref.current.rotation.x -= delta / 10;
ref.current.rotation.y -= delta / 15;
}
});
return (
<group rotation={[0, 0, Math.PI / 4]}>
<Points ref={ref} positions={sphere} stride={3} frustumCulled={false} {...props}>
<PointMaterial
transparent
color="#22d3ee" // Cyan-400
size={0.005}
sizeAttenuation={true}
depthWrite={false}
opacity={0.6}
/>
</Points>
</group>
);
}
const NeuralNetwork = () => {
return (
<div className="absolute inset-0 z-0 h-full w-full">
<Canvas camera={{ position: [0, 0, 1] }}>
<Particles />
<Preload all />
</Canvas>
</div>
);
};
export default NeuralNetwork;

View File

@@ -0,0 +1,81 @@
// src/components/canvas/NeuronNetwork.tsx
"use client";
import { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { Points, PointMaterial } from "@react-three/drei";
import * as THREE from "three";
function NeuronParticles({ count = 200 }) {
// Create a ref for the points to animate them
const points = useRef<THREE.Points>(null!);
// Generate random positions for the neurons
const positions = useMemo(() => {
const pos = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
// Spread particles across a wide area (x, y, z)
pos[i * 3] = (Math.random() - 0.5) * 25; // x
pos[i * 3 + 1] = (Math.random() - 0.5) * 25; // y
pos[i * 3 + 2] = (Math.random() - 0.5) * 10; // z
}
return pos;
}, [count]);
useFrame((state, delta) => {
if (points.current) {
// subtle rotation of the entire system
points.current.rotation.x -= delta / 50;
points.current.rotation.y -= delta / 60;
// Optional: Gentle wave motion could be added here
}
});
return (
<group rotation={[0, 0, Math.PI / 4]}>
<Points
ref={points}
positions={positions}
stride={3}
frustumCulled={false}
>
<PointMaterial
transparent
color="#22d3ee" // Cyan-400
size={0.08}
sizeAttenuation={true}
depthWrite={false}
opacity={0.6}
/>
</Points>
{/* For true "connections" (lines), calculating distance between 200 points
every frame is heavy. A visual trick is to render a second layer
of slightly larger, fainter particles or use a custom shader.
However, for a clean "Neuron" look, we often just need the floating nodes
and a faint fog, or we can use the <Line> component from drei
if the count is low (<50).
*/}
</group>
);
}
// A wrapper to handle the Canvas settings
export default function NeuronNetwork() {
return (
<div className="absolute inset-0 z-0">
<Canvas
camera={{ position: [0, 0, 10], fov: 60 }}
gl={{ alpha: true, antialias: true }}
dpr={[1, 2]} // Handle high-res screens
>
<color attach="background" args={["transparent"]} />
<ambientLight intensity={0.5} />
<NeuronParticles count={300} />
{/* Fog creates depth, fading distant neurons */}
<fog attach="fog" args={['#030712', 5, 20]} />
</Canvas>
</div>
);
}

View File

@@ -0,0 +1,183 @@
"use client";
import { useMemo, useRef } from "react";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { Preload } from "@react-three/drei";
// CONFIGURATION
const PARTICLE_COUNT = 80;
const CONNECT_DISTANCE = 3.5;
const MOUSE_INFLUENCE_RADIUS = 6; // Radius of the mouse wave
const WAVE_AMPLITUDE = 1.5; // How high the wave ripples (Z-axis)
function NeuralMesh() {
const groupRef = useRef<THREE.Group>(null);
const particlesRef = useRef<THREE.Points>(null);
const linesRef = useRef<THREE.LineSegments>(null);
// Get viewport to map mouse coordinates correctly to world space
const { viewport } = useThree();
// 1. Generate Initial Random Positions & Velocities
// We store "original positions" to calculate the wave offset from
const [positions, velocities, originalPositions] = useMemo(() => {
const pos = new Float32Array(PARTICLE_COUNT * 3);
const origPos = new Float32Array(PARTICLE_COUNT * 3);
const vel = [];
for (let i = 0; i < PARTICLE_COUNT; i++) {
const x = (Math.random() - 0.5) * 18;
const y = (Math.random() - 0.5) * 18;
const z = (Math.random() - 0.5) * 10;
pos[i * 3] = x;
pos[i * 3 + 1] = y;
pos[i * 3 + 2] = z;
origPos[i * 3] = x;
origPos[i * 3 + 1] = y;
origPos[i * 3 + 2] = z;
vel.push({
x: (Math.random() - 0.5) * 0.05, // Slower natural drift
y: (Math.random() - 0.5) * 0.05,
z: (Math.random() - 0.5) * 0.05,
});
}
return [pos, vel, origPos];
}, []);
useFrame((state, delta) => {
// 2. Map Mouse to World Coordinates
// state.pointer is normalized (-1 to 1). Convert to world units using viewport.
const mouseX = (state.pointer.x * viewport.width) / 2;
const mouseY = (state.pointer.y * viewport.height) / 2;
if (particlesRef.current && linesRef.current) {
const positionsAttr = particlesRef.current.geometry.attributes.position;
const currentPositions = positionsAttr.array as Float32Array;
// 3. Update Particles
for (let i = 0; i < PARTICLE_COUNT; i++) {
const i3 = i * 3;
// A. Natural Drift (Gravity)
// We use the "original" position as an anchor to prevent them from flying away too far
// but we add the velocity to keep them moving organically.
originalPositions[i3] += velocities[i].x * delta * 2;
originalPositions[i3 + 1] += velocities[i].y * delta * 2;
// Bounce logic for the anchor points
if (Math.abs(originalPositions[i3]) > 10) velocities[i].x *= -1;
if (Math.abs(originalPositions[i3 + 1]) > 10) velocities[i].y *= -1;
// B. Mouse Interaction (The Wave)
// Calculate distance from particle to mouse
const dx = mouseX - originalPositions[i3];
const dy = mouseY - originalPositions[i3 + 1];
const dist = Math.sqrt(dx * dx + dy * dy);
// Apply Wave Effect
// If the mouse is close, we disturb the position
let xOffset = 0;
let yOffset = 0;
let zOffset = 0;
if (dist < MOUSE_INFLUENCE_RADIUS) {
// 1. Repulsion force (XY Plane) - pushes them slightly aside
const force = (MOUSE_INFLUENCE_RADIUS - dist) / MOUSE_INFLUENCE_RADIUS;
const angle = Math.atan2(dy, dx);
xOffset = -Math.cos(angle) * force * 2; // Push away X
yOffset = -Math.sin(angle) * force * 2; // Push away Y
// 2. Wave Ripple (Z Plane) - Sine wave based on distance and time
// This creates the "water ripple" effect in 3D depth
zOffset = Math.sin(dist * 1.5 - state.clock.elapsedTime * 3) * WAVE_AMPLITUDE * force;
}
// Apply calculated positions
currentPositions[i3] = originalPositions[i3] + xOffset;
currentPositions[i3 + 1] = originalPositions[i3 + 1] + yOffset;
currentPositions[i3 + 2] = originalPositions[i3 + 2] + zOffset;
}
positionsAttr.needsUpdate = true;
// 4. Update Connections (Plexus)
// Re-calculate lines based on the NEW modified positions
const linePositions: number[] = [];
for (let i = 0; i < PARTICLE_COUNT; i++) {
for (let j = i + 1; j < PARTICLE_COUNT; j++) {
const x1 = currentPositions[i * 3];
const y1 = currentPositions[i * 3 + 1];
const z1 = currentPositions[i * 3 + 2];
const x2 = currentPositions[j * 3];
const y2 = currentPositions[j * 3 + 1];
const z2 = currentPositions[j * 3 + 2];
const dist = Math.sqrt(
Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2)
);
if (dist < CONNECT_DISTANCE) {
linePositions.push(x1, y1, z1, x2, y2, z2);
}
}
}
linesRef.current.geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(linePositions, 3)
);
}
});
return (
<group ref={groupRef}>
<points ref={particlesRef}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={PARTICLE_COUNT}
array={positions}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial
size={0.15}
color="#22d3ee" // Cyan
sizeAttenuation={true}
transparent
opacity={0.8}
/>
</points>
<lineSegments ref={linesRef}>
<bufferGeometry />
<lineBasicMaterial
color="#0891b2" // Cyan-600
transparent
opacity={0.15}
/>
</lineSegments>
</group>
);
}
export default function NeuronsGravity() {
return (
<div className="absolute inset-0 z-0 h-full w-full">
<Canvas
camera={{ position: [0, 0, 12], fov: 50 }}
gl={{ alpha: true, antialias: true }}
dpr={[1, 2]}
>
<NeuralMesh />
<Preload all />
</Canvas>
</div>
);
}

View File

@@ -0,0 +1,259 @@
"use client";
import { useMemo, useRef } from "react";
import { Canvas, useFrame, useThree } from "@react-three/fiber";
import { MeshDistortMaterial, Sphere, Preload, Float } from "@react-three/drei";
import { EffectComposer, Bloom, Vignette } from "@react-three/postprocessing";
import * as THREE from "three";
// --- CONFIGURATION ---
const NEURON_COUNT = 15; // Keep low for high-quality geometry
const CONNECTION_DISTANCE = 6.5;
const PULSE_SPEED = 1.8;
// 1. REALISTIC CELL (Soma + Nucleus)
function NeuronCell({ position }: { position: [number, number, number] }) {
const membraneRef = useRef<THREE.Mesh>(null!);
const nucleusRef = useRef<THREE.Mesh>(null!);
// Randomize biology
const size = useMemo(() => 0.5 + Math.random() * 0.3, []);
useFrame((state) => {
const t = state.clock.getElapsedTime();
// Biological breathing/pulsing
if (membraneRef.current) {
const scale = size + Math.sin(t * 2 + position[0]) * 0.05;
membraneRef.current.scale.setScalar(scale);
}
// Nucleus gentle pulse (faster)
if (nucleusRef.current) {
const nScale = (size * 0.4) + Math.sin(t * 4) * 0.02;
nucleusRef.current.scale.setScalar(nScale);
}
});
return (
<Float speed={1.5} rotationIntensity={0.6} floatIntensity={0.8}>
<group position={position}>
{/* A. The Membrane (Outer Shell) */}
<Sphere ref={membraneRef} args={[1, 64, 64]}>
<MeshDistortMaterial
color="#083344" // Deep blue-black (organic tissue)
emissive="#155e75" // Cyan glow from within
emissiveIntensity={0.2}
roughness={0.1}
metalness={0.8}
distort={0.5} // High distortion for "blobby" look
speed={2}
transparent
opacity={0.7}
/>
</Sphere>
{/* B. The Nucleus (Inner Core) */}
<Sphere ref={nucleusRef} args={[1, 32, 32]}>
<meshStandardMaterial
color="#a5f3fc" // Bright Cyan
emissive="#22d3ee" // Strong Glow
emissiveIntensity={2} // Push this high for Bloom to pick it up
toneMapped={false}
/>
<pointLight distance={4} intensity={2} color="#22d3ee" decay={2} />
</Sphere>
</group>
</Float>
);
}
// 2. SYNAPSE (Axon + Electric Pulse)
function Synapse({ start, end }: { start: THREE.Vector3; end: THREE.Vector3 }) {
const curve = useMemo(() => {
// Create organic curve
const mid = new THREE.Vector3().lerpVectors(start, end, 0.5);
// Randomize control point for "tendon" shape
mid.add(new THREE.Vector3(
(Math.random() - 0.5) * 3,
(Math.random() - 0.5) * 3,
(Math.random() - 0.5) * 3
));
return new THREE.QuadraticBezierCurve3(start, mid, end);
}, [start, end]);
const points = useMemo(() => curve.getPoints(30), [curve]);
const impulseRef = useRef<THREE.Mesh>(null!);
useFrame((state) => {
// Move electric pulse
const t = (state.clock.getElapsedTime() * PULSE_SPEED) % 1;
if (impulseRef.current) {
const pos = curve.getPointAt(t);
impulseRef.current.position.copy(pos);
// Stretch effect for speed illusion
const tangent = curve.getTangent(t).normalize();
impulseRef.current.lookAt(pos.clone().add(tangent));
impulseRef.current.scale.set(0.6, 0.6, 2.5); // Stretch Z
}
});
return (
<group>
{/* The physical connection (Axon) */}
<line>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={points.length}
array={new Float32Array(points.flatMap(p => [p.x, p.y, p.z]))}
itemSize={3}
/>
</bufferGeometry>
<lineBasicMaterial color="#0e7490" transparent opacity={0.15} />
</line>
{/* The Traveling Spark (Electricity) */}
<mesh ref={impulseRef}>
<sphereGeometry args={[0.08, 8, 8]} />
<meshBasicMaterial color="#ccfbf1" />
<pointLight distance={3} intensity={3} color="#22d3ee" decay={2} />
</mesh>
</group>
);
}
// 3. BACKGROUND PARTICLES (Neurotransmitters)
function NeuroDust() {
const count = 200;
const mesh = useRef<THREE.InstancedMesh>(null!);
const particles = useMemo(() => {
const temp = [];
for(let i=0; i<count; i++) {
const t = Math.random() * 100;
const factor = 20 + Math.random() * 10;
const speed = 0.01 + Math.random() / 200;
const x = (Math.random() - 0.5) * 30;
const y = (Math.random() - 0.5) * 30;
const z = (Math.random() - 0.5) * 15;
temp.push({ t, factor, speed, x, y, z, mx: 0, my: 0 });
}
return temp;
}, []);
const dummy = useMemo(() => new THREE.Object3D(), []);
useFrame((state) => {
particles.forEach((particle, i) => {
let { t, factor, speed, x, y, z } = particle;
t = particle.t += speed / 2;
const a = Math.cos(t) + Math.sin(t * 1) / 10;
const b = Math.sin(t) + Math.cos(t * 2) / 10;
const s = Math.cos(t);
dummy.position.set(
x + Math.cos(t) + Math.sin(t) * 2,
y + Math.sin(t) + Math.cos(t) * 2,
z + Math.cos(t)
);
dummy.scale.setScalar(s * 0.03); // Tiny dust
dummy.rotation.set(s * 5, s * 5, s * 5);
dummy.updateMatrix();
if (mesh.current) mesh.current.setMatrixAt(i, dummy.matrix);
});
if (mesh.current) mesh.current.instanceMatrix.needsUpdate = true;
});
return (
<instancedMesh ref={mesh} args={[undefined, undefined, count]}>
<dodecahedronGeometry args={[0.2, 0]} />
<meshPhongMaterial color="#0891b2" emissive="#06b6d4" transparent opacity={0.4} />
</instancedMesh>
);
}
// 4. MAIN SCENE
function Scene() {
const { viewport } = useThree();
// Generate random positions for neurons
const neurons = useMemo(() => {
const temp = [];
for (let i = 0; i < NEURON_COUNT; i++) {
temp.push({
position: new THREE.Vector3(
(Math.random() - 0.5) * 22,
(Math.random() - 0.5) * 22,
(Math.random() - 0.5) * 10
),
id: i
});
}
return temp;
}, []);
// Connect them
const connections = useMemo(() => {
const conns = [];
for(let i=0; i<neurons.length; i++) {
for(let j=i+1; j<neurons.length; j++) {
if(neurons[i].position.distanceTo(neurons[j].position) < CONNECTION_DISTANCE) {
conns.push({ start: neurons[i].position, end: neurons[j].position, key: `${i}-${j}` });
}
}
}
return conns;
}, [neurons]);
useFrame((state) => {
// Subtle Camera Parallax based on Mouse
const x = (state.pointer.x * viewport.width) / 12;
const y = (state.pointer.y * viewport.height) / 12;
state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, x, 0.02);
state.camera.position.y = THREE.MathUtils.lerp(state.camera.position.y, y, 0.02);
state.camera.lookAt(0,0,0);
});
return (
<>
<color attach="background" args={["#020617"]} /> {/* Match gray-950 */}
<ambientLight intensity={0.2} />
<pointLight position={[10, 10, 10]} intensity={1} color="#22d3ee" />
{neurons.map(n => <NeuronCell key={n.id} position={[n.position.x, n.position.y, n.position.z]} />)}
{connections.map(c => <Synapse key={c.key} start={c.start} end={c.end} />)}
<NeuroDust />
{/* POST PROCESSING - The Secret Sauce for Realism */}
<EffectComposer disableNormalPass>
<Bloom
luminanceThreshold={1} // Only very bright things glow
mipmapBlur
intensity={1.5} // Strength of the glow
radius={0.6}
/>
<Vignette eskil={false} offset={0.1} darkness={1.1} />
</EffectComposer>
</>
);
}
export default function RealisticNeurons() {
return (
<div className="absolute inset-0 z-0 h-full w-full">
<Canvas
camera={{ position: [0, 0, 16], fov: 40 }}
gl={{ alpha: false, antialias: false, toneMapping: THREE.ReinhardToneMapping }}
dpr={[1, 1.5]} // Limit DPR for performance with post-processing
>
<Scene />
<Preload all />
</Canvas>
</div>
);
}

View File

@@ -5,7 +5,7 @@ import { motion } from "framer-motion";
export default function Footer() {
return (
<footer className="relative z-10 mt-20 sm:mt-32 bg-gradient-to-b from-black via-zinc-950 to-black border-t border-white/5">
<footer className="relative z-10 mt-20 sm:mt-32 border-t border-white/5">
{/* Top decorative line */}
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-blue-500/50 to-transparent" />

View File

@@ -1,37 +1,27 @@
"use client";
import { useState, useEffect } from "react";
import { motion, AnimatePresence, stagger } from "framer-motion";
import { Menu, X, Activity, Brain, Mic, ShieldCheck, ChevronRight, Sparkles } from "lucide-react";
import { motion, AnimatePresence } from "framer-motion";
import { Menu, X, Activity, Home,Brain, Mic, ShieldCheck, ChevronDown, Sparkles } from "lucide-react";
const navItems = [
{ name: "Home", icon: Home, href: "/" },
{ name: "Devices", icon: Activity, href: "/devices" },
{ name: "Voice-to-SOAP", icon: Mic, href: "/#voice-soap" },
{ name: "AI Diagnosis", icon: Brain, href: "/#diagnosis" },
{ name: "Apps & Security", icon: ShieldCheck, href: "/apps" },
] as const;
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.07, delayChildren: 0.1 },
},
exit: { opacity: 0, transition: { staggerChildren: 0.05, staggerDirection: -1 } },
};
const itemVariants = {
hidden: { opacity: 0, x: 40, scale: 0.96 },
visible: { opacity: 1, x: 0, scale: 1, transition: { type: "spring", damping: 22, stiffness: 140 } },
exit: { opacity: 0, x: 20, scale: 0.95 },
};
export default function Navbar() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [activeItem, setActiveItem] = useState<number | null>(null);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 20);
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
@@ -54,7 +44,7 @@ export default function Navbar() {
: "bg-gradient-to-r from-white/5 via-white/10 to-white/5 backdrop-blur-lg border border-white/5"
}`}
>
{/* Animated background gradient (unchanged) */}
{/* Animated background gradient */}
<div className="absolute inset-0 rounded-2xl lg:rounded-3xl overflow-hidden pointer-events-none">
<motion.div
animate={{
@@ -69,45 +59,55 @@ export default function Navbar() {
/>
</div>
{/* Logo (unchanged) */}
{/* Logo */}
<a href="/" className="flex items-center gap-2 sm:gap-3 group relative z-10">
<div className="relative">
<div className="absolute inset-0 bg-blue-500/20 blur-xl rounded-full group-hover:bg-blue-500/30 transition-all" />
<div className="absolute inset-0 bg-cyan-500/20 blur-xl rounded-full group-hover:bg-cyan-500/30 transition-all" />
<Activity className="w-7 h-7 sm:w-9 sm:h-9 text-white relative z-10 transition-transform group-hover:scale-110 group-hover:rotate-180 duration-500" />
</div>
<span className="text-xl sm:text-2xl lg:text-3xl font-bold tracking-tight text-white relative">
Skyheal
<span className="absolute -top-1 -right-6 text-[8px] text-blue-400 font-semibold">AI</span>
<span className="absolute -top-1 -right-6 text-[8px] text-cyan-400 font-semibold">AI</span>
</span>
</a>
{/* Desktop Menu (unchanged) */}
{/* Desktop Menu */}
<div className="hidden lg:flex items-center gap-1 xl:gap-2 relative z-10">
{navItems.map((item, index) => (
<motion.a
key={item.name}
href={item.href}
onHoverStart={() => setActiveItem(index)}
onHoverEnd={() => setActiveItem(null)}
className="relative px-4 py-2.5 rounded-xl text-sm font-semibold text-zinc-300 hover:text-white transition-colors group"
>
<span className="relative z-10 flex items-center gap-2">
<item.icon className="w-4 h-4 transition-transform group-hover:scale-110" />
{item.name}
</span>
{/* You can keep or remove the active indicator if not needed */}
{activeItem === index && (
<motion.div
layoutId="navbar-indicator"
className="absolute inset-0 bg-white/10 backdrop-blur-sm rounded-xl border border-white/20 pointer-events-none"
transition={{ type: "spring", bounce: 0.2, duration: 0.6 }}
/>
)}
</motion.a>
))}
{/* CTA Button */}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="ml-4 relative group overflow-hidden bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-2.5 rounded-xl text-sm font-bold shadow-lg shadow-blue-500/25 hover:shadow-blue-500/40 transition-all"
className="ml-4 relative group overflow-hidden bg-gradient-to-r from-cyan-600 to-cyan-200 text-white px-6 py-2.5 rounded-xl text-sm font-bold shadow-lg shadow-cyan-500/25 hover:shadow-cyan-500/40 transition-all"
>
<span className="relative z-10 flex items-center gap-2">
<Sparkles className="w-4 h-4" />
Get Started
</span>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-purple-600 to-blue-600"
className="absolute inset-0 bg-gradient-to-r from-cyan-200 to-cyan-600"
initial={{ x: "100%" }}
whileHover={{ x: 0 }}
transition={{ duration: 0.3 }}
@@ -115,10 +115,10 @@ export default function Navbar() {
</motion.button>
</div>
{/* Mobile Toggle bigger touch target */}
{/* Mobile Toggle */}
<motion.button
whileTap={{ scale: 0.92 }}
className="lg:hidden relative z-10 text-white p-3 rounded-full hover:bg-white/10 transition-colors"
whileTap={{ scale: 0.9 }}
className="lg:hidden relative z-10 text-white p-2 rounded-lg hover:bg-white/10 transition-colors"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
<AnimatePresence mode="wait">
@@ -128,19 +128,19 @@ export default function Navbar() {
initial={{ rotate: -90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }}
transition={{ duration: 0.25 }}
transition={{ duration: 0.2 }}
>
<X className="w-7 h-7" />
<X className="w-6 h-6" />
</motion.div>
) : (
<motion.div
key="menu"
initial={{ rotate: -90, opacity: 0 }}
initial={{ rotate: 90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }}
transition={{ duration: 0.25 }}
exit={{ rotate: -90, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<Menu className="w-7 h-7" />
<Menu className="w-6 h-6" />
</motion.div>
)}
</AnimatePresence>
@@ -149,87 +149,72 @@ export default function Navbar() {
</div>
</nav>
{/* Mobile Menu Slide from right + better visuals */}
{/* Mobile Menu Overlay */}
<AnimatePresence>
{isMobileMenuOpen && (
<>
{/* Backdrop with subtle vignette */}
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.4 }}
className="fixed inset-0 bg-black/70 backdrop-blur-md z-40 lg:hidden"
transition={{ duration: 0.3 }}
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-40 lg:hidden"
onClick={() => setIsMobileMenuOpen(false)}
/>
{/* Side sheet style menu */}
{/* Mobile Menu Content */}
<motion.div
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{ type: "spring", damping: 30, stiffness: 200 }}
className="fixed top-0 right-0 bottom-0 w-4/5 max-w-sm z-50 lg:hidden"
initial={{ opacity: 0, y: -20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -20, scale: 0.95 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="fixed top-20 sm:top-24 left-4 right-4 z-50 lg:hidden"
>
<div className="h-full bg-gradient-to-b from-black/95 via-black/97 to-black/95 backdrop-blur-2xl border-l border-white/10 shadow-2xl flex flex-col">
{/* Header */}
<div className="p-6 border-b border-white/10 flex items-center justify-between">
<div className="flex items-center gap-3">
<Activity className="w-8 h-8 text-blue-400" />
<span className="text-xl font-bold text-white tracking-tight">Skyheal</span>
</div>
<button
onClick={() => setIsMobileMenuOpen(false)}
className="p-2 rounded-full hover:bg-white/10 transition-colors"
>
<X className="w-6 h-6 text-zinc-300" />
</button>
<div className="bg-black/95 backdrop-blur-2xl rounded-2xl border border-white/10 shadow-2xl overflow-hidden">
{/* Menu Header */}
<div className="p-6 border-b border-white/10 bg-gradient-to-r from-cyan-500/10 to-purple-500/10">
<h3 className="text-sm font-semibold text-zinc-400 uppercase tracking-wider">
Navigation
</h3>
</div>
{/* Menu Items */}
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
exit="exit"
className="flex-1 p-6 space-y-2 overflow-y-auto"
>
{navItems.map((item) => (
<div className="p-4">
{navItems.map((item, index) => (
<motion.a
key={item.name}
href={item.href}
variants={itemVariants}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
onClick={() => setIsMobileMenuOpen(false)}
className="group flex items-center gap-4 p-4 rounded-2xl hover:bg-white/5 active:bg-white/8 transition-all duration-200"
className="flex items-center gap-4 p-4 rounded-xl hover:bg-white/5 transition-all group mb-2"
>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500/10 to-purple-500/10 flex items-center justify-center border border-white/10 group-hover:border-blue-500/40 group-hover:shadow-[0_0_16px_rgba(59,130,246,0.25)] transition-all duration-300">
<item.icon className="w-6 h-6 text-blue-400 group-hover:scale-110 transition-transform" />
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-white/10 to-white/5 flex items-center justify-center border border-white/10 group-hover:border-white/20 transition-all">
<item.icon className="w-5 h-5 text-white group-hover:scale-110 transition-transform" />
</div>
<span className="text-lg font-medium text-white group-hover:text-blue-300 transition-colors">
{item.name}
</span>
<ChevronRight className="ml-auto w-5 h-5 text-zinc-500 group-hover:text-blue-400 group-hover:translate-x-1 transition-all" />
<div className="flex-1">
<span className="text-base font-semibold text-white group-hover:text-cyan-400 transition-colors">
{item.name}
</span>
</div>
<ChevronDown className="w-4 h-4 text-zinc-500 -rotate-90 group-hover:translate-x-1 transition-transform" />
</motion.a>
))}
</motion.div>
</div>
{/* CTA at bottom */}
<div className="p-6 border-t border-white/10 mt-auto">
{/* Mobile CTA */}
<div className="p-4 border-t border-white/10">
<motion.button
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
className="w-full relative overflow-hidden bg-gradient-to-r from-blue-600 to-purple-600 text-white py-4 rounded-2xl font-semibold text-base shadow-xl shadow-blue-600/30 hover:shadow-blue-600/50 transition-all group"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
whileTap={{ scale: 0.98 }}
className="w-full bg-gradient-to-r from-cyan-600 to-cyan-200 text-white py-4 rounded-xl font-bold text-base shadow-lg shadow-cyan-500/25 flex items-center justify-center gap-2 hover:shadow-cyan-500/40 transition-all"
>
<span className="relative z-10 flex items-center justify-center gap-2.5">
<Sparkles className="w-5 h-5" />
Get Started Free
</span>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-purple-600 via-blue-600 to-purple-600 bg-[length:200%_100%]"
initial={{ x: "100%" }}
whileHover={{ x: "-100%" }}
transition={{ duration: 1.2, ease: "linear" }}
/>
<Sparkles className="w-5 h-5" />
Get Started Free
</motion.button>
</div>
</div>

View File

@@ -17,6 +17,7 @@ import { useRef, useEffect, useState } from "react";
export default function AIDiagnosis() {
const containerRef = useRef<HTMLDivElement>(null);
const canvasContainerRef = useRef<HTMLDivElement>(null); // Ref for the wrapper of the canvas
const canvasRef = useRef<HTMLCanvasElement>(null);
const [modelLoaded, setModelLoaded] = useState(false);
const [scanProgress, setScanProgress] = useState(0);
@@ -28,7 +29,7 @@ export default function AIDiagnosis() {
const y = useTransform(scrollYProgress, [0, 0.5], [100, 0]);
const opacity = useTransform(scrollYProgress, [0, 0.3], [0, 1]);
const scale = useTransform(scrollYProgress, [0, 0.5], [0.9, 1]);
const scale = useTransform(scrollYProgress, [0, 0.5], [0.95, 1]); // Subtle scale
// Simulate scan progress
useEffect(() => {
@@ -40,7 +41,7 @@ export default function AIDiagnosis() {
// Three.js setup
useEffect(() => {
if (typeof window === "undefined" || !canvasRef.current) return;
if (typeof window === "undefined" || !canvasRef.current || !canvasContainerRef.current) return;
let animationId: number | undefined;
let scene: any;
@@ -53,8 +54,12 @@ export default function AIDiagnosis() {
const THREE = await import("three");
const { GLTFLoader, OrbitControls } = await import("three-stdlib");
// Get initial dimensions from the parent container
const width = canvasContainerRef.current?.clientWidth || 500;
const height = canvasContainerRef.current?.clientHeight || 500;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.set(0, 0, 5);
renderer = new THREE.WebGLRenderer({
@@ -62,17 +67,15 @@ export default function AIDiagnosis() {
alpha: true,
antialias: true,
});
renderer.setSize(500, 500);
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setClearColor(0x000000, 0);
// Lighting
scene.add(new THREE.AmbientLight(0xffffff, 0.8));
const dirLight1 = new THREE.DirectionalLight(0x6366f1, 1.2);
dirLight1.position.set(5, 5, 5);
scene.add(dirLight1);
const dirLight2 = new THREE.DirectionalLight(0xa855f7, 0.8);
dirLight2.position.set(-5, 3, -5);
scene.add(dirLight2);
@@ -85,6 +88,21 @@ export default function AIDiagnosis() {
controls.autoRotate = true;
controls.autoRotateSpeed = 1.5;
// Handle Resize - Critical for responsiveness
const handleResize = () => {
if (!canvasContainerRef.current || !camera || !renderer) return;
const newWidth = canvasContainerRef.current.clientWidth;
const newHeight = canvasContainerRef.current.clientHeight;
camera.aspect = newWidth / newHeight;
camera.updateProjectionMatrix();
renderer.setSize(newWidth, newHeight);
};
// Use ResizeObserver for robust sizing (handles container changes, not just window)
const resizeObserver = new ResizeObserver(() => handleResize());
resizeObserver.observe(canvasContainerRef.current!);
// Model loading
const loader = new GLTFLoader();
const modelURL = "/human_body.glb";
@@ -103,7 +121,6 @@ export default function AIDiagnosis() {
model.scale.setScalar(scaleFactor);
model.position.sub(center.multiplyScalar(scaleFactor));
// Performance: disable shadows
model.traverse((child: any) => {
if (child.isMesh) {
child.castShadow = false;
@@ -114,8 +131,7 @@ export default function AIDiagnosis() {
scene.add(model);
setModelLoaded(true);
} catch (error) {
console.error("Failed to load GLB model → using fallback", error);
console.warn("Using fallback geometry");
const geometry = new THREE.IcosahedronGeometry(1.5, 2);
const material = new THREE.MeshStandardMaterial({
color: 0x6366f1,
@@ -123,6 +139,7 @@ export default function AIDiagnosis() {
metalness: 0.7,
emissive: 0x3b82f6,
emissiveIntensity: 0.2,
wireframe: true, // Added wireframe for a more "tech" fallback look
});
model = new THREE.Mesh(geometry, material);
scene.add(model);
@@ -138,29 +155,22 @@ export default function AIDiagnosis() {
};
animate();
// Clean up specifically for this closure
return () => {
resizeObserver.disconnect();
};
};
initThreeJS();
const cleanupPromise = initThreeJS();
// Cleanup
return () => {
if (animationId !== undefined) {
cancelAnimationFrame(animationId);
}
cleanupPromise.then((cleanup) => cleanup && cleanup());
if (animationId !== undefined) cancelAnimationFrame(animationId);
if (controls) controls.dispose();
if (renderer) renderer.dispose();
if (scene) {
scene.traverse((obj: any) => {
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
if (Array.isArray(obj.material)) {
obj.material.forEach((m: any) => m.dispose());
} else {
obj.material.dispose();
}
}
});
}
// Simplified scene disposal
if (scene) scene.clear();
};
}, []);
@@ -168,11 +178,11 @@ export default function AIDiagnosis() {
<section
ref={containerRef}
id="diagnosis"
className="relative py-20 md:py-32 lg:py-40 overflow-hidden"
className="relative py-16 sm:py-20 md:py-32 lg:py-40 2xl:py-48 overflow-hidden w-full"
>
{/* Ambient glows */}
<div className="absolute top-1/4 left-0 w-[700px] h-[700px] bg-blue-500/10 rounded-full blur-[150px] animate-pulse" />
<div className="absolute bottom-1/4 right-0 w-[600px] h-[600px] bg-purple-500/10 rounded-full blur-[120px]" />
{/* Ambient glows - Optimized for mobile by reducing blur radius slightly */}
{/* <div className="absolute top-1/4 left-0 w-[300px] sm:w-[500px] lg:w-[700px] h-[300px] sm:h-[500px] lg:h-[700px] bg-cyan-500/10 rounded-full blur-[80px] sm:blur-[150px] animate-pulse" />
<div className="absolute bottom-1/4 right-0 w-[250px] sm:w-[400px] lg:w-[600px] h-[250px] sm:h-[400px] lg:h-[600px] bg-purple-500/10 rounded-full blur-[60px] sm:blur-[120px]" /> */}
{/* Subtle grid */}
<div
@@ -184,159 +194,157 @@ export default function AIDiagnosis() {
}}
/>
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid lg:grid-cols-2 gap-12 lg:gap-16 xl:gap-24 items-center">
{/* 3D Viewer Column */}
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl 2xl:max-w-[1600px]">
<div className="grid lg:grid-cols-2 gap-10 sm:gap-12 lg:gap-16 xl:gap-24 2xl:gap-32 items-center">
{/* --- 3D Viewer Column --- */}
{/* Order-2 on mobile ensures text comes first if desired, but default is fine */}
<motion.div
initial={{ opacity: 0, x: -50 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 1 }}
className="relative"
className="relative w-full max-w-[500px] lg:max-w-none mx-auto"
>
<div className="absolute inset-0 rounded-[3rem] blur-3xl opacity-60" />
{/* Background Blur blob */}
<div className="absolute inset-0 rounded-[3rem] blur-3xl opacity-60 bg-gradient-to-tr from-cyan-900/20 to-purple-900/20" />
<div className="relative rounded-3xl backdrop-blur-2xl shadow-2xl overflow-hidden">
{/* Header */}
<div className="relative p-6 border-b border-white/5">
<div className="relative rounded-3xl backdrop-blur-2xl bg-black/40 border border-white/10 shadow-2xl overflow-hidden">
{/* Card Header */}
<div className="relative p-4 sm:p-6 border-b border-white/5">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="relative">
<div className="w-10 h-10 rounded-xl flex items-center justify-center">
<Brain className="w-5 h-5 text-blue-400" />
<div className="w-10 h-10 rounded-xl bg-white/5 flex items-center justify-center">
<Brain className="w-5 h-5 text-cyan-400" />
</div>
<div className="absolute -top-1 -right-1 w-3 h-3 bg-green-400 rounded-full border-2 border-black animate-pulse" />
<div className="absolute -top-1 -right-1 w-2.5 h-2.5 bg-green-400 rounded-full border-2 border-black animate-pulse" />
</div>
<div>
<h3 className="text-sm font-bold text-white">Neural Diagnostic Engine</h3>
<p className="text-xs text-zinc-500">Real-time Analysis Active</p>
<h3 className="text-sm font-bold text-white">Neural Engine</h3>
<p className="text-[10px] sm:text-xs text-zinc-500">Analysis Active</p>
</div>
</div>
<div className="px-3 py-1 rounded-full bg-green-500/10 border border-green-500/20">
<span className="text-xs font-semibold text-green-400">Live</span>
<div className="hidden xs:flex px-3 py-1 rounded-full bg-green-500/10 border border-green-500/20">
<span className="text-[10px] sm:text-xs font-semibold text-green-400">Live Feed</span>
</div>
</div>
</div>
{/* Canvas area */}
<div className="relative p-6 sm:p-8">
<div className="absolute top-8 left-8 w-16 h-16 border-l-2 border-t-2 border-blue-400/30 rounded-tl-2xl" />
<div className="absolute top-8 right-8 w-16 h-16 border-r-2 border-t-2 border-purple-400/30 rounded-tr-2xl" />
<div className="absolute bottom-8 left-8 w-16 h-16 border-l-2 border-b-2 border-pink-400/30 rounded-bl-2xl" />
<div className="absolute bottom-8 right-8 w-16 h-16 border-r-2 border-b-2 border-cyan-400/30 rounded-br-2xl" />
{/* Canvas Container - Responsive Aspect Ratio */}
<div className="relative p-4 sm:p-8">
{/* Decorative corners */}
<div className="absolute top-4 sm:top-8 left-4 sm:left-8 w-12 sm:w-16 h-12 sm:h-16 border-l-2 border-t-2 border-cyan-400/30 rounded-tl-2xl" />
<div className="absolute top-4 sm:top-8 right-4 sm:right-8 w-12 sm:w-16 h-12 sm:h-16 border-r-2 border-t-2 border-purple-400/30 rounded-tr-2xl" />
<div className="absolute bottom-4 sm:bottom-8 left-4 sm:left-8 w-12 sm:w-16 h-12 sm:h-16 border-l-2 border-b-2 border-pink-400/30 rounded-bl-2xl" />
<div className="absolute bottom-4 sm:bottom-8 right-4 sm:right-8 w-12 sm:w-16 h-12 sm:h-16 border-r-2 border-b-2 border-cyan-400/30 rounded-br-2xl" />
<div className="relative aspect-square flex items-center justify-center">
{/* The actual canvas wrapper */}
<div
ref={canvasContainerRef}
className="relative w-full aspect-square max-h-[400px] lg:max-h-[500px] flex items-center justify-center mx-auto"
>
<canvas
ref={canvasRef}
className="w-full h-full max-w-[500px] max-h-[500px] relative z-10"
className="w-full h-full relative z-10 block"
/>
{/* Loading State */}
{!modelLoaded && (
<div className="absolute inset-0 flex items-center justify-center">
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className="space-y-4 text-center">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
>
<Brain className="w-16 h-16 text-blue-400 mx-auto" />
<Brain className="w-12 h-12 sm:w-16 sm:h-16 text-cyan-400 mx-auto" />
</motion.div>
<p className="text-sm text-zinc-400 font-semibold">
Initializing Neural Network...
<p className="text-xs sm:text-sm text-zinc-400 font-semibold">
Initializing Network...
</p>
</div>
</div>
)}
{/* Scanning Animation Overlay */}
{modelLoaded && (
<>
<div className="absolute inset-0 pointer-events-none overflow-hidden rounded-xl">
<motion.div
animate={{ y: [-250, 250] }}
animate={{ top: ["0%", "100%", "0%"] }}
transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
className="absolute inset-x-0 h-px bg-gradient-to-r from-transparent via-blue-400 to-transparent opacity-50"
className="absolute left-0 right-0 h-px bg-gradient-to-r from-transparent via-cyan-400 to-transparent opacity-50 shadow-[0_0_15px_rgba(34,211,238,0.5)]"
/>
<motion.div
animate={{ y: [250, -250] }}
transition={{ duration: 4, repeat: Infinity, ease: "linear", delay: 1.5 }}
className="absolute inset-x-0 h-px bg-gradient-to-r from-transparent via-purple-400 to-transparent opacity-30"
/>
</>
</div>
)}
</div>
</div>
{/* Status footer */}
<div className="p-6 space-y-4 border-t border-white/5">
<div className="p-4 sm:p-6 space-y-4 border-t border-white/5">
<div className="space-y-2">
<div className="flex items-center justify-between text-xs">
<span className="text-zinc-400 font-semibold">Analysis Progress</span>
<span className="text-blue-400 font-bold">{scanProgress}%</span>
<span className="text-cyan-400 font-bold">{scanProgress}%</span>
</div>
<div className="h-2 bg-white/5 rounded-full overflow-hidden">
<div className="h-1.5 sm:h-2 bg-white/5 rounded-full overflow-hidden">
<motion.div
style={{ width: `${scanProgress}%` }}
className="h-full bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500"
className="h-full bg-gradient-to-r from-cyan-500 via-purple-500 to-pink-500"
/>
</div>
</div>
<div className="grid grid-cols-3 gap-3">
<div className="grid grid-cols-3 gap-2 sm:gap-3">
{[
{ icon: Cpu, label: "Processing", value: "98.4%", color: "text-blue-400" },
{ icon: Network, label: "Neural Load", value: "76.2%", color: "text-purple-400" },
{ icon: Activity, label: "Accuracy", value: "99.8%", color: "text-green-400" },
{ icon: Cpu, label: "CPU Load", value: "98%", color: "text-cyan-400" },
{ icon: Network, label: "Net Load", value: "76%", color: "text-purple-400" },
{ icon: Activity, label: "Accuracy", value: "99%", color: "text-green-400" },
].map((stat, idx) => (
<motion.div
<div
key={idx}
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: idx * 0.1 }}
className="p-3 rounded-xl bg-white/[0.03] border border-white/5"
className="p-2 sm:p-3 rounded-xl bg-white/[0.03] border border-white/5 text-center sm:text-left"
>
<stat.icon className={`w-4 h-4 ${stat.color} mb-2`} />
<p className="text-xs text-zinc-500 mb-1">{stat.label}</p>
<p className="text-sm font-bold text-white">{stat.value}</p>
</motion.div>
<stat.icon className={`w-3 h-3 sm:w-4 sm:h-4 ${stat.color} mb-1 sm:mb-2 mx-auto sm:mx-0`} />
<p className="hidden sm:block text-[10px] text-zinc-500 mb-0.5">{stat.label}</p>
<p className="text-xs sm:text-sm font-bold text-white">{stat.value}</p>
</div>
))}
</div>
</div>
<div className="px-6 pb-6">
<p className="text-center text-xs text-zinc-600 font-medium flex items-center justify-center gap-2">
<span className="w-1 h-1 rounded-full bg-zinc-600 animate-pulse" />
Drag to rotate Interactive 3D visualization
<span className="w-1 h-1 rounded-full bg-zinc-600 animate-pulse" />
</p>
<div className="pb-4 sm:pb-6 text-center">
<p className="text-[10px] text-zinc-600 font-medium">Interactive 3D Visualization</p>
</div>
</div>
</motion.div>
{/* Text / Features Column */}
{/* --- Text / Features Column --- */}
<motion.div
style={{ y, opacity, scale }}
className="space-y-10 lg:space-y-12"
className="space-y-8 sm:space-y-10 lg:space-y-12 text-center lg:text-left"
>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7, delay: 0.1 }}
className="space-y-6"
className="space-y-4 sm:space-y-6"
>
<h2 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold leading-[1.1] tracking-tight text-white">
<h2 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl 2xl:text-7xl font-bold leading-[1.1] tracking-tight text-white">
Neural Clinical
<br />
<span className="bg-gradient-to-r from-purple-400 via-pink-400 to-blue-400 bg-clip-text text-transparent">
<span className="bg-gradient-to-r from-purple-400 via-pink-400 to-cyan-400 bg-clip-text text-transparent">
Synthesis
</span>
</h2>
<p className="text-base sm:text-lg lg:text-xl text-zinc-400 leading-relaxed max-w-2xl">
<p className="text-sm sm:text-base md:text-lg lg:text-xl text-zinc-400 leading-relaxed max-w-2xl mx-auto lg:mx-0">
Enterprise-grade diagnostic assistance powered by advanced AI. Cross-reference clinical patterns against{" "}
<span className="text-white font-semibold">global medical databases</span> with{" "}
<span className="text-white font-semibold">sub-second latency</span> and unparalleled accuracy.
<span className="text-white font-semibold">sub-second latency</span>.
</p>
</motion.div>
{/* Feature Cards */}
{/* Feature Cards - Stack on mobile, grid on tablet+ */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
@@ -349,8 +357,8 @@ export default function AIDiagnosis() {
icon: Search,
title: "Pattern Recognition",
text: "AI-powered clinical marker identification with zero-bias analysis technology.",
color: "from-blue-500 to-cyan-500",
iconColor: "text-blue-400",
color: "from-cyan-500 to-cyan-500",
iconColor: "text-cyan-400",
},
{
icon: Database,
@@ -362,58 +370,49 @@ export default function AIDiagnosis() {
].map((item, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.3 + i * 0.1 }}
whileHover={{
y: -4,
transition: { duration: 0.2 },
}}
className="group p-5 sm:p-6 rounded-2xl bg-white/[0.02] border border-white/5 backdrop-blur-sm hover:bg-white/[0.04] hover:border-white/10 transition-all cursor-pointer"
whileHover={{ y: -4, transition: { duration: 0.2 }}}
className="group p-5 rounded-2xl bg-white/[0.02] border border-white/5 backdrop-blur-sm hover:bg-white/[0.04] hover:border-white/10 transition-all cursor-pointer text-left"
>
<div className="space-y-4">
<div className="relative inline-block">
<div
className={`absolute inset-0 bg-gradient-to-br ${item.color} rounded-xl blur-lg opacity-0 group-hover:opacity-50 transition-opacity`}
/>
<div className="relative w-12 h-12 rounded-xl bg-gradient-to-br from-white/10 to-white/5 border border-white/10 flex items-center justify-center group-hover:border-white/20 transition-colors">
<item.icon className={`w-6 h-6 ${item.iconColor}`} />
<div className="flex sm:block items-start gap-4">
<div className="relative shrink-0">
<div className={`absolute inset-0 bg-gradient-to-br ${item.color} rounded-xl blur-lg opacity-0 group-hover:opacity-50 transition-opacity`} />
<div className="relative w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-gradient-to-br from-white/10 to-white/5 border border-white/10 flex items-center justify-center group-hover:border-white/20 transition-colors">
<item.icon className={`w-5 h-5 sm:w-6 sm:h-6 ${item.iconColor}`} />
</div>
</div>
<div className="space-y-2">
<h3 className="text-lg font-bold text-white group-hover:text-white/90 transition-colors">
<div className="space-y-1 sm:space-y-2 sm:mt-4">
<h3 className="text-base sm:text-lg font-bold text-white group-hover:text-white/90 transition-colors">
{item.title}
</h3>
<p className="text-sm text-zinc-400 leading-relaxed">{item.text}</p>
<p className="text-xs sm:text-sm text-zinc-400 leading-relaxed">{item.text}</p>
</div>
</div>
</motion.div>
))}
</motion.div>
{/* Stats */}
{/* Stats - Grid adjustments */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7, delay: 0.4 }}
className="grid grid-cols-3 gap-4 sm:gap-6 pt-6"
className="grid grid-cols-3 gap-2 sm:gap-6 pt-4 sm:pt-6 border-t border-white/5 lg:border-none"
>
{[
{ icon: Zap, value: "<500ms", label: "Query Time" },
{ icon: Shield, value: "100%", label: "Secure" },
{ icon: TrendingUp, value: "99.8%", label: "Accuracy" },
].map((stat, i) => (
<div key={i} className="text-center space-y-2">
<div key={i} className="text-center space-y-1 sm:space-y-2">
<div className="flex justify-center">
<div className="p-2 rounded-lg bg-white/5 border border-white/10">
<stat.icon className="w-4 h-4 sm:w-5 sm:h-5 text-purple-400" />
<div className="p-1.5 sm:p-2 rounded-lg bg-white/5 border border-white/10">
<stat.icon className="w-3.5 h-3.5 sm:w-5 sm:h-5 text-purple-400" />
</div>
</div>
<p className="text-xl sm:text-2xl font-bold text-white">{stat.value}</p>
<p className="text-[10px] sm:text-xs text-zinc-500 font-semibold uppercase tracking-wider">
<p className="text-lg sm:text-xl md:text-2xl font-bold text-white">{stat.value}</p>
<p className="text-[9px] sm:text-xs text-zinc-500 font-semibold uppercase tracking-wider">
{stat.label}
</p>
</div>
@@ -426,12 +425,12 @@ export default function AIDiagnosis() {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7, delay: 0.5 }}
className="flex flex-wrap gap-3 sm:gap-4 pt-4"
className="flex flex-wrap justify-center lg:justify-start gap-2 sm:gap-4 pt-2"
>
{["FDA Compliant", "HIPAA Certified", "ISO 27001"].map((badge, i) => (
<div
key={i}
className="flex items-center gap-2 px-3 sm:px-4 py-2 rounded-full bg-white/[0.03] border border-white/10 backdrop-blur-sm"
className="flex items-center gap-1.5 sm:gap-2 px-3 py-1.5 sm:px-4 sm:py-2 rounded-full bg-white/[0.03] border border-white/10 backdrop-blur-sm"
>
<CheckCircle className="w-3 h-3 sm:w-4 sm:h-4 text-green-400" />
<span className="text-[10px] sm:text-xs text-zinc-400 font-semibold">{badge}</span>

View File

@@ -1,173 +1,172 @@
"use client";
import { motion, useScroll, useTransform } from "framer-motion";
import { Mic, CheckCircle2, Brain, Zap, Lock, Sparkles } from "lucide-react";
import { Mic, Brain, Zap, Lock, Sparkles, AudioWaveform } from "lucide-react";
import { useRef } from "react";
export default function AIVoiceSoap() {
const containerRef = useRef(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start end", "end start"],
});
const containerRef = useRef(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start end", "end start"],
});
const scale = useTransform(scrollYProgress, [0, 0.5], [0.95, 1]);
const opacity = useTransform(scrollYProgress, [0, 0.4], [0.6, 1]);
const y = useTransform(scrollYProgress, [0, 0.5], [40, 0]);
const scale = useTransform(scrollYProgress, [0, 0.5], [0.95, 1]);
const opacity = useTransform(scrollYProgress, [0, 0.4], [0.6, 1]);
const y = useTransform(scrollYProgress, [0, 0.5], [40, 0]);
const bars = Array.from({ length: 24 }, (_, i) => ({
height: [20, 45 + Math.sin(i * 0.8) * 30, 20],
duration: 2 + (i % 8) * 0.25,
delay: i * 0.03,
}));
// Dynamic bar animation for visualizer
const bars = Array.from({ length: 24 }, (_, i) => ({
height: [20, 45 + Math.sin(i * 0.8) * 30, 20],
duration: 2 + (i % 8) * 0.25,
delay: i * 0.03,
}));
return (
<section
ref={containerRef}
id="voice-soap"
className="relative py-20 md:py-32 lg:py-40 text-white overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-b from-zinc-950 via-zinc-900/50 to-zinc-950" />
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-950/20 via-transparent to-transparent" />
return (
<section
ref={containerRef}
id="voice-soap"
className="relative py-16 sm:py-20 md:py-32 lg:py-40 2xl:py-48 text-white overflow-hidden bg-transparent"
>
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-12 max-w-7xl 2xl:max-w-[1600px]">
<div className="grid lg:grid-cols-2 gap-12 lg:gap-24 xl:gap-32 items-center">
<motion.div
style={{ scale, opacity, y }}
className="space-y-10 lg:space-y-14 order-2 lg:order-1"
>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="inline-flex items-center gap-2 sm:gap-3 px-4 py-2 rounded-full bg-cyan-950/30 border border-cyan-500/20 text-cyan-400 text-[10px] sm:text-xs font-semibold tracking-wide uppercase backdrop-blur-md"
>
<Mic className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
Ambient Clinical Intelligence
</motion.div>
<div className="container relative z-10 mx-auto px-5 md:px-8 lg:px-12 max-w-7xl">
<div className="grid lg:grid-cols-2 gap-16 lg:gap-24 xl:gap-32 items-center">
<motion.div
style={{ scale, opacity, y }}
className="space-y-10 lg:space-y-14"
>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="inline-flex items-center gap-3 px-6 py-2 rounded-full bg-blue-950/40 border border-blue-800/40 text-blue-400 text-xs font-semibold tracking-wide uppercase"
>
<Mic className="w-4 h-4" />
Ambient Clinical Intelligence
</motion.div>
<div className="space-y-6">
<h2 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight leading-tight">
Autonomous{" "}
<span className="text-blue-400">Voice-to-SOAP</span>
</h2>
<p className="text-xl md:text-2xl text-zinc-400 leading-relaxed max-w-3xl font-light">
Transform ambient conversations into structured, accurate{" "}
<span className="text-white font-medium">SOAP notes</span> in real-time with enterprise-grade security and clinical precision.
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6 pt-6">
{[
{ icon: Brain, title: "Neural Context Understanding", desc: "Advanced NLP for medical terminology and context" },
{ icon: Zap, title: "Instant SOAP Generation", desc: "Zero-latency transcription to structured notes" },
{ icon: Lock, title: "End-to-End PHI Security", desc: "AES-256 encryption & HIPAA/GDPR compliant" },
{ icon: Sparkles, title: "Auto-Suggest & Edit", desc: "AI-powered refinements & clinician review" },
].map((item, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 15 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.15 * i }}
viewport={{ once: true }}
whileHover={{ scale: 1.03, transition: { duration: 0.2 } }}
className="flex gap-5 p-5 rounded-2xl bg-zinc-900/50 border border-zinc-800 hover:border-blue-800/50 transition-all"
>
<div className="w-12 h-12 rounded-xl bg-blue-950/40 border border-blue-800/40 flex items-center justify-center flex-shrink-0">
<item.icon className="w-6 h-6 text-blue-400" />
</div>
<div className="space-y-1">
<h4 className="text-lg font-semibold text-white">{item.title}</h4>
</div>
</motion.div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, scale: 0.96 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
viewport={{ once: true }}
className="relative w-full max-w-lg mx-auto lg:mx-0"
>
<div className="relative rounded-3xl backdrop-blur-xl shadow-2xl overflow-hidden">
<div className="absolute inset-0 pointer-events-none" />
<div className="p-8 md:p-10 lg:p-12 space-y-10 lg:space-y-12 relative">
<div className="flex justify-center">
<div className="relative">
<motion.div
animate={{ scale: [1, 1.4, 1], opacity: [0.4, 0, 0.4] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
className="absolute inset-[-20%] rounded-full bg-blue-500/20 blur-2xl"
/>
<motion.div
animate={{ scale: [1, 1.3, 1], opacity: [0.3, 0, 0.3] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut", delay: 0.6 }}
className="absolute inset-[-20%] rounded-full bg-purple-500/15 blur-3xl"
/>
<div className="relative w-24 h-24 md:w-32 md:h-32 lg:w-40 lg:h-40 rounded-full bg-zinc-800/70 border-2 border-zinc-700 flex items-center justify-center shadow-inner">
<Mic className="w-12 h-12 md:w-16 md:h-16 lg:w-20 lg:h-20 text-blue-400 drop-shadow-lg" />
</div>
</div>
</div>
<div className="flex justify-center items-end gap-1.5 h-32 md:h-6 px-4">
{bars.map((bar, i) => (
<motion.div
key={i}
animate={{ height: bar.height }}
transition={{
repeat: Infinity,
duration: bar.duration,
ease: "easeInOut",
delay: bar.delay,
}}
className="w-1.5 md:w-2 rounded-full bg-gradient-to-t from-blue-700 to-blue-400 shadow-md"
style={{ opacity: 0.4 + (i % 8) * 0.08 }}
/>
))}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.4 }}
viewport={{ once: true }}
className="relative rounded-2xl bg-zinc-800/60 border border-zinc-700/80 p-6 md:p-8 shadow-inner overflow-hidden"
>
<motion.div
animate={{ x: ["-100%", "200%"] }}
transition={{ duration: 4, repeat: Infinity, ease: "linear", repeatDelay: 3 }}
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent pointer-events-none"
/>
<div className="space-y-4 text-sm md:text-base">
<div className="flex items-center gap-3">
<div className="w-2.5 h-2.5 rounded-full bg-blue-500 shadow-lg" />
<div className="h-3 bg-gradient-to-r from-blue-400/30 to-transparent rounded-full flex-1" />
</div>
<div className="h-2.5 bg-zinc-700/50 rounded-full ml-8" />
<div className="h-2.5 bg-zinc-700/50 rounded-full ml-8 w-5/6" />
<div className="h-2.5 bg-zinc-700/50 rounded-full ml-8 w-3/4" />
</div>
<div className="mt-6 flex items-center gap-3 text-xs md:text-sm text-zinc-400">
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
<span>Processing in real-time</span>
</div>
<div className="ml-auto text-emerald-400 font-medium">Secure Encrypted</div>
</div>
</motion.div>
</div>
</div>
</motion.div>
</div>
<div className="space-y-4 sm:space-y-6">
<h2 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl 2xl:text-8xl font-bold tracking-tight leading-[1.1]">
Autonomous{" "}
<span className="text-cyan-400 block sm:inline">Voice-to-SOAP</span>
</h2>
<p className="text-lg sm:text-xl md:text-2xl 2xl:text-3xl text-zinc-400 leading-relaxed max-w-3xl font-light">
Transform ambient conversations into structured, accurate{" "}
<span className="text-white font-medium">SOAP notes</span> in real-time with enterprise-grade security.
</p>
</div>
</section>
);
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 pt-2 sm:pt-6">
{[
{ icon: Brain, title: "Neural Context", desc: "Advanced NLP for medical terminology" },
{ icon: Zap, title: "Instant Generation", desc: "Zero-latency transcription to notes" },
{ icon: Lock, title: "PHI Security", desc: "AES-256 encryption & HIPAA compliant" },
{ icon: Sparkles, title: "Auto-Refinement", desc: "AI-powered suggestions & review" },
].map((item, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 15 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.15 * i }}
viewport={{ once: true }}
whileHover={{ scale: 1.02, backgroundColor: "rgba(255,255,255,0.08)" }}
className="flex gap-4 p-4 sm:p-5 rounded-2xl bg-white/5 border border-white/10 backdrop-blur-sm hover:border-cyan-500/30 transition-all cursor-default"
>
<div className="w-10 h-10 rounded-lg bg-cyan-500/10 border border-cyan-500/20 flex items-center justify-center flex-shrink-0">
<item.icon className="w-5 h-5 text-cyan-400" />
</div>
<div>
<h4 className="text-sm sm:text-base font-bold text-white mb-1">{item.title}</h4>
<p className="text-xs text-zinc-400 leading-relaxed">{item.desc}</p>
</div>
</motion.div>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, scale: 0.96 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
viewport={{ once: true }}
className="relative w-full max-w-lg 2xl:max-w-xl mx-auto lg:mx-0 order-1 lg:order-2"
>
<div className="relative rounded-[2rem] sm:rounded-[2.5rem] backdrop-blur-xl shadow-2xl overflow-hidden">
{/* Inner Content Padding */}
<div className="p-6 sm:p-10 lg:p-12 2xl:p-16 space-y-8 sm:space-y-10 lg:space-y-12 relative z-10">
{/* Microphone Animation */}
<div className="flex justify-center">
<div className="relative">
<motion.div
animate={{ scale: [1, 1.4, 1], opacity: [0.4, 0, 0.4] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
className="absolute inset-[-20%] rounded-full bg-cyan-500/20 blur-2xl"
/>
<div className="relative w-20 h-20 sm:w-28 sm:h-28 2xl:w-36 2xl:h-36 rounded-full bg-gradient-to-b from-zinc-800 to-zinc-900 border border-zinc-700 flex items-center justify-center shadow-[0_8px_32px_rgba(0,0,0,0.5)]">
<Mic className="w-8 h-8 sm:w-12 sm:h-12 2xl:w-16 2xl:h-16 text-cyan-400 drop-shadow-lg" />
</div>
</div>
</div>
{/* Audio Waveform Bars - Responsive height/gap */}
<div className="flex justify-center items-end gap-1 sm:gap-1.5 h-12 sm:h-16 px-2 sm:px-4">
{bars.map((bar, i) => (
<motion.div
key={i}
animate={{ height: bar.height }}
transition={{
repeat: Infinity,
duration: bar.duration,
ease: "easeInOut",
delay: bar.delay,
}}
className="w-1 sm:w-1.5 md:w-2 rounded-full bg-cyan-500"
style={{ opacity: 0.3 + (i % 8) * 0.1 }}
/>
))}
</div>
{/* Processing Status Box */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 0.4 }}
viewport={{ once: true }}
className="relative rounded-2xl bg-black/20 border border-white/5 p-4 sm:p-6 backdrop-blur-md"
>
{/* Scan Line Animation */}
<motion.div
animate={{ x: ["-100%", "200%"] }}
transition={{ duration: 3, repeat: Infinity, ease: "linear", repeatDelay: 2 }}
className="absolute inset-0 bg-gradient-to-r from-transparent via-cyan-400/10 to-transparent pointer-events-none"
/>
{/* Abstract Data Lines */}
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="w-2 h-2 rounded-full bg-cyan-500 shadow-[0_0_10px_rgba(6,182,212,0.5)]" />
<div className="h-1.5 sm:h-2 bg-white/10 rounded-full flex-1 w-full" />
</div>
<div className="h-1.5 sm:h-2 bg-white/10 rounded-full ml-5 w-3/4" />
<div className="h-1.5 sm:h-2 bg-white/10 rounded-full ml-5 w-5/6" />
</div>
<div className="mt-4 sm:mt-5 flex items-center justify-between text-[10px] sm:text-xs text-zinc-400 font-mono">
<div className="flex items-center gap-2">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500"></span>
</span>
<span>Processing...</span>
</div>
<div className="text-emerald-400">Encrypted</div>
</div>
</motion.div>
</div>
</div>
</motion.div>
</div>
</div>
</section>
);
}

View File

@@ -40,8 +40,8 @@ export default function AIVoiceSoap() {
icon: Activity,
title: "Context-Aware Intelligence",
desc: "Understands medical terminology, patient history context & follow-up questions.",
color: "text-blue-400",
bg: "from-blue-600/15 to-blue-800/5",
color: "text-cyan-400",
bg: "from-cyan-600/15 to-cyan-800/5",
},
{
icon: Zap,

View File

@@ -142,10 +142,10 @@ function TechCard({ tech, i }: { tech: Technology; i: number }) {
<tech.icon className="w-8 h-8 text-white" />
</motion.div>
<div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-green-500/10 border border-green-500/20">
{/* <div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-green-500/10 border border-green-500/20">
<div className="w-1.5 h-1.5 rounded-full bg-green-400 animate-pulse" />
<span className="text-[10px] text-green-400 font-bold uppercase tracking-wider">Active</span>
</div>
</div> */}
</div>
</motion.div>
@@ -230,7 +230,7 @@ export default function TechShowcase() {
return (
<section id="technologies" className="relative py-20 md:py-32 lg:py-40 overflow-hidden">
{/* Ambient Effects - More Subtle */}
<div className="absolute top-1/3 left-0 w-[500px] h-[500px] bg-blue-500/5 rounded-full blur-[120px]" />
<div className="absolute top-1/3 left-0 w-[500px] h-[500px] bg-cyan-500/5 rounded-full blur-[120px]" />
<div className="absolute bottom-1/3 right-0 w-[500px] h-[500px] bg-purple-500/5 rounded-full blur-[120px]" />
{/* Dot Grid Pattern */}