Compare commits
	
		
			44 Commits
		
	
	
		
			main_backu
			...
			developmen
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 6d6cbd115a | ||
|  | 3c9823bf80 | ||
|  | 1260afdd82 | ||
| ff1f29b652 | |||
| 41bd49dfaf | |||
| 9ae3785c70 | |||
| 24a0a981a9 | |||
| e5cbce4c86 | |||
| 2ba55566b5 | |||
|  | d82d3351f3 | ||
| 05cd30f510 | |||
| 2a141cbc91 | |||
| 2ebdb14409 | |||
| 3ea9e77a7b | |||
| a52e838d17 | |||
| b543aebce9 | |||
| 8844ec8a63 | |||
| 68b2119c76 | |||
| 94f4e72e57 | |||
| c1952754d7 | |||
| c0df7c21a7 | |||
| be74079de2 | |||
| 752668b38d | |||
| 11689fdd37 | |||
|  | 45b1c43b97 | ||
|  | 26fbea3ec4 | ||
|  | 3a7aa82ff7 | ||
| 644bb34419 | |||
| 9b3c620706 | |||
| 987b46588b | |||
| df88c4a14f | |||
| dfb469e7f7 | |||
| a3cd28fc6b | |||
| 758a004ce9 | |||
| 73832e2686 | |||
| 4cc98af570 | |||
| 8f8d45c7ff | |||
| f2cb22ef66 | |||
| 82e730346e | |||
| c9129c5c66 | |||
| 8f1df965f2 | |||
| af77948679 | |||
| ce33e0f7c3 | |||
| 1f0f18105e | 
							
								
								
									
										25
									
								
								components.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| { | ||||
|   "$schema": "https://ui.shadcn.com/schema.json", | ||||
|   "style": "new-york", | ||||
|   "rsc": false, | ||||
|   "tsx": true, | ||||
|   "tailwind": { | ||||
|     "config": "", | ||||
|     "css": "src/styles/tailwind.css", | ||||
|     "baseColor": "neutral", | ||||
|     "cssVariables": true, | ||||
|     "prefix": "" | ||||
|   }, | ||||
|   "iconLibrary": "lucide", | ||||
|   "aliases": { | ||||
|     "components": "@/components", | ||||
|     "utils": "@/lib/utils", | ||||
|     "ui": "@/components/ui", | ||||
|     "lib": "@/lib", | ||||
|     "hooks": "@/hooks" | ||||
|   }, | ||||
|   "registries": { | ||||
|     "@aceternity": "https://ui.aceternity.com/registry/{name}.json", | ||||
|     "@magicui": "https://magicui.design/r/{name}.json" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										181
									
								
								docs/mycelium_cloud_for_devs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,181 @@ | ||||
| # Mycelium Cloud for Developers | ||||
|  | ||||
| *Developer Guide to Decentralized Cloud Computing* | ||||
|  | ||||
| ## Overview | ||||
| Mycelium Cloud provides a comprehensive platform for deploying and managing Kubernetes clusters on the decentralized ThreeFold Grid infrastructure. This guide covers the core features and capabilities available to developers. | ||||
|  | ||||
| ## Architecture | ||||
|  | ||||
| ### Decentralized Infrastructure | ||||
| Mycelium Cloud deploys on ThreeFold Grid's distributed network, providing: | ||||
| - **Direct Node Access**: Each node gets a unique Mycelium IP address | ||||
| - **Cross-Node Communication**: Services communicate across nodes using Mycelium networking | ||||
| - **Secure Communication**: All traffic is encrypted through the Mycelium network | ||||
| - **No Public IPs Required**: Services accessible via Mycelium IPs | ||||
|  | ||||
| ### Network Flow | ||||
| User Machine → Mycelium Network → Cluster Node → Service | ||||
|  | ||||
| ### Kubernetes Management | ||||
| - **K3s Support**: Full K3s cluster deployment and management | ||||
| - **IPv6 Networking**: Mycelium peer-to-peer networking | ||||
| - **High Availability**: Multi-master cluster support | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Core Features | ||||
|  | ||||
| ### Mycelium Networking | ||||
| Ultra-fast, decentralized networking inspired by nature. Mycelium Networking forms a resilient, adaptive mesh that routes around failures and optimizes for speed and security. | ||||
|  | ||||
| **Features:** | ||||
| - End-to-end encrypted | ||||
| - Nature-inspired | ||||
| - Direct node communication | ||||
| - Secure peer-to-peer connections | ||||
|  | ||||
| ### Zero-Image Technology | ||||
| Metadata-driven zero-images cut artifacts up to 100x, slashing bandwidth and deployment overhead. | ||||
|  | ||||
| **Benefits:** | ||||
| - **Deterministic Deployments**: Cryptographic verification ensures every workload deploys exactly as intended—no tampering, no drift | ||||
| - **Multi-Workload Compatibility with Secure Boot**: Run containers, VMs, and Linux workloads anywhere with stateless secure boot and continuous verification | ||||
| - **Smart Contract-Based Deployment**: Cryptographically signed contracts orchestrate every workload with transparent, tamper-proof execution | ||||
|  | ||||
| ### Quantum-Safe Storage (QSS) | ||||
| Quantum-resistant encryption secures data beyond the app layer so ownership and control stay yours. | ||||
|  | ||||
| **Features:** | ||||
| - **Self-Healing Storage System**: Autonomous recovery heals failures or corruption instantly, preserving integrity without human intervention | ||||
| - **Multi-Protocol Data Access**: Serve the same data via IPFS, S3, WebDAV, HTTP, and native file systems for seamless integration | ||||
| - **Geo-Aware Data Placement & Replication**: Define residency, redundancy, and distribution per workload while zone-to-zone replication hardens resilience | ||||
|  | ||||
| ### Multi-Master Clusters | ||||
| High-availability Kubernetes clusters with multiple control plane nodes. Automatic failover, leader election, and zero-downtime upgrades built-in. | ||||
|  | ||||
| **Capabilities:** | ||||
| - **HA Control Plane** | ||||
| - **Automatic Failover** | ||||
| - **Zero-downtime Upgrades** | ||||
|  | ||||
| ### Effortless Load Balancing & Scaling | ||||
| Mycelium Cloud automatically balances traffic and scales your services up or down based on demand. Enjoy high availability and optimal performance with zero manual intervention. | ||||
|  | ||||
| **Features:** | ||||
| - **Auto-scaling** | ||||
| - **Built-in load balancing** | ||||
| - **High availability** | ||||
|  | ||||
| ### Simple Web Gateway Access | ||||
| Expose any service to the public web with a simple Kubernetes resource. No complex Ingress controllers. Domain and prefix-based routing is built-in. | ||||
|  | ||||
| **Benefits:** | ||||
| - **Simple configuration** | ||||
| - **Built-in routing** | ||||
| - **No ingress controllers** | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Getting Started | ||||
|  | ||||
| ### 1. Account Setup | ||||
| - **Sign Up**: Create your account from signup page | ||||
| - **Verify Email**: Check your email and verify your account | ||||
| - **Add Funds**: Navigate to your dashboard and add credits to your account | ||||
| - **Add SSH Key**: Navigate to Add SSH card and upload your public SSH key | ||||
|  | ||||
| ### 2. Deploy Your First Cluster | ||||
| - **Access Deploy**: Click "Deploy Cluster" from your dashboard | ||||
| - **Configure VMs**: Define your virtual machines: | ||||
|   - Choose CPU, memory, and storage requirements | ||||
|   - Select the number of master and worker nodes | ||||
| - **Select Nodes**: Choose ThreeFold Grid nodes for deployment | ||||
| - **Review & Deploy**: Confirm your configuration and deploy | ||||
|  | ||||
| ### 3. Access Your Cluster | ||||
|  | ||||
| #### Download Kubeconfig | ||||
| - Go to dashboard → Clusters → Click download icon (⬇️) | ||||
| - Set kubeconfig: `export KUBECONFIG=/path/to/config` | ||||
| - Test: `kubectl get nodes` | ||||
|  | ||||
| #### SSH Access | ||||
| - **Find Mycelium IPs**: Check cluster details page for node IPs | ||||
| - **Download Mycelium Binary**: | ||||
|   ```bash | ||||
|   wget https://github.com/threefoldtech/mycelium/releases/latest/download/mycelium-private-x86_64-unknown-linux-musl.tar.gz | ||||
|   tar -xzf mycelium-private-x86_64-unknown-linux-musl.tar.gz | ||||
|   sudo chmod +x mycelium-private | ||||
|   sudo mv mycelium-private /usr/local/bin/mycelium | ||||
|   ``` | ||||
| - **Start Mycelium**: | ||||
|   ```bash | ||||
|   sudo mycelium --peers tcp://188.40.132.242:9651 tcp://136.243.47.186:9651 tcp://185.69.166.7:9651 tcp://185.69.166.8:9651 tcp://65.21.231.58:9651 tcp://65.109.18.113:9651 tcp://209.159.146.190:9651 tcp://5.78.122.16:9651 tcp://5.223.43.251:9651 tcp://142.93.217.194:9651 | ||||
|   ``` | ||||
| - **SSH to nodes**: `ssh root@<mycelium-ip>` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Use Cases | ||||
|  | ||||
| ### AI/ML Training | ||||
| Run GPU-accelerated workloads for deep learning and data science on demand. | ||||
| - **GPU Acceleration** | ||||
| - **Scalable Compute** | ||||
| - **Cost Optimization** | ||||
|  | ||||
| ### Enterprise Kubernetes | ||||
| Deploy production-grade clusters with full control and no vendor lock-in. | ||||
| - **High Availability** | ||||
| - **Security** | ||||
| - **Compliance** | ||||
|  | ||||
| ### Edge & IoT | ||||
| Leverage global nodes for low-latency edge computing and IoT applications. | ||||
| - **Low Latency** | ||||
| - **Global Distribution** | ||||
| - **Real-time Processing** | ||||
|  | ||||
| ### DigitalMe Example | ||||
| Example of what can be done with Mycelium Cloud. With a very simple execution, the following can be deployed on k8s cluster and served on Mycelium IPs: | ||||
|  | ||||
| - **Cryptpad**: Encrypted document collaboration | ||||
| - **Elements**: Matrix chat (the Rust one) | ||||
| - **Stallwart**: Mail/calendar/contacts | ||||
| - **Gitea**: Git hosting and code collaboration | ||||
| - **Nextcloud**: File storage and synchronization | ||||
| - **LiveKit or Jitsi**: Video conferencing integrated with elements/nextcloud | ||||
| - **SSO**: Single Sign-On (can this be gitea as backend - later) | ||||
|  | ||||
| This demonstrates the platform's capability to host complex, integrated applications with minimal configuration. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Key Differentiators | ||||
|  | ||||
| ### Sovereign by Design | ||||
| Control jurisdiction, residency, and governance for every workload with transparent, verifiable operations. | ||||
|  | ||||
| ### Secure by Default | ||||
| Cryptographic verification, secure boot, and zero-image delivery protect the entire lifecycle automatically. | ||||
|  | ||||
| ### Ready to Scale | ||||
| Autonomous orchestration keeps the platform elastic, cost-efficient, and always available across the globe. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Security Architecture | ||||
|  | ||||
| ### Provable Sovereignty | ||||
| Assign workloads to trusted zones, verify state with cryptographic proofs, and maintain full lineage for every byte. | ||||
|  | ||||
| ### Autonomous Zero-Trust | ||||
| Identity, policy, and attestation are enforced continuously—no manual keys, no hidden backdoors, no shared control. | ||||
|  | ||||
| ### Planetary-Scale Resilience | ||||
| Mesh-connected infrastructure routes around failure, keeping applications responsive even when regions go dark. | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Mycelium Cloud - Comprehensive platform for decentralized Kubernetes deployment.* | ||||
							
								
								
									
										215
									
								
								docs/mycelium_compute_for_devs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,215 @@ | ||||
| # Mycelium Compute for Developers | ||||
|  | ||||
| *Deterministic Compute Fabric* | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| Mycelium Compute provides a sovereign, deterministic compute fabric that enables developers to launch workloads with cryptographic certainty and autonomous operations. Built on decentralized infrastructure, it offers transparent, verifiable computing environments with zero manual intervention. | ||||
|  | ||||
| ## Core Concept | ||||
|  | ||||
| Deterministic compute fabric ensures that every workload deploys exactly as intended with cryptographic verification, eliminating tampering and drift while maintaining full operational autonomy. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Core Features | ||||
|  | ||||
| ### Deterministic Deployments | ||||
| Cryptographic verification ensures every workload deploys exactly as intended—no tampering, no drift. | ||||
|  | ||||
| **Benefits:** | ||||
| - **Cryptographic Verification**: Every workload component verified before deployment | ||||
| - **No Configuration Drift**: Environments remain exactly as specified | ||||
| - **Tamper Detection**: Any unauthorized changes immediately identified | ||||
| - **Complete Reproducibility**: Exact same environment every deployment | ||||
|  | ||||
| ### Self-Managing & Stateless Infrastructure | ||||
| Fully autonomous infrastructure that scales globally without manual intervention. | ||||
|  | ||||
| **Capabilities:** | ||||
| - **Autonomous Operations**: No manual intervention required for scaling | ||||
| - **Global Scaling**: Infrastructure scales across the ThreeFold Grid | ||||
| - **Stateless Design**: No persistent dependencies on specific hardware | ||||
| - **Self-Healing**: Automatic recovery from failures and issues | ||||
|  | ||||
| ### Smart Contract-Based Deployment | ||||
| Cryptographically signed contracts orchestrate every workload with transparent, tamper-proof execution. | ||||
|  | ||||
| **Benefits:** | ||||
| - **Cryptographic Signatures**: Every deployment contract cryptographically signed | ||||
| - **Transparent Execution**: All operations verifiable and auditable | ||||
| - **Tamper-Proof**: Immutable deployment contracts | ||||
| - **Automated Orchestration**: Smart contracts manage workload lifecycle | ||||
|  | ||||
| ### Multi-Workload Compatibility with Secure Boot | ||||
| Run containers, VMs, and Linux workloads anywhere with stateless secure boot and continuous verification. | ||||
|  | ||||
| **Capabilities:** | ||||
| - **Container Support**: Full Kubernetes and Docker compatibility | ||||
| - **VM Workloads**: Support for virtual machine deployments | ||||
| - **Linux Workloads**: Native Linux application support | ||||
| - **Secure Boot**: Hardware-level verification and security | ||||
| - **Continuous Verification**: Ongoing integrity checking | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Zero-Image Integration | ||||
|  | ||||
| Mycelium Compute integrates with Mycelium Storage's Zero-Image technology for ultra-efficient deployments: | ||||
|  | ||||
| **Benefits:** | ||||
| - **Metadata-Only**: Deployments use metadata instead of full artifacts | ||||
| - **Instant Startup**: Rapid workload initialization | ||||
| - **Bandwidth Optimization**: Minimal transfer requirements | ||||
| - **Global Distribution**: Deploy anywhere on ThreeFold Grid | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Technical Architecture | ||||
|  | ||||
| ### Deterministic Computing | ||||
| Mycelium Compute ensures every computational step is predictable and verifiable: | ||||
|  | ||||
| - **Cryptographic Verification**: All computations verified before execution | ||||
| - **State Determinism**: Same inputs always produce same outputs | ||||
| - **Tamper Resistance**: Any modification detected immediately | ||||
| - **Audit Trail**: Complete verification history for all operations | ||||
|  | ||||
| ### Stateless Infrastructure | ||||
| - **No Persistent State**: Workloads don't depend on specific hardware | ||||
| - **Global Distribution**: Compute resources available worldwide | ||||
| - **Auto-Scaling**: Automatic resource allocation and scaling | ||||
| - **Fault Tolerance**: Automatic failover and recovery | ||||
|  | ||||
| ### Zero-Image System | ||||
| - **Metadata-Only**: Images represented as metadata, not full artifacts | ||||
| - **Instant Deployment**: Rapid workload startup using metadata | ||||
| - **Efficient Storage**: Minimal storage requirements for images | ||||
| - **Bandwidth Optimization**: Significant reduction in transfer overhead | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Developer Experience | ||||
|  | ||||
| ### Simple Deployment | ||||
| ```yaml | ||||
| # Basic compute workload | ||||
| apiVersion: v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: deterministic-app | ||||
| spec: | ||||
|   replicas: 3 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: deterministic-app | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: deterministic-app | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: app | ||||
|         image: ubuntu:latest | ||||
|         command: ["echo", "Deterministic deployment"] | ||||
| ``` | ||||
|  | ||||
| ### Zero-Image Deployment | ||||
| ```yaml | ||||
| # Using zero-image technology | ||||
| apiVersion: v1 | ||||
| kind: Pod | ||||
| metadata: | ||||
|   name: zero-image-pod | ||||
| spec: | ||||
|   containers: | ||||
|   - name: app | ||||
|     image: "zero-image://ubuntu-latest"  # Metadata-only image | ||||
|     command: ["echo", "Running on zero-image"] | ||||
| ``` | ||||
|  | ||||
| ### Smart Contract Deployment | ||||
| ```yaml | ||||
| # Smart contract orchestrated deployment | ||||
| apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: deployment-contract | ||||
| data: | ||||
|   contract: | | ||||
|     smart_contract: | ||||
|       signature: "cryptographically_signed_deployment" | ||||
|       workload_spec: | ||||
|         image: "ubuntu-latest" | ||||
|         replicas: 3 | ||||
|         verification_hash: "sha256_hash_of_workload" | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Use Cases | ||||
|  | ||||
| ### AI/ML Training | ||||
| - **Deterministic Training**: Reproducible machine learning experiments | ||||
| - **Secure Model Deployment**: Cryptographically verified model artifacts | ||||
| - **Distributed Training**: Autonomous scaling across compute nodes | ||||
| - **Zero-Drift Environments**: Consistent training environments | ||||
|  | ||||
| ### Application Hosting | ||||
| - **Transparent Deployments**: Verifiable application deployments | ||||
| - **Auto-Scaling**: Automatic resource allocation based on demand | ||||
| - **Global Distribution**: Deploy applications worldwide instantly | ||||
| - **Secure Execution**: Cryptographically verified runtime environments | ||||
|  | ||||
| ### Data Processing | ||||
| - **Deterministic Pipelines**: Reproducible data processing workflows | ||||
| - **Secure Computation**: Verified data transformation operations | ||||
| - **Auto-Scaling**: Dynamic resource allocation for processing workloads | ||||
| - **Global Processing**: Distribute workloads across ThreeFold Grid | ||||
|  | ||||
| ### Scientific Computing | ||||
| - **Reproducible Research**: Verifiable computational experiments | ||||
| - **Secure Workloads**: Cryptographically verified scientific applications | ||||
| - **Auto-Scaling**: Dynamic resource allocation for compute-intensive tasks | ||||
| - **Global Collaboration**: Share computational resources worldwide | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Compute-Specific Use Cases | ||||
|  | ||||
| ### Deterministic Training Environments | ||||
| - **Reproducible ML Experiments**: Identical training conditions every time | ||||
| - **Scientific Computing**: Verifiable computational research | ||||
| - **Financial Modeling**: Auditable risk calculations | ||||
| - **IoT Processing**: Consistent edge computing environments | ||||
|  | ||||
| ### Multi-Platform Application Hosting | ||||
| - **Container Orchestration**: Kubernetes workloads with deterministic deployment | ||||
| - **VM Management**: Virtual machines with secure boot verification | ||||
| - **Linux Workloads**: Native applications with cryptographic assurance | ||||
| - **Hybrid Deployments**: Mix containers, VMs, and bare metal | ||||
|  | ||||
| ### Auto-Scaling Workloads | ||||
| - **Demand-Based Scaling**: Automatic resource allocation based on load | ||||
| - **Global Distribution**: Deploy compute across ThreeFold Grid | ||||
| - **Failure Recovery**: Automatic failover and service restoration | ||||
| - **Cost Optimization**: Scale resources efficiently | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Key Differentiators | ||||
|  | ||||
| ### Deterministic Guarantee | ||||
| Every computation is cryptographically verified and guaranteed to produce consistent results. | ||||
|  | ||||
| ### Autonomous Operations | ||||
| Infrastructure manages itself without human intervention, scaling and healing automatically. | ||||
|  | ||||
| ### Smart Contract Security | ||||
| Workload orchestration through cryptographically signed, tamper-proof contracts. | ||||
|  | ||||
| ### Multi-Platform Support | ||||
| Run any workload type - containers, VMs, or native Linux applications. | ||||
|  | ||||
| ### Secure Boot Verification | ||||
| Hardware-level security verification for all deployments. | ||||
							
								
								
									
										157
									
								
								docs/mycelium_gpu_for_devs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,157 @@ | ||||
| # Mycelium GPU for Developers | ||||
|  | ||||
| *The Energy Behind Intelligence* | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| Mycelium GPU provides unified access to distributed GPU acceleration across the ThreeFold Grid. It transforms fragmented GPU resources into a single sovereign fabric for running AI, ML, and rendering workloads. | ||||
|  | ||||
| ## Core Concept | ||||
|  | ||||
| Mycelium GPU unifies distributed acceleration into a single sovereign fabric — turning fragmented hardware into one adaptive intelligence layer. Run AI, ML, and rendering workloads anywhere, from edge to core, with deterministic performance and transparent cost. | ||||
|  | ||||
| ### Key Principles | ||||
| - **No Silos**: All GPU resources accessible through single interface | ||||
| - **No Intermediaries**: Direct access to GPU resources | ||||
| - **Raw, Verifiable Power**: Every GPU cycle cryptographically verified | ||||
| - **Orchestrated Through Code**: GPU resources managed through APIs and smart contracts | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Use Cases | ||||
|  | ||||
| ### AI/ML Training | ||||
| Run GPU-accelerated workloads for deep learning and data science on demand. | ||||
|  | ||||
| **Features:** | ||||
| - **GPU Acceleration**: High-performance computing for machine learning | ||||
| - **Scalable Compute**: Scale training across multiple GPU resources | ||||
| - **Cost Optimization**: Pay only for actual GPU usage | ||||
|  | ||||
| ### Rendering & Visualization | ||||
| Run high-performance graphics processing workloads. | ||||
|  | ||||
| **Applications:** | ||||
| - **3D Rendering**: Distributed rendering for film, games, and architecture | ||||
| - **Scientific Visualization**: Complex data visualization and analysis | ||||
| - **Virtual Reality**: Real-time VR/AR processing | ||||
| - **Digital Twins**: Real-time simulation and modeling | ||||
|  | ||||
| ### General GPU Computing | ||||
| High-performance computing for various computational workloads. | ||||
|  | ||||
| **Applications:** | ||||
| - **Scientific Simulations**: Physics, chemistry, climate modeling | ||||
| - **Financial Modeling**: Risk analysis and algorithmic trading | ||||
| - **Cryptocurrency**: Mining and blockchain processing | ||||
| - **Protein Folding**: Drug discovery and molecular modeling | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Integration with Mycelium Cloud | ||||
|  | ||||
| Mycelium GPU works seamlessly with Mycelium Cloud infrastructure: | ||||
|  | ||||
| - **Unified Networking**: GPU nodes accessible via Mycelium network | ||||
| - **Shared Security**: Zero-trust security model applies to GPU operations | ||||
| - **Storage Integration**: Access quantum-safe storage from GPU workloads | ||||
| - **Kubernetes Support**: GPU workloads can be deployed as Kubernetes resources | ||||
|  | ||||
| ### Deployment Example | ||||
| ```yaml | ||||
| # GPU workload specification for Kubernetes | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: gpu-workload | ||||
| spec: | ||||
|   replicas: 1 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: gpu-compute | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: gpu-compute | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: gpu-compute | ||||
|         image: tensorflow/tensorflow:latest-gpu | ||||
|         resources: | ||||
|           limits: | ||||
|             nvidia.com/gpu: 1 | ||||
|         env: | ||||
|         - name: MYCELIUM_GPU_REGION | ||||
|           value: "auto" | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Getting Started | ||||
|  | ||||
| ### Access GPU Resources | ||||
| 1. **Account Setup**: Create Mycelium account with GPU access | ||||
| 2. **Resource Request**: Use Mycelium GPU APIs to request GPU resources | ||||
| 3. **Workload Deployment**: Deploy your AI/ML or compute workload | ||||
| 4. **Monitor Usage**: Track GPU utilization and costs through dashboard | ||||
|  | ||||
| ### Basic Workflow | ||||
| ``` | ||||
| Application → Mycelium GPU API → GPU Resource Allocation → Workload Execution | ||||
| ``` | ||||
|  | ||||
| ### Key Benefits | ||||
| - **Deterministic Performance**: Predictable GPU allocation and performance | ||||
| - **Global Distribution**: Access GPU resources worldwide | ||||
| - **Transparent Costs**: Clear pricing without hidden fees | ||||
| - **Sovereign Control**: Full control over GPU workloads and data | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Technical Architecture | ||||
|  | ||||
| ### Distributed GPU Mesh | ||||
| Mycelium GPU creates a peer-to-peer network of GPU resources accessible through the Mycelium Network. | ||||
|  | ||||
| **Components:** | ||||
| - **GPU Nodes**: Physical GPU hardware distributed globally | ||||
| - **Mycelium Network**: Encrypted peer-to-peer communication layer | ||||
| - **Orchestration Layer**: API and smart contract-based resource management | ||||
| - **Monitoring**: Real-time GPU utilization and health monitoring | ||||
|  | ||||
| ### Performance Characteristics | ||||
| - **Edge-to-Core Deployment**: Run workloads from edge devices to data centers | ||||
| - **Adaptive Intelligence Layer**: Optimizes GPU resource allocation | ||||
| - **Deterministic Performance**: Guaranteed resource availability and performance | ||||
| - **Transparent Cost**: All GPU usage tracked and billed transparently | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Key Differentiators | ||||
|  | ||||
| ### Unified Fabric | ||||
| Transforms fragmented GPU resources into a single, unified acceleration fabric accessible through standard APIs. | ||||
|  | ||||
| ### Sovereign Control | ||||
| Complete control over GPU workloads with no vendor lock-in or geographical restrictions. | ||||
|  | ||||
| ### Code-Driven Orchestration | ||||
| GPU resources managed through APIs and smart contracts, enabling automated and verifiable resource allocation. | ||||
|  | ||||
| ### Deterministic Performance | ||||
| Guaranteed GPU allocation with consistent performance characteristics across all workloads. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Cost Efficiency | ||||
|  | ||||
| Mycelium GPU provides cost-effective access to GPU resources through: | ||||
|  | ||||
| - **Transparent Pricing**: No hidden fees or surprise charges | ||||
| - **Pay-per-Usage**: Pay only for actual GPU consumption | ||||
| - **Global Optimization**: Access GPUs where they're most cost-effective | ||||
| - **No Vendor Lock-in**: Avoid premium pricing from single providers | ||||
|  | ||||
| --- | ||||
|  | ||||
| *Mycelium GPU - Unifying distributed acceleration into a sovereign fabric.* | ||||
							
								
								
									
										234
									
								
								docs/mycelium_storage_for_devs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,234 @@ | ||||
| # Mycelium Storage for Developers | ||||
|  | ||||
| *Quantum-Safe, Sovereign Data Plane* | ||||
|  | ||||
| ## Overview | ||||
|  | ||||
| Mycelium Storage provides quantum-safe, sovereign data management that protects and places data precisely while keeping access effortless. Built on advanced cryptographic principles and autonomous recovery systems, it ensures data integrity, availability, and sovereignty across global distributed infrastructure. | ||||
|  | ||||
| ## Core Concept | ||||
|  | ||||
| Quantum-safe, sovereign data plane protects data beyond the application layer while providing multi-protocol access and geo-aware placement for complete data control and ownership. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Core Features | ||||
|  | ||||
| ### Quantum-Safe Storage (QSS) | ||||
| Quantum-resistant encryption secures data beyond the app layer so ownership and control stay yours. | ||||
|  | ||||
| **Benefits:** | ||||
| - **Beyond AES-256**: Quantum-resistant encryption algorithms | ||||
| - **Multi-Layer Protection**: Data secured beyond application level | ||||
| - **Future-Proof**: Protected against current and future quantum threats | ||||
| - **Complete Ownership**: Data control and sovereignty maintained | ||||
|  | ||||
| ### Self-Healing Storage System | ||||
| Autonomous recovery heals failures or corruption instantly, preserving integrity without human intervention. | ||||
|  | ||||
| **Capabilities:** | ||||
| - **Instant Recovery**: Automatic detection and repair of failures | ||||
| - **Integrity Preservation**: Data integrity maintained during recovery | ||||
| - **Autonomous Operation**: No manual intervention required | ||||
| - **Corruption Healing**: Automatic detection and repair of data corruption | ||||
|  | ||||
| ### Multi-Protocol Data Access | ||||
| Serve the same data via IPFS, S3, WebDAV, HTTP, and native file systems for seamless integration. | ||||
|  | ||||
| **Protocol Support:** | ||||
| - **IPFS**: Decentralized, content-addressed storage | ||||
| - **S3**: Amazon S3 compatible API for existing tools | ||||
| - **WebDAV**: Web-based file access and synchronization | ||||
| - **HTTP**: Direct API access for web applications | ||||
| - **Native File Systems**: Standard POSIX file system access | ||||
|  | ||||
| ### Geo-Aware Data Placement & Replication | ||||
| Define residency, redundancy, and distribution per workload while zone-to-zone replication hardens resilience. | ||||
|  | ||||
| **Features:** | ||||
| - **Data Residency**: Choose where data physically resides | ||||
| - **Custom Redundancy**: Define replication levels per workload | ||||
| - **Zone-to-Zone Replication**: Automatic cross-zone data replication | ||||
| - **Geographic Distribution**: Global data placement across ThreeFold Grid | ||||
|  | ||||
| ### Ultra-Efficient Zero-Images (Flists) | ||||
| Metadata-only flists shrink images up to 100x, replacing heavy VMs and powering instant Zero-OS deployments. | ||||
|  | ||||
| **Benefits:** | ||||
| - **100x Size Reduction**: Dramatic reduction in image sizes | ||||
| - **Metadata-Only**: Images represented as metadata, not full artifacts | ||||
| - **Zero-OS Support**: Instant deployment of operating systems | ||||
| - **Bandwidth Efficiency**: Minimal transfer requirements | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Technical Architecture | ||||
|  | ||||
| ### Quantum-Safe Data Protection | ||||
| - **Post-Quantum Encryption**: Algorithms resistant to quantum computing attacks | ||||
| - **Beyond Application Layer**: Protection at infrastructure level | ||||
| - **Cryptographic Verification**: All storage operations verified | ||||
| - **Future-Proof Security**: Designed for long-term data protection | ||||
|  | ||||
| ### Autonomous Self-Healing | ||||
| - **Continuous Monitoring**: 24/7 detection of failures and corruption | ||||
| - **Instant Recovery**: Automatic repair without service interruption | ||||
| - **Data Integrity**: Ongoing verification of all stored data | ||||
| - **Zero Intervention**: Complete autonomous operation | ||||
|  | ||||
| ### Multi-Protocol Access | ||||
| ``` | ||||
| Application → Protocol Adapter → Mycelium Storage → Data Nodes | ||||
| ``` | ||||
|  | ||||
| ### Geo-Aware Data Governance | ||||
| - **Sovereignty Control**: Choose specific jurisdictions for data residency | ||||
| - **Custom Redundancy**: Define replication levels per workload | ||||
| - **Zone Replication**: Automatic cross-zone data synchronization | ||||
| - **Global Distribution**: Deploy data across ThreeFold Grid worldwide | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Developer Experience | ||||
|  | ||||
| ### S3-Compatible Access | ||||
| ```python | ||||
| # Using S3-compatible API | ||||
| import boto3 | ||||
|  | ||||
| # Initialize S3 client | ||||
| s3_client = boto3.client( | ||||
|     's3', | ||||
|     endpoint_url='https://storage.mycelium.com', | ||||
|     aws_access_key_id='your_access_key', | ||||
|     aws_secret_access_key='your_secret_key' | ||||
| ) | ||||
|  | ||||
| # Upload file | ||||
| s3_client.upload_file('local_file.txt', 'my-bucket', 'remote_file.txt') | ||||
|  | ||||
| # Download file | ||||
| s3_client.download_file('my-bucket', 'remote_file.txt', 'downloaded_file.txt') | ||||
| ``` | ||||
|  | ||||
| ### WebDAV Access | ||||
| ```bash | ||||
| # Mount WebDAV storage | ||||
| mount -t davfs https://storage.mycelium.com/dav /mnt/storage | ||||
|  | ||||
| # Access files normally | ||||
| cp /mnt/storage/data.txt ./ | ||||
| echo "Data updated" > /mnt/storage/updated.txt | ||||
| ``` | ||||
|  | ||||
| ### IPFS Integration | ||||
| ```python | ||||
| # Using IPFS for decentralized access | ||||
| import ipfshttpclient | ||||
|  | ||||
| # Connect to IPFS | ||||
| client = ipfshttpclient.connect('/ip4/127.0.0.1/tcp/5001') | ||||
|  | ||||
| # Add file to IPFS | ||||
| res = client.add('data.txt') | ||||
|  | ||||
| # Access file via IPFS hash | ||||
| print(f"File available at: {res['Hash']}") | ||||
| ``` | ||||
|  | ||||
| ### Geo-Aware Configuration | ||||
| ```yaml | ||||
| # Data placement configuration | ||||
| apiVersion: v1 | ||||
| kind: ConfigMap | ||||
| metadata: | ||||
|   name: storage-config | ||||
| data: | ||||
|   placement: | | ||||
|     geo_aware_storage: | ||||
|       residency: "eu-west" | ||||
|       redundancy: 3 | ||||
|       zones: | ||||
|         - "zone-1" | ||||
|         - "zone-2"  | ||||
|         - "zone-3" | ||||
|       protocols: | ||||
|         - "s3" | ||||
|         - "ipfs" | ||||
|         - "webdav" | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Use Cases | ||||
|  | ||||
| ### Data Sovereignty Applications | ||||
| - **Privacy-First Applications**: Complete control over user data location | ||||
| - **Regulatory Compliance**: Ensure data stays in specific jurisdictions | ||||
| - **Enterprise Data**: Keep sensitive data in preferred locations | ||||
| - **DigitalMe Applications**: Self-hosted services with full data control | ||||
|  | ||||
| ### Multi-Protocol Applications | ||||
| - **Legacy System Integration**: Support multiple access protocols simultaneously | ||||
| - **Hybrid Applications**: Combine centralized and decentralized storage | ||||
| - **Developer Flexibility**: Choose protocol based on application needs | ||||
| - **Migration Scenarios**: Gradual migration from traditional storage | ||||
|  | ||||
| ### Backup and Recovery | ||||
| - **Autonomous Backup**: Self-healing storage with automatic recovery | ||||
| - **Cross-Zone Replication**: Automatic data replication across zones | ||||
| - **Integrity Verification**: Continuous verification of backup integrity | ||||
| - **Instant Recovery**: Rapid restoration from storage failures | ||||
|  | ||||
| ### Content Distribution | ||||
| - **Global CDN**: Distribute content across ThreeFold Grid worldwide | ||||
| - **IPFS Integration**: Decentralized content addressing | ||||
| - **Multi-Protocol Access**: Serve content via HTTP, S3, WebDAV | ||||
| - **Geo-Optimization**: Place content near users for optimal performance | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Storage-Specific Use Cases | ||||
|  | ||||
| ### Data Sovereignty & Compliance | ||||
| - **Regulatory Requirements**: Ensure data stays in specific jurisdictions | ||||
| - **Privacy-First Applications**: Complete control over personal data location | ||||
| - **Enterprise Data Governance**: Keep sensitive business data in controlled regions | ||||
| - **Cross-Border Compliance**: Navigate international data regulations | ||||
|  | ||||
| ### Multi-Protocol Data Solutions | ||||
| - **Legacy System Integration**: Support existing S3, WebDAV, and HTTP workflows | ||||
| - **Decentralized Applications**: IPFS integration for blockchain and Web3 projects | ||||
| - **Hybrid Storage**: Combine centralized and decentralized access patterns | ||||
| - **Developer Flexibility**: Choose protocols based on application requirements | ||||
|  | ||||
| ### Autonomous Backup & Recovery | ||||
| - **Self-Healing Backups**: Storage automatically maintains backup integrity | ||||
| - **Cross-Zone Resilience**: Automatic replication across geographic zones | ||||
| - **Instant Recovery**: Rapid restoration from storage failures | ||||
| - **Continuous Verification**: Ongoing integrity checking without intervention | ||||
|  | ||||
| ### Content Distribution & CDN | ||||
| - **Global Content Delivery**: Distribute content across ThreeFold Grid worldwide | ||||
| - **IPFS Integration**: Decentralized content addressing and access | ||||
| - **Multi-Protocol Serving**: Serve same content via HTTP, S3, WebDAV | ||||
| - **Geo-Optimized Placement**: Position content near end users | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Key Differentiators | ||||
|  | ||||
| ### Quantum-Safe Protection | ||||
| Data protected with encryption algorithms resistant to quantum computing threats. | ||||
|  | ||||
| ### Autonomous Self-Healing | ||||
| Storage manages itself with automatic detection, recovery, and verification. | ||||
|  | ||||
| ### Universal Protocol Support | ||||
| Same data accessible via IPFS, S3, WebDAV, HTTP, and native file systems. | ||||
|  | ||||
| ### Geo-Aware Data Governance | ||||
| Complete control over data placement, residency, and replication. | ||||
|  | ||||
| ### Ultra-Efficient Storage | ||||
| Zero-Image technology reduces storage requirements by 100x. | ||||
							
								
								
									
										10
									
								
								index.html
									
									
									
									
									
								
							
							
						
						| @@ -4,16 +4,10 @@ | ||||
|     <meta charset="UTF-8" /> | ||||
|     <link rel="icon" type="image/x-icon" href="/favicon.ico" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Mycelium - Unleash the Power of Decentralized Networks</title> | ||||
|     <meta name="description" content="Discover Mycelium, an end-to-end encrypted IPv6 overlay network. The future of secure, efficient, and scalable networking." /> | ||||
|     <title>Project Mycelium - Unleash the Power of Decentralized Networks</title> | ||||
|     <meta name="description" content="Project Mycelium's technology enables anyone to deploy their own Internet infrastructure, anywhere." /> | ||||
|     <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||||
|     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | ||||
|     <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" /> | ||||
|     <style> | ||||
|       :root { | ||||
|         --font-inter: 'Inter', sans-serif; | ||||
|       } | ||||
|     </style> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="root"></div> | ||||
|   | ||||
							
								
								
									
										4420
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						| @@ -10,9 +10,13 @@ | ||||
|     "preview": "vite preview" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@emailjs/browser": "^4.4.1", | ||||
|     "@headlessui/react": "^2.2.9", | ||||
|     "@heroicons/react": "^2.2.0", | ||||
|     "@lobehub/icons": "^1.97.2", | ||||
|     "@radix-ui/react-icons": "^1.3.2", | ||||
|     "@react-three/drei": "^9.89.2", | ||||
|     "@react-three/fiber": "^8.15.12", | ||||
|     "@tabler/icons-react": "^3.35.0", | ||||
|     "@tailwindcss/forms": "^0.5.10", | ||||
|     "@types/node": "^20.19.23", | ||||
| @@ -22,10 +26,10 @@ | ||||
|     "class-variance-authority": "^0.7.1", | ||||
|     "clsx": "^2.1.1", | ||||
|     "cobe": "^0.6.5", | ||||
|     "dotted-map": "^2.2.3", | ||||
|     "framer-motion": "^10.18.0", | ||||
|     "lucide-react": "^0.544.0", | ||||
|     "motion": "^12.23.24", | ||||
|     "next": "^14.2.33", | ||||
|     "popmotion": "^11.0.5", | ||||
|     "react": "^18.3.1", | ||||
|     "react-countup": "^6.5.3", | ||||
| @@ -35,15 +39,16 @@ | ||||
|     "react-type-animation": "^3.2.0", | ||||
|     "tailwind-merge": "^3.3.1", | ||||
|     "tailwindcss": "^4.1.15", | ||||
|     "three": "^0.151.0", | ||||
|     "typescript": "^5.9.3", | ||||
|     "use-debounce": "^10.0.6" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@tailwindcss/postcss": "^4.1.15", | ||||
|     "@types/three": "^0.151.0", | ||||
|     "@vitejs/plugin-react": "^5.0.4", | ||||
|     "autoprefixer": "^10.4.20", | ||||
|     "eslint": "^8.57.1", | ||||
|     "eslint-config-next": "^14.2.33", | ||||
|     "prettier": "^3.6.2", | ||||
|     "prettier-plugin-tailwindcss": "^0.6.14", | ||||
|     "sharp": "^0.33.1", | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								public/images/ChatGPT Image Oct 24, 2025, 05_03_52 PM.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/agent_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 802 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/agentshero.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/benefits/autonomous.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 51 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/benefits/cost.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 50 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/benefits/energy.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 30 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/benefits/sovereign.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 68 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/cloud.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/cloud1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/cloud_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 757 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/cloudimg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/computehero.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 116 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/computehero.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/computehero2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/computehero3.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 110 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/computehero3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/computehero4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/gpuhero.jpeg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/gpuhero.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 943 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/home.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/homehero1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 MiB | 
| After Width: | Height: | Size: 232 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/kubernetes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1024 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/logo_1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 22 KiB | 
| Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 66 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/mchip.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/mchip2.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 126 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/mchip3.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 48 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/network_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 835 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/networkhero.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 MiB | 
| @@ -214,7 +214,7 @@ | ||||
|     </filter> | ||||
|     <radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" | ||||
|       gradientTransform="matrix(0 727 -642 0 184 1)"> | ||||
|       <stop stop-color="#FAFAFA" /> | ||||
|       <stop stop-color="#FFFFFF" /> | ||||
|       <stop offset="1" stop-color="#E6E6E6" /> | ||||
|     </radialGradient> | ||||
|     <radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" | ||||
|   | ||||
| Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/storagehero.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/images/storagehero2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 MiB | 
							
								
								
									
										
											BIN
										
									
								
								public/videos/chip_vid.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								public/videos/cloud.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -4,6 +4,10 @@ import HomePage from './pages/home/HomePage' | ||||
| import CloudPage from './pages/cloud/CloudPage' | ||||
| import NetworkPage from './pages/network/NetworkPage' | ||||
| import AgentsPage from './pages/agents/AgentsPage' | ||||
| import DownloadPage from './pages/download/DownloadPage' | ||||
| import ComputePage from './pages/compute/ComputePage' | ||||
| import StoragePage from './pages/storage/StoragePage' | ||||
| import GpuPage from './pages/gpu/GpuPage' | ||||
|  | ||||
| function App() { | ||||
|   return ( | ||||
| @@ -14,6 +18,10 @@ function App() { | ||||
|           <Route path="cloud" element={<CloudPage />} /> | ||||
|           <Route path="network" element={<NetworkPage />} /> | ||||
|           <Route path="agents" element={<AgentsPage />} /> | ||||
|                     <Route path="download" element={<DownloadPage />} /> | ||||
|           <Route path="compute" element={<ComputePage />} /> | ||||
|           <Route path="storage" element={<StoragePage />} /> | ||||
|           <Route path="gpu" element={<GpuPage />} /> | ||||
|         </Route> | ||||
|       </Routes> | ||||
|     </BrowserRouter> | ||||
|   | ||||
| @@ -1,19 +1,20 @@ | ||||
| import { useRef } from 'react' | ||||
| import { motion, useInView } from 'framer-motion' | ||||
| import { motion } from 'framer-motion' | ||||
|  | ||||
| export function AnimatedSection({ children }: { children: React.ReactNode }) { | ||||
|   const ref = useRef(null) | ||||
|   const isInView = useInView(ref, { once: true, margin: '-20% 0px -20% 0px' }) | ||||
| type AnimatedSectionProps = { | ||||
|   children: React.ReactNode | ||||
|   id?: string | ||||
|   className?: string | ||||
| } | ||||
|  | ||||
| export function AnimatedSection({ children, id, className }: AnimatedSectionProps) { | ||||
|   return ( | ||||
|     <motion.section | ||||
|       ref={ref} | ||||
|       initial={{ opacity: 0, y: 50 }} | ||||
|       animate={{ | ||||
|         opacity: isInView ? 1 : 0, | ||||
|         y: isInView ? 0 : 50, | ||||
|       }} | ||||
|       transition={{ duration: 0.5 }} | ||||
|       id={id} | ||||
|       className={className} | ||||
|       initial={{ opacity: 0, y: 40 }} | ||||
|       whileInView={{ opacity: 1, y: 0 }} | ||||
|       viewport={{ once: true, margin: '-25% 0px -20% 0px' }} | ||||
|       transition={{ duration: 0.5, ease: 'easeOut' }} | ||||
|     > | ||||
|       {children} | ||||
|     </motion.section> | ||||
|   | ||||
| @@ -3,9 +3,9 @@ import clsx from 'clsx' | ||||
|  | ||||
| const baseStyles = { | ||||
|   solid: | ||||
|     'inline-flex justify-center rounded-lg py-2 px-3 text-sm font-semibold transition-colors', | ||||
|     'inline-flex justify-center rounded-full py-2 px-4 text-sm font-semibold transition-colors', | ||||
|   outline: | ||||
|     'inline-flex justify-center rounded-lg border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-sm transition-colors', | ||||
|     'inline-flex justify-center rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(4)-1px)] text-sm transition-colors', | ||||
| } | ||||
|  | ||||
| const variantStyles = { | ||||
| @@ -17,6 +17,7 @@ const variantStyles = { | ||||
|     green: 'bg-green-500 text-white hover:bg-green-600', | ||||
|   }, | ||||
|   outline: { | ||||
|     cyan: 'border-cyan-500 text-cyan-500 hover:bg-cyan-50', | ||||
|     gray: 'border-gray-300 text-gray-700 hover:border-cyan-500 active:border-cyan-500', | ||||
|     white: 'border-gray-300 text-white hover:border-cyan-500 active:border-cyan-500', | ||||
|   }, | ||||
| @@ -29,7 +30,7 @@ type ButtonProps = ( | ||||
|     } | ||||
|   | { | ||||
|       variant: 'outline' | ||||
|       color?: keyof typeof variantStyles.outline | ||||
|       color?: (keyof typeof variantStyles.outline) | 'cyan' | ||||
|     } | ||||
| ) & | ||||
|   ( | ||||
|   | ||||
							
								
								
									
										226
									
								
								src/components/ContactForm.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,226 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { AnimatePresence, motion } from 'framer-motion' | ||||
| import { useState, type ChangeEvent, type FormEvent } from 'react' | ||||
| import emailjs from '@emailjs/browser' | ||||
| import { CheckCircle, Send, X } from 'lucide-react' | ||||
|  | ||||
| interface ContactFormProps { | ||||
|   isOpen: boolean | ||||
|   onClose: () => void | ||||
|   title?: string | ||||
|   formType?: 'investor' | 'partner' | 'agent_waitlist' | 'storage_waitlist' | 'compute_waitlist' | 'gpu_waitlist' | ||||
| } | ||||
|  | ||||
| const initialFormState = { | ||||
|   name: '', | ||||
|   email: '', | ||||
|   company: '', | ||||
|   message: '', | ||||
| } | ||||
|  | ||||
| export default function ContactForm({ | ||||
|   isOpen, | ||||
|   onClose, | ||||
|   title = 'Book a Meeting', | ||||
|   formType, | ||||
| }: ContactFormProps) { | ||||
|   const [formData, setFormData] = useState(initialFormState) | ||||
|   const [isSubmitting, setIsSubmitting] = useState(false) | ||||
|   const [isSubmitted, setIsSubmitted] = useState(false) | ||||
|  | ||||
|   const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | ||||
|     const { name, value } = e.target | ||||
|     setFormData((prev) => ({ | ||||
|       ...prev, | ||||
|       [name]: value, | ||||
|     })) | ||||
|   } | ||||
|  | ||||
|   const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { | ||||
|     e.preventDefault() | ||||
|     setIsSubmitting(true) | ||||
|  | ||||
|     try { | ||||
|       const templateParams = { | ||||
|         from_name: formData.name, | ||||
|         from_email: formData.email, | ||||
|         company: formData.company, | ||||
|         message: formData.message, | ||||
|         to_email: 'emre@incubaid.com', | ||||
|         form_type: formType || 'General Inquiry', | ||||
|       } | ||||
|  | ||||
|       await emailjs.send( | ||||
|         'service_03d0vf8', | ||||
|         'template_6o6e8oe', | ||||
|         templateParams, | ||||
|         'bhkly3gzrO-SA9w7v', | ||||
|       ) | ||||
|  | ||||
|       setIsSubmitted(true) | ||||
|       setTimeout(() => { | ||||
|         setIsSubmitted(false) | ||||
|         setFormData(initialFormState) | ||||
|         onClose() | ||||
|       }, 3000) | ||||
|     } catch (error) { | ||||
|       console.error('Email sending failed:', error) | ||||
|       alert('Failed to send message. Please try again.') | ||||
|     } finally { | ||||
|       setIsSubmitting(false) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <AnimatePresence> | ||||
|       {isOpen && ( | ||||
|         <motion.div | ||||
|           className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm" | ||||
|           initial={{ opacity: 0 }} | ||||
|           animate={{ opacity: 1 }} | ||||
|           exit={{ opacity: 0 }} | ||||
|         > | ||||
|           <motion.div | ||||
|             className="relative w-full max-w-md overflow-hidden rounded-2xl bg-card shadow-2xl" | ||||
|             initial={{ scale: 0.9, opacity: 0 }} | ||||
|             animate={{ scale: 1, opacity: 1 }} | ||||
|             exit={{ scale: 0.9, opacity: 0 }} | ||||
|             transition={{ type: 'spring', damping: 25, stiffness: 300 }} | ||||
|           > | ||||
|             <div className="flex items-center justify-between border-b border-border p-6"> | ||||
|               <h3 className="text-xl font-bold text-foreground">{title}</h3> | ||||
|               <button | ||||
|                 onClick={onClose} | ||||
|                 className="rounded-lg p-2 transition-colors hover:bg-muted" | ||||
|                 aria-label="Close form" | ||||
|               > | ||||
|                 <X className="h-5 w-5 text-muted-foreground" /> | ||||
|               </button> | ||||
|             </div> | ||||
|  | ||||
|             <div className="p-6"> | ||||
|               {isSubmitted ? ( | ||||
|                 <motion.div | ||||
|                   className="py-8 text-center" | ||||
|                   initial={{ opacity: 0, y: 20 }} | ||||
|                   animate={{ opacity: 1, y: 0 }} | ||||
|                 > | ||||
|                   <CheckCircle className="mx-auto mb-4 h-16 w-16 text-primary" /> | ||||
|                   <h4 className="mb-2 text-lg font-semibold text-foreground">Thank you!</h4> | ||||
|                   <p className="text-muted-foreground">We'll get back to you soon.</p> | ||||
|                 </motion.div> | ||||
|               ) : ( | ||||
|                 <form onSubmit={handleSubmit} className="space-y-4"> | ||||
|                   <div> | ||||
|                     <label | ||||
|                       htmlFor="name" | ||||
|                       className="mb-1 block text-sm font-medium text-muted-foreground" | ||||
|                     > | ||||
|                       Full Name * | ||||
|                     </label> | ||||
|                     <input | ||||
|                       type="text" | ||||
|                       id="name" | ||||
|                       name="name" | ||||
|                       value={formData.name} | ||||
|                       onChange={handleInputChange} | ||||
|                       required | ||||
|                       className="w-full rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary" | ||||
|                       placeholder="Your full name" | ||||
|                     /> | ||||
|                   </div> | ||||
|  | ||||
|                   <div> | ||||
|                     <label | ||||
|                       htmlFor="email" | ||||
|                       className="mb-1 block text-sm font-medium text-muted-foreground" | ||||
|                     > | ||||
|                       Email Address * | ||||
|                     </label> | ||||
|                     <input | ||||
|                       type="email" | ||||
|                       id="email" | ||||
|                       name="email" | ||||
|                       value={formData.email} | ||||
|                       onChange={handleInputChange} | ||||
|                       required | ||||
|                       className="w-full rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary" | ||||
|                       placeholder="your.email@company.com" | ||||
|                     /> | ||||
|                   </div> | ||||
|  | ||||
|                   <div> | ||||
|                     <label | ||||
|                       htmlFor="company" | ||||
|                       className="mb-1 block text-sm font-medium text-muted-foreground" | ||||
|                     > | ||||
|                       Company | ||||
|                     </label> | ||||
|                     <input | ||||
|                       type="text" | ||||
|                       id="company" | ||||
|                       name="company" | ||||
|                       value={formData.company} | ||||
|                       onChange={handleInputChange} | ||||
|                       className="w-full rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary" | ||||
|                       placeholder="Your company name" | ||||
|                     /> | ||||
|                   </div> | ||||
|  | ||||
|                   <div> | ||||
|                     <label | ||||
|                       htmlFor="message" | ||||
|                       className="mb-1 block text-sm font-medium text-muted-foreground" | ||||
|                     > | ||||
|                       Message | ||||
|                     </label> | ||||
|                     <textarea | ||||
|                       id="message" | ||||
|                       name="message" | ||||
|                       value={formData.message} | ||||
|                       onChange={handleInputChange} | ||||
|                       rows={4} | ||||
|                       className="w-full resize-none rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary" | ||||
|                       placeholder={ | ||||
|                         formType === 'investor' | ||||
|                           ? 'Tell us about your investment interests and how we can collaborate.' | ||||
|                           : formType === 'agent_waitlist' | ||||
|                             ? 'Tell us about your sovereign agent requirements.' | ||||
|                             : formType === 'storage_waitlist' | ||||
|                             ? 'Tell us about your storage requirements.' | ||||
|                             : formType === 'compute_waitlist' | ||||
|                             ? 'Tell us about your compute requirements.' | ||||
|                             : formType === 'gpu_waitlist' | ||||
|                             ? 'Tell us about your GPU requirements.' | ||||
|                             : 'Tell us about your project or how we can help.' | ||||
|                       } | ||||
|                     /> | ||||
|                   </div> | ||||
|  | ||||
|                   <button | ||||
|                     type="submit" | ||||
|                     disabled={isSubmitting} | ||||
|                     className="flex w-full items-center justify-center rounded-lg bg-primary px-6 py-3 font-semibold text-primary-foreground transition-opacity disabled:cursor-not-allowed disabled:opacity-50" | ||||
|                   > | ||||
|                     {isSubmitting ? ( | ||||
|                       <> | ||||
|                         <div className="mr-2 h-5 w-5 animate-spin rounded-full border-2 border-primary-foreground/30 border-t-primary-foreground" /> | ||||
|                         Sending... | ||||
|                       </> | ||||
|                     ) : ( | ||||
|                       <> | ||||
|                         <Send className="h-5 w-5" /> | ||||
|                         <span className="ml-2">Send Message</span> | ||||
|                       </> | ||||
|                     )} | ||||
|                   </button> | ||||
|                 </form> | ||||
|               )} | ||||
|             </div> | ||||
|           </motion.div> | ||||
|         </motion.div> | ||||
|       )} | ||||
|     </AnimatePresence> | ||||
|   ) | ||||
| } | ||||
| @@ -18,11 +18,10 @@ export function FadeIn({ children, transition, className }: FadeInProps) { | ||||
|       className={className} | ||||
|       initial={{ opacity: 0, y: 20 }} | ||||
|       whileInView={{ opacity: 1, y: 0 }} | ||||
|       viewport={{ once: false, margin: isMobile ? '0px 0px -50px 0px' : '0px 0px -100px 0px' }} | ||||
|       transition={transition || { duration: 0.5 }} | ||||
|       viewport={{ once: true, amount: isMobile ? 0.2 : 0.3 }} | ||||
|       transition={transition || { duration: 0.5, ease: 'easeOut' }} | ||||
|     > | ||||
|       {children} | ||||
|     </motion.div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ export function Footer() { | ||||
|         <div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8"> | ||||
|           <div> | ||||
|             <div className="flex items-center text-gray-900"> | ||||
|               <img src="/src/images/logomark.svg" alt="Mycelium Logomark" className="h-20 w-20 flex-none" /> | ||||
|               <img src="/src/images/logomark.svg" alt="Mycelium Logomark" className="h-15 w-15 flex-none" /> | ||||
|               <div className="ml-4"> | ||||
|                 <p className="text-base font-semibold">Project Mycelium</p> | ||||
|                 <p className="mt-1 text-sm">Unleash the Power of Decentralization</p> | ||||
| @@ -35,10 +35,10 @@ export function Footer() { | ||||
|             </div> | ||||
|             <div className="ml-4 lg:w-72"> | ||||
|               <p className="text-base font-semibold text-gray-900"> | ||||
|                 <a href="https://github.com/threefoldtech/mycelium/releases/" target="_blank" rel="noopener noreferrer"> | ||||
|                 <Link to="/download"> | ||||
|                   <span className="absolute inset-0 sm:rounded-2xl" /> | ||||
|                   Download Mycelium | ||||
|                 </a> | ||||
|                   Download Mycelium Connector | ||||
|                 </Link> | ||||
|               </p> | ||||
|               <p className="mt-1 text-sm text-gray-700"> | ||||
|                 Head to the GitHub to access the latest Mycelium builds for your devices. | ||||
|   | ||||
| @@ -1,29 +1,34 @@ | ||||
| import { Link } from 'react-router-dom' | ||||
| import { Dropdown } from './ui/Dropdown' | ||||
| import { ChevronDownIcon } from '@heroicons/react/20/solid' | ||||
| import { Container } from './Container' | ||||
| import { Button } from './Button' | ||||
| import pmyceliumLogo from '../images/logos/logo_1.png' | ||||
|  | ||||
| export function Header() { | ||||
|   return ( | ||||
|     <header> | ||||
|       <nav> | ||||
|         <Container className="relative z-50 flex justify-between py-8"> | ||||
|                 <Container className="relative z-50 flex justify-between py-4"> | ||||
|           <div className="relative z-10 flex items-center gap-16"> | ||||
|             <Link to="/" aria-label="Home"> | ||||
|               <img src="/src/images/logomark.svg" alt="Mycelium" className="h-10 w-auto" /> | ||||
|               <img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" /> | ||||
|             </Link> | ||||
|             <div className="hidden lg:flex lg:gap-10"> | ||||
|               <Link | ||||
|                 to="/" | ||||
|                 className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors" | ||||
|               > | ||||
|                 Home | ||||
|               </Link> | ||||
|               <Link | ||||
|                 to="/cloud" | ||||
|                 className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors" | ||||
|               > | ||||
|                 Cloud | ||||
|               </Link> | ||||
|               <Dropdown | ||||
|                 buttonContent={ | ||||
|                   <> | ||||
|                     Cloud | ||||
|                     <ChevronDownIcon className="h-5 w-5" aria-hidden="true" /> | ||||
|                   </> | ||||
|                 } | ||||
|                 items={[ | ||||
|                   { name: 'Cloud', href: '/cloud' }, | ||||
|                   { name: 'Compute', href: '/compute' }, | ||||
|                   { name: 'Storage', href: '/storage' }, | ||||
|                   { name: 'GPU', href: '/gpu' }, | ||||
|                 ]} | ||||
|               /> | ||||
|               <Link | ||||
|                 to="/network" | ||||
|                 className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors" | ||||
| @@ -41,16 +46,16 @@ export function Header() { | ||||
|           <div className="flex items-center gap-6"> | ||||
|             <div className="flex items-center gap-6 max-lg:hidden"> | ||||
|               <Button | ||||
|                 to="https://threefold.info/mycelium_network/docs/" | ||||
|                 to="https://myceliumcloud.tf" | ||||
|                 variant="outline" | ||||
|                 as="a" | ||||
|                 target="_blank" | ||||
|                 rel="noopener noreferrer" | ||||
|               > | ||||
|                 Docs | ||||
|                 Start Deployment | ||||
|               </Button> | ||||
|               <Button to="/download" variant="solid" color="cyan"> | ||||
|                 Get Mycelium | ||||
|                 Get Mycelium Connector | ||||
|               </Button> | ||||
|             </div> | ||||
|           </div> | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| import { Outlet } from 'react-router-dom' | ||||
| import { Header } from './Header' | ||||
| import { Footer } from './Footer' | ||||
| import { Header } from './Header' | ||||
|  | ||||
| export function Layout() { | ||||
|  | ||||
|   return ( | ||||
|     <div className="bg-gray-50 antialiased" style={{ fontFamily: 'var(--font-inter)' }}> | ||||
|     <div className="bg-white antialiased" style={{ fontFamily: 'var(--font-inter)' }}> | ||||
|       <Header /> | ||||
|       <main> | ||||
|             <main className=""> | ||||
|         <Outlet /> | ||||
|       </main> | ||||
|       <Footer /> | ||||
|   | ||||
							
								
								
									
										22
									
								
								src/components/ScrollDownArrow.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { ChevronDown } from 'lucide-react' | ||||
|  | ||||
| export function ScrollDownArrow() { | ||||
|   const scrollToNextSection = () => { | ||||
|     const nextSection = document.querySelector('#next-section') // Assuming the next section has this id | ||||
|     if (nextSection) { | ||||
|       nextSection.scrollIntoView({ behavior: 'smooth' }) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <button | ||||
|       onClick={scrollToNextSection} | ||||
|       className="animate-bounce text-gray-500 hover:text-gray-700" | ||||
|       aria-label="Scroll to next section" | ||||
|     > | ||||
|       <ChevronDown size={32} /> | ||||
|     </button> | ||||
|   ) | ||||
| } | ||||
| @@ -3,6 +3,11 @@ | ||||
| import React from 'react' | ||||
| import { cn } from '@/lib/utils' | ||||
|  | ||||
| const fontVariants = { | ||||
|   sans: 'font-sans', | ||||
|   neuton: 'font-neuton', | ||||
| } as const | ||||
|  | ||||
| const colorVariants = { | ||||
|   primary: 'text-gray-900', | ||||
|   secondary: 'text-gray-600', | ||||
| @@ -15,8 +20,10 @@ const colorVariants = { | ||||
| } as const | ||||
|  | ||||
| type TextOwnProps = { | ||||
|   font?: keyof typeof fontVariants | ||||
|   color?: keyof typeof colorVariants | ||||
|   className?: string | ||||
|   children?: React.ReactNode | ||||
| } | ||||
|  | ||||
| // Polymorphic helpers | ||||
| @@ -34,6 +41,7 @@ const createTextComponent = <DefaultElement extends React.ElementType>( | ||||
|   > | ||||
|  | ||||
|   function Text<E extends React.ElementType = DefaultElement>({ | ||||
|     font = 'sans', | ||||
|     as, | ||||
|     color = 'primary', | ||||
|     className, | ||||
| @@ -43,7 +51,12 @@ const createTextComponent = <DefaultElement extends React.ElementType>( | ||||
|     const Tag = (as || defaultElement) as React.ElementType | ||||
|     return ( | ||||
|       <Tag | ||||
|         className={cn(defaultClassName, colorVariants[color], className)} | ||||
|         className={cn( | ||||
|           defaultClassName, | ||||
|           fontVariants[font], | ||||
|           colorVariants[color], | ||||
|           className | ||||
|         )} | ||||
|         {...props} | ||||
|       > | ||||
|         {children} | ||||
| @@ -87,7 +100,7 @@ export const Subtle = createTextComponent( | ||||
| ) | ||||
| export const H5 = createTextComponent( | ||||
|   'h5', | ||||
|   'text-xl lg:text-2xl font-semibold leading-snug tracking-tight' | ||||
|   'text-xl lg:text-2xl font-light leading-snug tracking-normal' | ||||
| ) | ||||
| export const Eyebrow = createTextComponent( | ||||
|   'h2', | ||||
| @@ -147,4 +160,4 @@ export const DownloadCardDescription = createTextComponent( | ||||
| ) | ||||
|  | ||||
| export const CT = createTextComponent('span', 'text-lg lg:text-xl font-semibold') | ||||
| export const CP = createTextComponent('p', 'text-sm lg:text-sm leading-[1.525] font-light') | ||||
| export const CP = createTextComponent('p', 'text-sm lg:text-sm tracking-wide leading-[1.525] font-light') | ||||
|   | ||||
							
								
								
									
										138
									
								
								src/components/features-section-demo-1.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,138 @@ | ||||
| import { useId } from "react"; | ||||
|  | ||||
| export default function FeaturesSectionDemo() { | ||||
|   return ( | ||||
|     <div className="py-20 lg:py-40"> | ||||
|       <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-10 md:gap-2 max-w-7xl mx-auto"> | ||||
|         {grid.map((feature) => ( | ||||
|           <div | ||||
|             key={feature.title} | ||||
|             className="relative bg-gradient-to-b dark:from-neutral-900 from-neutral-100 dark:to-neutral-950 to-white p-6 rounded-3xl overflow-hidden" | ||||
|           > | ||||
|             <Grid size={20} /> | ||||
|             <p className="text-base font-bold text-neutral-800 dark:text-white relative z-20"> | ||||
|               {feature.title} | ||||
|             </p> | ||||
|             <p className="text-neutral-600 dark:text-neutral-400 mt-4 text-base font-normal relative z-20"> | ||||
|               {feature.description} | ||||
|             </p> | ||||
|           </div> | ||||
|         ))} | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const grid = [ | ||||
|   { | ||||
|     title: "HIPAA and SOC2 Compliant", | ||||
|     description: | ||||
|       "Our applications are HIPAA and SOC2 compliant, your data is safe with us, always.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Automated Social Media Posting", | ||||
|     description: | ||||
|       "Schedule and automate your social media posts across multiple platforms to save time and maintain a consistent online presence.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Advanced Analytics", | ||||
|     description: | ||||
|       "Gain insights into your social media performance with detailed analytics and reporting tools to measure engagement and ROI.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Content Calendar", | ||||
|     description: | ||||
|       "Plan and organize your social media content with an intuitive calendar view, ensuring you never miss a post.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Audience Targeting", | ||||
|     description: | ||||
|       "Reach the right audience with advanced targeting options, including demographics, interests, and behaviors.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Social Listening", | ||||
|     description: | ||||
|       "Monitor social media conversations and trends to stay informed about what your audience is saying and respond in real-time.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Customizable Templates", | ||||
|     description: | ||||
|       "Create stunning social media posts with our customizable templates, designed to fit your brand's unique style and voice.", | ||||
|   }, | ||||
|   { | ||||
|     title: "Collaboration Tools", | ||||
|     description: | ||||
|       "Work seamlessly with your team using our collaboration tools, allowing you to assign tasks, share drafts, and provide feedback in real-time.", | ||||
|   }, | ||||
| ]; | ||||
|  | ||||
| export const Grid = ({ | ||||
|   pattern, | ||||
|   size, | ||||
| }: { | ||||
|   pattern?: number[][]; | ||||
|   size?: number; | ||||
| }) => { | ||||
|   const p = pattern ?? [ | ||||
|     [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], | ||||
|     [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], | ||||
|     [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], | ||||
|     [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], | ||||
|     [Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1], | ||||
|   ]; | ||||
|   return ( | ||||
|     <div className="pointer-events-none absolute left-1/2 top-0  -ml-20 -mt-2 h-full w-full [mask-image:linear-gradient(white,transparent)]"> | ||||
|       <div className="absolute inset-0 bg-gradient-to-r  [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-zinc-900/30 from-zinc-100/30 to-zinc-300/30 dark:to-zinc-900/30 opacity-100"> | ||||
|         <GridPattern | ||||
|           width={size ?? 20} | ||||
|           height={size ?? 20} | ||||
|           x="-12" | ||||
|           y="4" | ||||
|           squares={p} | ||||
|           className="absolute inset-0 h-full w-full  mix-blend-overlay dark:fill-white/10 dark:stroke-white/10 stroke-black/10 fill-black/10" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export function GridPattern({ width, height, x, y, squares, ...props }: any) { | ||||
|   const patternId = useId(); | ||||
|  | ||||
|   return ( | ||||
|     <svg aria-hidden="true" {...props}> | ||||
|       <defs> | ||||
|         <pattern | ||||
|           id={patternId} | ||||
|           width={width} | ||||
|           height={height} | ||||
|           patternUnits="userSpaceOnUse" | ||||
|           x={x} | ||||
|           y={y} | ||||
|         > | ||||
|           <path d={`M.5 ${height}V.5H${width}`} fill="none" /> | ||||
|         </pattern> | ||||
|       </defs> | ||||
|       <rect | ||||
|         width="100%" | ||||
|         height="100%" | ||||
|         strokeWidth={0} | ||||
|         fill={`url(#${patternId})`} | ||||
|       /> | ||||
|       {squares && ( | ||||
|         <svg x={x} y={y} className="overflow-visible"> | ||||
|           {squares.map(([x, y]: any) => ( | ||||
|             <rect | ||||
|               strokeWidth="0" | ||||
|               key={`${x}-${y}`} | ||||
|               width={width + 1} | ||||
|               height={height + 1} | ||||
|               x={x * width} | ||||
|               y={y * height} | ||||
|             /> | ||||
|           ))} | ||||
|         </svg> | ||||
|       )} | ||||
|     </svg> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										109
									
								
								src/components/features-section-demo-2.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,109 @@ | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { | ||||
|   IconAdjustmentsBolt, | ||||
|   IconCloud, | ||||
|   IconCurrencyDollar, | ||||
|   IconEaseInOut, | ||||
|   IconHeart, | ||||
|   IconHelp, | ||||
|   IconRouteAltLeft, | ||||
|   IconTerminal2, | ||||
| } from "@tabler/icons-react"; | ||||
|  | ||||
| export default function FeaturesSectionDemo() { | ||||
|   const features = [ | ||||
|     { | ||||
|       title: "Built for developers", | ||||
|       description: | ||||
|         "Built for engineers, developers, dreamers, thinkers and doers.", | ||||
|       icon: <IconTerminal2 />, | ||||
|     }, | ||||
|     { | ||||
|       title: "Ease of use", | ||||
|       description: | ||||
|         "It's as easy as using an Apple, and as expensive as buying one.", | ||||
|       icon: <IconEaseInOut />, | ||||
|     }, | ||||
|     { | ||||
|       title: "Pricing like no other", | ||||
|       description: | ||||
|         "Our prices are best in the market. No cap, no lock, no credit card required.", | ||||
|       icon: <IconCurrencyDollar />, | ||||
|     }, | ||||
|     { | ||||
|       title: "100% Uptime guarantee", | ||||
|       description: "We just cannot be taken down by anyone.", | ||||
|       icon: <IconCloud />, | ||||
|     }, | ||||
|     { | ||||
|       title: "Multi-tenant Architecture", | ||||
|       description: "You can simply share passwords instead of buying new seats", | ||||
|       icon: <IconRouteAltLeft />, | ||||
|     }, | ||||
|     { | ||||
|       title: "24/7 Customer Support", | ||||
|       description: | ||||
|         "We are available a 100% of the time. Atleast our AI Agents are.", | ||||
|       icon: <IconHelp />, | ||||
|     }, | ||||
|     { | ||||
|       title: "Money back guarantee", | ||||
|       description: | ||||
|         "If you donot like EveryAI, we will convince you to like us.", | ||||
|       icon: <IconAdjustmentsBolt />, | ||||
|     }, | ||||
|     { | ||||
|       title: "And everything else", | ||||
|       description: "I just ran out of copy ideas. Accept my sincere apologies", | ||||
|       icon: <IconHeart />, | ||||
|     }, | ||||
|   ]; | ||||
|   return ( | ||||
|     <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4  relative z-10 py-10 max-w-7xl mx-auto"> | ||||
|       {features.map((feature, index) => ( | ||||
|         <Feature key={feature.title} {...feature} index={index} /> | ||||
|       ))} | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const Feature = ({ | ||||
|   title, | ||||
|   description, | ||||
|   icon, | ||||
|   index, | ||||
| }: { | ||||
|   title: string; | ||||
|   description: string; | ||||
|   icon: React.ReactNode; | ||||
|   index: number; | ||||
| }) => { | ||||
|   return ( | ||||
|     <div | ||||
|       className={cn( | ||||
|         "flex flex-col lg:border-r  py-10 relative group/feature dark:border-neutral-800", | ||||
|         (index === 0 || index === 4) && "lg:border-l dark:border-neutral-800", | ||||
|         index < 4 && "lg:border-b dark:border-neutral-800" | ||||
|       )} | ||||
|     > | ||||
|       {index < 4 && ( | ||||
|         <div className="opacity-0 group-hover/feature:opacity-100 transition duration-200 absolute inset-0 h-full w-full bg-gradient-to-t from-neutral-100 dark:from-neutral-800 to-transparent pointer-events-none" /> | ||||
|       )} | ||||
|       {index >= 4 && ( | ||||
|         <div className="opacity-0 group-hover/feature:opacity-100 transition duration-200 absolute inset-0 h-full w-full bg-gradient-to-b from-neutral-100 dark:from-neutral-800 to-transparent pointer-events-none" /> | ||||
|       )} | ||||
|       <div className="mb-4 relative z-10 px-10 text-neutral-600 dark:text-neutral-400"> | ||||
|         {icon} | ||||
|       </div> | ||||
|       <div className="text-lg font-bold mb-2 relative z-10 px-10"> | ||||
|         <div className="absolute left-0 inset-y-0 h-6 group-hover/feature:h-8 w-1 rounded-tr-full rounded-br-full bg-neutral-300 dark:bg-neutral-700 group-hover/feature:bg-blue-500 transition-all duration-200 origin-center" /> | ||||
|         <span className="group-hover/feature:translate-x-2 transition duration-200 inline-block text-neutral-800 dark:text-neutral-100"> | ||||
|           {title} | ||||
|         </span> | ||||
|       </div> | ||||
|       <p className="text-sm text-neutral-600 dark:text-neutral-300 max-w-xs relative z-10 px-10"> | ||||
|         {description} | ||||
|       </p> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										281
									
								
								src/components/features-section-demo-3.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,281 @@ | ||||
| import React from "react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import createGlobe from "cobe"; | ||||
| import { useEffect, useRef } from "react"; | ||||
| import { motion } from "motion/react"; | ||||
| import { IconBrandYoutubeFilled } from "@tabler/icons-react"; | ||||
|  | ||||
|  | ||||
| export default function FeaturesSectionDemo() { | ||||
|   const features = [ | ||||
|     { | ||||
|       title: "Track issues effectively", | ||||
|       description: | ||||
|         "Track and manage your project issues with ease using our intuitive interface.", | ||||
|       skeleton: <SkeletonOne />, | ||||
|       className: | ||||
|         "col-span-1 lg:col-span-4 border-b lg:border-r dark:border-neutral-800", | ||||
|     }, | ||||
|     { | ||||
|       title: "Capture pictures with AI", | ||||
|       description: | ||||
|         "Capture stunning photos effortlessly using our advanced AI technology.", | ||||
|       skeleton: <SkeletonTwo />, | ||||
|       className: "border-b col-span-1 lg:col-span-2 dark:border-neutral-800", | ||||
|     }, | ||||
|     { | ||||
|       title: "Watch our AI on YouTube", | ||||
|       description: | ||||
|         "Whether its you or Tyler Durden, you can get to know about our product on YouTube", | ||||
|       skeleton: <SkeletonThree />, | ||||
|       className: | ||||
|         "col-span-1 lg:col-span-3 lg:border-r  dark:border-neutral-800", | ||||
|     }, | ||||
|     { | ||||
|       title: "Deploy in seconds", | ||||
|       description: | ||||
|         "With our blazing fast, state of the art, cutting edge, we are so back cloud servies (read AWS) - you can deploy your model in seconds.", | ||||
|       skeleton: <SkeletonFour />, | ||||
|       className: "col-span-1 lg:col-span-3 border-b lg:border-none", | ||||
|     }, | ||||
|   ]; | ||||
|   return ( | ||||
|     <div className="relative z-20 py-10 lg:py-40 max-w-7xl mx-auto"> | ||||
|       <div className="px-8"> | ||||
|         <h4 className="text-3xl lg:text-5xl lg:leading-tight max-w-5xl mx-auto text-center tracking-tight font-medium text-black dark:text-white"> | ||||
|           Packed with thousands of features | ||||
|         </h4> | ||||
|  | ||||
|         <p className="text-sm lg:text-base  max-w-2xl  my-4 mx-auto text-neutral-500 text-center font-normal dark:text-neutral-300"> | ||||
|           From Image generation to video generation, Everything AI has APIs for | ||||
|           literally everything. It can even create this website copy for you. | ||||
|         </p> | ||||
|       </div> | ||||
|  | ||||
|       <div className="relative "> | ||||
|         <div className="grid grid-cols-1 lg:grid-cols-6 mt-12 xl:border rounded-md dark:border-neutral-800"> | ||||
|           {features.map((feature) => ( | ||||
|             <FeatureCard key={feature.title} className={feature.className}> | ||||
|               <FeatureTitle>{feature.title}</FeatureTitle> | ||||
|               <FeatureDescription>{feature.description}</FeatureDescription> | ||||
|               <div className=" h-full w-full">{feature.skeleton}</div> | ||||
|             </FeatureCard> | ||||
|           ))} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const FeatureCard = ({ | ||||
|   children, | ||||
|   className, | ||||
| }: { | ||||
|   children?: React.ReactNode; | ||||
|   className?: string; | ||||
| }) => { | ||||
|   return ( | ||||
|     <div className={cn(`p-4 sm:p-8 relative overflow-hidden`, className)}> | ||||
|       {children} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const FeatureTitle = ({ children }: { children?: React.ReactNode }) => { | ||||
|   return ( | ||||
|     <p className=" max-w-5xl mx-auto text-left tracking-tight text-black dark:text-white text-xl md:text-2xl md:leading-snug"> | ||||
|       {children} | ||||
|     </p> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const FeatureDescription = ({ children }: { children?: React.ReactNode }) => { | ||||
|   return ( | ||||
|     <p | ||||
|       className={cn( | ||||
|         "text-sm md:text-base  max-w-4xl text-left mx-auto", | ||||
|         "text-neutral-500 text-center font-normal dark:text-neutral-300", | ||||
|         "text-left max-w-sm mx-0 md:text-sm my-2" | ||||
|       )} | ||||
|     > | ||||
|       {children} | ||||
|     </p> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const SkeletonOne = () => { | ||||
|   return ( | ||||
|     <div className="relative flex py-8 px-2 gap-10 h-full"> | ||||
|       <div className="w-full  p-5  mx-auto bg-white dark:bg-neutral-900 shadow-2xl group h-full"> | ||||
|         <div className="flex flex-1 w-full h-full flex-col space-y-2  "> | ||||
|           {/* TODO */} | ||||
|           <img | ||||
|             src="/linear.webp" | ||||
|             alt="header" | ||||
|             width={800} | ||||
|             height={800} | ||||
|             className="h-full w-full aspect-square object-cover object-left-top rounded-sm" | ||||
|           /> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div className="absolute bottom-0 z-40 inset-x-0 h-60 bg-gradient-to-t from-white dark:from-black via-white dark:via-black to-transparent w-full pointer-events-none" /> | ||||
|       <div className="absolute top-0 z-40 inset-x-0 h-60 bg-gradient-to-b from-white dark:from-black via-transparent to-transparent w-full pointer-events-none" /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const SkeletonThree = () => { | ||||
|   return ( | ||||
|     <a | ||||
|       href="https://www.youtube.com/watch?v=RPa3_AD1_Vs" | ||||
|       target="__blank" | ||||
|       className="relative flex gap-10  h-full group/image" | ||||
|     > | ||||
|       <div className="w-full  mx-auto bg-transparent dark:bg-transparent group h-full"> | ||||
|         <div className="flex flex-1 w-full h-full flex-col space-y-2  relative"> | ||||
|           {/* TODO */} | ||||
|           <IconBrandYoutubeFilled className="h-20 w-20 absolute z-10 inset-0 text-red-500 m-auto " /> | ||||
|           <img | ||||
|             src="https://assets.aceternity.com/fireship.jpg" | ||||
|             alt="header" | ||||
|             width={800} | ||||
|             height={800} | ||||
|             className="h-full w-full aspect-square object-cover object-center rounded-sm blur-none group-hover/image:blur-md transition-all duration-200" | ||||
|           /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </a> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const SkeletonTwo = () => { | ||||
|   const images = [ | ||||
|     "https://images.unsplash.com/photo-1517322048670-4fba75cbbb62?q=80&w=3000&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", | ||||
|     "https://images.unsplash.com/photo-1573790387438-4da905039392?q=80&w=3425&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", | ||||
|     "https://images.unsplash.com/photo-1555400038-63f5ba517a47?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", | ||||
|     "https://images.unsplash.com/photo-1554931670-4ebfabf6e7a9?q=80&w=3387&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", | ||||
|     "https://images.unsplash.com/photo-1546484475-7f7bd55792da?q=80&w=2581&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", | ||||
|   ]; | ||||
|  | ||||
|   const imageVariants = { | ||||
|     whileHover: { | ||||
|       scale: 1.1, | ||||
|       rotate: 0, | ||||
|       zIndex: 100, | ||||
|     }, | ||||
|     whileTap: { | ||||
|       scale: 1.1, | ||||
|       rotate: 0, | ||||
|       zIndex: 100, | ||||
|     }, | ||||
|   }; | ||||
|   return ( | ||||
|     <div className="relative flex flex-col items-start p-8 gap-10 h-full overflow-hidden"> | ||||
|       {/* TODO */} | ||||
|       <div className="flex flex-row -ml-20"> | ||||
|         {images.map((image, idx) => ( | ||||
|           <motion.div | ||||
|             variants={imageVariants} | ||||
|             key={"images-first" + idx} | ||||
|             style={{ | ||||
|               rotate: Math.random() * 20 - 10, | ||||
|             }} | ||||
|             whileHover="whileHover" | ||||
|             whileTap="whileTap" | ||||
|             className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 shrink-0 overflow-hidden" | ||||
|           > | ||||
|             <img | ||||
|               src={image} | ||||
|               alt="bali images" | ||||
|               width="500" | ||||
|               height="500" | ||||
|               className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover shrink-0" | ||||
|             /> | ||||
|           </motion.div> | ||||
|         ))} | ||||
|       </div> | ||||
|       <div className="flex flex-row"> | ||||
|         {images.map((image, idx) => ( | ||||
|           <motion.div | ||||
|             key={"images-second" + idx} | ||||
|             style={{ | ||||
|               rotate: Math.random() * 20 - 10, | ||||
|             }} | ||||
|             variants={imageVariants} | ||||
|             whileHover="whileHover" | ||||
|             whileTap="whileTap" | ||||
|             className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 shrink-0 overflow-hidden" | ||||
|           > | ||||
|             <img | ||||
|               src={image} | ||||
|               alt="bali images" | ||||
|               width="500" | ||||
|               height="500" | ||||
|               className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover shrink-0" | ||||
|             /> | ||||
|           </motion.div> | ||||
|         ))} | ||||
|       </div> | ||||
|  | ||||
|       <div className="absolute left-0 z-[100] inset-y-0 w-20 bg-gradient-to-r from-white dark:from-black to-transparent  h-full pointer-events-none" /> | ||||
|       <div className="absolute right-0 z-[100] inset-y-0 w-20 bg-gradient-to-l from-white dark:from-black  to-transparent h-full pointer-events-none" /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const SkeletonFour = () => { | ||||
|   return ( | ||||
|     <div className="h-60 md:h-60  flex flex-col items-center relative bg-transparent dark:bg-transparent mt-10"> | ||||
|       <Globe className="absolute -right-10 md:-right-10 -bottom-80 md:-bottom-72" /> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const Globe = ({ className }: { className?: string }) => { | ||||
|   const canvasRef = useRef<HTMLCanvasElement>(null); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     let phi = 0; | ||||
|  | ||||
|     if (!canvasRef.current) return; | ||||
|  | ||||
|     const globe = createGlobe(canvasRef.current, { | ||||
|       devicePixelRatio: 2, | ||||
|       width: 600 * 2, | ||||
|       height: 600 * 2, | ||||
|       phi: 0, | ||||
|       theta: 0, | ||||
|       dark: 1, | ||||
|       diffuse: 1.2, | ||||
|       mapSamples: 16000, | ||||
|       mapBrightness: 6, | ||||
|       baseColor: [0.3, 0.3, 0.3], | ||||
|       markerColor: [0.1, 0.8, 1], | ||||
|       glowColor: [1, 1, 1], | ||||
|       markers: [ | ||||
|         // longitude latitude | ||||
|         { location: [37.7595, -122.4367], size: 0.03 }, | ||||
|         { location: [40.7128, -74.006], size: 0.1 }, | ||||
|       ], | ||||
|       onRender: (state) => { | ||||
|         // Called on every animation frame. | ||||
|         // `state` will be an empty object, return updated params. | ||||
|         state.phi = phi; | ||||
|         phi += 0.01; | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     return () => { | ||||
|       globe.destroy(); | ||||
|     }; | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <canvas | ||||
|       ref={canvasRef} | ||||
|       style={{ width: 600, height: 600, maxWidth: "100%", aspectRatio: 1 }} | ||||
|       className={className} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										77
									
								
								src/components/magicui/bento-grid.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,77 @@ | ||||
| import type { ReactNode } from "react"; | ||||
| import { ArrowRight } from "lucide-react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
|  | ||||
| const BentoGrid = ({ | ||||
|   children, | ||||
|   className, | ||||
| }: { | ||||
|   children: ReactNode; | ||||
|   className?: string; | ||||
| }) => { | ||||
|   return ( | ||||
|     <div | ||||
|       className={cn( | ||||
|         "grid w-full auto-rows-[22rem] grid-cols-3 gap-4", | ||||
|         className, | ||||
|       )} | ||||
|     > | ||||
|       {children} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const BentoCard = ({ | ||||
|   name, | ||||
|   className, | ||||
|   background, | ||||
|   Icon, | ||||
|   description, | ||||
|   href, | ||||
|   cta, | ||||
| }: { | ||||
|   name: string; | ||||
|   className: string; | ||||
|   background: ReactNode; | ||||
|   Icon: any; | ||||
|   description: string; | ||||
|   href: string; | ||||
|   cta: string; | ||||
| }) => ( | ||||
|   <div | ||||
|     key={name} | ||||
|     className={cn( | ||||
|       "group relative col-span-3 flex flex-col justify-between overflow-hidden rounded-xl", | ||||
|       // light styles | ||||
|       "bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]", | ||||
|       // dark styles | ||||
|       "transform-gpu dark:bg-black dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]", | ||||
|       className, | ||||
|     )} | ||||
|   > | ||||
|     <div>{background}</div> | ||||
|     <div className="pointer-events-none z-10 flex transform-gpu flex-col gap-1 p-6 transition-all duration-300 group-hover:-translate-y-10"> | ||||
|       <Icon className="h-12 w-12 origin-left transform-gpu text-neutral-700 -translate-y-12 transition-all duration-300 ease-in-out group-hover:scale-75 group-hover:text-sky-700" /> | ||||
|       <h3 className="text-xl font-semibold text-neutral-700 dark:text-neutral-300"> | ||||
|         {name} | ||||
|       </h3> | ||||
|       <p className="max-w-lg text-neutral-400">{description}</p> | ||||
|     </div> | ||||
|  | ||||
|     <div | ||||
|       className={cn( | ||||
|         "pointer-events-none absolute bottom-0 flex w-full translate-y-10 transform-gpu flex-row items-center p-4 opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100", | ||||
|       )} | ||||
|     > | ||||
|       <a | ||||
|         href={href} | ||||
|         className="pointer-events-auto inline-flex items-center gap-2 text-sm font-medium text-neutral-600 transition-colors hover:text-sky-700" | ||||
|       > | ||||
|         {cta} | ||||
|         <ArrowRight className="h-4 w-4" /> | ||||
|       </a> | ||||
|     </div> | ||||
|   </div> | ||||
| ); | ||||
|  | ||||
| export { BentoCard, BentoGrid }; | ||||
							
								
								
									
										63
									
								
								src/components/magicui/magic-card.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,63 @@ | ||||
| 'use client'; | ||||
|  | ||||
| import { cn } from '@/lib/utils'; | ||||
| import { type ReactNode, useEffect, useRef, useState } from 'react'; | ||||
|  | ||||
| interface MagicCardProps { | ||||
|   children: ReactNode; | ||||
|   className?: string; | ||||
|   gradientSize?: number; | ||||
|   gradientColor?: string; | ||||
| } | ||||
|  | ||||
| export const MagicCard = ({ children, className, gradientSize = 200, gradientColor = '#262626' }: MagicCardProps) => { | ||||
|   const mouseX = useRef<number>(0); | ||||
|   const mouseY = useRef<number>(0); | ||||
|   const cardRef = useRef<HTMLDivElement>(null); | ||||
|   const [isHovering, setIsHovering] = useState(false); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     const handleMouseMove = (e: MouseEvent) => { | ||||
|       if (cardRef.current) { | ||||
|         const rect = cardRef.current.getBoundingClientRect(); | ||||
|         mouseX.current = e.clientX - rect.left; | ||||
|         mouseY.current = e.clientY - rect.top; | ||||
|         cardRef.current.style.setProperty('--mouse-x', `${mouseX.current}px`); | ||||
|         cardRef.current.style.setProperty('--mouse-y', `${mouseY.current}px`); | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     const currentCardRef = cardRef.current; | ||||
|     if (currentCardRef) { | ||||
|       currentCardRef.addEventListener('mousemove', handleMouseMove); | ||||
|     } | ||||
|  | ||||
|     return () => { | ||||
|       if (currentCardRef) { | ||||
|         currentCardRef.removeEventListener('mousemove', handleMouseMove); | ||||
|       } | ||||
|     }; | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       ref={cardRef} | ||||
|       onMouseEnter={() => setIsHovering(true)} | ||||
|       onMouseLeave={() => setIsHovering(false)} | ||||
|       className={cn( | ||||
|         'relative w-full h-full p-px rounded-2xl transition-all duration-300 ease-in-out', | ||||
|         'bg-neutral-900 hover:bg-neutral-800', | ||||
|         className | ||||
|       )} | ||||
|       style={{ | ||||
|         background: isHovering | ||||
|           ? `radial-gradient(var(--gradient-size, ${gradientSize}px) circle at var(--mouse-x) var(--mouse-y), var(--gradient-color, ${gradientColor}), transparent 100%)` | ||||
|           : 'transparent', | ||||
|       }} | ||||
|     > | ||||
|       <div className="relative w-full h-full bg-neutral-950/90 rounded-[15px] p-8"> | ||||
|         {children} | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										18
									
								
								src/components/ui/CountUpNumber.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,18 @@ | ||||
| 'use client' | ||||
|  | ||||
| import CountUp from 'react-countup' | ||||
| import { SectionHeader } from '@/components/Texts' | ||||
|  | ||||
| interface CountUpNumberProps { | ||||
|   end: number | ||||
|   className?: string | ||||
|   color?: 'light' | 'primary' | 'secondary' | 'white' | ||||
| } | ||||
|  | ||||
| export function CountUpNumber({ end, className, color }: CountUpNumberProps) { | ||||
|   return ( | ||||
|     <SectionHeader color={color} className={className}> | ||||
|       <CountUp end={end} duration={2.75} separator="," /> | ||||
|     </SectionHeader> | ||||
|   ) | ||||
| } | ||||
| @@ -26,8 +26,11 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ | ||||
|     <path | ||||
|       fill={`url(#cube-gradient-${index})`} | ||||
|       d="M491.651 144.747L287.198 227.339C265.219 236.22 241.783 236.22 219.802 227.339L15.3486 144.747C-5.11621 136.479 -5.11621 97.5191 15.3486 89.2539L219.802 6.65884C241.783 -2.21961 265.219 -2.21961 287.198 6.65884L491.651 89.2539C512.116 97.5191 512.116 136.479 491.651 144.747Z" | ||||
|       stroke="rgba(59, 130, 246, 0.25)" | ||||
|       strokeWidth="0.5" | ||||
|     /> | ||||
|     <defs> | ||||
|       {/* Blue-white soft gradient */} | ||||
|       <linearGradient | ||||
|         id={`cube-gradient-${index}`} | ||||
|         x1="185.298" | ||||
| @@ -36,14 +39,24 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ | ||||
|         y2="206.448" | ||||
|         gradientUnits="userSpaceOnUse" | ||||
|       > | ||||
|         <stop stopColor="#E5E7EB" /> | ||||
|         <stop offset="1" stopColor="#9CA3AF" /> | ||||
|         <stop offset="0%" stopColor="#EBF8FF" stopOpacity="0.75" /> | ||||
|         <stop offset="40%" stopColor="#BEE3F8" stopOpacity="0.8" /> | ||||
|         <stop offset="100%" stopColor="#FFFFFF" stopOpacity="0.9" /> | ||||
|       </linearGradient> | ||||
|     </defs> | ||||
|   </svg> | ||||
| ); | ||||
|  | ||||
| export function CubeLight({ title, descriptionTitle, description, isActive, index, onHover, onLeave, onClick }: CubeProps) { | ||||
| export function CubeLight({ | ||||
|   title, | ||||
|   descriptionTitle, | ||||
|   description, | ||||
|   isActive, | ||||
|   index, | ||||
|   onHover, | ||||
|   onLeave, | ||||
|   onClick, | ||||
| }: CubeProps) { | ||||
|   return ( | ||||
|     <div className="relative flex flex-col items-center"> | ||||
|       <motion.div | ||||
| @@ -62,22 +75,34 @@ export function CubeLight({ title, descriptionTitle, description, isActive, inde | ||||
|           ease: "easeOut", | ||||
|         }} | ||||
|       > | ||||
|         {/* Glow aura behind cube */} | ||||
|         <div | ||||
|           className={`absolute inset-0 blur-3xl rounded-2xl transition-all duration-500 ${ | ||||
|             isActive | ||||
|               ? "bg-blue-400/40 opacity-70" | ||||
|               : "bg-blue-200/20 opacity-40" | ||||
|           }`} | ||||
|         /> | ||||
|  | ||||
|         {/* SVG Cube */} | ||||
|         <CubeSvg  | ||||
|         <CubeSvg | ||||
|           index={index} | ||||
|           className="w-48 sm:w-64 lg:w-80 h-auto drop-shadow-lg opacity-80" | ||||
|           className="w-48 sm:w-64 lg:w-80 h-auto relative" | ||||
|           style={{ | ||||
|             filter: isActive ? 'brightness(1.1) drop-shadow(0 0 15px rgba(0, 0, 0, 0.2))' : 'brightness(1)', | ||||
|             filter: isActive | ||||
|               ? "drop-shadow(0 0 25px rgba(59, 130, 246, 0.4)) brightness(1.1)" | ||||
|               : "drop-shadow(0 0 10px rgba(59, 130, 246, 0.15)) brightness(1)", | ||||
|             transition: "all 0.4s ease", | ||||
|           }} | ||||
|         /> | ||||
|          | ||||
|  | ||||
|         {/* Title overlay */} | ||||
|         <div className="absolute inset-0 flex items-center justify-center"> | ||||
|           <h3  | ||||
|             className="text-black text-sm lg:text-base font-medium text-center px-4 drop-shadow-sm" | ||||
|             style={{  | ||||
|               transform: 'rotate(0deg) skewX(0deg)', | ||||
|               transformOrigin: 'center' | ||||
|           <h3 | ||||
|             className="text-blue-900 text-sm lg:text-base font-medium text-center px-4" | ||||
|             style={{ | ||||
|               textShadow: | ||||
|                 "0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(59, 130, 246, 0.5)", | ||||
|             }} | ||||
|           > | ||||
|             {title} | ||||
| @@ -106,26 +131,24 @@ export function CubeLight({ title, descriptionTitle, description, isActive, inde | ||||
|                 y1="1" | ||||
|                 x2="120" | ||||
|                 y2="1" | ||||
|                 stroke="black" | ||||
|                 stroke="rgba(59, 130, 246, 0.6)" | ||||
|                 strokeWidth="1" | ||||
|                 opacity="0.6" | ||||
|                 opacity="0.8" | ||||
|               /> | ||||
|             </svg> | ||||
|              | ||||
|  | ||||
|             {/* Description text */} | ||||
|             <div className="ml-32 w-80"> | ||||
|               <h4 className="text-black text-base font-semibold mb-2"> | ||||
|               <h4 className="text-black text-base font-semibold mb-2 drop-shadow-[0_0_6px_rgba(255,255,255,0.6)]"> | ||||
|                 {descriptionTitle} | ||||
|               </h4> | ||||
|               <p className="text-gray-800 text-sm leading-relaxed font-light"> | ||||
|               <p className="text-gray-800 text-sm leading-relaxed font-light drop-shadow-[0_0_4px_rgba(255,255,255,0.4)]"> | ||||
|                 {description} | ||||
|               </p> | ||||
|             </div> | ||||
|           </motion.div> | ||||
|         )} | ||||
|  | ||||
|         {/* Description for Mobile - Below cube */} | ||||
|               </motion.div> | ||||
|       </motion.div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|   | ||||
							
								
								
									
										55
									
								
								src/components/ui/Dropdown.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,55 @@ | ||||
| import { Menu, Transition } from '@headlessui/react' | ||||
| import { Fragment } from 'react' | ||||
| import { Link } from 'react-router-dom' | ||||
|  | ||||
| interface DropdownItem { | ||||
|   name: string | ||||
|   href: string | ||||
| } | ||||
|  | ||||
| interface DropdownProps { | ||||
|   buttonContent: React.ReactNode | ||||
|   items: DropdownItem[] | ||||
| } | ||||
|  | ||||
| export function Dropdown({ buttonContent, items }: DropdownProps) { | ||||
|   return ( | ||||
|     <Menu as="div" className="relative inline-block text-left"> | ||||
|       <div> | ||||
|         <Menu.Button className="inline-flex w-full justify-center items-center gap-x-1.5 rounded-md bg-white text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"> | ||||
|           {buttonContent} | ||||
|         </Menu.Button> | ||||
|       </div> | ||||
|  | ||||
|       <Transition | ||||
|         as={Fragment} | ||||
|         enter="transition ease-out duration-100" | ||||
|         enterFrom="transform opacity-0 scale-95" | ||||
|         enterTo="transform opacity-100 scale-100" | ||||
|         leave="transition ease-in duration-75" | ||||
|         leaveFrom="transform opacity-100 scale-100" | ||||
|         leaveTo="transform opacity-0 scale-95" | ||||
|       > | ||||
|         <Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"> | ||||
|           <div className="py-1"> | ||||
|             {items.map((item) => ( | ||||
|               <Menu.Item key={item.href}> | ||||
|                 {({ active }: { active: boolean }) => ( | ||||
|                   <Link | ||||
|                     to={item.href} | ||||
|                     className={` | ||||
|                       ${active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'} | ||||
|                       block px-4 py-2 text-sm | ||||
|                     `} | ||||
|                   > | ||||
|                     {item.name} | ||||
|                   </Link> | ||||
|                 )} | ||||
|               </Menu.Item> | ||||
|             ))} | ||||
|           </div> | ||||
|         </Menu.Items> | ||||
|       </Transition> | ||||
|     </Menu> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/components/ui/FadeIn.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { motion, type Transition } from 'framer-motion' | ||||
| import React from 'react' | ||||
| import { useMediaQuery } from '@/hooks/useMediaQuery' | ||||
|  | ||||
| type FadeInProps = { | ||||
|   children: React.ReactNode | ||||
|   transition?: Transition | ||||
|   className?: string | ||||
| } | ||||
|  | ||||
| export function FadeIn({ children, transition, className }: FadeInProps) { | ||||
|   const isMobile = useMediaQuery('(max-width: 768px)') | ||||
|  | ||||
|   return ( | ||||
|     <motion.div | ||||
|       className={className} | ||||
|       initial={{ opacity: 0, y: 20 }} | ||||
|       whileInView={{ opacity: 1, y: 0 }} | ||||
|       viewport={{ once: true, amount: isMobile ? 0.2 : 0.3 }} | ||||
|       transition={transition || { duration: 0.5, ease: 'easeOut' }} | ||||
|     > | ||||
|       {children} | ||||
|     </motion.div> | ||||
|   ) | ||||
| } | ||||
| @@ -1,47 +1,159 @@ | ||||
| import { useEffect, useRef } from 'react' | ||||
| import createGlobe from 'cobe' | ||||
| "use client"; | ||||
|  | ||||
| export function Globe({ className }: { className?: string }) { | ||||
|   const canvasRef = useRef<HTMLCanvasElement>(null) | ||||
| import createGlobe, { type COBEOptions } from "cobe"; | ||||
| import { useMotionValue, useSpring } from "motion/react"; | ||||
| import { useEffect, useRef } from "react"; | ||||
|  | ||||
| import { cn } from "@/lib/utils"; | ||||
|  | ||||
| const MOVEMENT_DAMPING = 1400; | ||||
|  | ||||
| const GLOBE_CONFIG: COBEOptions = { | ||||
|   width: 800, | ||||
|   height: 800, | ||||
|   onRender: () => {}, | ||||
|   devicePixelRatio: 2, | ||||
|   phi: 0, | ||||
|   theta: 0.3, | ||||
|   dark: 0, | ||||
|   diffuse: 0.25, // softer shading for premium look | ||||
|   mapSamples: 16000, | ||||
|   mapBrightness: 1.1, | ||||
|   baseColor: [0.8, 0.8, 0.8], // sleek dark gray globe | ||||
|   markerColor: [0.02, 0.71, 0.83], // cyan-500 | ||||
|   glowColor: [0.8, 0.8, 0.85], // grey | ||||
|    | ||||
|   markers: [ | ||||
|     // --- Core Global Markers --- | ||||
|     { location: [14.5995, 120.9842], size: 0.03 }, // Manila | ||||
|     { location: [19.076, 72.8777], size: 0.1 }, // Mumbai | ||||
|     { location: [23.8103, 90.4125], size: 0.05 }, // Dhaka | ||||
|     { location: [30.0444, 31.2357], size: 0.07 }, // Cairo | ||||
|     { location: [39.9042, 116.4074], size: 0.08 }, // Beijing | ||||
|     { location: [-23.5505, -46.6333], size: 0.1 }, // São Paulo | ||||
|     { location: [19.4326, -99.1332], size: 0.1 }, // Mexico City | ||||
|     { location: [40.7128, -74.006], size: 0.1 }, // New York | ||||
|     { location: [34.6937, 135.5022], size: 0.05 }, // Osaka | ||||
|     { location: [41.0082, 28.9784], size: 0.06 }, // Istanbul | ||||
|     { location: [48.8566, 2.3522], size: 0.08 }, // Paris | ||||
|     { location: [51.5072, -0.1276], size: 0.08 }, // London | ||||
|     { location: [52.52, 13.405], size: 0.07 }, // Berlin | ||||
|     { location: [35.6895, 139.6917], size: 0.06 }, // Tokyo | ||||
|     { location: [-33.8688, 151.2093], size: 0.06 }, // Sydney | ||||
|     { location: [-1.2921, 36.8219], size: 0.05 }, // Nairobi | ||||
|     { location: [-34.6037, -58.3816], size: 0.07 }, // Buenos Aires | ||||
|     { location: [37.7749, -122.4194], size: 0.08 }, // San Francisco | ||||
|     { location: [1.3521, 103.8198], size: 0.06 }, // Singapore | ||||
|     { location: [28.6139, 77.2090], size: 0.08 }, // New Delhi | ||||
|     { location: [13.7563, 100.5018], size: 0.06 }, // Bangkok | ||||
|     { location: [59.9343, 30.3351], size: 0.05 }, // St. Petersburg | ||||
|     { location: [33.6844, 73.0479], size: 0.05 }, // Islamabad | ||||
|     { location: [25.276987, 55.296249], size: 0.07 }, // Dubai | ||||
|     { location: [60.1699, 24.9384], size: 0.05 }, // Helsinki | ||||
|     { location: [43.6532, -79.3832], size: 0.07 }, // Toronto | ||||
|     { location: [6.5244, 3.3792], size: 0.08 }, // Lagos | ||||
|     { location: [50.1109, 8.6821], size: 0.06 }, // Frankfurt | ||||
|  | ||||
|     // --- 12 New US + European Cities --- | ||||
|     { location: [34.0522, -118.2437], size: 0.08 }, // Los Angeles | ||||
|     { location: [41.8781, -87.6298], size: 0.07 }, // Chicago | ||||
|     { location: [29.7604, -95.3698], size: 0.07 }, // Houston | ||||
|     { location: [25.7617, -80.1918], size: 0.07 }, // Miami | ||||
|     { location: [45.5017, -73.5673], size: 0.06 }, // Montreal | ||||
|     { location: [47.6062, -122.3321], size: 0.06 }, // Seattle | ||||
|     { location: [40.4406, -79.9959], size: 0.05 }, // Pittsburgh | ||||
|     { location: [41.3851, 2.1734], size: 0.06 }, // Barcelona | ||||
|     { location: [45.4642, 9.19], size: 0.06 }, // Milan | ||||
|     { location: [52.3676, 4.9041], size: 0.06 }, // Amsterdam | ||||
|     { location: [38.7169, -9.139], size: 0.05 }, // Lisbon | ||||
|     { location: [59.3293, 18.0686], size: 0.05 }, // Stockholmx | ||||
|   ], | ||||
| }; | ||||
|  | ||||
| export function Globe({ | ||||
|   className, | ||||
|   config = GLOBE_CONFIG, | ||||
| }: { | ||||
|   className?: string; | ||||
|   config?: COBEOptions; | ||||
| }) { | ||||
|   let phi = 0; | ||||
|   let width = 0; | ||||
|   const canvasRef = useRef<HTMLCanvasElement>(null); | ||||
|   const pointerInteracting = useRef<number | null>(null); | ||||
|  | ||||
|   const r = useMotionValue(0); | ||||
|   const rs = useSpring(r, { | ||||
|     mass: 1, | ||||
|     damping: 35, // slightly smoother motion | ||||
|     stiffness: 100, | ||||
|   }); | ||||
|  | ||||
|   const updatePointerInteraction = (value: number | null) => { | ||||
|     pointerInteracting.current = value; | ||||
|     if (canvasRef.current) { | ||||
|       canvasRef.current.style.cursor = value !== null ? "grabbing" : "grab"; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const updateMovement = (clientX: number) => { | ||||
|     if (pointerInteracting.current !== null) { | ||||
|       const delta = clientX - pointerInteracting.current; | ||||
|       r.set(r.get() + delta / MOVEMENT_DAMPING); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   useEffect(() => { | ||||
|     let phi = 0 | ||||
|     const onResize = () => { | ||||
|       if (canvasRef.current) width = canvasRef.current.offsetWidth; | ||||
|     }; | ||||
|  | ||||
|     if (!canvasRef.current) return | ||||
|     window.addEventListener("resize", onResize); | ||||
|     onResize(); | ||||
|  | ||||
|     const globe = createGlobe(canvasRef.current, { | ||||
|       devicePixelRatio: 2, | ||||
|       width: 600 * 2, | ||||
|       height: 600 * 2, | ||||
|       phi: 0, | ||||
|       theta: 0, | ||||
|       dark: 0, | ||||
|       diffuse: 1.2, | ||||
|       mapSamples: 16000, | ||||
|       mapBrightness: 6, | ||||
|       baseColor: [0.3, 0.3, 0.3], | ||||
|       markerColor: [0.1, 0.8, 1], | ||||
|       glowColor: [1, 1, 1], | ||||
|       markers: [ | ||||
|         { location: [37.7595, -122.4367], size: 0.03 }, | ||||
|         { location: [40.7128, -74.006], size: 0.1 }, | ||||
|       ], | ||||
|     const globe = createGlobe(canvasRef.current!, { | ||||
|       ...config, | ||||
|       width: width * 2, | ||||
|       height: width * 2, | ||||
|       onRender: (state) => { | ||||
|         state.phi = phi | ||||
|         phi += 0.01 | ||||
|         if (!pointerInteracting.current) phi += 0.004; // slightly slower rotation for elegance | ||||
|         state.phi = phi + rs.get(); | ||||
|         state.width = width * 2; | ||||
|         state.height = width * 2; | ||||
|       }, | ||||
|     }) | ||||
|     }); | ||||
|  | ||||
|     setTimeout(() => (canvasRef.current!.style.opacity = "1"), 0); | ||||
|     return () => { | ||||
|       globe.destroy() | ||||
|     } | ||||
|   }, []) | ||||
|       globe.destroy(); | ||||
|       window.removeEventListener("resize", onResize); | ||||
|     }; | ||||
|   }, [rs, config]); | ||||
|  | ||||
|   return ( | ||||
|     <canvas | ||||
|       ref={canvasRef} | ||||
|       className={className} | ||||
|       style={{ width: '100%', height: '100%', maxWidth: '100%', aspectRatio: 1 }} | ||||
|     /> | ||||
|   ) | ||||
|     <div | ||||
|       className={cn( | ||||
|         // Radial gradient background that fades to pure black at edges | ||||
|         "absolute inset-0 mx-auto aspect-[1/1] w-full max-w-[600px] rounded-full", | ||||
|         className, | ||||
|       )} | ||||
|     > | ||||
|       <canvas | ||||
|         className={cn( | ||||
|           "size-full opacity-0 transition-opacity duration-500 [contain:layout_paint_size]", | ||||
|         )} | ||||
|         ref={canvasRef} | ||||
|         onPointerDown={(e) => { | ||||
|           pointerInteracting.current = e.clientX; | ||||
|           updatePointerInteraction(e.clientX); | ||||
|         }} | ||||
|         onPointerUp={() => updatePointerInteraction(null)} | ||||
|         onPointerOut={() => updatePointerInteraction(null)} | ||||
|         onMouseMove={(e) => updateMovement(e.clientX)} | ||||
|         onTouchMove={(e) => | ||||
|           e.touches[0] && updateMovement(e.touches[0].clientX) | ||||
|         } | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|   | ||||
							
								
								
									
										47
									
								
								src/components/ui/Globe2.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,47 @@ | ||||
| import { useEffect, useRef } from 'react' | ||||
| import createGlobe from 'cobe' | ||||
|  | ||||
| export function Globe({ className }: { className?: string }) { | ||||
|   const canvasRef = useRef<HTMLCanvasElement>(null) | ||||
|  | ||||
|   useEffect(() => { | ||||
|     let phi = 0 | ||||
|  | ||||
|     if (!canvasRef.current) return | ||||
|  | ||||
|     const globe = createGlobe(canvasRef.current, { | ||||
|       devicePixelRatio: 2, | ||||
|       width: 600 * 2, | ||||
|       height: 600 * 2, | ||||
|       phi: 0, | ||||
|       theta: 0, | ||||
|       dark: 0, | ||||
|       diffuse: 1.2, | ||||
|       mapSamples: 16000, | ||||
|       mapBrightness: 6, | ||||
|       baseColor: [0.3, 0.3, 0.3], | ||||
|       markerColor: [0.1, 0.8, 1], | ||||
|       glowColor: [1, 1, 1], | ||||
|       markers: [ | ||||
|         { location: [37.7595, -122.4367], size: 0.03 }, | ||||
|         { location: [40.7128, -74.006], size: 0.1 }, | ||||
|       ], | ||||
|       onRender: (state) => { | ||||
|         state.phi = phi | ||||
|         phi += 0.01 | ||||
|       }, | ||||
|     }) | ||||
|  | ||||
|     return () => { | ||||
|       globe.destroy() | ||||
|     } | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <canvas | ||||
|       ref={canvasRef} | ||||
|       className={className} | ||||
|       style={{ width: '100%', height: '100%', maxWidth: '100%', aspectRatio: 1 }} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										89
									
								
								src/components/ui/HalfGlobe.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,89 @@ | ||||
| "use client"; | ||||
|  | ||||
| import { Canvas, useFrame } from "@react-three/fiber"; | ||||
| import { Line, OrbitControls, useTexture } from "@react-three/drei"; | ||||
| import { useMemo, useRef } from "react"; | ||||
|  | ||||
| type RotatableGroup = { | ||||
|   rotation: { y: number }; | ||||
| }; | ||||
|  | ||||
| function Globe() { | ||||
|   const groupRef = useRef<RotatableGroup | null>(null); | ||||
|   const cloudTexture = useTexture("/images/cloud1.png"); | ||||
|  | ||||
|   // Rotate the globe slowly | ||||
|   useFrame(() => { | ||||
|     if (groupRef.current) { | ||||
|       groupRef.current.rotation.y += 0.002; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // Coordinates for markers (half-globe) | ||||
|   const markers = [ | ||||
|     [0, 1, 0], | ||||
|     [0.7, 0.5, 0.2], | ||||
|     [-0.5, 0.4, 0.5], | ||||
|     [0.4, 0.3, -0.7], | ||||
|     [-0.6, -0.1, 0.3], | ||||
|     [0.3, -0.2, 0.8], | ||||
|   ]; | ||||
|   const arcPoints = useMemo(() => { | ||||
|     const radius = 2.5; | ||||
|     const verticalRadius = radius / 2; | ||||
|     const segments = 120; | ||||
|  | ||||
|     return Array.from({ length: 8 }, () => { | ||||
|       const points: number[] = []; | ||||
|       for (let i = 0; i < segments; i++) { | ||||
|         const t = (i / (segments - 1)) * Math.PI; | ||||
|         const x = Math.cos(t) * radius; | ||||
|         const z = Math.sin(t) * verticalRadius; | ||||
|         const y = Math.sin(x / radius) * 0.5; | ||||
|         points.push(x, y, z); | ||||
|       } | ||||
|       return points; | ||||
|     }); | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <group ref={groupRef}> | ||||
|       {/* Cyan arcs */} | ||||
|       {arcPoints.map((points, i) => ( | ||||
|         <Line | ||||
|           key={i} | ||||
|           points={points} | ||||
|           color="#00e5ff" | ||||
|           lineWidth={1} | ||||
|           transparent | ||||
|           opacity={0.5} | ||||
|         /> | ||||
|       ))} | ||||
|  | ||||
|       {/* Cloud markers */} | ||||
|       {markers.map(([x, y, z], i) => ( | ||||
|         <mesh key={i} position={[x * 2.5, y * 2.5, z * 2.5]}> | ||||
|           <planeGeometry args={[0.3, 0.3]} /> | ||||
|           <meshBasicMaterial | ||||
|             map={cloudTexture} | ||||
|             transparent | ||||
|             opacity={1} | ||||
|             side={2 /* DoubleSide */} | ||||
|           /> | ||||
|         </mesh> | ||||
|       ))} | ||||
|     </group> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function HalfGlobe() { | ||||
|   return ( | ||||
|     <div className="w-full h-[500px] bg-white flex items-center justify-center"> | ||||
|       <Canvas camera={{ position: [0, 1.5, 4], fov: 45 }}> | ||||
|         <ambientLight intensity={0.8} /> | ||||
|         <Globe /> | ||||
|         <OrbitControls enableZoom={false} enablePan={false} /> | ||||
|       </Canvas> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @@ -13,7 +13,7 @@ export function ScrollDown() { | ||||
|   return ( | ||||
|     <button | ||||
|       onClick={scrollToNext} | ||||
|       className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-white lg:text-3xl animate-blink" | ||||
|       className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-black/20 p-2 text-2xl font-medium text-white backdrop-blur-sm lg:text-3xl animate-blink" | ||||
|     > | ||||
|       <span>scroll</span> | ||||
|       <ChevronDoubleDownIcon className="h-6 w-6" /> | ||||
|   | ||||
| @@ -13,7 +13,7 @@ export function ScrollUp() { | ||||
|   return ( | ||||
|     <button | ||||
|       onClick={scrollToTop} | ||||
|       className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-[#1c1c49] lg:text-3xl animate-blink" | ||||
|       className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-white/20 p-2 text-2xl font-medium text-[#1c1c49] backdrop-blur-sm lg:text-3xl animate-blink" | ||||
|     > | ||||
|       <span>top</span> | ||||
|       <ChevronDoubleUpIcon className="h-6 w-6" /> | ||||
|   | ||||
| @@ -2,13 +2,14 @@ | ||||
|  | ||||
| import { useState } from "react"; | ||||
| import { motion } from "framer-motion"; | ||||
| import { CubeLight } from "@/components/ui/CubeLight" | ||||
| import { CubeLight } from "@/components/ui/CubeLight"; | ||||
|  | ||||
| const stackData = [ | ||||
|   { | ||||
|     id: "agent", | ||||
|     title: "Agent Layer", | ||||
|     descriptionTitle: "Your sovereign agent with private memory and permissioned data access—always under your control.", | ||||
|     descriptionTitle: | ||||
|       "Your sovereign agent with private memory and permissioned data access—always under your control.", | ||||
|     description: | ||||
|       "Choose from a wide library of open-source LLMs, paired with built-in semantic search and retrieval.\nIt coordinates across people, apps, and other agents to plan, create, and execute.\nIt operates inside a compliant legal & financial sandbox, ready for real-world transactions and operations.\nMore than just an assistant—an intelligent partner that learns and does your way.", | ||||
|     position: "top", | ||||
| @@ -16,7 +17,8 @@ const stackData = [ | ||||
|   { | ||||
|     id: "network", | ||||
|     title: "Network Layer", | ||||
|     descriptionTitle: "A global, end-to-end encrypted overlay that simply doesn’t break.", | ||||
|     descriptionTitle: | ||||
|       "A global, end-to-end encrypted overlay that simply doesn’t break.", | ||||
|     description: | ||||
|       "Shortest-path routing moves your traffic the fastest way, every time.\nInstant discovery with integrated DNS, semantic search, and indexing.\nA distributed CDN and edge delivery keep content available and tamper-resistant worldwide.\nBuilt-in tool services and secure coding sandboxes—seamless on phones, desktops, and edge.", | ||||
|     position: "middle", | ||||
| @@ -24,7 +26,8 @@ const stackData = [ | ||||
|   { | ||||
|     id: "cloud", | ||||
|     title: "Cloud Layer", | ||||
|     descriptionTitle: "An autonomous, stateless OS that enforces pre-deterministic deployments you define.", | ||||
|     descriptionTitle: | ||||
|       "An autonomous, stateless OS that enforces pre-deterministic deployments you define.", | ||||
|     description: | ||||
|       "Workloads are cryptographically bound to your private key—location and access are yours.\nNo cloud vendor or middleman in the path: end-to-end ownership and isolation by default.\nGeo-aware placement delivers locality, compliance, and ultra-low latency where it matters.\nEncrypted, erasure-coded storage, decentralized compute and GPU on demand—including LLMs.", | ||||
|     position: "bottom", | ||||
| @@ -36,60 +39,74 @@ export function StackedCubesLight() { | ||||
|   const [selectedForMobile, setSelectedForMobile] = useState<string | null>("agent"); | ||||
|  | ||||
|   const handleCubeClick = (id: string) => { | ||||
|     setSelectedForMobile(prev => (prev === id ? null : id)); | ||||
|     setSelectedForMobile((prev) => (prev === id ? null : id)); | ||||
|   }; | ||||
|  | ||||
|   const selectedMobileLayer = stackData.find(layer => layer.id === selectedForMobile); | ||||
|   const selectedMobileLayer = stackData.find( | ||||
|     (layer) => layer.id === selectedForMobile | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <div className="flex flex-col items-center"> | ||||
|     <div  | ||||
|       className="relative w-full flex items-center justify-center lg:justify-center min-h-[450px] lg:min-h-[400px]" | ||||
|       onMouseLeave={() => setActive("agent")} | ||||
|     > | ||||
|       <motion.div | ||||
|         className="relative lg:pl-0 pl-6 h-[300px] lg:h-[400px] w-64 sm:w-80 lg:w-96 scale-120 lg:scale-100" | ||||
|         animate={{ y: ["-8px", "8px"] }} | ||||
|         transition={{ | ||||
|           duration: 4, | ||||
|           repeat: Infinity, | ||||
|           repeatType: "reverse", | ||||
|           ease: "easeInOut", | ||||
|         }} | ||||
|     <div className="flex flex-col items-center relative"> | ||||
|       {/* ✨ Ambient cyan-white gradient background */} | ||||
|       <div className="absolute inset-0 bg-gradient-to-b from-white via-cyan-50/40 to-white blur-3xl opacity-70 pointer-events-none" /> | ||||
|       <div | ||||
|         className="relative w-full flex items-center justify-center lg:justify-center min-h-[450px] lg:min-h-[400px]" | ||||
|         onMouseLeave={() => setActive("agent")} | ||||
|       > | ||||
|         {stackData.map((layer, index) => ( | ||||
|           <div | ||||
|             key={layer.id} | ||||
|             className="absolute" | ||||
|             style={{ | ||||
|               top: `calc(${index * 30}% - ${index * 10}px)`, | ||||
|               zIndex: active === layer.id ? 20 : 10 - index, | ||||
|             }} | ||||
|           > | ||||
|             <CubeLight | ||||
|               title={layer.title} | ||||
|               descriptionTitle={layer.descriptionTitle} | ||||
|               description={layer.description} | ||||
|               isActive={active === layer.id} | ||||
|               index={index} | ||||
|               onHover={() => setActive(layer.id)} | ||||
|               onLeave={() => {}} | ||||
|               onClick={() => handleCubeClick(layer.id)} | ||||
|             /> | ||||
|           </div> | ||||
|         ))} | ||||
|       </motion.div> | ||||
|     </div> | ||||
|     {selectedMobileLayer && ( | ||||
|       <div className="lg:hidden w-full max-w-md p-6 -mt-8 bg-gray-200/50 rounded-lg"> | ||||
|         <h4 className="text-black text-lg font-semibold mb-2 text-center"> | ||||
|           {selectedMobileLayer.descriptionTitle} | ||||
|         </h4> | ||||
|         <p className="text-gray-700 text-sm leading-relaxed text-center"> | ||||
|           {selectedMobileLayer.description} | ||||
|         </p> | ||||
|         <motion.div | ||||
|           className="relative lg:pl-0 pl-6 h-[300px] lg:h-[400px] w-64 sm:w-80 lg:w-96" | ||||
|           animate={{ y: ["-8px", "8px"] }} | ||||
|           transition={{ | ||||
|             duration: 4, | ||||
|             repeat: Infinity, | ||||
|             repeatType: "reverse", | ||||
|             ease: "easeInOut", | ||||
|           }} | ||||
|         > | ||||
|           {stackData.map((layer, index) => ( | ||||
|             <div | ||||
|               key={layer.id} | ||||
|               className="absolute transition-all duration-500" | ||||
|               style={{ | ||||
|                 top: `calc(${index * 30}% - ${index * 10}px)`, | ||||
|                 zIndex: active === layer.id ? 20 : 10 - index, | ||||
|               }} | ||||
|             > | ||||
|               {/* 🌫 subtle glow behind each cube */} | ||||
|               <div | ||||
|                 className={`absolute inset-0 blur-2xl rounded-3xl transition-all duration-500 ${ | ||||
|                   active === layer.id | ||||
|                     ? "bg-cyan-300/40 opacity-70" | ||||
|                     : "bg-cyan-200/20 opacity-40" | ||||
|                 }`} | ||||
|               /> | ||||
|               <CubeLight | ||||
|                 title={layer.title} | ||||
|                 descriptionTitle={layer.descriptionTitle} | ||||
|                 description={layer.description} | ||||
|                 isActive={active === layer.id} | ||||
|                 index={index} | ||||
|                 onHover={() => setActive(layer.id)} | ||||
|                 onLeave={() => {}} | ||||
|                 onClick={() => handleCubeClick(layer.id)} | ||||
|               /> | ||||
|             </div> | ||||
|           ))} | ||||
|         </motion.div> | ||||
|       </div> | ||||
|     )} | ||||
|  | ||||
|       {/* Mobile layer description */} | ||||
|       {selectedMobileLayer && ( | ||||
|         <div className="lg:hidden w-full max-w-md p-6 -mt-8 bg-white/50 backdrop-blur-xl border border-cyan-100/40 rounded-xl shadow-[0_0_25px_rgba(0,255,255,0.15)]"> | ||||
|           <h4 className="text-black text-lg font-semibold mb-2 text-center drop-shadow-[0_0_6px_rgba(255,255,255,0.6)]"> | ||||
|             {selectedMobileLayer.descriptionTitle} | ||||
|           </h4> | ||||
|           <p className="text-cyan-900/80 text-sm leading-relaxed text-center drop-shadow-[0_0_4px_rgba(255,255,255,0.4)]"> | ||||
|             {selectedMobileLayer.description} | ||||
|           </p> | ||||
|         </div> | ||||
|       )} | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|   | ||||
							
								
								
									
										196
									
								
								src/components/ui/apple-cards-carousel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,196 @@ | ||||
| "use client"; | ||||
| import React, { | ||||
|   useEffect, | ||||
|   useState, | ||||
| } from "react"; | ||||
| import { | ||||
|   IconArrowNarrowLeft, | ||||
|   IconArrowNarrowRight, | ||||
|   IconChevronRight, | ||||
| } from "@tabler/icons-react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { Link } from "react-router-dom"; | ||||
| import { motion } from "motion/react"; | ||||
|  | ||||
| interface CarouselProps { | ||||
|   items: JSX.Element[]; | ||||
|   initialScroll?: number; | ||||
| } | ||||
|  | ||||
| type Card = { | ||||
|   src: string; | ||||
|   title: string; | ||||
|   category: string; | ||||
|   description: string; | ||||
|   link: string; | ||||
|   bg: any; | ||||
| }; | ||||
|  | ||||
| export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => { | ||||
|   const carouselRef = React.useRef<HTMLDivElement>(null); | ||||
|   const [canScrollLeft, setCanScrollLeft] = React.useState(false); | ||||
|   const [canScrollRight, setCanScrollRight] = React.useState(true); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (carouselRef.current) { | ||||
|       carouselRef.current.scrollLeft = initialScroll; | ||||
|       checkScrollability(); | ||||
|     } | ||||
|   }, [initialScroll]); | ||||
|  | ||||
|   const checkScrollability = () => { | ||||
|     if (carouselRef.current) { | ||||
|       const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current; | ||||
|       setCanScrollLeft(scrollLeft > 0); | ||||
|       setCanScrollRight(scrollLeft < scrollWidth - clientWidth); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const scrollLeft = () => { | ||||
|     if (carouselRef.current) { | ||||
|       carouselRef.current.scrollBy({ left: -300, behavior: "smooth" }); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   const scrollRight = () => { | ||||
|     if (carouselRef.current) { | ||||
|       carouselRef.current.scrollBy({ left: 300, behavior: "smooth" }); | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|       <div className="relative w-full"> | ||||
|         <div | ||||
|           className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 [scrollbar-width:none] md:py-20" | ||||
|           ref={carouselRef} | ||||
|           onScroll={checkScrollability} | ||||
|         > | ||||
|           <div | ||||
|             className={cn( | ||||
|               "absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l", | ||||
|             )} | ||||
|           ></div> | ||||
|  | ||||
|           <div | ||||
|             className={cn( | ||||
|               "flex flex-row justify-start gap-4 pl-4", | ||||
|               "mx-auto max-w-7xl", // remove max-w-4xl if you want the carousel to span the full width of its container | ||||
|             )} | ||||
|           > | ||||
|             {items.map((item, index) => ( | ||||
|               <motion.div | ||||
|                 initial={{ | ||||
|                   opacity: 0, | ||||
|                   y: 20, | ||||
|                 }} | ||||
|                 animate={{ | ||||
|                   opacity: 1, | ||||
|                   y: 0, | ||||
|                   transition: { | ||||
|                     duration: 0.5, | ||||
|                     delay: 0.2 * index, | ||||
|                     ease: "easeOut", | ||||
|                   }, | ||||
|                 }} | ||||
|                 key={"card" + index} | ||||
|                 className="rounded-3xl last:pr-[5%] md:last:pr-[33%]" | ||||
|               > | ||||
|                 {item} | ||||
|               </motion.div> | ||||
|             ))} | ||||
|           </div> | ||||
|         </div> | ||||
|         <div className="mr-10 flex justify-end gap-2"> | ||||
|           <button | ||||
|             className="relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-neutral-800 disabled:opacity-50" | ||||
|             onClick={scrollLeft} | ||||
|             disabled={!canScrollLeft} | ||||
|           > | ||||
|             <IconArrowNarrowLeft className="h-6 w-6 text-white" /> | ||||
|           </button> | ||||
|           <button | ||||
|             className="relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-neutral-800 disabled:opacity-50" | ||||
|             onClick={scrollRight} | ||||
|             disabled={!canScrollRight} | ||||
|           > | ||||
|             <IconArrowNarrowRight className="h-6 w-6 text-white" /> | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const Card = ({ | ||||
|   card, | ||||
|   layout = false, | ||||
| }: { | ||||
|   card: Card; | ||||
|   layout?: boolean; | ||||
| }) => { | ||||
|    | ||||
|   return ( | ||||
|     <Link to={card.link}> | ||||
|       <motion.div | ||||
|         layoutId={layout ? `card-${card.title}` : undefined} | ||||
|         className="relative z-10 flex h-60 w-56 flex-col items-start justify-start overflow-hidden rounded-3xl md:h-120 md:w-96 hover:scale-105 transition-transform duration-200" | ||||
|         style={{ | ||||
|           backgroundImage: `url(${card.bg})`, | ||||
|           backgroundSize: 'cover', | ||||
|           backgroundPosition: 'center', | ||||
|         }} | ||||
|       > | ||||
|         <div className="pointer-events-none absolute inset-x-0 top-0 z-30 h-full bg-gradient-to-b from-black/50 via-transparent to-transparent" /> | ||||
|         <div className="relative z-40 p-8 w-full"> | ||||
|           <motion.p | ||||
|             layoutId={layout ? `category-${card.category}` : undefined} | ||||
|             className="text-left font-sans text-sm font-medium text-white md:text-base" | ||||
|           > | ||||
|             {card.category} | ||||
|           </motion.p> | ||||
|           <motion.p | ||||
|             layoutId={layout ? `title-${card.title}` : undefined} | ||||
|             className="mt-2 max-w-xs text-left font-sans text-xl font-semibold [text-wrap:balance] text-white md:text-3xl" | ||||
|           > | ||||
|             {card.title} | ||||
|           </motion.p> | ||||
|           <div className="flex flex-row justify-between items-center w-full mt-4"> | ||||
|             <motion.p className="max-w-xs text-left font-sans text-sm text-neutral-300"> | ||||
|               {card.description} | ||||
|             </motion.p> | ||||
|             <div className="h-8 w-8 bg-[#212121] rounded-full flex items-center justify-center text-[#858585] shrink-0 hover:bg-[#262626] hover:text-white active:bg-[#262626] active:text-white transition-colors duration-200"> | ||||
|               <IconChevronRight className="h-6 w-6" /> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </motion.div> | ||||
|     </Link> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const BlurImage = ({ | ||||
|   src, | ||||
|   className, | ||||
|   width, | ||||
|   height, | ||||
|   alt, | ||||
|   ...rest | ||||
| }: React.ImgHTMLAttributes<HTMLImageElement>) => { | ||||
|   const [isLoading, setLoading] = useState(true); | ||||
|   return ( | ||||
|     <img | ||||
|       className={cn( | ||||
|         "h-full w-full transition duration-300", | ||||
|         isLoading ? "blur-sm" : "blur-0", | ||||
|         className, | ||||
|       )} | ||||
|       onLoad={() => setLoading(false)} | ||||
|       src={src as string} | ||||
|       width={width} | ||||
|       height={height} | ||||
|       loading="lazy" | ||||
|       decoding="async" | ||||
|       alt={alt ? alt : "Background of a beautiful view"} | ||||
|       {...rest} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										64
									
								
								src/components/ui/aurora-background.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,64 @@ | ||||
| "use client"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import type { CSSProperties, HTMLProps, ReactNode } from "react"; | ||||
|  | ||||
| interface AuroraBackgroundProps extends HTMLProps<HTMLDivElement> { | ||||
|   children: ReactNode; | ||||
|   showRadialGradient?: boolean; | ||||
| } | ||||
|  | ||||
| export const AuroraBackground = ({ | ||||
|   className, | ||||
|   children, | ||||
|   showRadialGradient = true, | ||||
|   ...props | ||||
| }: AuroraBackgroundProps) => { | ||||
|   return ( | ||||
|     <main> | ||||
|       <div | ||||
|                 className={cn( | ||||
|           "transition-bg relative flex h-[110vh] flex-col items-center justify-center bg-cover bg-center", | ||||
|           className | ||||
|         )} | ||||
|         style={{ | ||||
|           backgroundImage: "url('/images/mchip.webp')", | ||||
|         }} | ||||
|         {...props} | ||||
|       > | ||||
|         <div | ||||
|           className="absolute inset-0 overflow-hidden" | ||||
|           style={ | ||||
|             { | ||||
|               "--aurora": | ||||
|                 "repeating-linear-gradient(100deg,#3b82f6_10%,#a5b4fc_15%,#93c5fd_20%,#ddd6fe_25%,#60a5fa_30%)", | ||||
|               "--dark-gradient": | ||||
|                 "repeating-linear-gradient(100deg,#000_0%,#000_7%,transparent_10%,transparent_12%,#000_16%)", | ||||
|               "--white-gradient": | ||||
|                 "repeating-linear-gradient(100deg,#fff_0%,#fff_7%,transparent_10%,transparent_12%,#fff_16%)", | ||||
|  | ||||
|               "--blue-300": "#93c5fd", | ||||
|               "--blue-400": "#60a5fa", | ||||
|               "--blue-500": "#3b82f6", | ||||
|               "--indigo-300": "#a5b4fc", | ||||
|               "--violet-200": "#ddd6fe", | ||||
|               "--black": "#000", | ||||
|               "--white": "#fff", | ||||
|               "--transparent": "transparent", | ||||
|             } as CSSProperties | ||||
|           } | ||||
|         > | ||||
|           <div | ||||
|             //   I'm sorry but this is what peak developer performance looks like // trigger warning | ||||
|             className={cn( | ||||
|               `after:animate-aurora pointer-events-none absolute -inset-[10px] [background-image:var(--white-gradient),var(--aurora)] [background-size:300%,_200%] [background-position:50%_50%,50%_50%] opacity-50 blur-[10px] invert filter will-change-transform [--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)] [--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)] [--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)] after:absolute after:inset-0 after:[background-image:var(--white-gradient),var(--aurora)] after:[background-size:200%,_100%] after:[background-attachment:fixed] after:mix-blend-difference after:content-[""] dark:[background-image:var(--dark-gradient),var(--aurora)] dark:invert-0 after:dark:[background-image:var(--dark-gradient),var(--aurora)]`, | ||||
|  | ||||
|               showRadialGradient && | ||||
|                 `[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`, | ||||
|             )} | ||||
|           ></div> | ||||
|         </div> | ||||
|         {children} | ||||
|       </div> | ||||
|     </main> | ||||
|   ); | ||||
| }; | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { CT, CP } from "@/components/Texts"; | ||||
| import Image from 'next/image'; | ||||
| import React from 'react'; | ||||
| import { motion } from 'framer-motion'; | ||||
|  | ||||
| @@ -55,11 +54,9 @@ export const BentoGridItem = React.forwardRef<HTMLDivElement, BentoGridItemProps | ||||
|               className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300" | ||||
|             /> | ||||
|           ) : img ? ( | ||||
|             <Image | ||||
|             <img | ||||
|               src={img} | ||||
|               alt={title as string} | ||||
|               width={300} | ||||
|               height={300} | ||||
|               className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300" | ||||
|             /> | ||||
|           ) : null} | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| "use client"; | ||||
|  | ||||
| import React, { useEffect, useRef, useState } from "react"; | ||||
| import { useEffect, useRef, useState } from "react"; | ||||
|  | ||||
| type DottedGlowBackgroundProps = { | ||||
|   className?: string; | ||||
| @@ -203,12 +203,8 @@ export function DottedGlowBackground({ | ||||
|  | ||||
|     regenDots(); | ||||
|  | ||||
|     let last = performance.now(); | ||||
|  | ||||
|     const draw = (now: number) => { | ||||
|       if (stopped) return; | ||||
|       const dt = (now - last) / 1000; // seconds | ||||
|       last = now; | ||||
|       const { width, height } = container.getBoundingClientRect(); | ||||
|  | ||||
|       ctx.clearRect(0, 0, el.width, el.height); | ||||
|   | ||||
							
								
								
									
										106
									
								
								src/components/ui/evervault-card.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,106 @@ | ||||
| "use client"; | ||||
| import { useMotionValue } from "motion/react"; | ||||
| import React, { useState, useEffect } from "react"; | ||||
| import { useMotionTemplate, motion } from "motion/react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
|  | ||||
| export const EvervaultCard = ({ | ||||
|   children, | ||||
|   className, | ||||
| }: { | ||||
|   children?: React.ReactNode; | ||||
|   className?: string; | ||||
| }) => { | ||||
|   let mouseX = useMotionValue(0); | ||||
|   let mouseY = useMotionValue(0); | ||||
|  | ||||
|   const [randomString, setRandomString] = useState(""); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     let str = generateRandomString(1500); | ||||
|     setRandomString(str); | ||||
|   }, []); | ||||
|  | ||||
|   function onMouseMove({ currentTarget, clientX, clientY }: any) { | ||||
|     let { left, top } = currentTarget.getBoundingClientRect(); | ||||
|     mouseX.set(clientX - left); | ||||
|     mouseY.set(clientY - top); | ||||
|  | ||||
|     const str = generateRandomString(1500); | ||||
|     setRandomString(str); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       className={cn( | ||||
|         "p-0.5  bg-transparent aspect-square  flex items-center justify-center w-full h-full relative", | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       <div | ||||
|         onMouseMove={onMouseMove} | ||||
|         className="group/card rounded-3xl w-full relative overflow-hidden bg-transparent flex items-center justify-center h-full" | ||||
|       > | ||||
|         <CardPattern | ||||
|           mouseX={mouseX} | ||||
|           mouseY={mouseY} | ||||
|           randomString={randomString} | ||||
|         /> | ||||
|         <div className="relative z-10 flex items-center justify-center"> | ||||
|           <div className="relative p-4"> | ||||
|             {children} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export function CardPattern({ mouseX, mouseY, randomString }: any) { | ||||
|   let maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`; | ||||
|   let style = { maskImage, WebkitMaskImage: maskImage }; | ||||
|  | ||||
|   return ( | ||||
|     <div className="pointer-events-none"> | ||||
|       <div className="absolute inset-0 rounded-2xl  [mask-image:linear-gradient(white,transparent)] group-hover/card:opacity-50"></div> | ||||
|       <motion.div | ||||
|         className="absolute inset-0 rounded-2xl bg-gradient-to-r from-green-500 to-blue-700 opacity-0  group-hover/card:opacity-100 backdrop-blur-xl transition duration-500" | ||||
|         style={style} | ||||
|       /> | ||||
|       <motion.div | ||||
|         className="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay  group-hover/card:opacity-100" | ||||
|         style={style} | ||||
|       > | ||||
|         <p className="absolute inset-x-0 text-xs h-full break-words whitespace-pre-wrap text-white font-mono font-bold transition duration-500"> | ||||
|           {randomString} | ||||
|         </p> | ||||
|       </motion.div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const characters = | ||||
|   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||
| export const generateRandomString = (length: number) => { | ||||
|   let result = ""; | ||||
|   for (let i = 0; i < length; i++) { | ||||
|     result += characters.charAt(Math.floor(Math.random() * characters.length)); | ||||
|   } | ||||
|   return result; | ||||
| }; | ||||
|  | ||||
| export const Icon = ({ className, ...rest }: any) => { | ||||
|   return ( | ||||
|     <svg | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       fill="none" | ||||
|       viewBox="0 0 24 24" | ||||
|       strokeWidth="1.5" | ||||
|       stroke="currentColor" | ||||
|       className={className} | ||||
|       {...rest} | ||||
|     > | ||||
|       <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" /> | ||||
|     </svg> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										90
									
								
								src/components/ui/floating-navbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,90 @@ | ||||
| "use client"; | ||||
| import { useState } from "react"; | ||||
| import { | ||||
|   motion, | ||||
|   AnimatePresence, | ||||
|   useScroll, | ||||
|   useMotionValueEvent, | ||||
| } from "motion/react"; | ||||
| import { Link } from "react-router-dom"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { Button } from "../Button"; | ||||
|  | ||||
|  | ||||
| export const FloatingNav = ({ | ||||
|   navItems, | ||||
|   className, | ||||
| }: { | ||||
|   navItems: { | ||||
|     name: string; | ||||
|     link: string; | ||||
|     icon?: JSX.Element; | ||||
|   }[]; | ||||
|   className?: string; | ||||
| }) => { | ||||
|   const { scrollYProgress } = useScroll(); | ||||
|  | ||||
|   const [visible, setVisible] = useState(true); | ||||
|  | ||||
|   useMotionValueEvent(scrollYProgress, "change", (current) => { | ||||
|     if (typeof current === "number") { | ||||
|       const previous = scrollYProgress.getPrevious(); | ||||
|       // Check if previous is a number to avoid errors | ||||
|       if (typeof previous === "number" && current > previous) { | ||||
|         setVisible(false); // Scrolling down | ||||
|       } else { | ||||
|         setVisible(true); // Scrolling up or at the top | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   return ( | ||||
|     <AnimatePresence mode="wait"> | ||||
|       <motion.div | ||||
|         initial={{ | ||||
|           opacity: 1, | ||||
|           y: -100, | ||||
|         }} | ||||
|         animate={{ | ||||
|           y: visible ? 0 : -100, | ||||
|           opacity: visible ? 1 : 0, | ||||
|         }} | ||||
|         transition={{ | ||||
|           duration: 0.2, | ||||
|         }} | ||||
|         className={cn( | ||||
|           "flex max-w-fit  fixed top-10 inset-x-0 mx-auto border border-transparent dark:border-white/[0.2] rounded-xl dark:bg-black bg-gray-700/50 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] z-[5000] pr-4 pl-8 py-2  items-center justify-center space-x-12", | ||||
|           className | ||||
|         )} | ||||
|       > | ||||
|         {navItems.map((navItem, idx: number) => ( | ||||
|           <Link | ||||
|             key={`link=${idx}`} | ||||
|             to={navItem.link} | ||||
|             className={cn( | ||||
|               "relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500" | ||||
|             )} | ||||
|           > | ||||
|             <span className="block sm:hidden">{navItem.icon}</span> | ||||
|             <span className="hidden sm:block text-sm">{navItem.name}</span> | ||||
|           </Link> | ||||
|         ))} | ||||
|         <div className="flex items-center gap-6"> | ||||
|           <a | ||||
|             href="https://threefold.info/mycelium_network/docs/" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             className={cn( | ||||
|               "relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500" | ||||
|             )} | ||||
|           > | ||||
|             <span className="hidden sm:block text-sm">Docs</span> | ||||
|           </a> | ||||
|           <Button to="/download" variant="solid" color="cyan"> | ||||
|             Get Mycelium Connector | ||||
|           </Button> | ||||
|         </div> | ||||
|       </motion.div> | ||||
|     </AnimatePresence> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										190
									
								
								src/components/ui/glowing-effect.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,190 @@ | ||||
| "use client"; | ||||
|  | ||||
| import { memo, useCallback, useEffect, useRef } from "react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { animate } from "motion/react"; | ||||
|  | ||||
| interface GlowingEffectProps { | ||||
|   blur?: number; | ||||
|   inactiveZone?: number; | ||||
|   proximity?: number; | ||||
|   spread?: number; | ||||
|   variant?: "default" | "white"; | ||||
|   glow?: boolean; | ||||
|   className?: string; | ||||
|   disabled?: boolean; | ||||
|   movementDuration?: number; | ||||
|   borderWidth?: number; | ||||
| } | ||||
| const GlowingEffect = memo( | ||||
|   ({ | ||||
|     blur = 0, | ||||
|     inactiveZone = 0.7, | ||||
|     proximity = 0, | ||||
|     spread = 20, | ||||
|     variant = "default", | ||||
|     glow = false, | ||||
|     className, | ||||
|     movementDuration = 2, | ||||
|     borderWidth = 1, | ||||
|     disabled = true, | ||||
|   }: GlowingEffectProps) => { | ||||
|     const containerRef = useRef<HTMLDivElement>(null); | ||||
|     const lastPosition = useRef({ x: 0, y: 0 }); | ||||
|     const animationFrameRef = useRef<number>(0); | ||||
|  | ||||
|     const handleMove = useCallback( | ||||
|       (e?: MouseEvent | { x: number; y: number }) => { | ||||
|         if (!containerRef.current) return; | ||||
|  | ||||
|         if (animationFrameRef.current) { | ||||
|           cancelAnimationFrame(animationFrameRef.current); | ||||
|         } | ||||
|  | ||||
|         animationFrameRef.current = requestAnimationFrame(() => { | ||||
|           const element = containerRef.current; | ||||
|           if (!element) return; | ||||
|  | ||||
|           const { left, top, width, height } = element.getBoundingClientRect(); | ||||
|           const mouseX = e?.x ?? lastPosition.current.x; | ||||
|           const mouseY = e?.y ?? lastPosition.current.y; | ||||
|  | ||||
|           if (e) { | ||||
|             lastPosition.current = { x: mouseX, y: mouseY }; | ||||
|           } | ||||
|  | ||||
|           const center = [left + width * 0.5, top + height * 0.5]; | ||||
|           const distanceFromCenter = Math.hypot( | ||||
|             mouseX - center[0], | ||||
|             mouseY - center[1] | ||||
|           ); | ||||
|           const inactiveRadius = 0.5 * Math.min(width, height) * inactiveZone; | ||||
|  | ||||
|           if (distanceFromCenter < inactiveRadius) { | ||||
|             element.style.setProperty("--active", "0"); | ||||
|             return; | ||||
|           } | ||||
|  | ||||
|           const isActive = | ||||
|             mouseX > left - proximity && | ||||
|             mouseX < left + width + proximity && | ||||
|             mouseY > top - proximity && | ||||
|             mouseY < top + height + proximity; | ||||
|  | ||||
|           element.style.setProperty("--active", isActive ? "1" : "0"); | ||||
|  | ||||
|           if (!isActive) return; | ||||
|  | ||||
|           const currentAngle = | ||||
|             parseFloat(element.style.getPropertyValue("--start")) || 0; | ||||
|           let targetAngle = | ||||
|             (180 * Math.atan2(mouseY - center[1], mouseX - center[0])) / | ||||
|               Math.PI + | ||||
|             90; | ||||
|  | ||||
|           const angleDiff = ((targetAngle - currentAngle + 180) % 360) - 180; | ||||
|           const newAngle = currentAngle + angleDiff; | ||||
|  | ||||
|           animate(currentAngle, newAngle, { | ||||
|             duration: movementDuration, | ||||
|             ease: [0.16, 1, 0.3, 1], | ||||
|             onUpdate: (value) => { | ||||
|               element.style.setProperty("--start", String(value)); | ||||
|             }, | ||||
|           }); | ||||
|         }); | ||||
|       }, | ||||
|       [inactiveZone, proximity, movementDuration] | ||||
|     ); | ||||
|  | ||||
|     useEffect(() => { | ||||
|       if (disabled) return; | ||||
|  | ||||
|       const handleScroll = () => handleMove(); | ||||
|       const handlePointerMove = (e: PointerEvent) => handleMove(e); | ||||
|  | ||||
|       window.addEventListener("scroll", handleScroll, { passive: true }); | ||||
|       document.body.addEventListener("pointermove", handlePointerMove, { | ||||
|         passive: true, | ||||
|       }); | ||||
|  | ||||
|       return () => { | ||||
|         if (animationFrameRef.current) { | ||||
|           cancelAnimationFrame(animationFrameRef.current); | ||||
|         } | ||||
|         window.removeEventListener("scroll", handleScroll); | ||||
|         document.body.removeEventListener("pointermove", handlePointerMove); | ||||
|       }; | ||||
|     }, [handleMove, disabled]); | ||||
|  | ||||
|     return ( | ||||
|       <> | ||||
|         <div | ||||
|           className={cn( | ||||
|             "pointer-events-none absolute -inset-px hidden rounded-[inherit] border opacity-0 transition-opacity", | ||||
|             glow && "opacity-100", | ||||
|             variant === "white" && "border-white", | ||||
|             disabled && "!block" | ||||
|           )} | ||||
|         /> | ||||
|         <div | ||||
|           ref={containerRef} | ||||
|           style={ | ||||
|             { | ||||
|               "--blur": `${blur}px`, | ||||
|               "--spread": spread, | ||||
|               "--start": "0", | ||||
|               "--active": "0", | ||||
|               "--glowingeffect-border-width": `${borderWidth}px`, | ||||
|               "--repeating-conic-gradient-times": "5", | ||||
|               "--gradient": | ||||
|                 variant === "white" | ||||
|                   ? `repeating-conic-gradient( | ||||
|                   from 236.84deg at 50% 50%, | ||||
|                   var(--black), | ||||
|                   var(--black) calc(25% / var(--repeating-conic-gradient-times)) | ||||
|                 )` | ||||
|                   : `radial-gradient(circle, #dd7bbb 10%, #dd7bbb00 20%), | ||||
|                 radial-gradient(circle at 40% 40%, #d79f1e 5%, #d79f1e00 15%), | ||||
|                 radial-gradient(circle at 60% 60%, #5a922c 10%, #5a922c00 20%),  | ||||
|                 radial-gradient(circle at 40% 60%, #4c7894 10%, #4c789400 20%), | ||||
|                 repeating-conic-gradient( | ||||
|                   from 236.84deg at 50% 50%, | ||||
|                   #dd7bbb 0%, | ||||
|                   #d79f1e calc(25% / var(--repeating-conic-gradient-times)), | ||||
|                   #5a922c calc(50% / var(--repeating-conic-gradient-times)),  | ||||
|                   #4c7894 calc(75% / var(--repeating-conic-gradient-times)), | ||||
|                   #dd7bbb calc(100% / var(--repeating-conic-gradient-times)) | ||||
|                 )`, | ||||
|             } as React.CSSProperties | ||||
|           } | ||||
|           className={cn( | ||||
|             "pointer-events-none absolute inset-0 rounded-[inherit] opacity-100 transition-opacity", | ||||
|             glow && "opacity-100", | ||||
|             blur > 0 && "blur-[var(--blur)] ", | ||||
|             className, | ||||
|             disabled && "!hidden" | ||||
|           )} | ||||
|         > | ||||
|           <div | ||||
|             className={cn( | ||||
|               "glow", | ||||
|               "rounded-[inherit]", | ||||
|               'after:content-[""] after:rounded-[inherit] after:absolute after:inset-[calc(-1*var(--glowingeffect-border-width))]', | ||||
|               "after:[border:var(--glowingeffect-border-width)_solid_transparent]", | ||||
|               "after:[background:var(--gradient)] after:[background-attachment:fixed]", | ||||
|               "after:opacity-[var(--active)] after:transition-opacity after:duration-300", | ||||
|               "after:[mask-clip:padding-box,border-box]", | ||||
|               "after:[mask-composite:intersect]", | ||||
|               "after:[mask-image:linear-gradient(#0000,#0000),conic-gradient(from_calc((var(--start)-var(--spread))*1deg),#00000000_0deg,#fff,#00000000_calc(var(--spread)*2deg))]" | ||||
|             )} | ||||
|           /> | ||||
|         </div> | ||||
|       </> | ||||
|     ); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| GlowingEffect.displayName = "GlowingEffect"; | ||||
|  | ||||
| export { GlowingEffect }; | ||||
							
								
								
									
										158
									
								
								src/components/ui/glowing-stars.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,158 @@ | ||||
| "use client"; | ||||
|  | ||||
| import React, { useEffect, useRef, useState } from "react"; | ||||
| import { AnimatePresence, motion } from "motion/react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
|  | ||||
| export const GlowingStarsBackgroundCard = ({ | ||||
|   className, | ||||
|   children, | ||||
| }: { | ||||
|   className?: string; | ||||
|   children?: React.ReactNode; | ||||
| }) => { | ||||
|   const [mouseEnter, setMouseEnter] = useState(false); | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       onMouseEnter={() => { | ||||
|         setMouseEnter(true); | ||||
|       }} | ||||
|       onMouseLeave={() => { | ||||
|         setMouseEnter(false); | ||||
|       }} | ||||
|       className={cn( | ||||
|         "bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 max-w-md max-h-[20rem] h-full w-full rounded-xl border border-[#eaeaea] dark:border-neutral-600", | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       <div className="flex justify-center items-center"> | ||||
|         <Illustration mouseEnter={mouseEnter} /> | ||||
|       </div> | ||||
|       <div className="px-2 pb-6">{children}</div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const GlowingStarsDescription = ({ | ||||
|   className, | ||||
|   children, | ||||
| }: { | ||||
|   className?: string; | ||||
|   children?: React.ReactNode; | ||||
| }) => { | ||||
|   return ( | ||||
|     <p className={cn("text-base text-white max-w-[16rem]", className)}> | ||||
|       {children} | ||||
|     </p> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const GlowingStarsTitle = ({ | ||||
|   className, | ||||
|   children, | ||||
| }: { | ||||
|   className?: string; | ||||
|   children?: React.ReactNode; | ||||
| }) => { | ||||
|   return ( | ||||
|     <h2 className={cn("font-bold text-2xl text-[#eaeaea]", className)}> | ||||
|       {children} | ||||
|     </h2> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| export const Illustration = ({ mouseEnter }: { mouseEnter: boolean }) => { | ||||
|   const stars = 108; | ||||
|   const columns = 18; | ||||
|  | ||||
|   const [glowingStars, setGlowingStars] = useState<number[]>([]); | ||||
|  | ||||
|   const highlightedStars = useRef<number[]>([]); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     const interval = setInterval(() => { | ||||
|       highlightedStars.current = Array.from({ length: 5 }, () => | ||||
|         Math.floor(Math.random() * stars) | ||||
|       ); | ||||
|       setGlowingStars([...highlightedStars.current]); | ||||
|     }, 3000); | ||||
|  | ||||
|     return () => clearInterval(interval); | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       className="h-48 p-1 w-full" | ||||
|       style={{ | ||||
|         display: "grid", | ||||
|         gridTemplateColumns: `repeat(${columns}, 1fr)`, | ||||
|         gap: `1px`, | ||||
|       }} | ||||
|     > | ||||
|       {[...Array(stars)].map((_, starIdx) => { | ||||
|         const isGlowing = glowingStars.includes(starIdx); | ||||
|         const delay = (starIdx % 10) * 0.1; | ||||
|         const staticDelay = starIdx * 0.01; | ||||
|         return ( | ||||
|           <div | ||||
|             key={`matrix-col-${starIdx}}`} | ||||
|             className="relative flex items-center justify-center" | ||||
|           > | ||||
|             <Star | ||||
|               isGlowing={mouseEnter ? true : isGlowing} | ||||
|               delay={mouseEnter ? staticDelay : delay} | ||||
|             /> | ||||
|             {mouseEnter && <Glow delay={staticDelay} />} | ||||
|             <AnimatePresence mode="wait"> | ||||
|               {isGlowing && <Glow delay={delay} />} | ||||
|             </AnimatePresence> | ||||
|           </div> | ||||
|         ); | ||||
|       })} | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const Star = ({ isGlowing, delay }: { isGlowing: boolean; delay: number }) => { | ||||
|   return ( | ||||
|     <motion.div | ||||
|       key={delay} | ||||
|       initial={{ | ||||
|         scale: 1, | ||||
|       }} | ||||
|       animate={{ | ||||
|         scale: isGlowing ? [1, 1.2, 2.5, 2.2, 1.5] : 1, | ||||
|         background: isGlowing ? "#fff" : "#666", | ||||
|       }} | ||||
|       transition={{ | ||||
|         duration: 2, | ||||
|         ease: "easeInOut", | ||||
|         delay: delay, | ||||
|       }} | ||||
|       className={cn("bg-[#666] h-[1px] w-[1px] rounded-full relative z-20")} | ||||
|     ></motion.div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const Glow = ({ delay }: { delay: number }) => { | ||||
|   return ( | ||||
|     <motion.div | ||||
|       initial={{ | ||||
|         opacity: 0, | ||||
|       }} | ||||
|       animate={{ | ||||
|         opacity: 1, | ||||
|       }} | ||||
|       transition={{ | ||||
|         duration: 2, | ||||
|         ease: "easeInOut", | ||||
|         delay: delay, | ||||
|       }} | ||||
|       exit={{ | ||||
|         opacity: 0, | ||||
|       }} | ||||
|       className="absolute  left-1/2 -translate-x-1/2 z-10 h-[4px] w-[4px] rounded-full bg-blue-500 blur-[1px] shadow-2xl shadow-blue-400" | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										104
									
								
								src/components/ui/lamp.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,104 @@ | ||||
| "use client"; | ||||
| import React from "react"; | ||||
| import { motion } from "motion/react"; | ||||
| import { cn } from "@/lib/utils"; | ||||
|  | ||||
| export default function LampDemo() { | ||||
|   return ( | ||||
|     <LampContainer> | ||||
|       <motion.h1 | ||||
|         initial={{ opacity: 0.5, y: 100 }} | ||||
|         whileInView={{ opacity: 1, y: 0 }} | ||||
|         transition={{ | ||||
|           delay: 0.3, | ||||
|           duration: 0.8, | ||||
|           ease: "easeInOut", | ||||
|         }} | ||||
|         className="mt-8 bg-gradient-to-br from-gray-300 to-gray-500 py-4 bg-clip-text text-center text-4xl font-medium tracking-tight text-transparent md:text-7xl" | ||||
|       > | ||||
|         Build lamps <br /> the right way | ||||
|       </motion.h1> | ||||
|     </LampContainer> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export const LampContainer = ({ | ||||
|   children, | ||||
|   className, | ||||
| }: { | ||||
|   children: React.ReactNode; | ||||
|   className?: string; | ||||
| }) => { | ||||
|   return ( | ||||
|     <div | ||||
|       className={cn( | ||||
|         "relative flex h-screen flex-col items-center justify-start pt-32 overflow-hidden bg-gray-950 w-full z-0", | ||||
|         className | ||||
|       )} | ||||
|     > | ||||
|       <div className="relative flex w-full flex-1 scale-y-125 items-center justify-center isolate z-0 "> | ||||
|         <motion.div | ||||
|           initial={{ opacity: 0.5, width: "15rem" }} | ||||
|           whileInView={{ opacity: 1, width: "30rem" }} | ||||
|           transition={{ | ||||
|             delay: 0.5, | ||||
|             duration: 1.0, | ||||
|             ease: "easeInOut", | ||||
|           }} | ||||
|           style={{ | ||||
|             backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`, | ||||
|           }} | ||||
|           className="absolute inset-auto right-1/2 h-56 overflow-visible w-[30rem] bg-gradient-conic from-cyan-500 via-transparent to-transparent text-white [--conic-position:from_70deg_at_center_top]" | ||||
|         > | ||||
|           <div className="absolute  w-[100%] left-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" /> | ||||
|           <div className="absolute  w-40 h-[100%] left-0 bg-gray-950  bottom-0 z-20 [mask-image:linear-gradient(to_right,white,transparent)]" /> | ||||
|         </motion.div> | ||||
|         <motion.div | ||||
|           initial={{ opacity: 0.5, width: "15rem" }} | ||||
|           whileInView={{ opacity: 1, width: "30rem" }} | ||||
|           transition={{ | ||||
|             delay: 0.3, | ||||
|             duration: 0.8, | ||||
|             ease: "easeInOut", | ||||
|           }} | ||||
|           style={{ | ||||
|             backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`, | ||||
|           }} | ||||
|           className="absolute inset-auto left-1/2 h-56 w-[30rem] bg-gradient-conic from-transparent via-transparent to-cyan-500 text-white [--conic-position:from_290deg_at_center_top]" | ||||
|         > | ||||
|           <div className="absolute  w-40 h-[100%] right-0 bg-gray-950  bottom-0 z-20 [mask-image:linear-gradient(to_left,white,transparent)]" /> | ||||
|           <div className="absolute  w-[100%] right-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" /> | ||||
|         </motion.div> | ||||
|         <div className="absolute top-1/2 h-48 w-full translate-y-12 scale-x-150 bg-gray-950 blur-2xl"></div> | ||||
|         <div className="absolute top-1/2 z-50 h-48 w-full bg-transparent opacity-10 backdrop-blur-md"></div> | ||||
|         <div className="absolute inset-auto z-50 h-36 w-[28rem] -translate-y-1/2 rounded-full bg-cyan-500 opacity-50 blur-3xl"></div> | ||||
|         <motion.div | ||||
|           initial={{ width: "8rem" }} | ||||
|           whileInView={{ width: "16rem" }} | ||||
|           transition={{ | ||||
|             delay: 0.3, | ||||
|             duration: 0.8, | ||||
|             ease: "easeInOut", | ||||
|           }} | ||||
|           className="absolute inset-auto z-30 h-36 w-64 -translate-y-[6rem] rounded-full bg-cyan-400 blur-2xl" | ||||
|         ></motion.div> | ||||
|         <motion.div | ||||
|           initial={{ width: "15rem" }} | ||||
|           whileInView={{ width: "30rem" }} | ||||
|           transition={{ | ||||
|             delay: 0.3, | ||||
|             duration: 0.8, | ||||
|             ease: "easeInOut", | ||||
|           }} | ||||
|           className="absolute inset-auto z-50 h-0.5 w-[30rem] -translate-y-[7rem] bg-cyan-400 " | ||||
|         ></motion.div> | ||||
|  | ||||
|         <div className="absolute inset-auto z-40 h-44 w-full -translate-y-[12.5rem] bg-gray-950 "></div> | ||||
|       </div> | ||||
|  | ||||
|       <div className="relative z-50 flex -translate-y-32 flex-col items-center px-5"> | ||||
|         {children} | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										58
									
								
								src/components/ui/layout-text-flip.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,58 @@ | ||||
| "use client"; | ||||
| import { useState, useEffect } from "react"; | ||||
| import { motion, AnimatePresence } from "framer-motion"; | ||||
| import { cn } from "@/lib/utils"; | ||||
|  | ||||
| export const LayoutTextFlip = ({ | ||||
|   text = "Build Amazing", | ||||
|   words = ["Landing Pages", "Component Blocks", "Page Sections", "3D Shaders"], | ||||
|   duration = 1500, | ||||
| }: { | ||||
|   text: string; | ||||
|   words: string[]; | ||||
|   duration?: number; | ||||
| }) => { | ||||
|   const [currentIndex, setCurrentIndex] = useState(0); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     const interval = setInterval(() => { | ||||
|       setCurrentIndex((prevIndex) => (prevIndex + 1) % words.length); | ||||
|     }, duration); | ||||
|  | ||||
|     return () => clearInterval(interval); | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <motion.span | ||||
|         layoutId="subtext" | ||||
|         className="text-2xl font-bold tracking-tight drop-shadow-lg md:text-4xl" | ||||
|       > | ||||
|         {text} | ||||
|       </motion.span> | ||||
|  | ||||
|       <motion.span | ||||
|         layout | ||||
|         className="relative w-fit overflow-hidden px-2 py-2 font-medium italic tracking-tight" | ||||
|       > | ||||
|         <AnimatePresence mode="popLayout"> | ||||
|           <motion.span | ||||
|             key={currentIndex} | ||||
|             initial={{ y: -40, filter: "blur(10px)" }} | ||||
|             animate={{ | ||||
|               y: 0, | ||||
|               filter: "blur(0px)", | ||||
|             }} | ||||
|             exit={{ y: 50, filter: "blur(10px)", opacity: 0 }} | ||||
|             transition={{ | ||||
|               duration: 0.5, | ||||
|             }} | ||||
|             className={cn("inline-block whitespace-nowrap")} | ||||
|           > | ||||
|             {words[currentIndex]} | ||||
|           </motion.span> | ||||
|         </AnimatePresence> | ||||
|       </motion.span> | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										119
									
								
								src/components/ui/pointer-highlight.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,119 @@ | ||||
| "use client"; | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { motion } from "motion/react"; | ||||
| import { useRef, useEffect, useState } from "react"; | ||||
|  | ||||
| export function PointerHighlight({ | ||||
|   children, | ||||
|   rectangleClassName, | ||||
|   pointerClassName, | ||||
|   containerClassName, | ||||
| }: { | ||||
|   children: React.ReactNode; | ||||
|   rectangleClassName?: string; | ||||
|   pointerClassName?: string; | ||||
|   containerClassName?: string; | ||||
| }) { | ||||
|   const containerRef = useRef<HTMLDivElement>(null); | ||||
|   const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (containerRef.current) { | ||||
|       const { width, height } = containerRef.current.getBoundingClientRect(); | ||||
|       setDimensions({ width, height }); | ||||
|     } | ||||
|  | ||||
|     const resizeObserver = new ResizeObserver((entries) => { | ||||
|       for (const entry of entries) { | ||||
|         const { width, height } = entry.contentRect; | ||||
|         setDimensions({ width, height }); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     if (containerRef.current) { | ||||
|       resizeObserver.observe(containerRef.current); | ||||
|     } | ||||
|  | ||||
|     return () => { | ||||
|       if (containerRef.current) { | ||||
|         resizeObserver.unobserve(containerRef.current); | ||||
|       } | ||||
|     }; | ||||
|   }, []); | ||||
|  | ||||
|   return ( | ||||
|     <div | ||||
|       className={cn("relative w-fit", containerClassName)} | ||||
|       ref={containerRef} | ||||
|     > | ||||
|       {children} | ||||
|       {dimensions.width > 0 && dimensions.height > 0 && ( | ||||
|         <motion.div | ||||
|           className="pointer-events-none absolute inset-0 z-0" | ||||
|           initial={{ opacity: 0, scale: 0.95, originX: 0, originY: 0 }} | ||||
|           animate={{ opacity: 1, scale: 1 }} | ||||
|           transition={{ duration: 0.5, ease: "easeOut" }} | ||||
|         > | ||||
|           <motion.div | ||||
|             className={cn( | ||||
|               "absolute inset-0 border border-neutral-800 dark:border-neutral-200", | ||||
|               rectangleClassName, | ||||
|             )} | ||||
|             initial={{ | ||||
|               width: 0, | ||||
|               height: 0, | ||||
|             }} | ||||
|             whileInView={{ | ||||
|               width: dimensions.width, | ||||
|               height: dimensions.height, | ||||
|             }} | ||||
|             transition={{ | ||||
|               duration: 1, | ||||
|               ease: "easeInOut", | ||||
|             }} | ||||
|           /> | ||||
|           <motion.div | ||||
|             className="pointer-events-none absolute" | ||||
|             initial={{ opacity: 0 }} | ||||
|             whileInView={{ | ||||
|               opacity: 1, | ||||
|               x: dimensions.width + 4, | ||||
|               y: dimensions.height + 4, | ||||
|             }} | ||||
|             style={{ | ||||
|               rotate: -90, | ||||
|             }} | ||||
|             transition={{ | ||||
|               opacity: { duration: 0.1, ease: "easeInOut" }, | ||||
|               duration: 1, | ||||
|               ease: "easeInOut", | ||||
|             }} | ||||
|           > | ||||
|             <Pointer | ||||
|               className={cn("h-5 w-5 text-blue-500", pointerClassName)} | ||||
|             /> | ||||
|           </motion.div> | ||||
|         </motion.div> | ||||
|       )} | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| const Pointer = ({ ...props }: React.SVGProps<SVGSVGElement>) => { | ||||
|   return ( | ||||
|     <svg | ||||
|       stroke="currentColor" | ||||
|       fill="currentColor" | ||||
|       strokeWidth="1" | ||||
|       strokeLinecap="round" | ||||
|       strokeLinejoin="round" | ||||
|       viewBox="0 0 16 16" | ||||
|       height="1em" | ||||
|       width="1em" | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       {...props} | ||||
|     > | ||||
|       <path d="M14.082 2.182a.5.5 0 0 1 .103.557L8.528 15.467a.5.5 0 0 1-.917-.007L5.57 10.694.803 8.652a.5.5 0 0 1-.006-.916l12.728-5.657a.5.5 0 0 1 .556.103z"></path> | ||||
|     </svg> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										42
									
								
								src/components/ui/son-of-a-glitch-text.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,42 @@ | ||||
| "use client"; | ||||
|  | ||||
| import { cn } from "@/lib/utils"; | ||||
| import { type HTMLAttributes, useEffect, useState } from "react"; | ||||
|  | ||||
| interface SonOfAGlitchProps extends HTMLAttributes<HTMLHeadingElement> { | ||||
|   text: string; | ||||
|   textClassName?: string; | ||||
|   glitchClassName?: string; | ||||
|   showGlitch?: boolean; | ||||
| } | ||||
|  | ||||
| export const SonOfAGlitch = ({ | ||||
|   text, | ||||
|   className, | ||||
|   textClassName, | ||||
|   glitchClassName, | ||||
|   showGlitch = true, | ||||
| }: SonOfAGlitchProps) => { | ||||
|   const [isGlitching, setIsGlitching] = useState(showGlitch); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     setIsGlitching(showGlitch); | ||||
|   }, [showGlitch]); | ||||
|  | ||||
|   return ( | ||||
|     <h1 className={cn("relative font-mono", className)}> | ||||
|       <span | ||||
|         className={cn( | ||||
|           "absolute top-0 left-0 w-full h-full bg-transparent", | ||||
|           isGlitching && | ||||
|             "before:content-[attr(data-text)] before:absolute before:top-0 before:w-full before:h-full before:bg-transparent before:left-[-2px] before:text-red-500 before:overflow-hidden before:animate-glitch-1", | ||||
|           isGlitching && | ||||
|             "after:content-[attr(data-text)] after:absolute after:top-0 after:w-full after:h-full after:bg-transparent after:left-[2px] after:text-blue-500 after:overflow-hidden after:animate-glitch-2", | ||||
|           glitchClassName, | ||||
|         )} | ||||
|         data-text={text} | ||||
|       ></span> | ||||
|       <span className={cn(textClassName)}>{text}</span> | ||||
|     </h1> | ||||
|   ); | ||||
| }; | ||||
							
								
								
									
										166
									
								
								src/components/ui/world-map.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,166 @@ | ||||
| "use client"; | ||||
|  | ||||
| import { useRef } from "react"; | ||||
| import { motion } from "motion/react"; | ||||
| import DottedMap from "dotted-map"; | ||||
|  | ||||
| interface MapProps { | ||||
|   dots?: Array<{ | ||||
|     start: { lat: number; lng: number; label?: string }; | ||||
|     end: { lat: number; lng: number; label?: string }; | ||||
|   }>; | ||||
|   lineColor?: string; | ||||
| } | ||||
|  | ||||
| export default function WorldMap({ | ||||
|   dots = [], | ||||
|   lineColor = "#06b6d4", | ||||
| }: MapProps) { | ||||
|   const svgRef = useRef<SVGSVGElement>(null); | ||||
|   const map = new DottedMap({ height: 100, grid: "diagonal" }); | ||||
|  | ||||
|   const svgMap = map.getSVG({ | ||||
|     radius: 0.22, | ||||
|     color: "#FFFFFF40", // Hardcoded for dark theme | ||||
|     shape: "circle", | ||||
|     backgroundColor: "black", // Hardcoded for dark theme | ||||
|   }); | ||||
|  | ||||
|   const projectPoint = (lat: number, lng: number) => { | ||||
|     const x = (lng + 180) * (800 / 360); | ||||
|     const y = (90 - lat) * (400 / 180); | ||||
|     return { x, y }; | ||||
|   }; | ||||
|  | ||||
|   const createCurvedPath = ( | ||||
|     start: { x: number; y: number }, | ||||
|     end: { x: number; y: number } | ||||
|   ) => { | ||||
|     const midX = (start.x + end.x) / 2; | ||||
|     const midY = Math.min(start.y, end.y) - 50; | ||||
|     return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`; | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <div className="w-full aspect-[2/1] dark:bg-black bg-white rounded-lg  relative font-sans"> | ||||
|       <img | ||||
|         src={`data:image/svg+xml;utf8,${encodeURIComponent(svgMap)}`} | ||||
|         className="h-full w-full [mask-image:linear-gradient(to_bottom,transparent,white_10%,white_90%,transparent)] pointer-events-none select-none" | ||||
|         alt="world map" | ||||
|         height="495" | ||||
|         width="1056" | ||||
|         draggable={false} | ||||
|       /> | ||||
|       <svg | ||||
|         ref={svgRef} | ||||
|         viewBox="0 0 800 400" | ||||
|         className="w-full h-full absolute inset-0 pointer-events-none select-none" | ||||
|       > | ||||
|         {dots.map((dot, i) => { | ||||
|           const startPoint = projectPoint(dot.start.lat, dot.start.lng); | ||||
|           const endPoint = projectPoint(dot.end.lat, dot.end.lng); | ||||
|           return ( | ||||
|             <g key={`path-group-${i}`}> | ||||
|               <motion.path | ||||
|                 d={createCurvedPath(startPoint, endPoint)} | ||||
|                 fill="none" | ||||
|                 stroke="url(#path-gradient)" | ||||
|                 strokeWidth="1" | ||||
|                 initial={{ | ||||
|                   pathLength: 0, | ||||
|                 }} | ||||
|                 animate={{ | ||||
|                   pathLength: 1, | ||||
|                 }} | ||||
|                 transition={{ | ||||
|                   duration: 1, | ||||
|                   delay: 0.5 * i, | ||||
|                   ease: "easeOut", | ||||
|                 }} | ||||
|                 key={`start-upper-${i}`} | ||||
|               ></motion.path> | ||||
|             </g> | ||||
|           ); | ||||
|         })} | ||||
|  | ||||
|         <defs> | ||||
|           <linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> | ||||
|             <stop offset="0%" stopColor="white" stopOpacity="0" /> | ||||
|             <stop offset="5%" stopColor={lineColor} stopOpacity="1" /> | ||||
|             <stop offset="95%" stopColor={lineColor} stopOpacity="1" /> | ||||
|             <stop offset="100%" stopColor="white" stopOpacity="0" /> | ||||
|           </linearGradient> | ||||
|         </defs> | ||||
|  | ||||
|         {dots.map((dot, i) => ( | ||||
|           <g key={`points-group-${i}`}> | ||||
|             <g key={`start-${i}`}> | ||||
|               <circle | ||||
|                 cx={projectPoint(dot.start.lat, dot.start.lng).x} | ||||
|                 cy={projectPoint(dot.start.lat, dot.start.lng).y} | ||||
|                 r="2" | ||||
|                 fill={lineColor} | ||||
|               /> | ||||
|               <circle | ||||
|                 cx={projectPoint(dot.start.lat, dot.start.lng).x} | ||||
|                 cy={projectPoint(dot.start.lat, dot.start.lng).y} | ||||
|                 r="2" | ||||
|                 fill={lineColor} | ||||
|                 opacity="0.5" | ||||
|               > | ||||
|                 <animate | ||||
|                   attributeName="r" | ||||
|                   from="2" | ||||
|                   to="8" | ||||
|                   dur="1.5s" | ||||
|                   begin="0s" | ||||
|                   repeatCount="indefinite" | ||||
|                 /> | ||||
|                 <animate | ||||
|                   attributeName="opacity" | ||||
|                   from="0.5" | ||||
|                   to="0" | ||||
|                   dur="1.5s" | ||||
|                   begin="0s" | ||||
|                   repeatCount="indefinite" | ||||
|                 /> | ||||
|               </circle> | ||||
|             </g> | ||||
|             <g key={`end-${i}`}> | ||||
|               <circle | ||||
|                 cx={projectPoint(dot.end.lat, dot.end.lng).x} | ||||
|                 cy={projectPoint(dot.end.lat, dot.end.lng).y} | ||||
|                 r="2" | ||||
|                 fill={lineColor} | ||||
|               /> | ||||
|               <circle | ||||
|                 cx={projectPoint(dot.end.lat, dot.end.lng).x} | ||||
|                 cy={projectPoint(dot.end.lat, dot.end.lng).y} | ||||
|                 r="2" | ||||
|                 fill={lineColor} | ||||
|                 opacity="0.5" | ||||
|               > | ||||
|                 <animate | ||||
|                   attributeName="r" | ||||
|                   from="2" | ||||
|                   to="8" | ||||
|                   dur="1.5s" | ||||
|                   begin="0s" | ||||
|                   repeatCount="indefinite" | ||||
|                 /> | ||||
|                 <animate | ||||
|                   attributeName="opacity" | ||||
|                   from="0.5" | ||||
|                   to="0" | ||||
|                   dur="1.5s" | ||||
|                   begin="0s" | ||||
|                   repeatCount="indefinite" | ||||
|                 /> | ||||
|               </circle> | ||||
|             </g> | ||||
|           </g> | ||||
|         ))} | ||||
|       </svg> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/hooks/use-outside-click.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | ||||
| import React, { useEffect } from "react"; | ||||
|  | ||||
| export const useOutsideClick = ( | ||||
|   ref: React.RefObject<HTMLDivElement>, | ||||
|   callback: Function | ||||
| ) => { | ||||
|   useEffect(() => { | ||||
|     const listener = (event: any) => { | ||||
|       if (!ref.current || ref.current.contains(event.target)) { | ||||
|         return; | ||||
|       } | ||||
|       callback(event); | ||||
|     }; | ||||
|  | ||||
|     document.addEventListener("mousedown", listener); | ||||
|     document.addEventListener("touchstart", listener); | ||||
|  | ||||
|     return () => { | ||||
|       document.removeEventListener("mousedown", listener); | ||||
|       document.removeEventListener("touchstart", listener); | ||||
|     }; | ||||
|   }, [ref, callback]); | ||||
| }; | ||||
							
								
								
									
										24
									
								
								src/hooks/use-outside-click.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,24 @@ | ||||
| import React, { useEffect } from "react"; | ||||
|  | ||||
| export const useOutsideClick = ( | ||||
|   ref: React.RefObject<HTMLDivElement>, | ||||
|   callback: Function | ||||
| ) => { | ||||
|   useEffect(() => { | ||||
|     const listener = (event: any) => { | ||||
|       // DO NOTHING if the element being clicked is the target element or their children | ||||
|       if (!ref.current || ref.current.contains(event.target)) { | ||||
|         return; | ||||
|       } | ||||
|       callback(event); | ||||
|     }; | ||||
|  | ||||
|     document.addEventListener("mousedown", listener); | ||||
|     document.addEventListener("touchstart", listener); | ||||
|  | ||||
|     return () => { | ||||
|       document.removeEventListener("mousedown", listener); | ||||
|       document.removeEventListener("touchstart", listener); | ||||
|     }; | ||||
|   }, [ref, callback]); | ||||
| }; | ||||
							
								
								
									
										
											BIN
										
									
								
								src/images/agent1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 27 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/cloudlayer.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 133 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/kubernetes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1024 KiB | 
| Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 66 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/logos/logo_1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 22 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/logos/pmyceliumlogo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.0 KiB | 
| @@ -214,7 +214,7 @@ | ||||
|     </filter> | ||||
|     <radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" | ||||
|       gradientTransform="matrix(0 727 -642 0 184 1)"> | ||||
|       <stop stop-color="#FAFAFA" /> | ||||
|       <stop stop-color="#FFFFFF" /> | ||||
|       <stop offset="1" stop-color="#E6E6E6" /> | ||||
|     </radialGradient> | ||||
|     <radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" | ||||
|   | ||||
| Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/slider/agent1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 30 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/slider/cloud1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 37 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/slider/compute1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 36 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/slider/gpu1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 35 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/slider/network1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 26 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/slider/storage1.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 32 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/images/storage.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 MiB | 
| @@ -1,5 +1,8 @@ | ||||
| @import url('https://fonts.googleapis.com/css2?family=Neuton&display=swap'); | ||||
| @import url('https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap'); | ||||
|  | ||||
| :root { | ||||
|   font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; | ||||
|   font-family: 'Mulish', system-ui, Avenir, Helvetica, Arial, sans-serif; | ||||
|   line-height: 1.5; | ||||
|   font-weight: 400; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { type ClassValue, clsx } from 'clsx' | ||||
| import { twMerge } from 'tailwind-merge' | ||||
| import { clsx, type ClassValue } from "clsx" | ||||
| import { twMerge } from "tailwind-merge" | ||||
|  | ||||
| export function cn(...inputs: ClassValue[]) { | ||||
|   return twMerge(clsx(inputs)) | ||||
|   | ||||
							
								
								
									
										125
									
								
								src/pages/agents/AgentsHero.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,125 @@ | ||||
| 'use client' | ||||
|  | ||||
| import { useId, useState } from 'react' | ||||
|  | ||||
| import { Button } from '../../components/Button' | ||||
| import { Container } from '../../components/Container' | ||||
| import ContactForm from '../../components/ContactForm' | ||||
|  | ||||
| function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) { | ||||
|   const id = useId() | ||||
|  | ||||
|   return ( | ||||
|     <div {...props}> | ||||
|       <svg | ||||
|         viewBox="0 0 1026 1026" | ||||
|         fill="none" | ||||
|         aria-hidden="true" | ||||
|         className="absolute inset-0 h-full w-full animate-spin-slow" | ||||
|       > | ||||
|         <path | ||||
|           d="M1025 513c0 282.77-229.23 512-512 512S1 795.77 1 513 230.23 1 513 1s512 229.23 512 512Z" | ||||
|           stroke="#D4D4D4" | ||||
|           strokeOpacity="0.7" | ||||
|         /> | ||||
|         <path | ||||
|           d="M513 1025C230.23 1025 1 795.77 1 513" | ||||
|           stroke={`url(#${id}-gradient-1)`} | ||||
|           strokeLinecap="round" | ||||
|         /> | ||||
|         <defs> | ||||
|           <linearGradient | ||||
|             id={`${id}-gradient-1`} | ||||
|             x1="1" | ||||
|             y1="513" | ||||
|             x2="1" | ||||
|             y2="1025" | ||||
|             gradientUnits="userSpaceOnUse" | ||||
|           > | ||||
|             <stop stopColor="#06b6d4" /> | ||||
|             <stop offset="1" stopColor="#06b6d4" stopOpacity="0" /> | ||||
|           </linearGradient> | ||||
|         </defs> | ||||
|       </svg> | ||||
|       <svg | ||||
|         viewBox="0 0 1026 1026" | ||||
|         fill="none" | ||||
|         aria-hidden="true" | ||||
|         className="absolute inset-0 h-full w-full animate-spin-reverse-slower" | ||||
|       > | ||||
|         <path | ||||
|           d="M913 513c0 220.914-179.086 400-400 400S113 733.914 113 513s179.086-400 400-400 400 179.086 400 400Z" | ||||
|           stroke="#D4D4D4" | ||||
|           strokeOpacity="0.7" | ||||
|         /> | ||||
|         <path | ||||
|           d="M913 513c0 220.914-179.086 400-400 400" | ||||
|           stroke={`url(#${id}-gradient-2)`} | ||||
|           strokeLinecap="round" | ||||
|         /> | ||||
|         <defs> | ||||
|           <linearGradient | ||||
|             id={`${id}-gradient-2`} | ||||
|             x1="913" | ||||
|             y1="513" | ||||
|             x2="913" | ||||
|             y2="913" | ||||
|             gradientUnits="userSpaceOnUse" | ||||
|           > | ||||
|             <stop stopColor="#06b6d4" /> | ||||
|             <stop offset="1" stopColor="#06b6d4" stopOpacity="0" /> | ||||
|           </linearGradient> | ||||
|         </defs> | ||||
|       </svg> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export function AgentsHero() { | ||||
|   const [isContactOpen, setIsContactOpen] = useState(false) | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <div className="overflow-hidden pb-24 lg:py-32 lg:pb-0"> | ||||
|         <Container> | ||||
|           <div className="flex flex-col-reverse gap-y-16 lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20"> | ||||
|             <div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6"> | ||||
|               <h1 className="text-4xl font-medium tracking-tight text-gray-900 lg:text-6xl"> | ||||
|                 Mycelium Agents | ||||
|               </h1> | ||||
|               <h2 className="mt-6 text-xl leading-normal tracking-tight text-gray-600 lg:text-2xl"> | ||||
|                 Sovereign AI agents, coming soon. | ||||
|               </h2> | ||||
|               <p className="mt-6 text-lg leading-tight text-gray-600 lg:text-xl lg:leading-normal"> | ||||
|                 Hero is the autonomous agent layer for the Mycelium platform—trusted, policy-aware AI that runs on infrastructure you control and remembers what matters. | ||||
|               </p> | ||||
|               <div className="mt-8 flex flex-wrap gap-x-6 gap-y-4"> | ||||
|                 <Button variant="solid" color="cyan" onClick={() => setIsContactOpen(true)}> | ||||
|                   Join the Waitlist | ||||
|                 </Button> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div className="relative mt-0 lg:mt-10 lg:col-span-5 lg:row-span-2 xl:col-span-6"> | ||||
|               <BackgroundIllustration className="absolute left-1/2 top-4 h-[1026px] w-[1026px] -translate-x-1/2 stroke-gray-300/70 sm:top-16 lg:-top-12 lg:ml-12" /> | ||||
|               <div className="mx-auto h-[420px] max-w-[640px] mask-[linear-gradient(to_bottom,white_60%,transparent)] lg:absolute lg:-inset-x-10 lg:-top-24 lg:h-auto lg:pt-10 xl:-bottom-36"> | ||||
|                 <img | ||||
|                   src="/images/agent_icon.png" | ||||
|                   alt="Preview of Hero sovereign agent orchestration" | ||||
|                   className="mx-auto w-full max-w-[520px]" | ||||
|                   width={1024} | ||||
|                   height={1024} | ||||
|                 /> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </Container> | ||||
|       </div> | ||||
|       <ContactForm | ||||
|         isOpen={isContactOpen} | ||||
|         onClose={() => setIsContactOpen(false)} | ||||
|         title="Join the Waitlist" | ||||
|         formType="agent_waitlist" | ||||
|       /> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||