759 lines
23 KiB
HTML
759 lines
23 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Project Article - Tech Blog</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
:root {
|
|
--deep-navy: #0A1128;
|
|
--rich-gold: #D4AF37;
|
|
--warm-gold: #F4E5C2;
|
|
--cream: #FFF8E7;
|
|
--charcoal: #2C2C2C;
|
|
--silver: #C0C0C0;
|
|
--burgundy: #7C2D37;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, serif;
|
|
line-height: 1.8;
|
|
color: var(--charcoal);
|
|
background: linear-gradient(180deg, var(--deep-navy) 0%, #1a2847 100%);
|
|
position: relative;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Art Deco geometric background pattern */
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-image:
|
|
repeating-linear-gradient(45deg, transparent, transparent 35px, rgba(212, 175, 55, 0.03) 35px, rgba(212, 175, 55, 0.03) 70px),
|
|
repeating-linear-gradient(-45deg, transparent, transparent 35px, rgba(212, 175, 55, 0.03) 35px, rgba(212, 175, 55, 0.03) 70px);
|
|
z-index: -1;
|
|
}
|
|
|
|
/* Navigation */
|
|
nav {
|
|
background: rgba(10, 17, 40, 0.95);
|
|
border-bottom: 2px solid var(--rich-gold);
|
|
padding: 1.0rem 0;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.nav-content {
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
padding: 0 2rem;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.nav-logo {
|
|
font-size: 1.5rem;
|
|
font-weight: 300;
|
|
color: var(--warm-gold);
|
|
text-decoration: none;
|
|
letter-spacing: 0.2em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.nav-back {
|
|
color: var(--silver);
|
|
text-decoration: none;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 0.9rem;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
transition: color 0.3s;
|
|
}
|
|
|
|
.nav-back:hover {
|
|
color: var(--rich-gold);
|
|
}
|
|
|
|
.nav-back::before {
|
|
content: '←';
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
/* Article Container */
|
|
.article-container {
|
|
max-width: 1000px;
|
|
margin: 4rem auto;
|
|
padding: 0 2rem;
|
|
}
|
|
|
|
/* Article Header */
|
|
.article-header {
|
|
background: var(--cream);
|
|
padding: 4rem 4rem 3rem;
|
|
border: 2px solid var(--rich-gold);
|
|
position: relative;
|
|
margin-bottom: 3rem;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
/* Corner decorations */
|
|
.article-header::before,
|
|
.article-header::after {
|
|
content: '';
|
|
position: absolute;
|
|
width: 60px;
|
|
height: 60px;
|
|
border: 2px solid var(--rich-gold);
|
|
}
|
|
|
|
.article-header::before {
|
|
top: 0;
|
|
left: 0;
|
|
border-right: none;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.article-header::after {
|
|
bottom: 0;
|
|
right: 0;
|
|
border-left: none;
|
|
border-top: none;
|
|
}
|
|
|
|
.article-number {
|
|
font-size: 4rem;
|
|
font-weight: 300;
|
|
color: var(--rich-gold);
|
|
line-height: 1;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.article-meta {
|
|
display: flex;
|
|
gap: 2rem;
|
|
margin-bottom: 2rem;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 0.85rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.15em;
|
|
}
|
|
|
|
.article-category {
|
|
color: var(--burgundy);
|
|
font-weight: 700;
|
|
position: relative;
|
|
padding-left: 1rem;
|
|
}
|
|
|
|
.article-category::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
transform: translateY(-50%) rotate(45deg);
|
|
width: 6px;
|
|
height: 6px;
|
|
background: var(--rich-gold);
|
|
}
|
|
|
|
.article-title {
|
|
font-size: 3.5rem;
|
|
font-weight: 400;
|
|
color: var(--deep-navy);
|
|
line-height: 1.2;
|
|
letter-spacing: 0.02em;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.article-subtitle {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 1.25rem;
|
|
line-height: 1.6;
|
|
color: var(--charcoal);
|
|
font-weight: 400;
|
|
}
|
|
|
|
/* Tech Stack */
|
|
.tech-stack {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
margin-top: 2rem;
|
|
padding-top: 2rem;
|
|
border-top: 2px solid var(--rich-gold);
|
|
}
|
|
|
|
.tech-item {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
color: var(--deep-navy);
|
|
padding: 0.75rem 1.5rem;
|
|
background: linear-gradient(135deg, rgba(212, 175, 55, 0.15), rgba(212, 175, 55, 0.05));
|
|
border-left: 3px solid var(--rich-gold);
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
/* Featured Image */
|
|
.featured-image {
|
|
width: 100%;
|
|
height: 500px;
|
|
background: linear-gradient(135deg, var(--deep-navy) 0%, var(--burgundy) 100%);
|
|
border: 2px solid var(--rich-gold);
|
|
margin-bottom: 3rem;
|
|
position: relative;
|
|
overflow: hidden;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
/* Sunburst pattern on image */
|
|
.featured-image::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 200%;
|
|
height: 200%;
|
|
background: repeating-conic-gradient(
|
|
from 0deg,
|
|
rgba(212, 175, 55, 0.1) 0deg 10deg,
|
|
transparent 10deg 20deg
|
|
);
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
.image-placeholder {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: 6rem;
|
|
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5));
|
|
}
|
|
|
|
/* Article Content */
|
|
.article-content {
|
|
background: var(--cream);
|
|
padding: 4rem;
|
|
border: 2px solid var(--rich-gold);
|
|
position: relative;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
/* Corner decorations for content */
|
|
.article-content::before,
|
|
.article-content::after {
|
|
content: '';
|
|
position: absolute;
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 2px solid var(--rich-gold);
|
|
}
|
|
|
|
.article-content::before {
|
|
top: 0;
|
|
right: 0;
|
|
border-left: none;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.article-content::after {
|
|
bottom: 0;
|
|
left: 0;
|
|
border-right: none;
|
|
border-top: none;
|
|
}
|
|
|
|
.article-content h2 {
|
|
font-size: 2rem;
|
|
font-weight: 400;
|
|
color: var(--deep-navy);
|
|
margin: 3rem 0 1.5rem 0;
|
|
letter-spacing: 0.02em;
|
|
position: relative;
|
|
padding-bottom: 1rem;
|
|
}
|
|
|
|
.article-content h2::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 80px;
|
|
height: 2px;
|
|
background: var(--rich-gold);
|
|
}
|
|
|
|
.article-content h2:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.article-content h3 {
|
|
font-size: 1.5rem;
|
|
font-weight: 400;
|
|
color: var(--burgundy);
|
|
margin: 2.5rem 0 1rem 0;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
.article-content p {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 1.1rem;
|
|
line-height: 1.9;
|
|
margin-bottom: 1.5rem;
|
|
color: var(--charcoal);
|
|
}
|
|
|
|
.article-content ul,
|
|
.article-content ol {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 1.1rem;
|
|
line-height: 1.9;
|
|
margin: 1.5rem 0 1.5rem 2rem;
|
|
color: var(--charcoal);
|
|
}
|
|
|
|
.article-content li {
|
|
margin-bottom: 0.75rem;
|
|
padding-left: 0.5rem;
|
|
}
|
|
|
|
.article-content ul li::marker {
|
|
color: var(--rich-gold);
|
|
}
|
|
|
|
.article-content code {
|
|
font-family: 'Courier New', monospace;
|
|
background: rgba(212, 175, 55, 0.1);
|
|
padding: 0.2rem 0.5rem;
|
|
border-radius: 3px;
|
|
font-size: 0.95rem;
|
|
color: var(--burgundy);
|
|
}
|
|
|
|
.article-content pre {
|
|
background: var(--charcoal);
|
|
color: var(--warm-gold);
|
|
padding: 1.5rem;
|
|
border-radius: 4px;
|
|
overflow-x: auto;
|
|
margin: 2rem 0;
|
|
border-left: 4px solid var(--rich-gold);
|
|
}
|
|
|
|
.article-content pre code {
|
|
background: none;
|
|
color: var(--warm-gold);
|
|
padding: 0;
|
|
}
|
|
|
|
.article-content blockquote {
|
|
border-left: 4px solid var(--rich-gold);
|
|
padding-left: 2rem;
|
|
margin: 2rem 0;
|
|
font-style: italic;
|
|
color: var(--burgundy);
|
|
background: linear-gradient(90deg, rgba(212, 175, 55, 0.05), transparent);
|
|
padding: 1.5rem 2rem;
|
|
}
|
|
|
|
/* Inline images */
|
|
.article-content img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
margin: 2rem 0;
|
|
border: 2px solid var(--rich-gold);
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
/* Decorative divider */
|
|
.deco-divider {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 3rem 0;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.deco-divider::before,
|
|
.deco-divider::after {
|
|
content: '';
|
|
width: 100px;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, transparent, var(--rich-gold), transparent);
|
|
}
|
|
|
|
.deco-divider .diamond {
|
|
width: 12px;
|
|
height: 12px;
|
|
background: var(--rich-gold);
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
/* Footer */
|
|
footer {
|
|
text-align: center;
|
|
padding: 4rem 0;
|
|
color: var(--silver);
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
font-size: 0.9rem;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
margin-top: 4rem;
|
|
}
|
|
|
|
.footer-ornament {
|
|
width: 200px;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, transparent, var(--rich-gold), transparent);
|
|
margin: 0 auto 1.5rem;
|
|
position: relative;
|
|
}
|
|
|
|
.footer-ornament::before {
|
|
content: '';
|
|
position: absolute;
|
|
width: 10px;
|
|
height: 10px;
|
|
background: var(--rich-gold);
|
|
transform: rotate(45deg);
|
|
top: -4px;
|
|
left: 50%;
|
|
margin-left: -5px;
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.article-header,
|
|
.article-content {
|
|
padding: 2rem;
|
|
}
|
|
|
|
.article-title {
|
|
font-size: 2.5rem;
|
|
}
|
|
|
|
.article-number {
|
|
font-size: 3rem;
|
|
}
|
|
|
|
.featured-image {
|
|
height: 300px;
|
|
}
|
|
|
|
.image-placeholder {
|
|
font-size: 4rem;
|
|
}
|
|
|
|
.article-container {
|
|
padding: 0 1rem;
|
|
margin: 2rem auto;
|
|
}
|
|
|
|
.nav-content {
|
|
padding: 0 1rem;
|
|
}
|
|
|
|
.nav-logo {
|
|
font-size: 1.2rem;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Navigation -->
|
|
<nav>
|
|
<div class="nav-content">
|
|
<a href="#" class="nav-logo">Atelier</a>
|
|
<a href="#" class="nav-back">Back to Projects</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Article Container -->
|
|
<div class="article-container">
|
|
|
|
<!-- Article Header -->
|
|
<header class="article-header">
|
|
<div class="article-number">01</div>
|
|
<div class="article-meta">
|
|
<span class="article-category">Web Development</span>
|
|
<span>November 2025</span>
|
|
</div>
|
|
<h1 class="article-title">Building a Real-Time Collaborative Editor</h1>
|
|
<p class="article-subtitle">
|
|
A deep dive into creating a production-ready collaborative text editor using WebSockets,
|
|
operational transformation, and modern web technologies.
|
|
</p>
|
|
<div class="tech-stack">
|
|
<div class="tech-item">React</div>
|
|
<div class="tech-item">Node.js</div>
|
|
<div class="tech-item">WebSocket</div>
|
|
<div class="tech-item">MongoDB</div>
|
|
<div class="tech-item">Redis</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Featured Image -->
|
|
<div class="featured-image">
|
|
<div class="image-placeholder">⚡</div>
|
|
</div>
|
|
|
|
<!-- Article Content -->
|
|
<article class="article-content">
|
|
<h2>The Challenge</h2>
|
|
<p>
|
|
Building a real-time collaborative editor presents unique technical challenges. When multiple users edit
|
|
the same document simultaneously, conflicts arise that must be resolved elegantly without losing data or
|
|
disrupting the user experience. This project explores operational transformation algorithms and their
|
|
implementation in a modern web application.
|
|
</p>
|
|
|
|
<p>
|
|
The goal was to create an editor that feels as responsive as Google Docs while maintaining data consistency
|
|
across all connected clients. Every keystroke needed to be transmitted, transformed, and applied in real-time
|
|
without introducing latency or conflicts.
|
|
</p>
|
|
|
|
<div class="deco-divider">
|
|
<div class="diamond"></div>
|
|
</div>
|
|
|
|
<h2>Technical Architecture</h2>
|
|
<p>
|
|
The system is built on a microservices architecture with distinct components handling different aspects
|
|
of the collaborative experience:
|
|
</p>
|
|
|
|
<h3>WebSocket Server</h3>
|
|
<p>
|
|
At the heart of the system is a Node.js WebSocket server that manages all real-time connections. Each
|
|
document session creates a room where clients can join, and all operations are broadcast through this central hub.
|
|
</p>
|
|
|
|
<ul>
|
|
<li>Handles client connections and disconnections gracefully</li>
|
|
<li>Broadcasts operations to all connected clients in a room</li>
|
|
<li>Implements heartbeat mechanisms to detect stale connections</li>
|
|
<li>Scales horizontally using Redis pub/sub for multi-server deployments</li>
|
|
</ul>
|
|
|
|
<h3>Operational Transformation</h3>
|
|
<p>
|
|
The core algorithm that makes collaboration possible is operational transformation (OT). When two users
|
|
edit different parts of the document, their operations must be transformed relative to each other to
|
|
maintain consistency.
|
|
</p>
|
|
|
|
<blockquote>
|
|
"Operational transformation is the mathematical foundation that allows distributed systems to converge
|
|
to the same state despite concurrent modifications."
|
|
</blockquote>
|
|
|
|
<p>
|
|
Our implementation uses a three-operation model: insert, delete, and retain. Each operation carries
|
|
positional information that gets transformed when concurrent operations occur.
|
|
</p>
|
|
|
|
<div class="deco-divider">
|
|
<div class="diamond"></div>
|
|
</div>
|
|
|
|
<h2>Key Features</h2>
|
|
<p>
|
|
The final implementation includes several sophisticated features that enhance the collaborative experience:
|
|
</p>
|
|
|
|
<ol>
|
|
<li><strong>Cursor Tracking:</strong> Real-time display of where other users are typing</li>
|
|
<li><strong>Presence Indicators:</strong> Shows who's currently viewing the document</li>
|
|
<li><strong>Conflict-Free Resolution:</strong> Automatic handling of concurrent edits</li>
|
|
<li><strong>Undo/Redo:</strong> Full history management that works across collaborative sessions</li>
|
|
<li><strong>Rich Text Formatting:</strong> Support for bold, italic, lists, and more</li>
|
|
</ol>
|
|
|
|
<h3>Performance Optimizations</h3>
|
|
<p>
|
|
To ensure smooth performance even with large documents and many concurrent users, several optimizations
|
|
were implemented:
|
|
</p>
|
|
|
|
<ul>
|
|
<li>Operation batching to reduce network overhead</li>
|
|
<li>Delta compression for efficient data transmission</li>
|
|
<li>Lazy loading of document history</li>
|
|
<li>Client-side caching with service workers</li>
|
|
</ul>
|
|
|
|
<div class="deco-divider">
|
|
<div class="diamond"></div>
|
|
</div>
|
|
|
|
<h2>Code Implementation</h2>
|
|
<p>
|
|
The operational transformation logic is implemented in JavaScript. Here's a simplified example of how
|
|
operations are transformed when they conflict:
|
|
</p>
|
|
|
|
<pre><code>function transformOperation(op1, op2) {
|
|
// If operations affect different positions, no transformation needed
|
|
if (op1.position < op2.position) {
|
|
return op1;
|
|
}
|
|
|
|
// Transform op1 based on op2's changes
|
|
if (op2.type === 'insert') {
|
|
return {
|
|
...op1,
|
|
position: op1.position + op2.length
|
|
};
|
|
}
|
|
|
|
if (op2.type === 'delete') {
|
|
return {
|
|
...op1,
|
|
position: Math.max(op1.position - op2.length, op2.position)
|
|
};
|
|
}
|
|
|
|
return op1;
|
|
}</code></pre>
|
|
|
|
<h3>WebSocket Event Handler</h3>
|
|
<p>
|
|
The server-side WebSocket handler manages incoming operations and broadcasts them to all connected clients:
|
|
</p>
|
|
|
|
<pre><code>io.on('connection', (socket) => {
|
|
console.log('Client connected:', socket.id);
|
|
|
|
socket.on('join-document', (docId) => {
|
|
socket.join(docId);
|
|
socket.to(docId).emit('user-joined', {
|
|
userId: socket.id,
|
|
timestamp: Date.now()
|
|
});
|
|
});
|
|
|
|
socket.on('operation', (data) => {
|
|
const { docId, operation, version } = data;
|
|
|
|
// Transform and broadcast operation
|
|
const transformed = applyTransformation(operation, version);
|
|
socket.to(docId).emit('operation', transformed);
|
|
|
|
// Save to database
|
|
saveOperation(docId, transformed);
|
|
});
|
|
});</code></pre>
|
|
|
|
<h3>React Component Example</h3>
|
|
<p>
|
|
On the client side, React components manage the editor state and handle user input:
|
|
</p>
|
|
|
|
<pre><code>const CollaborativeEditor = () => {
|
|
const [content, setContent] = useState('');
|
|
const [cursors, setCursors] = useState({});
|
|
const wsRef = useRef(null);
|
|
|
|
useEffect(() => {
|
|
// Connect to WebSocket server
|
|
wsRef.current = io('wss://api.example.com');
|
|
|
|
wsRef.current.on('operation', (op) => {
|
|
setContent(prev => applyOperation(prev, op));
|
|
});
|
|
|
|
wsRef.current.on('cursor-move', (data) => {
|
|
setCursors(prev => ({
|
|
...prev,
|
|
[data.userId]: data.position
|
|
}));
|
|
});
|
|
|
|
return () => wsRef.current.disconnect();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="editor">
|
|
<textarea
|
|
value={content}
|
|
onChange={handleChange}
|
|
onSelect={handleCursorMove}
|
|
/>
|
|
{renderCursors(cursors)}
|
|
</div>
|
|
);
|
|
};</code></pre>
|
|
|
|
<div class="deco-divider">
|
|
<div class="diamond"></div>
|
|
</div>
|
|
|
|
<h2>Lessons Learned</h2>
|
|
<p>
|
|
Throughout this project, several important insights emerged about building real-time collaborative systems:
|
|
</p>
|
|
|
|
<p>
|
|
<strong>Network Reliability:</strong> Never assume the network is stable. Implementing robust reconnection
|
|
logic and conflict resolution for offline edits proved essential for a production-ready system.
|
|
</p>
|
|
|
|
<p>
|
|
<strong>State Management:</strong> Keeping client and server state synchronized requires careful attention
|
|
to edge cases. The operational transformation algorithm must handle not just simple concurrent edits, but
|
|
also complex scenarios involving multiple clients making rapid changes.
|
|
</p>
|
|
|
|
<p>
|
|
<strong>User Experience:</strong> The technical implementation, no matter how elegant, means nothing if
|
|
users experience lag or data loss. Optimizing for perceived performance was as important as actual performance.
|
|
</p>
|
|
|
|
<div class="deco-divider">
|
|
<div class="diamond"></div>
|
|
</div>
|
|
|
|
<h2>Future Enhancements</h2>
|
|
<p>
|
|
While the current implementation is robust, several exciting enhancements are planned for future iterations:
|
|
</p>
|
|
|
|
<ul>
|
|
<li>End-to-end encryption for sensitive documents</li>
|
|
<li>Voice and video integration for enhanced collaboration</li>
|
|
<li>AI-powered suggestions and auto-completion</li>
|
|
<li>Mobile app support with native performance</li>
|
|
<li>Advanced permission systems for enterprise deployments</li>
|
|
</ul>
|
|
|
|
<p>
|
|
This project demonstrates that building truly collaborative software requires deep understanding of
|
|
distributed systems, careful attention to user experience, and thoughtful architecture decisions. The
|
|
result is a system that feels magical to users while being built on solid engineering principles.
|
|
</p>
|
|
</article>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer>
|
|
<div class="footer-ornament"></div>
|
|
<p>Crafted with Precision & Elegance © 2025</p>
|
|
</footer>
|
|
</body>
|
|
</html>
|