diff --git a/nexus/components/memory-inspect.js b/nexus/components/memory-inspect.js
new file mode 100644
index 00000000..e356166e
--- /dev/null
+++ b/nexus/components/memory-inspect.js
@@ -0,0 +1,407 @@
+/**
+ * Memory Inspect Panel — click-to-read detail view for Mnemosyne crystals.
+ *
+ * When a memory crystal is clicked in the Nexus, this panel slides in from
+ * the right showing the memory's content, links, region, and metadata.
+ *
+ * Depends on SpatialMemory (for data access) — wired from app.js.
+ */
+
+const MemoryInspect = (() => {
+ let _panel = null;
+ let _isOpen = false;
+ let _currentMemId = null;
+ let _spatialMemory = null;
+
+ // ─── PUBLIC API ──────────────────────────────────────
+
+ function init(spatialMemoryRef) {
+ _spatialMemory = spatialMemoryRef;
+ _buildPanel();
+ _injectStyles();
+ console.info('[Mnemosyne] Memory Inspect panel initialized');
+ }
+
+ function inspect(memId) {
+ if (!_spatialMemory) return;
+ const memObj = _getMemoryObject(memId);
+ if (!memObj) return;
+
+ _currentMemId = memId;
+ _renderPanel(memObj);
+ _open();
+ _spatialMemory.highlightMemory(memId);
+ }
+
+ function close() {
+ if (!_isOpen) return;
+ if (_currentMemId && _spatialMemory) {
+ _spatialMemory.clearHighlight();
+ }
+ _currentMemId = null;
+ _close();
+ }
+
+ function isOpen() {
+ return _isOpen;
+ }
+
+ function getCurrentMemId() {
+ return _currentMemId;
+ }
+
+ // ─── INTERNALS ───────────────────────────────────────
+
+ function _getMemoryObject(memId) {
+ // Access SpatialMemory's internal _memoryObjects via getAllMemories
+ const all = _spatialMemory.getAllMemories();
+ if (!all) return null;
+ // getAllMemories returns array of { id, ...data } objects
+ const entry = all.find(m => m.id === memId);
+ if (!entry) return null;
+
+ // Get region info
+ const regions = _spatialMemory.REGIONS;
+ const region = regions[entry.category] || regions.working;
+
+ return { data: entry, region };
+ }
+
+ function _buildPanel() {
+ _panel = document.createElement('div');
+ _panel.id = 'memory-inspect-panel';
+ _panel.className = 'memory-inspect-panel memory-inspect-hidden';
+ _panel.innerHTML = `
+
+
+
+ `;
+ document.body.appendChild(_panel);
+
+ document.getElementById('inspect-close').addEventListener('click', (e) => {
+ e.stopPropagation();
+ close();
+ });
+ }
+
+ function _renderPanel(memObj) {
+ const { data, region } = memObj;
+ const strength = data.strength != null ? data.strength : 0.7;
+ const vitalityBand = _getVitalityBand(strength);
+ const bandColors = {
+ vibrant: '#00e5ff',
+ alive: '#4488ff',
+ fading: '#ffaa00',
+ dim: '#ff6644',
+ ghost: '#667788'
+ };
+ const bandColor = bandColors[vitalityBand] || bandColors.alive;
+
+ // Header
+ document.getElementById('inspect-glyph').textContent = region.glyph || '\uD83D\uDCCB';
+ document.getElementById('inspect-title').textContent = data.content
+ ? (data.content.length > 60 ? data.content.slice(0, 60) + '\u2026' : data.content)
+ : data.id || 'Memory';
+
+ // Meta bar
+ const metaEl = document.getElementById('inspect-meta');
+ const catLabel = region.label || data.category || 'Unknown';
+ const created = data.timestamp ? _formatTime(data.timestamp) : '';
+ metaEl.innerHTML = `
+ ${catLabel}
+
+ \u25CF ${vitalityBand} (${Math.round(strength * 100)}%)
+
+ ${created ? `${created}` : ''}
+ `;
+
+ // Content
+ const contentEl = document.getElementById('inspect-content');
+ contentEl.textContent = data.content || '(no content)';
+
+ // Links
+ const linksEl = document.getElementById('inspect-links');
+ const connections = data.connections || [];
+ if (connections.length > 0) {
+ let linksHtml = '';
+ linksHtml += '';
+ connections.forEach(cid => {
+ const linked = _getLinkedPreview(cid);
+ linksHtml += `
+
+ ${linked}
+
`;
+ });
+ linksHtml += '
';
+ linksEl.innerHTML = linksHtml;
+
+ // Wire click handlers for linked memories
+ linksEl.querySelectorAll('.inspect-link-item').forEach(el => {
+ el.addEventListener('click', () => {
+ const targetId = el.dataset.memId;
+ if (targetId) inspect(targetId);
+ });
+ });
+ } else {
+ linksEl.innerHTML = 'No linked memories
';
+ }
+
+ // Footer
+ const footerEl = document.getElementById('inspect-footer');
+ footerEl.innerHTML = `
+
+ ${data.id || ''}
+ `;
+ document.getElementById('inspect-copy-btn').addEventListener('click', () => {
+ navigator.clipboard.writeText(data.content || '').then(() => {
+ const btn = document.getElementById('inspect-copy-btn');
+ btn.textContent = '\u2713 Copied';
+ setTimeout(() => { btn.textContent = '\uD83D\uDCCB Copy'; }, 1500);
+ });
+ });
+ }
+
+ function _getLinkedPreview(memId) {
+ if (!_spatialMemory) return memId;
+ const all = _spatialMemory.getAllMemories();
+ if (!all) return memId;
+ const entry = all.find(m => m.id === memId);
+ if (!entry || !entry.content) return memId;
+ return entry.content.length > 40 ? entry.content.slice(0, 40) + '\u2026' : entry.content;
+ }
+
+ function _getVitalityBand(strength) {
+ if (strength >= 0.8) return 'vibrant';
+ if (strength >= 0.5) return 'alive';
+ if (strength >= 0.25) return 'fading';
+ if (strength >= 0.1) return 'dim';
+ return 'ghost';
+ }
+
+ function _formatTime(isoStr) {
+ try {
+ const d = new Date(isoStr);
+ const now = new Date();
+ const diffMs = now - d;
+ const diffMin = Math.floor(diffMs / 60000);
+ if (diffMin < 1) return 'just now';
+ if (diffMin < 60) return diffMin + 'm ago';
+ const diffHr = Math.floor(diffMin / 60);
+ if (diffHr < 24) return diffHr + 'h ago';
+ const diffDay = Math.floor(diffHr / 24);
+ if (diffDay < 30) return diffDay + 'd ago';
+ return d.toLocaleDateString();
+ } catch {
+ return '';
+ }
+ }
+
+ function _open() {
+ _isOpen = true;
+ _panel.classList.remove('memory-inspect-hidden');
+ _panel.classList.add('memory-inspect-visible');
+ }
+
+ function _close() {
+ _isOpen = false;
+ _panel.classList.remove('memory-inspect-visible');
+ _panel.classList.add('memory-inspect-hidden');
+ }
+
+ function _injectStyles() {
+ if (document.getElementById('memory-inspect-styles')) return;
+ const style = document.createElement('style');
+ style.id = 'memory-inspect-styles';
+ style.textContent = `
+ .memory-inspect-panel {
+ position: fixed;
+ top: 60px;
+ right: 0;
+ width: 340px;
+ max-height: calc(100vh - 80px);
+ background: rgba(8, 12, 32, 0.92);
+ border: 1px solid rgba(74, 240, 192, 0.2);
+ border-right: none;
+ border-radius: 8px 0 0 8px;
+ color: #c8d8e8;
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
+ font-size: 13px;
+ z-index: 900;
+ display: flex;
+ flex-direction: column;
+ backdrop-filter: blur(12px);
+ box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
+ transition: transform 0.25s ease, opacity 0.25s ease;
+ overflow: hidden;
+ }
+ .memory-inspect-hidden {
+ transform: translateX(100%);
+ opacity: 0;
+ pointer-events: none;
+ }
+ .memory-inspect-visible {
+ transform: translateX(0);
+ opacity: 1;
+ pointer-events: auto;
+ }
+ .memory-inspect-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 12px;
+ border-bottom: 1px solid rgba(74, 240, 192, 0.15);
+ background: rgba(74, 240, 192, 0.05);
+ }
+ .memory-inspect-glyph {
+ font-size: 18px;
+ flex-shrink: 0;
+ }
+ .memory-inspect-title {
+ flex: 1;
+ font-weight: 600;
+ color: #4af0c0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .memory-inspect-close {
+ background: none;
+ border: none;
+ color: #667788;
+ font-size: 20px;
+ cursor: pointer;
+ padding: 0 4px;
+ line-height: 1;
+ }
+ .memory-inspect-close:hover {
+ color: #ff4466;
+ }
+ .memory-inspect-body {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12px;
+ }
+ .memory-inspect-meta {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ margin-bottom: 12px;
+ font-size: 11px;
+ }
+ .inspect-badge {
+ padding: 2px 8px;
+ border: 1px solid;
+ border-radius: 4px;
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+ .inspect-vitality {
+ font-size: 11px;
+ }
+ .inspect-time {
+ color: #556677;
+ font-size: 10px;
+ }
+ .memory-inspect-content {
+ background: rgba(0, 0, 0, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: 6px;
+ padding: 10px;
+ line-height: 1.5;
+ white-space: pre-wrap;
+ word-break: break-word;
+ margin-bottom: 12px;
+ max-height: 200px;
+ overflow-y: auto;
+ }
+ .inspect-links-header {
+ font-size: 11px;
+ color: #7b5cff;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ margin-bottom: 6px;
+ }
+ .inspect-links-list {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+ .inspect-link-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 8px;
+ background: rgba(123, 92, 255, 0.08);
+ border: 1px solid rgba(123, 92, 255, 0.15);
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 11px;
+ transition: background 0.15s;
+ }
+ .inspect-link-item:hover {
+ background: rgba(123, 92, 255, 0.18);
+ }
+ .inspect-link-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #7b5cff;
+ flex-shrink: 0;
+ }
+ .inspect-link-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .inspect-no-links {
+ color: #445566;
+ font-size: 11px;
+ font-style: italic;
+ }
+ .memory-inspect-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 12px;
+ border-top: 1px solid rgba(74, 240, 192, 0.1);
+ }
+ .inspect-action-btn {
+ background: rgba(74, 240, 192, 0.1);
+ border: 1px solid rgba(74, 240, 192, 0.25);
+ color: #4af0c0;
+ padding: 4px 12px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 11px;
+ font-family: inherit;
+ transition: background 0.15s;
+ }
+ .inspect-action-btn:hover {
+ background: rgba(74, 240, 192, 0.2);
+ }
+ .inspect-id {
+ color: #334455;
+ font-size: 9px;
+ max-width: 160px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ `;
+ document.head.appendChild(style);
+ }
+
+ return { init, inspect, close, isOpen, getCurrentMemId };
+})();
+
+export { MemoryInspect };