diff --git a/docs/home/products/ai/get-started/capabilities/_mermaid/ai-ivr-flow.mdx b/docs/home/products/ai/get-started/capabilities/_mermaid/ai-ivr-flow.mdx
new file mode 100644
index 0000000..5f3f773
--- /dev/null
+++ b/docs/home/products/ai/get-started/capabilities/_mermaid/ai-ivr-flow.mdx
@@ -0,0 +1,22 @@
+ ```mermaid
+ sequenceDiagram
+ autonumber
+ participant C as Customer
+ participant A as AI Agent
+ C->>A: Calls clinic
+ Note over A: Understands natural speech
+ A->>C: "Hello, how can I help you today?"
+ C->>A: "I need to schedule a cardiology appointment"
+ Note over A: Instantly identifies:
1. Appointment scheduling
2. Specialist type
3. New appointment
+ A->>C: "I can help you schedule that. Would you prefer morning or afternoon appointments?"
+ C->>A: "Morning would be better"
+ Note over A: Processes multiple criteria:
1. Time preference
2. Available slots
3. Doctor availability
+ A->>C: "I have morning slots available with Dr. Smith next Tuesday at 9AM or Thursday at 10AM"
+ C->>A: "What's the cancellation policy?"
+ Note over A: Handles context switch
while maintaining booking flow
+ A->>C: "You can cancel up to 24 hours before your appointment. Would you like one of those morning slots?"
+ C->>A: "Yes, I'll take Tuesday at 9AM"
+ Note over A: Completes booking while
maintaining conversation context
+ A->>C: "Perfect, I've scheduled your cardiology appointment for Tuesday at 9AM with Dr. Smith"
+ Note over C,A: 5 natural exchanges
Handles multiple inputs per turn
Allows questions mid-flow - flexible conversation.
+```
\ No newline at end of file
diff --git a/docs/home/products/ai/get-started/capabilities/_mermaid/ai-support-flow.mdx b/docs/home/products/ai/get-started/capabilities/_mermaid/ai-support-flow.mdx
new file mode 100644
index 0000000..26c9a25
--- /dev/null
+++ b/docs/home/products/ai/get-started/capabilities/_mermaid/ai-support-flow.mdx
@@ -0,0 +1,17 @@
+```mermaid
+sequenceDiagram
+ autonumber
+ participant C as Customer
+ participant A as AI Agent
+
+ Note over A: Support Context
Prompt: General inquiries instructions
+ C->>A: "I need help with my account"
+ A->>C: "I can help with account-related questions"
+ C->>A: "I'm getting API errors"
+ Note over A: New Context
Prompt: Technical support instructions
+ A->>C: "Let me check your API logs..."
+ C->>A: "What about my invoice?"
+ Note over A: New Context
Prompt: Billing support instructions
+ A->>C: "I see your billing history..."
+ Note over C,A: Each topic creates a fresh agent
with specialized knowledge
+```
\ No newline at end of file
diff --git a/docs/home/products/ai/get-started/capabilities/_mermaid/traditional-ivr-flow.mdx b/docs/home/products/ai/get-started/capabilities/_mermaid/traditional-ivr-flow.mdx
new file mode 100644
index 0000000..e75aa8c
--- /dev/null
+++ b/docs/home/products/ai/get-started/capabilities/_mermaid/traditional-ivr-flow.mdx
@@ -0,0 +1,36 @@
+ ```mermaid
+sequenceDiagram
+ autonumber
+ participant C as Customer
+ participant I as IVR System
+ C->>I: Calls clinic
+ Note over I: Start Input Collection
+ I->>C: "Press or say 1 for appointments, 2 for billing..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Selected: Appointments Menu
Start Input Collection
+ I->>C: "Press or say 1 for new appointment, 2 to modify..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Selected: New Appointment Flow
Start Input Collection
+ I->>C: "Press or say 1 for general, 2 for specialist..."
+ C->>I: Presses or says "2"
+ Note over I: End Input Collection
Selected: Specialist Menu
Start Input Collection
+ I->>C: "Press or say 1 for cardiology, 2 for dermatology..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Selected: Cardiology Scheduling
Start Input Collection
+ I->>C: "Press or say 1 for morning slots, 2 for afternoon..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Selected: Morning Slots
Start Input Collection
+ I->>C: "For next Tuesday at 9AM, press or say 1. For Thursday at 10AM, press or say 2..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Selected: Tuesday 9AM
Start Input Collection
+ I->>C: "For cancellation policy, press 1. To confirm appointment, press 2..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Selected: Cancellation Policy
+ I->>C: "You may cancel up to 24 hours before your appointment."
+ Note over I: Start Input Collection
+ I->>C: "To confirm appointment, press 1. To start over, press 2..."
+ C->>I: Presses or says "1"
+ Note over I: End Input Collection
Appointment Confirmed
+ I->>C: "Your appointment is scheduled for Tuesday at 9AM with Dr. Smith."
+ Note over C,I: 9 menu selections • Each question requires new menu • Linear path only • Inflexible conversation
+```
\ No newline at end of file
diff --git a/docs/home/products/ai/get-started/capabilities/_mermaid/traditional-support-flow.mdx b/docs/home/products/ai/get-started/capabilities/_mermaid/traditional-support-flow.mdx
new file mode 100644
index 0000000..9f160aa
--- /dev/null
+++ b/docs/home/products/ai/get-started/capabilities/_mermaid/traditional-support-flow.mdx
@@ -0,0 +1,21 @@
+```mermaid
+sequenceDiagram
+ autonumber
+ participant C as Customer
+ participant G as General Support
+ participant T as Technical Support
+ participant B as Billing Team
+
+ C->>G: "I need account help"
+ G->>C: "I can assist with basic account questions"
+ C->>G: "I'm seeing API errors"
+ G->>C: "Let me transfer you to technical support"
+ Note over G,T: Customer waits in new queue
+ C->>T: "I'm having API issues"
+ T->>C: "I can help with technical problems"
+ C->>T: "What about my invoice?"
+ T->>C: "Let me transfer you to billing"
+ Note over T,B: Customer waits again
+ C->>B: "About my invoice..."
+ Note over C,B: Customer repeats information
+```
\ No newline at end of file
diff --git a/docs/home/products/ai/get-started/capabilities/index.mdx b/docs/home/products/ai/get-started/capabilities/index.mdx
new file mode 100644
index 0000000..6182de2
--- /dev/null
+++ b/docs/home/products/ai/get-started/capabilities/index.mdx
@@ -0,0 +1,298 @@
+---
+title: AI platform capabilities
+slug: /ai/get-started/platform-capabilities
+description: Learn about the capabilities of SignalWire's AI platform, including natural language processing, voice technology, business applications, advanced features, multi-channel intelligence, real-time analytics, and security.
+---
+
+import TraditionalIVRFlow from './_mermaid/traditional-ivr-flow.mdx';
+import AIVRFlow from './_mermaid/ai-ivr-flow.mdx';
+import TraditionalSupportFlow from './_mermaid/traditional-support-flow.mdx';
+import AISupportFlow from './_mermaid/ai-support-flow.mdx';
+
+## Introduction
+
+SignalWire's AI platform is a unified system for building and deploying conversational AI solutions.
+The platform delivers a comprehensive suite of capabilities that work together seamlessly.
+At its core, it provides a single platform for orchestrating voice, video, and messaging channels, complemented by native integrations
+with leading LLM, Text-to-Speech, and Speech-to-Text providers. The system is built on serverless functions that execute with minimal
+latency during live conversations, supported by a multi-threaded architecture for parallel, asynchronous function execution.
+With a global edge network featuring points of presence in every major region and enterprise-grade security, compliance, logging and analytics,
+the platform ensures reliable and secure operations worldwide.
+
+---
+
+## Core capabilities
+
+### Voice technology
+
+SignalWire's voice technology provides comprehensive control over how your AI agents sound and understand speech.
+The platform enables you to select from multiple Text-to-Speech providers and fine-tune voice parameters to perfectly match your brand identity.
+Natural speech fillers
+maintain smooth conversation flow during processing pauses, while real-time audio processing handles noise filtering and accent variations
+with precision.
+
+### Conversation intelligence
+
+SignalWire AI revolutionizes how agents handle complex conversations. Unlike traditional IVR systems that follow rigid
+decision trees, our AI agents operate with natural fluidity. They excel at maintaining their assigned role while juggling multiple
+conversation threads, seamlessly interfacing with backend systems without breaking natural dialogue flow.
+Perhaps most importantly, they can handle unexpected topic changes without losing context, ensuring a more human-like interaction.
+
+The below diagrams illustrate how a customer might schedule a medical appointment using a conventional IVR ("interactive voice response", left), compared to using a SignalWire AI Agent (right).
+
+
+
+
+
+
+
+
+
+
+
+
+For example, when a caller asks "What about the premium version?", the AI understands this refers to a product discussed
+earlier in the conversation. This context awareness extends across different topics and requests within the same interaction, allowing for natural conversation
+flows like:
+
+"I'd like to schedule an appointment" → "What time works for you?" → "Actually, before we do that, what's your cancellation policy?"
+
+The AI handles these context switches seamlessly while maintaining the original intent to schedule an appointment.
+
+### Dynamic context switching
+
+One powerful way to structure conversations is through
+contexts. Instead of transferring callers between departments like a traditional system, your AI agent can switch context internally
+to handle different topics & requests within the same conversation.
+
+Each context operates as an independent entity with its own specialized prompt, fresh conversation memory,
+and focused expertise in areas such as technical support, billing, or sales. This independence is maintained through strict information
+boundaries that ensure clear separation between different roles.
+
+This sophisticated approach enables several key benefits. The system can intelligently route each task to the most appropriate specialized context
+while controlling information flow between contexts. It maintains natural conversation flow during role transitions and implements
+robust security boundaries for sensitive operations. The result is a system that delivers specialized knowledge within appropriate contexts,
+prevents information bleed between different roles, and maintains clear compliance and security boundaries while delivering purpose-built
+responses for each domain.
+
+For example, when a customer moves from technical support to billing questions, the AI swaps context to focus solely on financial
+matters, leaving technical details in the previous context. This isolation maintains security while ensuring each
+interaction benefits from specialized expertise.
+
+Below is an example of a context switch in a customer support scenario for both a traditional IVR and a SignalWire AI Agent.
+
+
+
+
+
+
+
+
+
+
+
+### Real-time analytics and monitoring
+
+The platform provides comprehensive analytics to understand and optimize your AI agents' performance.
+It continuously captures vital metrics including conversation flow and role adherence, speech recognition accuracy, response timing and latency,
+voice quality metrics, and integration performance. This wealth of data flows through a robust webhook system that enables
+real-time conversation monitoring, performance metric tracking, human supervision when needed, and ongoing agent behavior optimization.
+
+Here's how this works in a customer support scenario:
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant C as Customer
+ participant A as AI Agent
+ participant W as Webhook
+
+ C->>A: Asks about product return
+ Note over A: Processes request
+ A->>W: Sends interaction data
+ Note over W: Analytics Processing
+ A->>C: Responds to customer
+ Note over W: Real-time updates:
- Speech confidence: 0.876
- Response time: 850ms
- Language: en-US
+```
+
+#### Webhook data and metrics
+
+Here's an example of the data you receive during an AI interaction:
+
+```json
+{
+ "call_info": {
+ "project_id": "b08dacad...",
+ "content_type": "text/json",
+ "call_id": "b3f4e4e1..."
+ },
+ "conversation_add": {
+ "role": "assistant",
+ "content": "...",
+ "lang": "en-US",
+ "tokens": 53,
+ "latency": 836,
+ "utterance_latency": 934,
+ "audio_latency": 1106
+ },
+ "webhook_reply": {
+ "status": "OK",
+ "request_id": "341de258...",
+ "parameters": {
+ "query": "...",
+ },
+ "data": {...}
+ }
+}
+```
+
+
+#### Management tools
+
+The real-time data enables powerful management capabilities across three key areas:
+
+- **Live Monitoring and Supervision:**
+The platform provides comprehensive real-time monitoring of high-value interactions, with intelligent alerting for critical situations and seamless intervention capabilities when human assistance is required.
+
+- **Performance Optimization:**
+Continuous performance improvement is achieved through AI behavior adjustments based on metrics, dynamic routing rule updates, and refined response pattern optimization.
+
+- **Quality Assurance:**
+The system maintains high service standards by quickly identifying and resolving issues, ensuring compliance requirements are met, and maintaining consistent service quality metrics.
+
+
+---
+
+## Integration & architecture
+
+### External service integration
+
+SignalWire AI connects with your business systems through its function framework. When your agent needs to perform an action - like checking inventory or booking an appointment - it can call functions that interact with your databases and services while keeping the conversation natural.
+
+Here's an example of scheduling a meeting:
+
+```mermaid
+sequenceDiagram
+ autonumber
+
+ participant Customer
+ participant AI
+ participant Functions
+ participant CalAPI as Calendar API
+ participant SMS
+
+ %% Initial Request
+ Customer->>+AI: I need to schedule a meeting with the sales team
+ Note right of AI: Detects scheduling intent
+ AI-->>AI: Identifies scheduling need
+ AI->>-Customer: I can help. When is best for you next week?
+
+ %% Time Selection
+ Customer->>+AI: Tuesday afternoon would be great
+
+ %% Availability Check
+ Note over AI,CalAPI: Checking team availability
+ AI->>+Functions: check_team_availability()
+ Functions->>+CalAPI: Query sales team calendar
+ CalAPI-->>-Functions: Return available slots
+ Functions-->>-AI: Provide available times
+
+ AI->>-Customer: The sales team is free at 2 PM or 4 PM on Tuesday
+ Customer->>+AI: 2 PM works perfectly
+
+ %% Meeting Setup
+ Note over AI,CalAPI: Scheduling the meeting
+ AI->>+Functions: schedule_meeting()
+ Functions->>+CalAPI: Create event and send invites
+ CalAPI-->>-Functions: Confirm booking and invites sent
+
+ %% Notifications
+ Note over Functions,SMS: Sending notifications
+ Functions->>Functions: Trigger SMS notification
+ Functions->>+SMS: Send confirmation text
+ SMS-->>-Functions: Confirm delivery status
+
+ Functions-->>-AI: Confirm meeting setup
+ AI->>-Customer: Meeting scheduled for Tuesday at 2 PM
+ Note over Customer,AI: You'll receive a calendar invite and a confirmation SMS
+```
+
+The process works like this:
+
+1. Your agent recognizes when a request needs external data or actions
+2. It calls the appropriate function
+3. The function handles the technical work with your systems
+4. Your agent incorporates the results naturally into the conversation
+
+This lets you automate complex processes without exposing the technical details to your users.
+
+### SignalWire's RAG stack integration - Datasphere
+
+[Datasphere](/rest/signalwire-rest/endpoints/datasphere/documents) serves as SignalWire's built-in knowledge integration system, providing AI agents with seamless access to your organization's information.
+The system excels at finding and utilizing relevant information during conversations, ensuring responses remain accurate and current by drawing from your latest documentation.
+Every answer is backed by official documentation, providing confidence and reliability in every interaction.
+
+Here's an example of how it works in practice:
+
+```mermaid
+sequenceDiagram
+ autonumber
+ participant Customer
+ participant AI
+ participant Datasphere
+ participant KB as Knowledge Base
+
+ Customer->>AI: "What's covered under my maternity benefits?"
+ AI->>Datasphere: Semantic search for maternity coverage
+ Datasphere->>KB: Query vectorized policy documents
+ KB-->>Datasphere: Return relevant policy sections
+ Datasphere-->>AI: Contextualized coverage details
+ AI->>Customer: Explains specific maternity benefits and coverage limits
+ Customer->>AI: "And what about prenatal care visits?"
+ Note over AI,Datasphere: Maintains context of maternity discussion
+ AI->>Datasphere: Search for prenatal care coverage
+ Datasphere->>KB: Query with maternity context
+ KB-->>Datasphere: Return relevant prenatal care policies
+ Datasphere-->>AI: Contextualized prenatal care info
+ AI->>Customer: Details prenatal visit coverage within maternity benefits
+```
+
+The system offers several key advantages:
+
+- **Always Current:** Your agents automatically use the latest information as you update your documentation
+
+- **Smart Information Use:**
+ - Combines conversation context with document searches
+ - References specific sources
+ - Maintains natural dialogue while using detailed info
+
+- **Flexible Organization:**
+ - Tag documents for easy finding
+ - Choose how to break up information
+ - Search using natural language
+
+- **High Accuracy:**
+ - Grounds responses in your actual documents
+ - Provides sources for information
+ - Keeps responses consistent
+
+To learn more about using Datasphere, see our [Use Datasphere with curl](/rest/signalwire-rest/guides/datasphere/curl-usage) guide.
+
+
+---
+
+## Real-world applications
+
+### Customer service
+
+SignalWire AI transforms the customer service experience by creating intelligent agents that deliver comprehensive support capabilities. These agents are designed to handle complex, multi-step inquiries while maintaining contextual awareness throughout the conversation. They seamlessly integrate with your knowledge base to provide accurate, consistent answers and can connect to your backend systems for real-time problem resolution. When situations require human expertise, the system smoothly facilitates transfers to human agents, ensuring no context is lost in the process.
+
+### Process automation
+
+The platform excels at automating multi-step processes while maintaining natural, fluid interactions. In appointment scheduling scenarios, for example, the AI demonstrates sophisticated capabilities in understanding complex time and date requests, managing multiple calendar systems simultaneously, and handling schedule conflicts with grace. The system proactively sends confirmations and can accommodate changes when needed, all while maintaining a natural conversation flow that feels effortless to the user.
+
+---
+
+## Security and compliance
+
+SignalWire's AI platform incorporates a comprehensive security framework that includes encrypted communications, sophisticated PII detection and protection mechanisms, and dedicated compliance tools for HIPAA and GDPR requirements. The system maintains detailed audit logging capabilities and granular access controls and permissions, enabling you to automate sensitive communications while maintaining strict regulatory compliance.
diff --git a/src/components/Extras/PreviewCard/Card/index.tsx b/src/components/Extras/PreviewCard/Card/index.tsx
new file mode 100644
index 0000000..4217e79
--- /dev/null
+++ b/src/components/Extras/PreviewCard/Card/index.tsx
@@ -0,0 +1,113 @@
+import React, { ReactNode, useState, isValidElement, useMemo, useCallback } from 'react';
+import clsx from 'clsx';
+import styles from './styles.module.css';
+import Modal from '../Modal';
+
+export interface PreviewCardProps {
+ title?: string;
+ description?: string;
+ children: ReactNode;
+ className?: string;
+ thumbnail?: ReactNode;
+}
+
+const CardMetadata = React.memo<{ title?: string; description?: string }>(({ title, description }) => {
+ if (!title && !description) return null;
+
+ return (
+
+ {title &&
{title}
}
+ {description &&
{description}
}
+
+ );
+});
+
+CardMetadata.displayName = 'CardMetadata';
+
+const PreviewCard: React.FC = React.memo(({
+ title,
+ description,
+ children,
+ className,
+ thumbnail,
+}) => {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ const handleOpen = useCallback(() => {
+ setIsModalOpen(true);
+ }, []);
+
+ const handleClose = useCallback(() => {
+ setIsModalOpen(false);
+ }, []);
+
+ const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ setIsModalOpen(true);
+ }
+ }, []);
+
+ // Memoize the rendered content
+ const renderedContent = useMemo(() => {
+ const renderComponent = (component: ReactNode, key: string) => {
+ if (isValidElement(component)) {
+ const ComponentType = component.type;
+ const { key: _, ...componentProps } = component.props;
+
+ return (
+
+ );
+ }
+ return component;
+ };
+
+ const previewContent = thumbnail ? renderComponent(thumbnail, 'preview-thumbnail') : renderComponent(children, 'preview-instance');
+ const modalContent = renderComponent(children, 'modal-instance');
+
+ return {
+ preview: (
+
+ {previewContent}
+
+ ),
+ modal: modalContent
+ };
+ }, [children, thumbnail]);
+
+ const cardTitle = title || 'Preview content';
+ const ariaLabel = `${cardTitle}. Click to open an expanded view of the content.`;
+
+ return (
+ <>
+
+
+ {renderedContent.preview}
+
+
+
+
+
+ {renderedContent.modal}
+
+ >
+ );
+});
+
+PreviewCard.displayName = 'PreviewCard';
+
+export default PreviewCard;
\ No newline at end of file
diff --git a/src/components/Extras/PreviewCard/Card/styles.module.css b/src/components/Extras/PreviewCard/Card/styles.module.css
new file mode 100644
index 0000000..f939711
--- /dev/null
+++ b/src/components/Extras/PreviewCard/Card/styles.module.css
@@ -0,0 +1,111 @@
+/* Preview card styles */
+.previewCard {
+ flex-direction: column;
+ background: var(--ifm-card-background-color);
+ border-radius: 0.75rem;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ cursor: zoom-in;
+ padding: 0;
+ border: 1px solid var(--sw-card-border-color);
+ width: 100%;
+ display: flex;
+ margin-bottom: 2rem;
+ position: relative;
+}
+
+/* When inside a PreviewCardGroup, make the card take full height */
+:global(.previewCardGroup) .previewCard {
+ height: 100%;
+ margin-bottom: 0;
+ min-height: 100%;
+}
+
+.previewCardItemContent {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ padding: 1.5rem;
+}
+
+/* Preview card content styles */
+.previewCardContent {
+ position: relative;
+ width: 100%;
+ flex: 1;
+ display: flex;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+/* Container for SVG/Mermaid content */
+.previewCardContent > div {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 1rem;
+}
+
+
+.previewCardContent :global(.docusaurus-mermaid-container) {
+ display: contents;
+}
+
+/* Image specific styles */
+.previewCardContent img {
+ width: 100%;
+ height: auto;
+ object-fit: contain;
+ margin-bottom: 3rem;
+}
+
+/* Metadata styles */
+.cardMetadata {
+ border-top: 1px solid var(--sw-card-border-color);
+ margin: 0 -1.5rem;
+ padding: 1rem 1.5rem 0;
+ margin-top: auto;
+ flex-shrink: 0;
+}
+
+.previewCardTitle {
+ margin: 0;
+ font-size: 1.1rem;
+ line-height: 1.2;
+ flex-shrink: 0;
+}
+
+.previewCardDescription {
+ margin: 0.25rem 0 0 0;
+ font-size: 1rem;
+ font-weight: normal;
+ color: var(--ifm-font-color-base);
+ line-height: 1.4;
+ flex-shrink: 0;
+}
+
+/* Dark mode styles */
+[data-theme="dark"] .previewCard {
+ background: color-mix(in srgb, var(--ifm-card-background-color) 40%, transparent);
+}
+
+/* Hover effects */
+[data-theme="light"] .previewCard:hover {
+ border-color: var(--sw-link-color);
+ transform: translateY(-4px);
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
+}
+
+[data-theme="dark"] .previewCard:hover {
+ border-color: transparent;
+ background-image: linear-gradient(
+ var(--ifm-card-background-color),
+ var(--ifm-card-background-color)
+ ),
+ var(--sw-horizontal-active-line-color);
+ background-origin: border-box;
+ background-clip: padding-box, border-box;
+ transform: scale(1.01);
+}
\ No newline at end of file
diff --git a/src/components/Extras/PreviewCard/Group/index.tsx b/src/components/Extras/PreviewCard/Group/index.tsx
new file mode 100644
index 0000000..49086fc
--- /dev/null
+++ b/src/components/Extras/PreviewCard/Group/index.tsx
@@ -0,0 +1,31 @@
+import React, { ReactNode } from 'react';
+import clsx from 'clsx';
+import styles from './styles.module.css';
+
+export interface PreviewCardGroupProps {
+ children: ReactNode;
+ columns?: 1 | 2 | 3 | 4;
+ className?: string;
+}
+
+const PreviewCardGroup: React.FC = ({
+ children,
+ columns = 2,
+ className,
+}) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default PreviewCardGroup;
\ No newline at end of file
diff --git a/src/components/Extras/PreviewCard/Group/styles.module.css b/src/components/Extras/PreviewCard/Group/styles.module.css
new file mode 100644
index 0000000..da6d399
--- /dev/null
+++ b/src/components/Extras/PreviewCard/Group/styles.module.css
@@ -0,0 +1,25 @@
+.previewCardGroup {
+ display: grid;
+ width: 100%;
+ gap: 1rem;
+ margin: 2rem 0;
+}
+
+
+@media (min-width: 640px) {
+ .cols1 {
+ grid-template-columns: 1fr;
+ }
+
+ .cols2 {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .cols3 {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .cols4 {
+ grid-template-columns: repeat(4, 1fr);
+ }
+}
\ No newline at end of file
diff --git a/src/components/Extras/PreviewCard/Modal/index.tsx b/src/components/Extras/PreviewCard/Modal/index.tsx
new file mode 100644
index 0000000..4e3df8c
--- /dev/null
+++ b/src/components/Extras/PreviewCard/Modal/index.tsx
@@ -0,0 +1,109 @@
+import React, { ReactNode, useEffect, isValidElement, useRef, useState } from 'react';
+import { createPortal } from 'react-dom';
+import Mermaid from '@theme/Mermaid';
+import BrowserOnly from '@docusaurus/BrowserOnly';
+import clsx from 'clsx';
+import styles from './styles.module.css';
+
+export interface ModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ children: ReactNode;
+}
+
+const ModalContent: React.FC = ({ isOpen, onClose, children }) => {
+ const overlayRef = useRef(null);
+ const contentRef = useRef(null);
+ const [isRendered, setIsRendered] = useState(false);
+ const [isAnimating, setIsAnimating] = useState(false);
+
+ useEffect(() => {
+ let animationFrame: number;
+ let timeout: NodeJS.Timeout;
+
+ if (isOpen) {
+ setIsRendered(true);
+
+ timeout = setTimeout(() => {
+ animationFrame = requestAnimationFrame(() => {
+ setIsAnimating(true);
+ contentRef.current?.focus();
+ });
+ }, 10);
+
+ document.body.style.overflow = 'hidden';
+
+ const handleEscape = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') {
+ onClose();
+ }
+ };
+ document.addEventListener('keydown', handleEscape);
+
+ return () => {
+ document.body.style.overflow = 'unset';
+ document.removeEventListener('keydown', handleEscape);
+ cancelAnimationFrame(animationFrame);
+ clearTimeout(timeout);
+ };
+ } else {
+ setIsAnimating(false);
+ timeout = setTimeout(() => {
+ setIsRendered(false);
+ }, 200);
+ return () => clearTimeout(timeout);
+ }
+ }, [isOpen, onClose]);
+
+ if (!isRendered) return null;
+
+ const modalContent = (
+
+
e.stopPropagation()}
+ tabIndex={-1}
+ >
+
+
Modal Content
+ {children}
+
+
+
+ );
+
+ return createPortal(modalContent, document.body);
+};
+
+const Modal: React.FC = React.memo((props) => {
+ return (
+
+ {() => }
+
+ );
+});
+
+Modal.displayName = 'PreviewCardModal';
+
+export default Modal;
\ No newline at end of file
diff --git a/src/components/Extras/PreviewCard/Modal/styles.module.css b/src/components/Extras/PreviewCard/Modal/styles.module.css
new file mode 100644
index 0000000..554c477
--- /dev/null
+++ b/src/components/Extras/PreviewCard/Modal/styles.module.css
@@ -0,0 +1,116 @@
+/* Modal styles */
+.modalOverlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ backdrop-filter: blur(0);
+ transform: translateZ(0);
+ will-change: opacity, backdrop-filter;
+ transition: background-color 0.2s ease-out, backdrop-filter 0.2s ease-out;
+ pointer-events: none;
+}
+
+/* Utility class for screen reader only content */
+.visuallyHidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.modalVisible {
+ background-color: rgba(0, 0, 0, 0.75);
+ backdrop-filter: blur(2px);
+ pointer-events: all;
+}
+
+.modalContent {
+ position: relative;
+ width: 95vw;
+ height: 95vh;
+ background: var(--ifm-background-surface-color);
+ border: 1px solid var(--sw-card-border-color);
+ border-radius: var(--ifm-card-border-radius);
+ transform: translateZ(0) scale(0.95);
+ opacity: 0;
+ will-change: transform, opacity;
+ backface-visibility: hidden;
+ perspective: 1000px;
+ transition: transform 0.2s ease-out, opacity 0.2s ease-out;
+ overflow: hidden;
+}
+
+.modalContentVisible {
+ transform: translateZ(0) scale(1);
+ opacity: 1;
+}
+
+.modalContentWrapper {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ display: flex;
+ flex-direction: column;
+ -webkit-overflow-scrolling: touch;
+ overscroll-behavior: contain;
+ transform: translateZ(0);
+ will-change: transform;
+ scroll-behavior: auto;
+ cursor: zoom-out;
+ padding: 2rem;
+}
+
+.modalContentWrapper > div {
+ width: 100%;
+ min-height: min-content;
+ display: flex;
+ justify-content: center;
+ margin: auto 0;
+}
+
+/* Image styles in modal */
+.modalContent img {
+ max-width: min(90vw, 1200px);
+ max-height: 85vh;
+ width: auto;
+ height: auto;
+ object-fit: contain;
+ margin: 0 auto;
+ display: block;
+}
+
+.modalClose {
+ position: absolute;
+ top: 1rem;
+ right: 2rem;
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 50%;
+ border: none;
+ background: var(--ifm-color-emphasis-200);
+ font-size: 1.5rem;
+ cursor: pointer;
+ z-index: 10;
+ transform: translateZ(0);
+ will-change: transform, background;
+ transition: background 0.2s ease;
+}
+
+.modalClose:hover {
+ background: var(--ifm-color-emphasis-300);
+ color: var(--ifm-color-emphasis-900);
+}
\ No newline at end of file
diff --git a/src/components/Extras/PreviewCard/index.tsx b/src/components/Extras/PreviewCard/index.tsx
new file mode 100644
index 0000000..f7829e9
--- /dev/null
+++ b/src/components/Extras/PreviewCard/index.tsx
@@ -0,0 +1,4 @@
+export { default as PreviewCard } from './Card';
+export { default as PreviewCardGroup } from './Group';
+export type { PreviewCardProps } from './Card';
+export type { PreviewCardGroupProps } from './Group';
diff --git a/src/components/Extras/Tooltips/index.tsx b/src/components/Extras/Tooltips/index.tsx
new file mode 100644
index 0000000..604cfff
--- /dev/null
+++ b/src/components/Extras/Tooltips/index.tsx
@@ -0,0 +1,64 @@
+import React, { ReactNode, useState } from 'react';
+import { useWindowSize } from '@docusaurus/theme-common';
+import clsx from 'clsx';
+import styles from './styles.module.css';
+
+interface TooltipProps {
+ children: ReactNode;
+ tip: ReactNode;
+ delay?: number;
+}
+
+export default function Tooltips({
+ children,
+ tip,
+ delay = 200
+}: TooltipProps) {
+ const windowSize = useWindowSize();
+ const isMobile = windowSize === 'mobile';
+ const [isVisible, setIsVisible] = useState(false);
+
+ if (!tip) {
+ throw new Error('Tooltip requires a non-empty tip prop');
+ }
+
+ if (!children) {
+ throw new Error('Tooltip requires children to be provided');
+ }
+
+ const handleMouseEnter = () => {
+ setIsVisible(true);
+ };
+
+ const handleMouseLeave = () => {
+ setIsVisible(false);
+ };
+
+ return (
+
+ {children}
+
+ {tip}
+
+
+ );
+}
diff --git a/src/components/Extras/Tooltips/styles.module.css b/src/components/Extras/Tooltips/styles.module.css
new file mode 100644
index 0000000..23e5b0c
--- /dev/null
+++ b/src/components/Extras/Tooltips/styles.module.css
@@ -0,0 +1,114 @@
+.tooltipContainer {
+ position: relative;
+ display: inline-block;
+ cursor: help;
+ font-weight: 600;
+ text-decoration: underline solid;
+ text-decoration-thickness: 0.05rem;
+ text-underline-offset: 4px;
+ text-decoration-color: var(--sw-link-decoration-color);
+ transition: 100ms;
+}
+
+.tooltip {
+ visibility: hidden;
+ position: absolute;
+ z-index: 1;
+ bottom: 125%;
+ left: 50%;
+ transform: translateX(-50%) translateY(5px);
+ padding: 8px 12px;
+ background-color: var(--ifm-background-surface-color);
+ color: var(--ifm-text-color);
+ text-align: left;
+ border-radius: 8px;
+ font-size: 14px;
+ width: max-content;
+ max-width: min(90vw, 400px);
+ white-space: normal;
+ word-wrap: break-word;
+ opacity: 0;
+ transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+ border: 1px solid var(--sw-card-border-color);
+ user-select: text;
+ cursor: text;
+ backdrop-filter: blur(8px);
+ line-height: 1.5;
+ font-weight: normal;
+}
+
+.tooltip::after {
+ content: "";
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -6px;
+ border-width: 6px;
+ border-style: solid;
+ border-color: var(--ifm-background-surface-color) transparent transparent transparent;
+ filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
+}
+
+/* Add a pseudo-element for the arrow border */
+.tooltip::before {
+ content: "";
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -7px;
+ border-width: 7px;
+ border-style: solid;
+ border-color: var(--sw-card-border-color) transparent transparent transparent;
+ z-index: -1;
+}
+
+.tooltip.visible {
+ visibility: visible;
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+}
+
+/* Keep tooltip visible when hovering over it */
+.tooltip:hover {
+ visibility: visible;
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+}
+
+/* Add a delay before hiding */
+.tooltipContainer:not(:hover) .tooltip:not(:hover) {
+ transition-delay: 100ms;
+}
+
+/* Mobile Styles */
+.tooltipMobile {
+ width: auto;
+ max-width: calc(100vw - 32px);
+ min-width: min(250px, calc(100vw - 48px));
+ font-size: 13px;
+ padding: 8px 12px;
+ top: auto;
+ bottom: 125%;
+ left: 50%;
+ transform: translateX(-50%) translateY(-5px);
+ /* Add safety margins from screen edges */
+ margin-left: 16px;
+ margin-right: 16px;
+}
+
+.tooltipMobile::before {
+ bottom: auto;
+ top: 100%;
+ border-color: var(--sw-card-border-color) transparent transparent transparent;
+}
+
+.tooltipMobile::after {
+ bottom: auto;
+ top: 100%;
+ border-color: var(--ifm-background-surface-color) transparent transparent transparent;
+}
+
+.tooltipMobile.visible {
+ transform: translateX(-50%) translateY(0);
+}
\ No newline at end of file
diff --git a/src/css/_common.scss b/src/css/_common.scss
index b05a9fc..1489042 100644
--- a/src/css/_common.scss
+++ b/src/css/_common.scss
@@ -66,8 +66,8 @@
--sw-gradient-border: var(--sw-horizontal-active-line-color);
--sw-card-border-color: var(--ifm-color-gray-800);
- --ifm-background-color: #070c2d !important;
- --ifm-background-surface-color: #0c1339 !important;
+ --ifm-background-color: #070c2d;
+ --ifm-background-surface-color: #0c1339;
background-image: var(--dark-mode-gradient-overlay), var(--dark-mode-bg-pattern);
background-repeat: no-repeat, repeat;
diff --git a/src/theme/MDXComponents/index.js b/src/theme/MDXComponents/index.js
index 4d953de..8260d6d 100644
--- a/src/theme/MDXComponents/index.js
+++ b/src/theme/MDXComponents/index.js
@@ -29,6 +29,11 @@ import CodeGroup from "../../components/Extras/Code";
import { ExpandableItemCoverIcon } from "../../components/Extras/Expandable/ExpandableItemCoverIcon";
import Frame from "../../components/Extras/Frame/Frame";
import Slideshow from "../../components/Extras/Slideshow/Slideshow";
+import Tooltips from "../../components/Extras/Tooltips";
+import { PreviewCardGroup, PreviewCard } from "../../components/Extras/PreviewCard";
+
+
+
export default {
...MDXComponents,
@@ -36,14 +41,11 @@ export default {
Language,
LangItem,
LangSwitch,
-
Frame,
Tabs,
TabItem,
-
Accordion,
AccordionGroup,
-
AlphaBadge,
APITable,
APITableRow,
@@ -56,6 +58,8 @@ export default {
DocCardList,
ExpandableItemCoverIcon,
Frame,
+ PreviewCardGroup,
+ PreviewCard,
HomepageHeader,
LinkBadge,
LinkCard,
@@ -67,4 +71,5 @@ export default {
Subtitle,
UseCaseLinks,
UseCaseView,
+ Tooltips,
};