diff --git a/src/plays/virtual-whiteboard/VirtualWhiteboard.jsx b/src/plays/virtual-whiteboard/VirtualWhiteboard.jsx
new file mode 100644
index 000000000..55b6de29c
--- /dev/null
+++ b/src/plays/virtual-whiteboard/VirtualWhiteboard.jsx
@@ -0,0 +1,202 @@
+import React, { useState, useRef, useCallback } from 'react';
+import PlayHeader from 'common/playlists/PlayHeader';
+import Toolbar from './components/Toolbar';
+import Canvas from './components/Canvas';
+import LayerPanel from './components/LayerPanel';
+import './styles.css';
+
+function VirtualWhiteboard(props) {
+ // Drawing state
+ const [tool, setTool] = useState('pen');
+ const [color, setColor] = useState('#000000');
+ const [lineWidth, setLineWidth] = useState(2);
+ const [fillColor, setFillColor] = useState('#ffffff');
+ const [fontSize, setFontSize] = useState(16);
+
+ // Layer state
+ const [layers, setLayers] = useState([
+ { id: 1, name: 'Layer 1', visible: true, locked: false, data: [] }
+ ]);
+ const [activeLayerId, setActiveLayerId] = useState(1);
+
+ // History state for undo/redo
+ const [history, setHistory] = useState([]);
+ const [historyStep, setHistoryStep] = useState(-1);
+
+ // Refs
+ const canvasRef = useRef(null);
+
+ // Save state to history
+ const saveToHistory = useCallback(() => {
+ const newHistory = history.slice(0, historyStep + 1);
+ newHistory.push(JSON.parse(JSON.stringify(layers)));
+ setHistory(newHistory);
+ setHistoryStep(newHistory.length - 1);
+ }, [history, historyStep, layers]);
+
+ // Undo
+ const handleUndo = useCallback(() => {
+ if (historyStep > 0) {
+ setHistoryStep(historyStep - 1);
+ setLayers(JSON.parse(JSON.stringify(history[historyStep - 1])));
+ }
+ }, [historyStep, history]);
+
+ // Redo
+ const handleRedo = useCallback(() => {
+ if (historyStep < history.length - 1) {
+ setHistoryStep(historyStep + 1);
+ setLayers(JSON.parse(JSON.stringify(history[historyStep + 1])));
+ }
+ }, [historyStep, history]);
+
+ // Layer management
+ const addLayer = () => {
+ const newLayer = {
+ id: Date.now(),
+ name: `Layer ${layers.length + 1}`,
+ visible: true,
+ locked: false,
+ data: []
+ };
+ setLayers([...layers, newLayer]);
+ setActiveLayerId(newLayer.id);
+ };
+
+ const deleteLayer = (layerId) => {
+ if (layers.length === 1) return; // Keep at least one layer
+ const newLayers = layers.filter((layer) => layer.id !== layerId);
+ setLayers(newLayers);
+ if (activeLayerId === layerId) {
+ setActiveLayerId(newLayers[0].id);
+ }
+ };
+
+ const toggleLayerVisibility = (layerId) => {
+ setLayers(
+ layers.map((layer) => (layer.id === layerId ? { ...layer, visible: !layer.visible } : layer))
+ );
+ };
+
+ const toggleLayerLock = (layerId) => {
+ setLayers(
+ layers.map((layer) => (layer.id === layerId ? { ...layer, locked: !layer.locked } : layer))
+ );
+ };
+
+ const renameLayer = (layerId, newName) => {
+ setLayers(layers.map((layer) => (layer.id === layerId ? { ...layer, name: newName } : layer)));
+ };
+
+ // Update layer data
+ const updateLayerData = (layerId, newData) => {
+ setLayers(layers.map((layer) => (layer.id === layerId ? { ...layer, data: newData } : layer)));
+ };
+
+ // Clear canvas
+ const handleClear = () => {
+ if (window.confirm('Clear all layers? This cannot be undone.')) {
+ setLayers([{ id: Date.now(), name: 'Layer 1', visible: true, locked: false, data: [] }]);
+ setHistory([]);
+ setHistoryStep(-1);
+ }
+ };
+
+ // Export to image
+ const handleExportImage = () => {
+ if (canvasRef.current) {
+ const canvas = canvasRef.current;
+ const link = document.createElement('a');
+ link.download = `whiteboard-${Date.now()}.png`;
+ link.href = canvas.toDataURL();
+ link.click();
+ }
+ };
+
+ // Export to PDF (using html2canvas approach)
+ const handleExportPDF = async () => {
+ if (canvasRef.current) {
+ try {
+ const canvas = canvasRef.current;
+ const imgData = canvas.toDataURL('image/png');
+
+ // Create a simple PDF export using a new window
+ const pdfWindow = window.open('', '_blank');
+ pdfWindow.document.write(`
+
+
Whiteboard Export
+
+
+
+
+
+ `);
+ pdfWindow.document.close();
+ } catch (error) {
+ console.error('Export failed:', error);
+ alert('Export failed. Please try again.');
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
0}
+ color={color}
+ fillColor={fillColor}
+ fontSize={fontSize}
+ lineWidth={lineWidth}
+ setColor={setColor}
+ setFillColor={setFillColor}
+ setFontSize={setFontSize}
+ setLineWidth={setLineWidth}
+ setTool={setTool}
+ tool={tool}
+ onClear={handleClear}
+ onExportImage={handleExportImage}
+ onExportPDF={handleExportPDF}
+ onRedo={handleRedo}
+ onUndo={handleUndo}
+ />
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default VirtualWhiteboard;
diff --git a/src/plays/virtual-whiteboard/components/Canvas.jsx b/src/plays/virtual-whiteboard/components/Canvas.jsx
new file mode 100644
index 000000000..3e0fb7c01
--- /dev/null
+++ b/src/plays/virtual-whiteboard/components/Canvas.jsx
@@ -0,0 +1,260 @@
+import React, { useEffect, useState, forwardRef } from 'react';
+
+const Canvas = forwardRef(
+ (
+ {
+ layers,
+ activeLayerId,
+ tool,
+ color,
+ lineWidth,
+ fillColor,
+ fontSize,
+ updateLayerData,
+ saveToHistory
+ },
+ ref
+ ) => {
+ const [isDrawing, setIsDrawing] = useState(false);
+ const [startPos, setStartPos] = useState({ x: 0, y: 0 });
+ const [currentShape, setCurrentShape] = useState(null);
+
+ const activeLayer = layers.find((layer) => layer.id === activeLayerId);
+
+ useEffect(() => {
+ const canvas = ref.current;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Draw all visible layers
+ layers.forEach((layer) => {
+ if (layer.visible) {
+ layer.data.forEach((item) => {
+ drawItem(ctx, item);
+ });
+ }
+ });
+
+ // Draw current shape being created
+ if (currentShape) {
+ drawItem(ctx, currentShape);
+ }
+ }, [layers, currentShape]);
+
+ // Keyboard shortcuts
+ useEffect(() => {
+ const handleKeyDown = (e) => {
+ if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
+ e.preventDefault();
+ // Undo handled in parent
+ }
+ if ((e.ctrlKey || e.metaKey) && e.key === 'y') {
+ e.preventDefault();
+ // Redo handled in parent
+ }
+ };
+
+ window.addEventListener('keydown', handleKeyDown);
+
+ return () => window.removeEventListener('keydown', handleKeyDown);
+ }, []);
+
+ const drawItem = (ctx, item) => {
+ ctx.strokeStyle = item.color;
+ ctx.lineWidth = item.lineWidth;
+ ctx.fillStyle = item.fillColor || 'transparent';
+ ctx.lineCap = 'round';
+ ctx.lineJoin = 'round';
+
+ switch (item.type) {
+ case 'pen':
+ ctx.beginPath();
+ item.points.forEach((point, index) => {
+ if (index === 0) {
+ ctx.moveTo(point.x, point.y);
+ } else {
+ ctx.lineTo(point.x, point.y);
+ }
+ });
+ ctx.stroke();
+
+ break;
+
+ case 'eraser':
+ ctx.save();
+ ctx.globalCompositeOperation = 'destination-out';
+ ctx.beginPath();
+ item.points.forEach((point, index) => {
+ if (index === 0) {
+ ctx.moveTo(point.x, point.y);
+ } else {
+ ctx.lineTo(point.x, point.y);
+ }
+ });
+ ctx.lineWidth = item.lineWidth * 2;
+ ctx.stroke();
+ ctx.restore();
+
+ break;
+
+ case 'line':
+ ctx.beginPath();
+ ctx.moveTo(item.startX, item.startY);
+ ctx.lineTo(item.endX, item.endY);
+ ctx.stroke();
+
+ break;
+
+ case 'rectangle': {
+ const width = item.endX - item.startX;
+ const height = item.endY - item.startY;
+ if (item.fillColor && item.fillColor !== '#ffffff') {
+ ctx.fillRect(item.startX, item.startY, width, height);
+ }
+ ctx.strokeRect(item.startX, item.startY, width, height);
+
+ break;
+ }
+
+ case 'circle': {
+ const radius = Math.sqrt(
+ Math.pow(item.endX - item.startX, 2) + Math.pow(item.endY - item.startY, 2)
+ );
+ ctx.beginPath();
+ ctx.arc(item.startX, item.startY, radius, 0, 2 * Math.PI);
+ if (item.fillColor && item.fillColor !== '#ffffff') {
+ ctx.fill();
+ }
+ ctx.stroke();
+
+ break;
+ }
+
+ case 'text':
+ ctx.font = `${item.fontSize}px Arial`;
+ ctx.fillStyle = item.color;
+ ctx.fillText(item.text, item.x, item.y);
+
+ break;
+
+ default:
+ break;
+ }
+ };
+
+ const getMousePos = (e) => {
+ const canvas = ref.current;
+ const rect = canvas.getBoundingClientRect();
+
+ return {
+ x: e.clientX - rect.left,
+ y: e.clientY - rect.top
+ };
+ };
+
+ const handleMouseDown = (e) => {
+ if (!activeLayer || activeLayer.locked) return;
+
+ const pos = getMousePos(e);
+ setIsDrawing(true);
+ setStartPos(pos);
+
+ if (tool === 'pen' || tool === 'eraser') {
+ const newItem = {
+ type: tool,
+ color: color,
+ lineWidth: lineWidth,
+ points: [pos]
+ };
+ setCurrentShape(newItem);
+ } else if (tool === 'text') {
+ const text = prompt('Enter text:');
+ if (text) {
+ const newItem = {
+ type: 'text',
+ text: text,
+ x: pos.x,
+ y: pos.y,
+ color: color,
+ fontSize: fontSize
+ };
+ const newData = [...activeLayer.data, newItem];
+ updateLayerData(activeLayerId, newData);
+ saveToHistory();
+ }
+ }
+ };
+
+ const handleMouseMove = (e) => {
+ if (!isDrawing || !activeLayer || activeLayer.locked) return;
+
+ const pos = getMousePos(e);
+
+ if (tool === 'pen' || tool === 'eraser') {
+ setCurrentShape((prev) => ({
+ ...prev,
+ points: [...prev.points, pos]
+ }));
+ } else if (tool === 'line' || tool === 'rectangle' || tool === 'circle') {
+ setCurrentShape({
+ type: tool,
+ startX: startPos.x,
+ startY: startPos.y,
+ endX: pos.x,
+ endY: pos.y,
+ color: color,
+ lineWidth: lineWidth,
+ fillColor: fillColor
+ });
+ }
+ };
+
+ const handleMouseUp = () => {
+ if (!isDrawing || !currentShape || !activeLayer || activeLayer.locked) {
+ setIsDrawing(false);
+ setCurrentShape(null);
+
+ return;
+ }
+
+ const newData = [...activeLayer.data, currentShape];
+ updateLayerData(activeLayerId, newData);
+ saveToHistory();
+
+ setIsDrawing(false);
+ setCurrentShape(null);
+ };
+
+ const handleMouseLeave = () => {
+ if (isDrawing) {
+ handleMouseUp();
+ }
+ };
+
+ return (
+
+
+ {activeLayer && activeLayer.locked && (
+
+ )}
+
+ );
+ }
+);
+
+Canvas.displayName = 'Canvas';
+
+export default Canvas;
diff --git a/src/plays/virtual-whiteboard/components/LayerPanel.jsx b/src/plays/virtual-whiteboard/components/LayerPanel.jsx
new file mode 100644
index 000000000..a8d4e35c8
--- /dev/null
+++ b/src/plays/virtual-whiteboard/components/LayerPanel.jsx
@@ -0,0 +1,146 @@
+import React, { useState } from 'react';
+import { FaPlus, FaTrash, FaEye, FaEyeSlash, FaLock, FaUnlock, FaEdit } from 'react-icons/fa';
+
+const LayerPanel = ({
+ layers,
+ activeLayerId,
+ setActiveLayerId,
+ onAddLayer,
+ onDeleteLayer,
+ onToggleVisibility,
+ onToggleLock,
+ onRenameLayer
+}) => {
+ const [editingLayerId, setEditingLayerId] = useState(null);
+ const [editName, setEditName] = useState('');
+
+ const handleStartEdit = (layer) => {
+ setEditingLayerId(layer.id);
+ setEditName(layer.name);
+ };
+
+ const handleFinishEdit = (layerId) => {
+ if (editName.trim()) {
+ onRenameLayer(layerId, editName.trim());
+ }
+ setEditingLayerId(null);
+ setEditName('');
+ };
+
+ const handleKeyDown = (e, layerId) => {
+ if (e.key === 'Enter') {
+ handleFinishEdit(layerId);
+ } else if (e.key === 'Escape') {
+ setEditingLayerId(null);
+ setEditName('');
+ }
+ };
+
+ return (
+
+
+
Layers
+
+
+
+
+ {[...layers].reverse().map((layer) => (
+
!layer.locked && setActiveLayerId(layer.id)}
+ >
+
+
+
+
+
+
+
+ {editingLayerId === layer.id ? (
+
handleFinishEdit(layer.id)}
+ onChange={(e) => setEditName(e.target.value)}
+ onClick={(e) => e.stopPropagation()}
+ onKeyDown={(e) => handleKeyDown(e, layer.id)}
+ />
+ ) : (
+
{layer.name}
+ )}
+
+
+ {layer.data.length} object{layer.data.length !== 1 ? 's' : ''}
+
+
+
+
+
+
+ {layers.length > 1 && (
+
+ )}
+
+
+ ))}
+
+
+
+
Layer Tips
+
+ - Click a layer to make it active
+ - Use 👁️ to show/hide layers
+ - Use 🔒 to lock/unlock layers
+ - Locked layers can't be edited
+
+
+
+ );
+};
+
+export default LayerPanel;
diff --git a/src/plays/virtual-whiteboard/components/Toolbar.jsx b/src/plays/virtual-whiteboard/components/Toolbar.jsx
new file mode 100644
index 000000000..44125eb2c
--- /dev/null
+++ b/src/plays/virtual-whiteboard/components/Toolbar.jsx
@@ -0,0 +1,162 @@
+import React from 'react';
+import {
+ FaPen,
+ FaEraser,
+ FaSquare,
+ FaCircle,
+ FaArrowRight,
+ FaFont,
+ FaUndo,
+ FaRedo,
+ FaTrash,
+ FaDownload,
+ FaFilePdf
+} from 'react-icons/fa';
+
+const Toolbar = ({
+ tool,
+ setTool,
+ color,
+ setColor,
+ lineWidth,
+ setLineWidth,
+ fillColor,
+ setFillColor,
+ fontSize,
+ setFontSize,
+ onUndo,
+ onRedo,
+ onClear,
+ onExportImage,
+ onExportPDF,
+ canUndo,
+ canRedo
+}) => {
+ const tools = [
+ { id: 'pen', icon: , label: 'Pen' },
+ { id: 'eraser', icon: , label: 'Eraser' },
+ { id: 'line', icon: , label: 'Line' },
+ { id: 'rectangle', icon: , label: 'Rectangle' },
+ { id: 'circle', icon: , label: 'Circle' },
+ { id: 'text', icon: , label: 'Text' }
+ ];
+
+ return (
+
+
+
Tools
+
+ {tools.map((t) => (
+
+ ))}
+
+
+
+
+
+
+
Size
+ {tool === 'text' ? (
+
+
+ setFontSize(Number(e.target.value))}
+ />
+
+ ) : (
+
+
+ setLineWidth(Number(e.target.value))}
+ />
+
+ )}
+
+
+
+
Actions
+
+
+
+
+
+
+
+
+
Export
+
+
+
+
+
+
+
+
+
Shortcuts
+
+ -
+ Ctrl+Z Undo
+
+ -
+ Ctrl+Y Redo
+
+ -
+ Del Clear
+
+
+
+
+
+ );
+};
+
+export default Toolbar;
diff --git a/src/plays/virtual-whiteboard/readme.md b/src/plays/virtual-whiteboard/readme.md
new file mode 100644
index 000000000..09519e128
--- /dev/null
+++ b/src/plays/virtual-whiteboard/readme.md
@@ -0,0 +1,119 @@
+# Virtual Whiteboard
+
+A powerful virtual whiteboard with real-time drawing capabilities, layer management, and export functionality. Perfect for brainstorming, teaching, or collaborative design work.
+
+## Play Demographic
+
+- Language: js
+- Level: Advanced
+
+## Creator Information
+
+- User: Abhrxdip
+- Github Link: https://github.com/Abhrxdip
+- Blog:
+- Video:
+
+## Features
+
+### 🎨 Drawing Tools
+- **Pen Tool**: Freehand drawing with customizable colors and widths
+- **Eraser**: Remove unwanted strokes
+- **Line Tool**: Draw straight lines
+- **Rectangle Tool**: Create rectangles with fill options
+- **Circle Tool**: Draw circles with customizable appearance
+- **Text Tool**: Add text with custom font sizes
+
+### 🎯 Advanced Features
+- **Layer Management**:
+ - Create multiple layers for organized drawing
+ - Show/hide layers individually
+ - Lock layers to prevent accidental edits
+ - Rename layers for better organization
+ - Delete unnecessary layers
+ - See object count per layer
+
+- **Undo/Redo**:
+ - Full history tracking
+ - Unlimited undo/redo steps
+ - Keyboard shortcuts (Ctrl+Z, Ctrl+Y)
+
+- **Export Options**:
+ - Export as PNG image
+ - Export as PDF (print-friendly)
+ - High-quality output
+
+- **Customization**:
+ - Adjustable stroke colors
+ - Fill colors for shapes
+ - Line width control (1-20px)
+ - Font size control (12-72px)
+
+## Implementation Details
+
+### React Concepts Used
+- **useState**: Managing drawing state, tool selection, colors, and layers
+- **useRef**: Canvas reference for drawing operations
+- **useCallback**: Optimizing undo/redo functions
+- **useEffect**: Canvas rendering and keyboard event handling
+- **forwardRef**: Canvas component ref forwarding
+
+### Technical Implementation
+- **Canvas API**: HTML5 Canvas for all drawing operations
+- **Layer System**: Array-based layer management with visibility and lock states
+- **History Management**: JSON-based state snapshots for undo/redo
+- **Event Handling**: Mouse events for drawing interactions
+- **Export Functionality**: Canvas.toDataURL() for image export
+
+### Architecture
+```
+VirtualWhiteboard (Parent)
+├── Toolbar (Tool selection & controls)
+├── Canvas (Drawing surface)
+└── LayerPanel (Layer management)
+```
+
+## Usage Instructions
+
+1. **Select a Tool**: Click on pen, eraser, line, rectangle, circle, or text tool
+2. **Customize**: Adjust colors, line width, or font size as needed
+3. **Draw**: Click and drag on the canvas to create your artwork
+4. **Manage Layers**:
+ - Click "+" to add a new layer
+ - Click on a layer to make it active
+ - Use eye icon to show/hide
+ - Use lock icon to prevent editing
+5. **Undo/Redo**: Use toolbar buttons or keyboard shortcuts
+6. **Export**: Click PNG or PDF to save your work
+
+## Keyboard Shortcuts
+- `Ctrl+Z` - Undo
+- `Ctrl+Y` - Redo
+- `Del` - Clear all (with confirmation)
+
+## Potential Enhancements
+- WebSocket integration for real-time collaboration
+- Cloud save/load functionality
+- More shape tools (triangle, polygon, etc.)
+- Background image support
+- Grid/ruler overlay
+- Color palette presets
+- Brush texture options
+- Layer opacity control
+- Transform tools (rotate, scale)
+
+## Considerations
+- Canvas size is fixed at 1200x700px for optimal performance
+- History is stored in memory (cleared on page refresh)
+- Export quality depends on canvas resolution
+- Locked layers prevent all editing operations
+- At least one layer must exist at all times
+
+## Resources
+- [HTML5 Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
+- [React Icons](https://react-icons.github.io/react-icons/)
+- [Canvas Drawing Tutorial](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial)
+
+---
+
+[@Abhrxdip](https://github.com/Abhrxdip)
diff --git a/src/plays/virtual-whiteboard/styles.css b/src/plays/virtual-whiteboard/styles.css
new file mode 100644
index 000000000..c461ceaf2
--- /dev/null
+++ b/src/plays/virtual-whiteboard/styles.css
@@ -0,0 +1,454 @@
+.virtual-whiteboard {
+ width: 100%;
+ min-height: 100vh;
+ background: #f5f5f5;
+}
+
+.whiteboard-container {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ padding: 20px;
+ gap: 20px;
+}
+
+/* Toolbar Styles */
+.toolbar {
+ background: white;
+ border-radius: 12px;
+ padding: 20px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ display: flex;
+ flex-wrap: wrap;
+ gap: 30px;
+ align-items: flex-start;
+}
+
+.toolbar-section {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.toolbar-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ margin: 0;
+ margin-bottom: 8px;
+}
+
+.tool-buttons {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.tool-btn {
+ width: 44px;
+ height: 44px;
+ border: 2px solid #e0e0e0;
+ background: white;
+ border-radius: 8px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ color: #666;
+ transition: all 0.2s;
+}
+
+.tool-btn:hover {
+ border-color: #4a90e2;
+ color: #4a90e2;
+ transform: translateY(-2px);
+}
+
+.tool-btn.active {
+ background: #4a90e2;
+ border-color: #4a90e2;
+ color: white;
+}
+
+.color-controls {
+ display: flex;
+ gap: 15px;
+}
+
+.control-group {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.control-group label {
+ font-size: 12px;
+ color: #666;
+ font-weight: 500;
+}
+
+.color-picker {
+ width: 60px;
+ height: 40px;
+ border: 2px solid #e0e0e0;
+ border-radius: 8px;
+ cursor: pointer;
+ padding: 2px;
+}
+
+.slider {
+ width: 150px;
+ cursor: pointer;
+}
+
+.action-buttons,
+.export-buttons {
+ display: flex;
+ gap: 8px;
+}
+
+.action-btn,
+.export-btn {
+ padding: 10px 16px;
+ border: 2px solid #e0e0e0;
+ background: white;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 16px;
+ color: #666;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.action-btn:hover:not(:disabled),
+.export-btn:hover {
+ border-color: #4a90e2;
+ color: #4a90e2;
+ transform: translateY(-2px);
+}
+
+.action-btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.action-btn.danger:hover {
+ border-color: #e74c3c;
+ color: #e74c3c;
+}
+
+.export-btn {
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.keyboard-shortcuts {
+ padding: 12px;
+ background: #f9f9f9;
+ border-radius: 8px;
+ font-size: 12px;
+}
+
+.keyboard-shortcuts h4 {
+ margin: 0 0 8px 0;
+ font-size: 13px;
+ color: #333;
+}
+
+.keyboard-shortcuts ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.keyboard-shortcuts li {
+ margin: 4px 0;
+ color: #666;
+}
+
+.keyboard-shortcuts kbd {
+ background: white;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ padding: 2px 6px;
+ font-size: 11px;
+ font-family: monospace;
+ margin-right: 6px;
+}
+
+/* Main Area */
+.whiteboard-main {
+ display: flex;
+ gap: 20px;
+ height: calc(100vh - 280px);
+}
+
+/* Canvas Styles */
+.canvas-container {
+ flex: 1;
+ background: white;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ overflow: auto;
+ padding: 20px;
+}
+
+.whiteboard-canvas {
+ border: 2px solid #e0e0e0;
+ border-radius: 8px;
+ cursor: crosshair;
+ background: white;
+}
+
+.canvas-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 12px;
+ pointer-events: none;
+}
+
+.lock-message {
+ background: rgba(0, 0, 0, 0.8);
+ color: white;
+ padding: 12px 24px;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+}
+
+/* Layer Panel Styles */
+.layer-panel {
+ width: 280px;
+ background: white;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.layer-panel-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 20px;
+ border-bottom: 2px solid #f0f0f0;
+}
+
+.layer-panel-header h3 {
+ margin: 0;
+ font-size: 16px;
+ color: #333;
+}
+
+.add-layer-btn {
+ width: 32px;
+ height: 32px;
+ border: none;
+ background: #4a90e2;
+ color: white;
+ border-radius: 6px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 14px;
+ transition: all 0.2s;
+}
+
+.add-layer-btn:hover {
+ background: #357abd;
+ transform: scale(1.05);
+}
+
+.layer-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12px;
+}
+
+.layer-item {
+ background: #f9f9f9;
+ border: 2px solid #e0e0e0;
+ border-radius: 8px;
+ padding: 12px;
+ margin-bottom: 8px;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.layer-item:hover {
+ border-color: #4a90e2;
+ transform: translateX(2px);
+}
+
+.layer-item.active {
+ background: #e3f2fd;
+ border-color: #4a90e2;
+}
+
+.layer-item.locked {
+ opacity: 0.7;
+}
+
+.layer-controls {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.layer-control-btn {
+ width: 28px;
+ height: 28px;
+ border: none;
+ background: white;
+ border-radius: 4px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ color: #666;
+ transition: all 0.2s;
+}
+
+.layer-control-btn:hover {
+ color: #4a90e2;
+ transform: scale(1.1);
+}
+
+.layer-info {
+ flex: 1;
+ min-width: 0;
+}
+
+.layer-name {
+ font-size: 14px;
+ font-weight: 500;
+ color: #333;
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.layer-name-input {
+ width: 100%;
+ padding: 4px 8px;
+ border: 2px solid #4a90e2;
+ border-radius: 4px;
+ font-size: 14px;
+ font-weight: 500;
+ outline: none;
+}
+
+.layer-stats {
+ font-size: 11px;
+ color: #999;
+ margin-top: 2px;
+}
+
+.layer-actions {
+ display: flex;
+ gap: 4px;
+}
+
+.layer-action-btn {
+ width: 28px;
+ height: 28px;
+ border: none;
+ background: white;
+ border-radius: 4px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ color: #666;
+ transition: all 0.2s;
+}
+
+.layer-action-btn:hover {
+ color: #4a90e2;
+ transform: scale(1.1);
+}
+
+.layer-action-btn.danger:hover {
+ color: #e74c3c;
+}
+
+.layer-info-box {
+ padding: 16px;
+ background: #f9f9f9;
+ border-top: 2px solid #f0f0f0;
+}
+
+.layer-info-box h4 {
+ margin: 0 0 8px 0;
+ font-size: 13px;
+ color: #333;
+}
+
+.layer-info-box ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ font-size: 12px;
+ color: #666;
+}
+
+.layer-info-box li {
+ margin: 4px 0;
+}
+
+/* Responsive Design */
+@media (max-width: 1200px) {
+ .whiteboard-main {
+ flex-direction: column;
+ height: auto;
+ }
+
+ .layer-panel {
+ width: 100%;
+ }
+
+ .canvas-container {
+ min-height: 500px;
+ }
+}
+
+@media (max-width: 768px) {
+ .toolbar {
+ gap: 15px;
+ }
+
+ .toolbar-section {
+ width: 100%;
+ }
+
+ .whiteboard-canvas {
+ width: 100% !important;
+ height: 400px !important;
+ }
+
+ .slider {
+ width: 100%;
+ }
+}