diff --git a/public/css/reading-mode.css b/public/css/reading-mode.css
new file mode 100644
index 00000000..9ded19fc
--- /dev/null
+++ b/public/css/reading-mode.css
@@ -0,0 +1,54 @@
+.reading-mode-toggle {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1.5rem;
+ margin: 1rem auto 2rem;
+ max-width: 900px;
+ background: #f8fafc;
+ border: 1px solid #e2e8f0;
+ border-radius: 0.5rem;
+ font-size: 0.875rem;
+}
+
+.reading-mode-label {
+ color: #64748b;
+ font-weight: 500;
+ margin-right: 0.25rem;
+}
+
+.reading-mode-btn {
+ padding: 0.375rem 0.75rem;
+ border: 1px solid #cbd5e1;
+ border-radius: 0.375rem;
+ background: white;
+ color: #475569;
+ cursor: pointer;
+ font-size: 0.8rem;
+ font-weight: 500;
+ transition: all 0.2s;
+}
+
+.reading-mode-btn:hover {
+ background: #f1f5f9;
+ border-color: #94a3b8;
+}
+
+.reading-mode-btn.active {
+ background: #1e40af;
+ color: white;
+ border-color: #1e40af;
+}
+
+.reading-mode-estimate {
+ margin-left: auto;
+ color: #94a3b8;
+ font-size: 0.8rem;
+}
+
+@media (max-width: 640px) {
+ .reading-mode-toggle {
+ flex-wrap: wrap;
+ padding: 0.5rem 1rem;
+ }
+}
diff --git a/public/js/reading-mode.js b/public/js/reading-mode.js
new file mode 100644
index 00000000..e1b01353
--- /dev/null
+++ b/public/js/reading-mode.js
@@ -0,0 +1,114 @@
+/**
+ * Reading Mode Toggle
+ * Provides overview/standard/deep density modes for long research papers.
+ *
+ * Usage: Add data-reading-level="overview|standard|deep" to content sections.
+ * Sections without the attribute are always visible.
+ *
+ * The toggle is auto-injected at the top of any page that includes this script
+ * and has elements with data-reading-level attributes.
+ */
+(function() {
+ 'use strict';
+
+ var STORAGE_KEY = 'tractatus-reading-mode';
+ var MODES = ['overview', 'standard', 'deep'];
+ var MODE_LABELS = {
+ overview: { label: 'Overview', desc: 'Key points only' },
+ standard: { label: 'Standard', desc: 'Main content' },
+ deep: { label: 'Deep', desc: 'Full detail' }
+ };
+
+ function init() {
+ // Only activate if page has reading-level content
+ var leveledContent = document.querySelectorAll('[data-reading-level]');
+ if (leveledContent.length === 0) return;
+
+ var savedMode = localStorage.getItem(STORAGE_KEY) || 'standard';
+
+ // Inject toggle UI after the first h1 or at the top of main content
+ var heroOrH1 = document.querySelector('.hero-section, .page-hero, h1');
+ var insertPoint = (heroOrH1 && heroOrH1.parentElement)
+ || document.querySelector('main')
+ || document.body;
+
+ var toggle = document.createElement('div');
+ toggle.className = 'reading-mode-toggle';
+ toggle.setAttribute('role', 'radiogroup');
+ toggle.setAttribute('aria-label', 'Reading depth');
+ toggle.innerHTML = 'Reading depth:' +
+ MODES.map(function(mode) {
+ return '';
+ }).join('') +
+ '';
+
+ // Insert after the hero/h1 section
+ if (insertPoint.nextSibling) {
+ insertPoint.parentNode.insertBefore(toggle, insertPoint.nextSibling);
+ } else {
+ insertPoint.parentNode.appendChild(toggle);
+ }
+
+ // Apply initial mode
+ applyMode(savedMode);
+
+ // Handle clicks
+ toggle.addEventListener('click', function(e) {
+ var btn = e.target.closest('.reading-mode-btn');
+ if (!btn) return;
+
+ var mode = btn.dataset.mode;
+ localStorage.setItem(STORAGE_KEY, mode);
+
+ // Update button states
+ var buttons = toggle.querySelectorAll('.reading-mode-btn');
+ for (var i = 0; i < buttons.length; i++) {
+ var b = buttons[i];
+ if (b.dataset.mode === mode) {
+ b.classList.add('active');
+ b.setAttribute('aria-checked', 'true');
+ } else {
+ b.classList.remove('active');
+ b.setAttribute('aria-checked', 'false');
+ }
+ }
+
+ applyMode(mode);
+ });
+ }
+
+ function applyMode(mode) {
+ var elements = document.querySelectorAll('[data-reading-level]');
+ var modeIndex = MODES.indexOf(mode);
+
+ for (var i = 0; i < elements.length; i++) {
+ var el = elements[i];
+ var level = el.dataset.readingLevel;
+ var levelIndex = MODES.indexOf(level);
+ // overview shows only overview; standard shows overview+standard; deep shows all
+ var visible = levelIndex <= modeIndex;
+ el.style.display = visible ? '' : 'none';
+ el.setAttribute('aria-hidden', !visible);
+ }
+
+ // Update reading time estimate if present
+ var counter = document.querySelector('.reading-mode-estimate');
+ if (counter) {
+ var labels = { overview: '~3 min read', standard: '~12 min read', deep: '~25 min read' };
+ counter.textContent = labels[mode] || '';
+ }
+ }
+
+ // Init on DOM ready
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+ } else {
+ init();
+ }
+})();
diff --git a/public/researcher.html b/public/researcher.html
index 6960c903..b25472c0 100644
--- a/public/researcher.html
+++ b/public/researcher.html
@@ -38,6 +38,7 @@
+