diff --git a/phd-advisor-frontend/src/App.js b/phd-advisor-frontend/src/App.js
index 9d03459c..897f05e5 100644
--- a/phd-advisor-frontend/src/App.js
+++ b/phd-advisor-frontend/src/App.js
@@ -38,7 +38,10 @@ function App() {
setCurrentView('auth');
};
- const navigateToCanvas = () => {
+ const navigateToCanvas = (canvasView) => {
+ if (['insights', 'workspace', 'deliverables'].includes(canvasView)) {
+ localStorage.setItem('canvas-view-v2', canvasView);
+ }
setCurrentView('canvas');
};
@@ -74,7 +77,9 @@ function App() {
{currentView === 'home' && (
)}
@@ -82,9 +87,10 @@ function App() {
)}
{currentView === 'canvas' && isAuthenticated && (
-
diff --git a/phd-advisor-frontend/src/components/AppHeader.js b/phd-advisor-frontend/src/components/AppHeader.js
new file mode 100644
index 00000000..853a487a
--- /dev/null
+++ b/phd-advisor-frontend/src/components/AppHeader.js
@@ -0,0 +1,100 @@
+import React from 'react';
+import { Home, Menu, Users } from 'lucide-react';
+import ThemeToggle from './ThemeToggle';
+import { useAppConfig } from '../contexts/AppConfigContext';
+
+/**
+ * Shared floating header used on every page so the app feels like one surface.
+ *
+ * Props:
+ * currentPage: 'home' | 'chat' | 'canvas'
+ * onNavigateToHome, onNavigateToChat, onNavigateToCanvas: navigation callbacks
+ * (onNavigateToCanvas may receive 'insights' | 'workspace' to deep-link a view)
+ * onMobileMenu?: () => void — when present, shows the mobile menu button
+ * children?: ReactNode — extra controls slotted between the tabs and the theme toggle
+ */
+const AppHeader = ({
+ currentPage = 'home',
+ onNavigateToHome,
+ onNavigateToChat,
+ onNavigateToCanvas,
+ onMobileMenu,
+ children,
+}) => {
+ const { config, resolveIcon } = useAppConfig();
+ const BrandIcon = resolveIcon ? resolveIcon('Users') : Users;
+
+ const goToCanvas = (view) => {
+ if (onNavigateToCanvas) onNavigateToCanvas(view);
+ };
+
+ // Accept either 'canvas' (all canvas tabs highlight equally) or a more specific
+ // 'canvas-
' from CanvasPage so only the active one highlights.
+ const isOnHome = currentPage === 'home';
+ const isOnChat = currentPage === 'chat';
+ const isOnCanvas = currentPage === 'canvas' || currentPage.startsWith('canvas-');
+ const canvasSub = currentPage.startsWith('canvas-') ? currentPage.slice(7) : null;
+ const tabActive = (sub) => isOnCanvas && (canvasSub === null ? false : canvasSub === sub);
+
+ return (
+
+ );
+};
+
+export default AppHeader;
diff --git a/phd-advisor-frontend/src/components/canvas/CanvasIcon.js b/phd-advisor-frontend/src/components/canvas/CanvasIcon.js
new file mode 100644
index 00000000..933b71f9
--- /dev/null
+++ b/phd-advisor-frontend/src/components/canvas/CanvasIcon.js
@@ -0,0 +1,90 @@
+import React from 'react';
+
+const ICONS = {
+ sparkles: <> >,
+ layout: <> >,
+ insights: <> >,
+ search: <> >,
+ bell: <> >,
+ settings: <> >,
+ plus: ,
+ x: ,
+ check: ,
+ refresh: <> >,
+ trash: <> >,
+ grip: <> >,
+ more: <> >,
+ pin: <> >,
+ link: <> >,
+ message: ,
+ task: <> >,
+ book: <> >,
+ kanban: <> >,
+ timer: <> >,
+ pencil: <> >,
+ calendar: <> >,
+ wallet: <> >,
+ flag: <> >,
+ gavel: <> >,
+ scale: <> >,
+ brain: <> >,
+ alert: <> >,
+ notes: <> >,
+ list: <> >,
+ zap: ,
+ flame: <> >,
+ heart: ,
+ graph: <> >,
+ award: <> >,
+ network: <> >,
+ database: <> >,
+ flask: <> >,
+ shield: ,
+ music: <> >,
+ bullseye: <> >,
+ arrow: <> >,
+ copy: <> >,
+ download: <> >,
+ play: ,
+ pause: <> >,
+ reset: <> >,
+ star: ,
+ shuffle: <> >,
+ expand: <> >,
+ resize: <> >,
+ smile: <> >,
+ send: ,
+ chevron: ,
+ user: <> >,
+ cite: <> >,
+ pinned: <> >,
+ microscope: <> >,
+ sun: <> >,
+ moon: ,
+ back: <> >,
+};
+
+const Icon = ({ name, size = 16, className = '', style }) => {
+ const paths = ICONS[name];
+ if (!paths) return null;
+ return (
+
+ {paths}
+
+ );
+};
+
+export default Icon;
diff --git a/phd-advisor-frontend/src/components/canvas/CanvasWelcomeTour.js b/phd-advisor-frontend/src/components/canvas/CanvasWelcomeTour.js
new file mode 100644
index 00000000..83e46ed2
--- /dev/null
+++ b/phd-advisor-frontend/src/components/canvas/CanvasWelcomeTour.js
@@ -0,0 +1,87 @@
+import React, { useState, useEffect } from 'react';
+import Icon from './CanvasIcon';
+
+const TOUR_KEY = 'canvas-tour-seen-v1';
+
+const STEPS = [
+ {
+ title: 'Welcome to your Canvas',
+ icon: 'sparkles',
+ body: 'AI-summarized highlights from your research conversations live here. Each insight is a discrete task you can mark open, in-progress, completed, or abandoned.',
+ },
+ {
+ title: 'Filter, sort, pin',
+ icon: 'layout',
+ body: 'Use the filter chips to narrow by status, category, or confidence. Pin the most important sections to keep them at the top. The Tasks view flattens everything into a single to-do list.',
+ },
+ {
+ title: 'Ask follow-up',
+ icon: 'message',
+ body: 'Each insight has an "Ask follow-up" action that opens a fresh chat with the relevant context preloaded — useful when a synthesis raises a new question worth digging into.',
+ },
+];
+
+const CanvasWelcomeTour = ({ forceShow = false, onClose }) => {
+ const [step, setStep] = useState(0);
+ const [visible, setVisible] = useState(false);
+
+ useEffect(() => {
+ const seen = localStorage.getItem(TOUR_KEY);
+ if (forceShow || !seen) setVisible(true);
+ }, [forceShow]);
+
+ const dismiss = () => {
+ localStorage.setItem(TOUR_KEY, '1');
+ setVisible(false);
+ if (onClose) onClose();
+ };
+
+ if (!visible) return null;
+
+ const s = STEPS[step];
+ const isLast = step === STEPS.length - 1;
+
+ return (
+ { if (e.target === e.currentTarget) dismiss(); }}>
+
e.stopPropagation()}>
+
+
+
+
{s.title}
+
Step {step + 1} of {STEPS.length}
+
+
+
+
+
+
+ {STEPS.map((_, i) => (
+ setStep(i)}
+ style={{
+ width: 8, height: 8, borderRadius: '50%', cursor: 'pointer',
+ background: i === step ? 'var(--canvas-accent)' : 'var(--canvas-surface-3)',
+ transition: 'background .15s',
+ }}/>
+ ))}
+
+
+ {step > 0 && (
+ setStep(s => s - 1)}>Back
+ )}
+ {!isLast && (
+ Skip
+ )}
+ isLast ? dismiss() : setStep(s => s + 1)}>
+ {isLast ? <>Get started> : <>Next>}
+
+
+
+
+
+ );
+};
+
+export default CanvasWelcomeTour;
diff --git a/phd-advisor-frontend/src/components/canvas/canvasData.js b/phd-advisor-frontend/src/components/canvas/canvasData.js
new file mode 100644
index 00000000..aa35c8df
--- /dev/null
+++ b/phd-advisor-frontend/src/components/canvas/canvasData.js
@@ -0,0 +1,124 @@
+// Demo data for a Year-2 PhD student in computational neuroscience.
+
+export const DEMO_PROJECT = {
+ title: "Cortical Predictive Coding in Mouse V1",
+ meta: "Year 2 · PhD · Adv. Dr. Reineke",
+};
+
+export const INSIGHTS = [
+ {
+ id: 'i-progress',
+ title: 'Research progress',
+ icon: 'graph',
+ category: 'progress',
+ confidence: 78,
+ summary: 'Primary recordings from 4 of 6 planned animals are complete. Remaining two scheduled for May 18 and May 25. Analysis pipeline working on existing data; first results draft expected June.',
+ bullets: [
+ 'V1 recordings: 4/6 animals complete (M1–M4)',
+ 'Pipeline: spike-sorting validated, GLM model converging on M1–M2',
+ 'Risk: M3 fixation drift suspected; need re-review with adv.',
+ ],
+ pinned: true,
+ sources: 12,
+ updatedMinutesAgo: 3,
+ quotes: [
+ '"Animal M4 recording finished today, sorting completes tomorrow." — May 6 lab notes',
+ '"Pipeline is happy with M1, M2; M3 looks drifty." — chat with Reineke advisor',
+ ],
+ },
+ {
+ id: 'i-method',
+ title: 'Methodology',
+ icon: 'flask',
+ category: 'theory',
+ confidence: 64,
+ summary: 'GLM with spike-history kernel + visual drive is your declared model. You\'ve resisted committing to a specific predictive-coding formulation; this comes up in every advisor meeting.',
+ bullets: [
+ 'Decided: GLM with history kernel + drift-reg covariates',
+ 'Open: which predictive-coding variant — Rao & Ballard vs. Bastos top-down',
+ 'Open: how to operationalize "prediction error" from extracellular spikes',
+ ],
+ sources: 8,
+ updatedMinutesAgo: 12,
+ quotes: [
+ '"Need to pick a PC formulation by next 1:1." — meeting notes May 2',
+ '"Bastos lets you predict laminar profile; Rao&Ballard does not." — methodologist chat',
+ ],
+ },
+ {
+ id: 'i-lit',
+ title: 'Literature review',
+ icon: 'book',
+ category: 'literature',
+ confidence: 71,
+ summary: 'Strong on canonical predictive coding (Rao & Ballard 1999, Bastos 2012, Keller & Mrsic-Flogel 2018). Thin on recent feedback-circuit anatomy and on counter-evidence — this is showing up as a critique gap.',
+ bullets: [
+ 'Coverage: 47 papers; ~30 well-summarized',
+ 'Gap: sparse on L5b feedback anatomy (Harris/Shepherd lab)',
+ 'Gap: no engagement with anti-PC critiques (e.g. Heeger 2017)',
+ ],
+ sources: 47,
+ updatedMinutesAgo: 22,
+ quotes: [
+ '"Have you read Heeger 2017? It changes a lot." — lit-review aide',
+ '"L5b feedback anatomy is your weak spot." — devil\'s advocate',
+ ],
+ },
+ {
+ id: 'i-questions',
+ title: 'Open research questions',
+ icon: 'sparkles',
+ category: 'theory',
+ confidence: 58,
+ summary: 'Three live threads. Question 1 (does L2/3 spiking encode prediction error?) is the dissertation core. Q2 and Q3 are scoped to specific aims.',
+ bullets: [
+ 'Q1: Does L2/3 firing during oddball encode prediction error vs. surprise?',
+ 'Q2: How does this depend on context length (1 vs. 4 vs. 16 trials)?',
+ 'Q3: Is the signal sharpened by feedback from V2/RSC?',
+ ],
+ sources: 6,
+ updatedMinutesAgo: 38,
+ quotes: [
+ '"Q1 is what the whole dissertation rests on." — methodologist',
+ '"Q3 is exciting but probably out of scope for the thesis." — Reineke',
+ ],
+ },
+ {
+ id: 'i-next',
+ title: 'Next steps',
+ icon: 'arrow',
+ category: 'action',
+ confidence: 82,
+ summary: 'Concrete, near-term actions. Two of these have been on the list for 3+ weeks.',
+ bullets: [
+ 'Re-review M3 drift artifact w/ adv. (overdue, 3w)',
+ 'Draft Aim 2 analysis section (target: May 22)',
+ 'Read Heeger 2017 + Aitchison & Lengyel 2017',
+ 'Schedule pilot with M5 (May 18)',
+ ],
+ sources: 5,
+ updatedMinutesAgo: 8,
+ quotes: [
+ '"Aim 2 draft has to land by May 22 or quals slip." — Reineke',
+ '"M3 review keeps getting punted." — last 3 advisor meetings',
+ ],
+ },
+ {
+ id: 'i-blockers',
+ title: 'Blockers & risks',
+ icon: 'alert',
+ category: 'risk',
+ confidence: 70,
+ summary: 'One technical, one structural. The structural one is more important and you are deferring it.',
+ bullets: [
+ 'Technical: Drift on M3 — may lose 1 animal of data',
+ 'Structural: No clear predictive-coding theory commitment yet → hard to define what counts as evidence',
+ ],
+ sources: 4,
+ updatedMinutesAgo: 18,
+ quotes: [
+ '"If M3 is unusable you\'re at n=5 — still publishable but tight." — methodologist',
+ '"Without a theory commitment you can\'t falsify anything." — devil\'s advocate',
+ ],
+ },
+];
diff --git a/phd-advisor-frontend/src/components/canvas/platform.js b/phd-advisor-frontend/src/components/canvas/platform.js
new file mode 100644
index 00000000..499766c1
--- /dev/null
+++ b/phd-advisor-frontend/src/components/canvas/platform.js
@@ -0,0 +1,5 @@
+// Tiny helper so keyboard shortcut hints adapt to the user's OS.
+// Detect once at module load — we don't expect platform to change mid-session.
+const isMac = typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
+export const MOD = isMac ? '⌘' : 'Ctrl';
+export const MOD_LABEL = isMac ? 'Cmd' : 'Ctrl';
diff --git a/phd-advisor-frontend/src/pages/CanvasPage.js b/phd-advisor-frontend/src/pages/CanvasPage.js
index 64e005d7..f5066df1 100644
--- a/phd-advisor-frontend/src/pages/CanvasPage.js
+++ b/phd-advisor-frontend/src/pages/CanvasPage.js
@@ -1,574 +1,701 @@
-import React, { useState, useEffect } from 'react';
-import {
- FileText,
- RefreshCw,
- Download,
- Calendar,
- TrendingUp,
- Target,
- BookOpen,
- Lightbulb,
- AlertTriangle,
- Users,
- BarChart3,
- Heart,
- ArrowLeft,
- Printer,
- Trash2,
- MessageCircle,
- ArrowRight
-} from 'lucide-react';
+import React, { useState, useEffect, useMemo } from 'react';
+import { HelpCircle } from 'lucide-react';
+import { useTheme } from '../contexts/ThemeContext';
import { useAppConfig } from '../contexts/AppConfigContext';
-import CopyrightNotice from '../components/CopyrightNotice';
-import ConfirmDialog from '../components/ConfirmDialog';
+import Sidebar from '../components/Sidebar';
+import AppHeader from '../components/AppHeader';
+import Icon from '../components/canvas/CanvasIcon';
+import { INSIGHTS } from '../components/canvas/canvasData';
+import CanvasWelcomeTour from '../components/canvas/CanvasWelcomeTour';
+import { MOD } from '../components/canvas/platform';
import '../styles/CanvasPage.css';
-// Section icons mapping
-const sectionIcons = {
- research_progress: TrendingUp,
- methodology: BarChart3,
- theoretical_framework: BookOpen,
- challenges_obstacles: AlertTriangle,
- next_steps: Target,
- writing_communication: FileText,
- career_development: Users,
- literature_review: BookOpen,
- data_analysis: BarChart3,
- motivation_mindset: Heart
-};
+const VIEW_KEY = 'canvas-view-v2';
+const STATES_KEY = 'canvas-states-v2';
-const CanvasSection = ({ section, sectionKey, isExpanded, onToggle }) => {
- const IconComponent = sectionIcons[sectionKey] || Lightbulb;
-
- return (
-
-
onToggle(sectionKey)}
- >
-
-
-
-
{section.title}
-
{section.description}
-
-
-
-
{section.insights.length} insights
-
- ▼
-
-
-
-
- {isExpanded && (
-
- {section.insights.length === 0 ? (
-
-
-
No insights yet. Keep chatting with your advisors to build this section!
-
- ) : (
-
- {section.insights.map((insight, index) => (
-
-
- {insight.content}
-
-
- {insight.source_persona}
-
- {Math.round(insight.confidence_score * 100)}% confidence
-
-
-
- ))}
-
- )}
-
- )}
-
- );
+// ============================================================================
+// Insights view — AI-synthesized highlights with stats bar, filters, and
+// click-to-expand source quotes.
+// ============================================================================
+const INSIGHT_CATEGORIES = [
+ { id: 'all', label: 'All' },
+ { id: 'open', label: 'Open' },
+ { id: 'in-progress', label: 'In progress' },
+ { id: 'completed', label: 'Completed' },
+ { id: 'abandoned', label: 'Abandoned' },
+ { id: 'pinned', label: 'Pinned' },
+ { id: 'high', label: 'High confidence' },
+ { id: 'progress', label: 'Progress' },
+ { id: 'theory', label: 'Theory' },
+ { id: 'literature', label: 'Literature' },
+ { id: 'action', label: 'Actions' },
+ { id: 'risk', label: 'Risks' },
+];
+const CATEGORY_TINT = {
+ progress: 'rgba(16, 185, 129, 0.12)',
+ theory: 'rgba(99, 102, 241, 0.12)',
+ literature: 'rgba(245, 158, 11, 0.12)',
+ action: 'rgba(59, 130, 246, 0.12)',
+ risk: 'rgba(220, 38, 38, 0.12)',
+};
+const CATEGORY_FG = {
+ progress: '#10B981',
+ theory: '#818CF8',
+ literature: '#F59E0B',
+ action: '#3B82F6',
+ risk: '#DC2626',
};
+const confidenceTier = (c) => c >= 75 ? 'high' : c >= 60 ? 'med' : 'low';
+
+// Task statuses live on each individual bullet within an insight, not on the
+// whole card — Daniel's feedback: "each card is a discrete task, not a set of tasks".
+const TASK_STATUSES = [
+ { id: 'open', label: 'Open', color: 'var(--canvas-text-3)', icon: 'sparkles' },
+ { id: 'in-progress', label: 'In progress', color: '#3B82F6', icon: 'graph' },
+ { id: 'completed', label: 'Completed', color: '#10B981', icon: 'check' },
+ { id: 'abandoned', label: 'Abandoned', color: 'var(--canvas-text-4)', icon: 'x' },
+];
+const TASK_STATUS_KEY = 'canvas-task-status-v1';
+const taskKey = (insId, idx) => `${insId}::${idx}`;
-const CanvasPage = ({ user, authToken, onNavigateToChat, onSignOut }) => {
- const { config } = useAppConfig();
- const appName = config?.app_settings?.app_name || 'Advisory Panel';
- const [canvasData, setCanvasData] = useState(null);
- const [isLoading, setIsLoading] = useState(true);
- const [isUpdating, setIsUpdating] = useState(false);
- const [expandedSections, setExpandedSections] = useState({});
- const [stats, setStats] = useState({});
- const [isPrintView, setIsPrintView] = useState(false);
- const [isProcessingFirstTime, setIsProcessingFirstTime] = useState(false);
- const [isRefreshing, setIsRefreshing] = useState(false);
- const [showClearConfirm, setShowClearConfirm] = useState(false);
- const [isClearing, setIsClearing] = useState(false);
+function InsightsView({ widgetStates, setWidgetStates, onNavigateToChat }) {
+ const [pinned, setPinned] = useState(() => new Set(INSIGHTS.filter(i => i.pinned).map(i => i.id)));
+ const [taskStatuses, setTaskStatuses] = useState(() => {
+ try { return JSON.parse(localStorage.getItem(TASK_STATUS_KEY) || '{}'); } catch { return {}; }
+ });
+ const [filter, setFilter] = useState('all');
+ const [sortBy, setSortBy] = useState('confidence');
+ const [expanded, setExpanded] = useState(new Set());
+ const [refreshing, setRefreshing] = useState(false);
+ const [openStatusMenu, setOpenStatusMenu] = useState(null);
+ // 'cards' = current cards-of-tasks layout, 'tasks' = flat task list per Daniel's
+ // "Sections in sidebar, Tasks in the main view" suggestion.
+ const [viewMode, setViewMode] = useState(() => localStorage.getItem('canvas-insights-view') || 'cards');
+ useEffect(() => { localStorage.setItem('canvas-insights-view', viewMode); }, [viewMode]);
useEffect(() => {
- let pollInterval = null;
-
- const initializeCanvas = async () => {
- await fetchCanvas();
- await fetchStats();
- await triggerAutoUpdate();
-
- setTimeout(() => {
- checkForEmptyCanvasWithChats();
- }, 2000);
- };
-
- initializeCanvas();
-
- // Cleanup on unmount
- return () => {
- if (pollInterval) {
- clearInterval(pollInterval);
- }
- };
- }, []);
+ localStorage.setItem(TASK_STATUS_KEY, JSON.stringify(taskStatuses));
+ }, [taskStatuses]);
- const fetchCanvas = async () => {
- try {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/phd-canvas`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const data = await response.json();
- setCanvasData(data);
-
- // Auto-expand sections with insights
- const sectionsToExpand = {};
- Object.entries(data.sections).forEach(([key, section]) => {
- if (section.insights.length > 0) {
- sectionsToExpand[key] = true;
- }
- });
- setExpandedSections(sectionsToExpand);
- } else {
- console.error('Failed to fetch canvas');
- }
- } catch (error) {
- console.error('Error fetching canvas:', error);
- } finally {
- setIsLoading(false);
- }
+ const taskStatusOf = (insId, idx) => taskStatuses[taskKey(insId, idx)] || 'open';
+ const setTaskStatus = (insId, idx, status) => {
+ setTaskStatuses(prev => ({ ...prev, [taskKey(insId, idx)]: status }));
+ const lbl = TASK_STATUSES.find(s => s.id === status)?.label || status;
+ window.dispatchEvent(new CustomEvent('canvas-toast', { detail: { msg: `Task marked ${lbl}`, kind: 'success' } }));
};
- const fetchStats = async () => {
- try {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/phd-canvas/stats`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const data = await response.json();
- setStats(data);
- }
- } catch (error) {
- console.error('Error fetching stats:', error);
- }
+ // Roll up to a card-level status: completed if all tasks done, abandoned if all abandoned,
+ // in-progress if any are in-progress, otherwise open.
+ const insightRollup = (ins) => {
+ const states = ins.bullets.map((_, idx) => taskStatusOf(ins.id, idx));
+ const total = states.length;
+ if (total === 0) return { state: 'open', done: 0, total: 0, pct: 0 };
+ const done = states.filter(s => s === 'completed').length;
+ const inProg = states.filter(s => s === 'in-progress').length;
+ const abandoned = states.filter(s => s === 'abandoned').length;
+ let state = 'open';
+ if (done === total) state = 'completed';
+ else if (abandoned === total) state = 'abandoned';
+ else if (inProg > 0 || done > 0) state = 'in-progress';
+ return { state, done, total, inProg, abandoned, pct: Math.round((done / total) * 100) };
};
- // Check if user has chats but empty canvas
- const checkForEmptyCanvasWithChats = async () => {
- try {
- const isEmpty = !canvasData || canvasData.total_insights === 0;
-
- if (isEmpty) {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/chat-sessions/count`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const { count } = await response.json();
- if (count > 0) {
- console.log(`User has ${count} chats but empty canvas. Triggering full refresh.`);
- await handleFullRefresh();
- }
- } else {
- console.error('Failed to fetch chat sessions count:', response.status);
- // Don't trigger refresh if count fails
- return;
- }
- }
- } catch (error) {
- console.error('Error checking for empty canvas with chats:', error);
- // Stop the polling if there's an error
- return;
- }
+ const togglePin = (id) => {
+ setPinned(prev => {
+ const n = new Set(prev);
+ if (n.has(id)) n.delete(id); else n.add(id);
+ return n;
+ });
};
-
- const triggerAutoUpdate = async () => {
- try {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/phd-canvas/auto-update`, {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const result = await response.json();
-
- // If this is a first-time canvas update, show appropriate message
- if (result.type === 'full_update') {
- console.log('First-time canvas detected. Processing all your chats...');
-
- // Show loading state
- setIsProcessingFirstTime(true);
- setIsUpdating(true);
-
- // Poll for updates every 10 seconds for up to 3 minutes
- let attempts = 0;
- const maxAttempts = 18; // 3 minutes / 10 seconds
-
- const pollForUpdates = setInterval(async () => {
- attempts++;
-
- try {
- await fetchCanvas();
-
- // If canvas now has insights, stop polling
- if (canvasData && canvasData.total_insights > 0) {
- clearInterval(pollForUpdates);
- setIsUpdating(false);
- setIsProcessingFirstTime(false);
- console.log('Canvas successfully populated with insights!');
- }
-
- // Stop polling after max attempts
- if (attempts >= maxAttempts) {
- clearInterval(pollForUpdates);
- setIsUpdating(false);
- setIsProcessingFirstTime(false);
- }
- } catch (error) {
- console.error('Error polling for updates:', error);
- }
- }, 10000);
- }
- }
- } catch (error) {
- console.error('Error triggering auto-update:', error);
- }
+ const toggleExpand = (id) => {
+ setExpanded(prev => {
+ const n = new Set(prev);
+ if (n.has(id)) n.delete(id); else n.add(id);
+ return n;
+ });
};
- const handleClearCanvas = async () => {
- setIsClearing(true);
- try {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/phd-canvas`, {
- method: 'DELETE',
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
- if (response.ok) {
- setCanvasData(null);
- await fetchCanvas();
- }
- } catch (error) {
- console.error('Error clearing canvas:', error);
- } finally {
- setIsClearing(false);
- setShowClearConfirm(false);
- }
+ const insightToTaskTitle = (ins) => {
+ const plain = (ins.bullets[0] || ins.summary || ins.title).replace(/<[^>]+>/g, '');
+ return plain.length > 80 ? plain.slice(0, 77) + '…' : plain;
};
- const handleRefreshCanvas = async () => {
- // Prevent multiple simultaneous refresh requests
- if (isRefreshing || isUpdating) {
- console.log('Refresh already in progress, ignoring duplicate request');
- return;
- }
-
- setIsRefreshing(true);
- setIsUpdating(true);
-
- try {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/phd-canvas/refresh`, {
- method: 'GET',
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const result = await response.json();
- console.log('Full refresh initiated:', result);
-
- // Poll for updates
- setTimeout(() => {
- fetchCanvas();
- fetchStats();
- }, 5000);
-
- setTimeout(() => {
- setIsUpdating(false);
- setIsRefreshing(false);
- }, 10000);
- }
- } catch (error) {
- console.error('Error refreshing canvas:', error);
- setIsUpdating(false);
- setIsRefreshing(false);
- }
+ // Pre-stages a kanban card in shared widget state so that when the user adds a
+ // Kanban widget (next PR) the work transferred from Insights is already there.
+ const sendToKanban = (ins) => {
+ if (!setWidgetStates) return;
+ const kanban = widgetStates.kanban || { cards: [] };
+ const card = {
+ id: 'k' + Date.now(),
+ col: 'todo',
+ title: insightToTaskTitle(ins),
+ priority: 'med',
+ meta: `from Insights · ${ins.title}`,
+ };
+ setWidgetStates(s => ({ ...s, kanban: { ...kanban, cards: [...(kanban.cards || []), card] } }));
+ window.dispatchEvent(new CustomEvent('canvas-toast', { detail: { msg: 'Sent to Kanban (To Do)', kind: 'success' } }));
};
- const handleFullRefresh = async () => {
- // Prevent multiple simultaneous refresh requests
- if (isRefreshing || isUpdating) {
- console.log('Refresh already in progress, ignoring duplicate request');
- return;
- }
-
- setIsRefreshing(true);
- setIsUpdating(true);
-
- try {
- const response = await fetch(`${process.env.REACT_APP_API_URL}/api/phd-canvas/refresh`, {
- method: 'GET',
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const result = await response.json();
- console.log('Full refresh initiated:', result);
-
- // Poll for updates
- setTimeout(() => {
- fetchCanvas();
- fetchStats();
- }, 5000);
-
- setTimeout(() => {
- setIsUpdating(false);
- setIsRefreshing(false);
- }, 10000);
- }
- } catch (error) {
- console.error('Error refreshing canvas:', error);
- setIsUpdating(false);
- setIsRefreshing(false);
- }
+ // TODO(LLM): real refresh hits the orchestrator and re-synthesizes insights.
+ const handleRefresh = () => {
+ setRefreshing(true);
+ setTimeout(() => setRefreshing(false), 900);
+ window.dispatchEvent(new CustomEvent('canvas-toast', { detail: { msg: 'Refreshing insights… (stub)', kind: 'success' } }));
};
- const toggleSection = (sectionKey) => {
- setExpandedSections(prev => ({
- ...prev,
- [sectionKey]: !prev[sectionKey]
+ // "Ask follow-up": stash a draft prompt + insight context that the chat page
+ // can pick up on next navigation. Backend hookup TODO: include source-conversation IDs.
+ const askFollowUp = (ins) => {
+ const plain = (ins.bullets[0] || ins.summary || '').replace(/<[^>]+>/g, '');
+ const prompt = `Follow up on the insight "${ins.title}": ${plain}`;
+ try {
+ localStorage.setItem('canvas-chat-handoff', JSON.stringify({
+ at: Date.now(),
+ prompt,
+ insightId: ins.id,
+ insightTitle: ins.title,
+ }));
+ } catch { /* ignore */ }
+ window.dispatchEvent(new CustomEvent('canvas-toast', {
+ detail: { msg: `Follow-up drafted: "${ins.title}" — opening chat`, kind: 'success' },
}));
+ // Use the existing navigation prop if present; falls back to the global event.
+ if (onNavigateToChat) onNavigateToChat();
};
- const handlePrint = () => {
- setIsPrintView(true);
- setTimeout(() => {
- window.print();
- setIsPrintView(false);
- }, 100);
- };
+ const filtered = useMemo(() => {
+ let r = INSIGHTS;
+ if (filter === 'pinned') r = r.filter(i => pinned.has(i.id));
+ else if (filter === 'high') r = r.filter(i => i.confidence >= 75);
+ else if (['open', 'in-progress', 'completed', 'abandoned'].includes(filter)) r = r.filter(i => insightRollup(i).state === filter);
+ else if (filter !== 'all') r = r.filter(i => i.category === filter);
+ if (sortBy === 'confidence') r = [...r].sort((a, b) => b.confidence - a.confidence);
+ if (sortBy === 'recent') r = [...r].sort((a, b) => (a.updatedMinutesAgo || 0) - (b.updatedMinutesAgo || 0));
+ if (sortBy === 'progress') r = [...r].sort((a, b) => insightRollup(b).pct - insightRollup(a).pct);
+ return r;
+ }, [filter, sortBy, pinned, taskStatuses]); // eslint-disable-line react-hooks/exhaustive-deps
- const formatDate = (dateString) => {
- if (!dateString) return 'Never';
- return new Date(dateString).toLocaleDateString();
- };
+ // Aggregate stats over individual TASKS, not insights (Daniel's framing)
+ const stats = useMemo(() => {
+ const allTasks = INSIGHTS.flatMap(i => i.bullets.map((_, idx) => taskStatusOf(i.id, idx)));
+ const taskTotal = allTasks.length;
+ const completed = allTasks.filter(s => s === 'completed').length;
+ const inProgress = allTasks.filter(s => s === 'in-progress').length;
+ const abandoned = allTasks.filter(s => s === 'abandoned').length;
+ const open = taskTotal - completed - inProgress - abandoned;
+ const totalSources = INSIGHTS.reduce((s, i) => s + (i.sources || 0), 0);
+ const avgConf = Math.round(INSIGHTS.reduce((s, i) => s + i.confidence, 0) / INSIGHTS.length);
+ return {
+ sections: INSIGHTS.length, taskTotal, completed, inProgress, abandoned, open,
+ totalSources, avgConf, pinnedCount: pinned.size,
+ };
+ }, [pinned, taskStatuses]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ const lastUpdated = Math.min(...INSIGHTS.map(i => i.updatedMinutesAgo || 0));
- if (isLoading) {
+ // Empty state — defensive (current data is hardcoded but a real backend could send [])
+ if (INSIGHTS.length === 0) {
return (
-
-
-
Loading your {appName} Canvas...
-
+ <>
+
+
+
Insights
+
AI-synthesized from your research conversations.
+
+
+
+
+
No insights yet
+
Have a conversation with your advisors and insights will appear here.
+
+ >
);
}
- // Sort sections by priority and insights count
- const sortedSections = Object.entries(canvasData?.sections || {})
- .sort(([, a], [, b]) => {
- // First by priority (lower number = higher priority)
- if (a.priority !== b.priority) {
- return a.priority - b.priority;
- }
- // Then by insights count (more insights first)
- return b.insights.length - a.insights.length;
- });
-
return (
-
- {/* Header */}
-
-
-
-
- Back to Chat
-
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
Insights
+
AI-synthesized from your research conversations.
+
+
-
setShowClearConfirm(true)}
- disabled={isClearing}
- title="Clear Canvas"
- aria-label="Clear canvas"
- >
-
-
+ {/* Stats bar */}
+
+
+ {stats.completed}/{stats.taskTotal}
+ tasks done
+
+
+
+
+
+
+
+ {stats.completed} done · {stats.inProgress} in progress · {stats.open} open
+ {stats.abandoned > 0 && ` · ${stats.abandoned} abandoned`}
+
-
-
-
-
- {appName} Canvas
-
-
Your research progress at a glance
+
+ {stats.sections}
+ sections
+
+ {stats.avgConf}%
+ avg confidence
+
+
+
+
+ updated {lastUpdated} min ago
+
+
+ {refreshing ?
: }
+ Refresh
+
- {/* Stats Bar */}
-
-
- {canvasData?.total_insights || 0}
- Total Insights
-
-
-
- {Object.keys(canvasData?.sections || {}).filter(key =>
- canvasData.sections[key].insights.length > 0
- ).length}
-
-
Active Sections
+ {/* View toggle: Cards (sections with their tasks) vs Tasks (flat list) */}
+
+ setViewMode('cards')}>
+ Cards
+
+ setViewMode('tasks')}>
+ Tasks
+
+
+
+ {/* Filter + sort */}
+
+
+ {INSIGHT_CATEGORIES.map(c => {
+ const count =
+ c.id === 'all' ? INSIGHTS.length :
+ c.id === 'pinned' ? pinned.size :
+ c.id === 'high' ? INSIGHTS.filter(i => i.confidence >= 75).length :
+ ['open', 'in-progress', 'completed', 'abandoned'].includes(c.id) ? INSIGHTS.filter(i => insightRollup(i).state === c.id).length :
+ INSIGHTS.filter(i => i.category === c.id).length;
+ if (count === 0 && c.id !== 'all') return null;
+ return (
+ setFilter(c.id)}>
+ {c.label}{count}
+
+ );
+ })}
-
-
- {formatDate(canvasData?.last_updated)}
+ setSortBy(e.target.value)}>
+ ↓ confidence
+ ↓ recent
+ ↓ progress
+
+
+
+ {/* Pinned strip — only when there are pins and we're not already filtering by pinned */}
+ {pinned.size > 0 && filter !== 'pinned' && (
+
+
+ Pinned
- Last Updated
+ {INSIGHTS.filter(i => pinned.has(i.id)).map(ins => (
+ {
+ const el = document.getElementById(`insight-${ins.id}`);
+ if (el) {
+ el.scrollIntoView({ block: 'center', behavior: 'smooth' });
+ el.style.boxShadow = '0 0 0 2px var(--canvas-accent), 0 0 24px var(--canvas-accent-glow)';
+ setTimeout(() => { el.style.boxShadow = ''; }, 1400);
+ }
+ }}>
+ {ins.title}
+
+ ))}
-
+ )}
- {/* Canvas Content */}
-
- {sortedSections.length === 0 ? (
-
-
-
Your Canvas is Empty
- {isUpdating || isProcessingFirstTime ? (
-
-
- {isProcessingFirstTime
- ? 'Processing your chat history to populate insights...'
- : 'Updating canvas with latest insights...'
- }
-
-
-
-
-
- This may take a few minutes for extensive chat history.
-
-
- ) : (
-
-
Start chatting with your AI advisors to populate your {appName} Canvas with insights!
-
+ {/* TASKS view — flat list of every bullet across insights */}
+ {viewMode === 'tasks' && (() => {
+ const allTasks = filtered.flatMap(ins => ins.bullets.map((b, idx) => ({
+ ins, idx, text: b, status: taskStatusOf(ins.id, idx),
+ })));
+ const statusOrder = { open: 0, 'in-progress': 1, completed: 2, abandoned: 3 };
+ const sorted = [...allTasks].sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
+ if (sorted.length === 0) {
+ return (
+
+
+
No tasks match this filter
+
setFilter('all')}>Show all
+
+ );
+ }
+ return (
+
+ {sorted.map(({ ins, idx, text, status }) => {
+ const meta = TASK_STATUSES.find(s => s.id === status);
+ const menuKey = `tv::${ins.id}::${idx}`;
+ const menuOpen = openStatusMenu === menuKey;
+ return (
+
setOpenStatusMenu(menuOpen ? null : menuKey)}
+ title={`Status: ${meta.label}`}
>
-
- Start Chatting
-
+ {status === 'completed' && }
+ {status === 'in-progress' && }
+ {status === 'abandoned' && }
-
-
- Process Existing Chats
+
+ {
+ setViewMode('cards');
+ setTimeout(() => {
+ const el = document.getElementById(`insight-${ins.id}`);
+ if (el) el.scrollIntoView({ block: 'center', behavior: 'smooth' });
+ }, 60);
+ }}
+ title="Jump to this section"
+ >
+ {ins.title}
+
+
+
+ sendToKanban(ins)} title="Send this section's open tasks to Kanban">
+ Kanban
+ {menuOpen && (
+ setOpenStatusMenu(null)}>
+ {TASK_STATUSES.map(s => (
+ { setTaskStatus(ins.id, idx, s.id); setOpenStatusMenu(null); }}>
+
+ {s.label}
+
+ ))}
+
+ )}
-
- )}
+ );
+ })}
- ) : (
-
- {sortedSections.map(([sectionKey, section]) => (
-
- ))}
-
- )}
-
+ );
+ })()}
- {/* Copyright Footer */}
-
+ {/* CARDS view (default) */}
+ {viewMode === 'cards' && (filtered.length === 0 ? (
+
+
+
No insights match this filter
+
setFilter('all')}>Show all
+
+ ) : (
+
+ {filtered.map(ins => {
+ const isExpanded = expanded.has(ins.id);
+ const isPinned = pinned.has(ins.id);
+ const tier = confidenceTier(ins.confidence);
+ const tint = CATEGORY_TINT[ins.category] || 'var(--canvas-surface-2)';
+ const fg = CATEGORY_FG[ins.category] || 'var(--canvas-accent)';
+ const rollup = insightRollup(ins);
+ return (
+
+
- {/* Print Footer */}
- {isPrintView && (
-
-
Generated by {appName} - {new Date().toLocaleDateString()}
-
Student: {user?.email} | Total Insights: {canvasData?.total_insights || 0}
+ {/* Per-card progress (rolls up the bullet tasks) */}
+ {rollup.total > 0 && (
+
+
+ {rollup.done}/{rollup.total} tasks
+ {rollup.state === 'completed' && ✓ Resolved }
+ {rollup.state === 'abandoned' && Abandoned }
+ {rollup.state === 'in-progress' && In progress }
+
+
+
+ {rollup.inProg > 0 && }
+ {rollup.abandoned > 0 && }
+
+
+ )}
+
+
+
+ {/* Detail panel — quotes from sources, only when expanded */}
+ {isExpanded && ins.quotes && (
+
+
Source quotes · {ins.sources} {ins.sources === 1 ? 'source' : 'sources'}
+ {ins.quotes.map((q, i) => (
+
{q}
+ ))}
+
+ )}
+
+
+
+ {ins.sources}
+
+ updated {ins.updatedMinutesAgo}m ago
+
+
+
+
+ askFollowUp(ins)} title="Open this insight in a new chat session">
+ Ask follow-up
+
+ sendToKanban(ins)} title="Add all open tasks from this insight to your Kanban (To Do)">
+ Add to Kanban
+
+ toggleExpand(ins.id)}>
+ {isExpanded ? 'Collapse' : 'Source quotes'}
+
+ togglePin(ins.id)}>
+ {isPinned ? 'Pinned' : 'Pin'}
+
+
+
+ );
+ })}
- )}
+ ))}
+ >
+ );
+}
-
setShowClearConfirm(false)}
+// Small SVG ring used for the confidence indicator
+function ConfidenceRing({ value }) {
+ const r = 14;
+ const c = 2 * Math.PI * r;
+ const dash = c * (1 - value / 100);
+ const tier = confidenceTier(value);
+ const color = tier === 'high' ? '#10B981' : tier === 'med' ? '#F59E0B' : '#DC2626';
+ return (
+
+
+
+
+
+ {value}
+
+ );
+}
+
+function ToastStack() {
+ const [toasts, setToasts] = useState([]);
+ useEffect(() => {
+ const handler = (e) => {
+ const id = Date.now() + Math.random();
+ setToasts(t => [...t, { id, ...e.detail }]);
+ setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3500);
+ };
+ window.addEventListener('canvas-toast', handler);
+ return () => window.removeEventListener('canvas-toast', handler);
+ }, []);
+ return (
+
+ {toasts.map(t => (
+
+
+ {t.msg}
+
+ ))}
+
+ );
+}
+
+
+const CanvasPage = ({ user, authToken, onNavigateToHome, onNavigateToChat, onSignOut }) => {
+ const { theme } = useTheme();
+ useAppConfig();
+ // Insights is the only view available in this PR. Workspace + Documents land
+ // in follow-up PRs but the storage key + tab-switching plumbing stay so those
+ // PRs can extend without touching this file's structure.
+ const [view, setView] = useState(() => {
+ const saved = localStorage.getItem(VIEW_KEY);
+ return saved === 'insights' ? saved : 'insights';
+ });
+ const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
+ const [tourForceShow, setTourForceShow] = useState(0);
+
+ // Shared widget-state store. Empty in this PR (no widgets yet); kept so the
+ // Insights "Send to Kanban" can pre-stage work for the next PR's Kanban widget.
+ const [widgetStates, setWidgetStates] = useState(() => {
+ try {
+ const saved = localStorage.getItem(STATES_KEY);
+ return saved ? JSON.parse(saved) : {};
+ } catch { return {}; }
+ });
+ useEffect(() => { localStorage.setItem(STATES_KEY, JSON.stringify(widgetStates)); }, [widgetStates]);
+ useEffect(() => { localStorage.setItem(VIEW_KEY, view); }, [view]);
+
+ // Apply canvas theme attribute on body for scoped styling
+ useEffect(() => {
+ document.body.dataset.canvasTheme = theme;
+ return () => { delete document.body.dataset.canvasTheme; };
+ }, [theme]);
+
+ // Critic widgets dispatch `canvas-open-in-chat` when the user wants real LLM history.
+ useEffect(() => {
+ const handler = () => onNavigateToChat && onNavigateToChat();
+ window.addEventListener('canvas-open-in-chat', handler);
+ return () => window.removeEventListener('canvas-open-in-chat', handler);
+ }, [onNavigateToChat]);
+
+ // ? opens the welcome tour for help (matches the icon in the topbar).
+ useEffect(() => {
+ const k = (e) => {
+ if (e.key === '?' && !['INPUT', 'TEXTAREA'].includes(e.target.tagName)) {
+ e.preventDefault();
+ setTourForceShow(n => n + 1);
+ }
+ };
+ window.addEventListener('keydown', k);
+ return () => window.removeEventListener('keydown', k);
+ }, []);
+
+ // Highlight a section when picked from the sidebar
+ const flashScrollTo = (selector) => {
+ const el = document.querySelector(selector);
+ if (!el) return;
+ el.scrollIntoView({ block: 'center', behavior: 'smooth' });
+ el.style.boxShadow = '0 0 0 2px var(--canvas-accent), 0 0 24px var(--canvas-accent-glow)';
+ setTimeout(() => { el.style.boxShadow = ''; }, 1400);
+ };
+
+ // Insights: list of sections — Daniel's feedback said sidebar should show sections here
+ const insightSections = useMemo(() => {
+ let taskMap = {};
+ try { taskMap = JSON.parse(localStorage.getItem(TASK_STATUS_KEY) || '{}'); } catch { /* ignore */ }
+ return INSIGHTS.map(ins => {
+ const states = ins.bullets.map((_, idx) => taskMap[taskKey(ins.id, idx)] || 'open');
+ const done = states.filter(s => s === 'completed').length;
+ return {
+ id: ins.id,
+ name: ins.title,
+ icon: ins.icon,
+ category: ins.category,
+ confidence: ins.confidence,
+ taskCount: ins.bullets.length,
+ doneCount: done,
+ onClick: () => flashScrollTo(`#insight-${ins.id}`),
+ };
+ });
+ }, [view, widgetStates]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ return (
+
+
{}}
+ onSelectSession={(id) => onNavigateToChat && onNavigateToChat(id)}
+ onNewChat={() => onNavigateToChat && onNavigateToChat()}
+ pageContext="canvas"
+ canvasSubview={view}
+ insightSections={insightSections}
/>
+
+
+
setView(v || 'insights')}
+ onMobileMenu={() => setIsMobileMenuOpen(true)}
+ >
+ setTourForceShow(n => n + 1)} title="Show tour">
+
+
+
+
+ {view === 'insights' && }
+
+
+
+
+ 0}/>
+
);
};
-export default CanvasPage;
\ No newline at end of file
+// Subtle floating hint bar showing the help shortcut.
+// Auto-hides on small screens and after the first 12s, until the user hovers.
+function ShortcutHint() {
+ const [visible, setVisible] = useState(true);
+ useEffect(() => {
+ const t = setTimeout(() => setVisible(false), 12000);
+ return () => clearTimeout(t);
+ }, []);
+ return (
+ setVisible(true)}
+ onMouseLeave={() => setVisible(false)}
+ >
+ ? help
+ {MOD}K commands · coming soon
+
+ );
+}
+
+export default CanvasPage;
diff --git a/phd-advisor-frontend/src/styles/CanvasPage.css b/phd-advisor-frontend/src/styles/CanvasPage.css
index 1f1c196f..42e1d100 100644
--- a/phd-advisor-frontend/src/styles/CanvasPage.css
+++ b/phd-advisor-frontend/src/styles/CanvasPage.css
@@ -1,708 +1,3467 @@
-/* CanvasPage.css - PhD Canvas Page Styling */
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
-.canvas-page {
- min-height: 100vh;
+/* Layout: app sidebar + canvas main area (matches chat-page-with-sidebar pattern) */
+.canvas-page-with-sidebar {
+ display: flex;
+ height: 100vh;
+ overflow: hidden;
background: var(--bg-gradient);
- color: var(--text-primary);
- padding: 2rem;
-}
-
-.canvas-page.print-view {
- background: white;
- color: black;
- padding: 1rem;
}
-
-/* Header Section */
-.canvas-header {
+.canvas-main-area {
+ flex: 1;
+ margin-left: 300px;
+ height: 100vh;
+ transition: margin-left 0.3s ease;
+ overflow: hidden;
display: flex;
flex-direction: column;
- gap: 1.25rem;
- margin-bottom: 2rem;
- background: var(--bg-primary);
- padding: 1.5rem;
- border-radius: 16px;
- border: 1px solid var(--border-primary);
- box-shadow: var(--shadow-lg);
}
-
-.canvas-header-top {
+.canvas-main-area.sidebar-collapsed { margin-left: 70px; }
+.canvas-app-shell {
+ flex: 1;
display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 1rem;
+ flex-direction: column;
+ min-height: 0;
+ overflow: hidden;
+}
+@media (max-width: 768px) {
+ .canvas-main-area, .canvas-main-area.sidebar-collapsed { margin-left: 0; }
+}
+
+/* PhD Canvas — color palette matched to the rest of the app
+ (Tailwind gray + indigo accent + violet "wedge" accent) */
+.canvas-page-with-sidebar {
+ --canvas-bg: #111827;
+ --canvas-bg-2: #0f172a;
+ --canvas-surface: #1F2937;
+ --canvas-surface-2: #374151;
+ --canvas-surface-3: #4B5563;
+ --canvas-border: #374151;
+ --canvas-border-2: #4B5563;
+ --canvas-text: #F9FAFB;
+ --canvas-text-2: #D1D5DB;
+ --canvas-text-3: #9CA3AF;
+ --canvas-text-4: #6B7280;
+ --canvas-accent: #818CF8;
+ --canvas-accent-dim: #4F46E5;
+ --canvas-accent-glow: rgba(129, 140, 248, 0.20);
+ --canvas-critic: #A78BFA;
+ --canvas-critic-dim: #7C3AED;
+ --canvas-critic-glow: rgba(167, 139, 250, 0.20);
+ --canvas-warn: #F59E0B;
+ --canvas-danger: #DC2626;
+ --canvas-ok: #10B981;
+ --canvas-shadow: 0 8px 24px rgba(0,0,0,0.4), 0 2px 6px rgba(0,0,0,0.3);
+ --canvas-shadow-lg: 0 24px 60px rgba(0,0,0,0.55), 0 6px 16px rgba(0,0,0,0.4);
+ --canvas-r-sm: 6px;
+ --canvas-r-md: 10px;
+ --canvas-r-lg: 14px;
+ --canvas-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
+ --canvas-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ color: var(--canvas-text);
+ font-family: var(--canvas-sans);
+ font-size: 14px;
+ -webkit-font-smoothing: antialiased;
+}
+
+/* Light theme — matches the app's white + Tailwind-gray palette */
+.canvas-page-with-sidebar[data-canvas-theme="light"] {
+ --canvas-bg: #F9FAFB; /* gray-50 */
+ --canvas-bg-2: #FFFFFF;
+ --canvas-surface: #FFFFFF;
+ --canvas-surface-2: #F3F4F6; /* gray-100 */
+ --canvas-surface-3: #E5E7EB; /* gray-200 */
+ --canvas-border: #E5E7EB; /* gray-200 */
+ --canvas-border-2: #D1D5DB; /* gray-300 */
+ --canvas-text: #111827; /* gray-900 */
+ --canvas-text-2: #374151; /* gray-700 */
+ --canvas-text-3: #6B7280; /* gray-500 */
+ --canvas-text-4: #9CA3AF; /* gray-400 */
+ --canvas-accent: #6366F1; /* indigo-500 (matches accent-primary) */
+ --canvas-accent-dim: #C7D2FE;
+ --canvas-accent-glow: rgba(99, 102, 241, 0.15);
+ --canvas-critic: #8B5CF6; /* violet-500 (matches accent-secondary) */
+ --canvas-critic-glow: rgba(139, 92, 246, 0.12);
+ --canvas-shadow: 0 4px 14px rgba(20, 30, 50, 0.08);
+ --canvas-shadow-lg: 0 18px 48px rgba(20, 30, 50, 0.16);
+}
+
+.canvas-page-with-sidebar *, .canvas-modal-backdrop *, .toast-stack * { box-sizing: border-box; }
+
+/* Standardized motion tokens */
+.canvas-page-with-sidebar, .canvas-modal-backdrop {
+ --canvas-ease: cubic-bezier(0.2, 0, 0, 1);
+ --canvas-fast: 120ms var(--canvas-ease);
+ --canvas-base: 180ms var(--canvas-ease);
+ --canvas-slow: 280ms var(--canvas-ease);
+}
+
+/* Widgets fade + slight rise on mount */
+@keyframes canvas-widget-in {
+ from { opacity: 0; transform: translateY(4px); }
+ to { opacity: 1; transform: none; }
+}
+.canvas-page-with-sidebar .widget {
+ animation: canvas-widget-in 240ms var(--canvas-ease);
+}
+
+/* Smooth content view transitions */
+@keyframes canvas-view-in {
+ from { opacity: 0; transform: translateY(2px); }
+ to { opacity: 1; transform: none; }
+}
+.canvas-content > * {
+ animation: canvas-view-in 220ms var(--canvas-ease);
+}
+
+/* Improved focus rings — accent halo, no harsh outline */
+.canvas-page-with-sidebar :focus-visible,
+.canvas-modal-backdrop :focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 3px var(--canvas-accent-glow);
+ border-radius: 4px;
+}
+.canvas-page-with-sidebar input:focus-visible,
+.canvas-page-with-sidebar textarea:focus-visible,
+.canvas-modal-backdrop input:focus-visible,
+.canvas-modal-backdrop textarea:focus-visible {
+ outline: none;
+ border-color: var(--canvas-accent);
+ box-shadow: 0 0 0 3px var(--canvas-accent-glow);
+}
+
+/* Respect users who don't want motion */
+@media (prefers-reduced-motion: reduce) {
+ .canvas-page-with-sidebar .widget,
+ .canvas-content > * { animation: none; }
+}
+
+.canvas-page-with-sidebar button {
+ font-family: inherit;
+ cursor: pointer;
+ border: none;
+ background: none;
+ color: inherit;
}
-.print-view .canvas-header {
- background: var(--bg-secondary);
- border: 1px solid var(--border-secondary);
+.canvas-page-with-sidebar input, .canvas-page-with-sidebar textarea, .canvas-page-with-sidebar select,
+.canvas-modal-backdrop input, .canvas-modal-backdrop textarea, .canvas-modal-backdrop select {
+ font-family: inherit;
+ color: inherit;
}
-.header-left {
- display: flex;
+.canvas-page-with-sidebar ::selection { background: var(--canvas-accent-glow); color: var(--canvas-text); }
+
+/* Floating header — matches the .floating-header pattern used on ChatPage */
+.canvas-page-with-sidebar .floating-header {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background: rgba(31, 41, 55, 0.95);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border-bottom: 1px solid var(--canvas-border);
+ padding: 12px 24px;
+ display: grid;
+ grid-template-columns: 1fr auto 1fr;
align-items: center;
- gap: 1rem;
+ gap: 16px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+ flex-shrink: 0;
+}
+.canvas-page-with-sidebar .floating-header .header-right { justify-self: end; }
+.canvas-page-with-sidebar[data-canvas-theme="light"] .floating-header {
+ background: rgba(255, 255, 255, 0.95);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
-.back-button {
+.canvas-page-with-sidebar .header-left {
display: flex;
align-items: center;
- gap: 0.5rem;
- padding: 10px 16px;
- background: var(--bg-secondary);
- border: 1px solid var(--border-primary);
- border-radius: 12px;
- color: var(--text-primary);
- cursor: pointer;
- transition: all 0.2s ease;
- font-size: 14px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ gap: 16px;
}
-
-.back-button:hover {
- background: var(--bg-tertiary);
- border-color: var(--accent-primary);
- transform: translateY(-1px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+.canvas-page-with-sidebar .header-right {
+ display: flex;
+ align-items: center;
+ gap: 8px;
}
-
-.print-view .back-button {
+.canvas-page-with-sidebar .mobile-menu-button {
display: none;
+ width: 40px;
+ height: 40px;
+ border-radius: 10px;
+ border: none;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-2);
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
}
-
-.canvas-title-section {
+@media (max-width: 768px) {
+ .canvas-page-with-sidebar .mobile-menu-button { display: flex; }
+}
+.canvas-page-with-sidebar .header-brand {
display: flex;
- flex-direction: column;
+ align-items: center;
+ gap: 12px;
}
-
-.canvas-title {
+.canvas-page-with-sidebar .brand-icon {
+ width: 40px;
+ height: 40px;
+ background: linear-gradient(135deg, var(--canvas-accent), var(--canvas-critic));
+ border-radius: 10px;
display: flex;
align-items: center;
- gap: 0.75rem;
- font-size: 2rem;
+ justify-content: center;
+ color: #fff;
+}
+.canvas-page-with-sidebar .brand-text h1 {
+ font-size: 20px;
font-weight: 700;
+ color: var(--canvas-text);
margin: 0;
- margin-bottom: 0.25rem;
- color: var(--text-primary);
-}
-
-.canvas-title-icon {
- color: var(--feature-icon-color);
+ line-height: 1.15;
+ letter-spacing: -0.01em;
+}
+.canvas-page-with-sidebar .brand-text p {
+ font-size: 13px;
+ color: var(--canvas-text-3);
+ margin: 2px 0 0;
+ line-height: 1;
+}
+.canvas-page-with-sidebar .theme-toggle .theme-icon { display: block; }
+
+/* When the tabs sit in ChatPage's flex .floating-header, absolutely-position
+ them so they center regardless of header-left/header-right widths. */
+.floating-header { position: sticky; }
+.floating-header > .chat-view-tabs {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+.canvas-tabs-mobile { display: none; }
+@media (max-width: 900px) {
+ .floating-header > .chat-view-tabs { display: none; }
+ .canvas-tabs-mobile {
+ display: inline-block;
+ background: var(--canvas-surface, var(--bg-tertiary, #f3f4f6));
+ border: 1px solid var(--canvas-border, var(--border-primary, #e5e7eb));
+ color: var(--canvas-text, var(--text-primary, #111827));
+ font-family: inherit;
+ font-size: 13px;
+ padding: 6px 10px;
+ border-radius: 7px;
+ }
}
-.canvas-subtitle {
- margin: 0;
- color: var(--text-secondary);
- font-size: 1rem;
+/* Shared view-tabs pill bar — used on both Canvas and Chat pages.
+ Uses canvas vars first, falls back to app global vars on Chat. */
+.canvas-tabs {
+ display: flex;
+ gap: 2px;
+ background: var(--canvas-surface, var(--bg-tertiary, #f3f4f6));
+ padding: 3px;
+ border-radius: 8px;
+ border: 1px solid var(--canvas-border, var(--border-primary, #e5e7eb));
+ justify-self: center;
+ cursor: pointer;
}
-
-.header-actions {
+.canvas-tabs .tab {
+ padding: 6px 14px;
+ font-size: 13px;
+ color: var(--canvas-text-2, var(--text-secondary, #6b7280));
+ border-radius: 6px;
display: flex;
align-items: center;
- gap: 0.75rem;
+ gap: 7px;
+ font-weight: 500;
+ border: none;
+ background: transparent;
+ font-family: inherit;
+ cursor: pointer;
+ transition: background .12s, color .12s;
+}
+.canvas-tabs .tab:hover { color: var(--canvas-text, var(--text-primary, #111827)); }
+.canvas-tabs .tab.active {
+ background: var(--canvas-surface-3, var(--bg-primary, #fff));
+ color: var(--canvas-text, var(--text-primary, #111827));
+ box-shadow: inset 0 0 0 1px var(--canvas-border-2, var(--border-secondary, #d1d5db));
+}
+.canvas-tabs .tab .badge {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ background: var(--canvas-accent-glow);
+ color: var(--canvas-accent);
+ padding: 1px 5px;
+ border-radius: 3px;
+ letter-spacing: 0.02em;
+}
+
+.canvas-page-with-sidebar .topbar-spacer { flex: 1; }
+
+.canvas-page-with-sidebar .icon-btn,
+.canvas-modal-backdrop .icon-btn {
+ width: 32px;
+ height: 32px;
+ display: grid;
+ place-items: center;
+ border-radius: 7px;
+ color: var(--canvas-text-2);
+ transition: background .12s, color .12s;
+ font-family: inherit;
+ cursor: pointer;
+ border: none;
+ background: none;
}
+.canvas-page-with-sidebar .icon-btn:hover,
+.canvas-modal-backdrop .icon-btn:hover { background: var(--canvas-surface); color: var(--canvas-text); }
-.canvas-powered-by {
+.canvas-page-with-sidebar .btn,
+.canvas-modal-backdrop .btn {
display: inline-flex;
align-items: center;
- gap: 4px;
- color: var(--text-tertiary);
- font-size: 12px;
- text-decoration: none;
- transition: opacity 0.2s ease;
+ gap: 7px;
+ padding: 7px 12px;
+ border-radius: 7px;
+ font-size: 13px;
+ font-weight: 500;
+ background: var(--canvas-surface-2);
+ border: 1px solid var(--canvas-border-2);
+ color: var(--canvas-text);
+ transition: all .12s;
+ font-family: inherit;
+ cursor: pointer;
}
-
-.canvas-powered-by:hover {
- opacity: 0.8;
+.canvas-page-with-sidebar .btn:hover,
+.canvas-modal-backdrop .btn:hover { background: var(--canvas-surface-3); border-color: var(--canvas-border-2); }
+.canvas-page-with-sidebar .btn-primary,
+.canvas-modal-backdrop .btn-primary {
+ background: var(--canvas-accent);
+ color: #FFFFFF;
+ border-color: var(--canvas-accent);
+ font-weight: 600;
}
-
-.canvas-powered-by-logo {
- height: 1em;
- width: auto;
- vertical-align: middle;
+.canvas-page-with-sidebar .btn-primary:hover,
+.canvas-modal-backdrop .btn-primary:hover { background: var(--canvas-accent-dim); border-color: var(--canvas-accent-dim); filter: brightness(1.05); box-shadow: 0 0 0 3px var(--canvas-accent-glow); }
+.canvas-page-with-sidebar .btn-critic,
+.canvas-modal-backdrop .btn-critic {
+ background: var(--canvas-critic);
+ color: #FFFFFF;
+ border-color: var(--canvas-critic);
+ font-weight: 600;
}
+.canvas-page-with-sidebar .btn-critic:hover,
+.canvas-modal-backdrop .btn-critic:hover { filter: brightness(1.08); box-shadow: 0 0 0 3px var(--canvas-critic-glow); }
+.canvas-page-with-sidebar .btn-ghost,
+.canvas-modal-backdrop .btn-ghost { background: transparent; border-color: transparent; color: var(--canvas-text-2); }
+.canvas-page-with-sidebar .btn-ghost:hover,
+.canvas-modal-backdrop .btn-ghost:hover { background: var(--canvas-surface); color: var(--canvas-text); border-color: transparent; }
+.canvas-page-with-sidebar .btn-danger:hover,
+.canvas-modal-backdrop .btn-danger:hover { background: rgba(240,106,106,0.12); border-color: rgba(240,106,106,0.4); color: var(--canvas-danger); }
-.print-view .header-actions {
- display: none;
+.canvas-page-with-sidebar .btn:disabled,
+.canvas-modal-backdrop .btn:disabled { opacity: 0.5; cursor: not-allowed; }
+
+/* Content */
+.canvas-content {
+ padding: 24px 28px 80px;
+ max-width: 100%;
+ flex: 1;
+ overflow-y: auto;
+ background: var(--canvas-bg);
}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .canvas-content { background: var(--canvas-bg); }
-/* Icon-only buttons in canvas header */
-.canvas-icon-btn {
+.canvas-page-with-sidebar .page-header {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 24px;
+ margin-bottom: 22px;
+}
+.canvas-page-with-sidebar .page-title { font-size: 28px; font-weight: 700; letter-spacing: -0.02em; margin: 0; color: var(--canvas-text); }
+.canvas-page-with-sidebar .page-sub { color: var(--canvas-text-3); font-size: 13px; margin-top: 6px; }
+.canvas-page-with-sidebar .page-meta {
+ font-family: var(--canvas-mono);
+ font-size: 11px;
+ color: var(--canvas-text-3);
display: flex;
align-items: center;
- justify-content: center;
- width: 40px;
- height: 40px;
- padding: 0;
- background: var(--bg-secondary);
- border: 1px solid var(--border-primary);
- border-radius: 12px;
- color: var(--text-primary);
- cursor: pointer;
- transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, transform 0.12s ease, box-shadow 0.18s ease;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ gap: 8px;
}
-
-.canvas-icon-btn svg {
- width: 18px;
- height: 18px;
+.canvas-page-with-sidebar .dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--canvas-ok);
+ box-shadow: 0 0 8px var(--canvas-ok);
}
-.canvas-icon-btn:hover:not(:disabled) {
- background: var(--bg-tertiary);
- border-color: var(--accent-primary);
- transform: translateY(-1px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+/* ----- Insights view ----- */
+.canvas-page-with-sidebar .insights-stats {
+ display: flex;
+ gap: 18px;
+ align-items: center;
+ padding: 14px 18px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 10px;
+ margin-bottom: 14px;
+ flex-wrap: wrap;
}
-
-.canvas-icon-btn:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- transform: none;
+.canvas-page-with-sidebar[data-canvas-theme="light"] .insights-stats {
+ background: #ffffff;
+ border-color: rgba(15, 15, 15, 0.06);
}
-
-/* Clear-canvas variant — destructive */
-.clear-canvas-btn {
- color: #ef4444;
- border-color: rgba(239, 68, 68, 0.35);
+.canvas-page-with-sidebar .insights-stat {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1px;
+ min-width: 70px;
}
-
-.clear-canvas-btn:hover:not(:disabled) {
- background: #ef4444;
- color: #ffffff;
- border-color: #ef4444;
- box-shadow: 0 4px 12px -2px rgba(239, 68, 68, 0.45);
+.canvas-page-with-sidebar .insights-stat-value {
+ font-family: var(--canvas-mono);
+ font-size: 22px;
+ font-weight: 700;
+ color: var(--canvas-text);
+ letter-spacing: -0.02em;
+ line-height: 1;
+}
+.canvas-page-with-sidebar .insights-stat-label {
+ font-size: 11px;
+ color: var(--canvas-text-3);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ font-weight: 500;
}
-
-[data-theme="dark"] .clear-canvas-btn {
- color: #f87171;
- border-color: rgba(248, 113, 113, 0.4);
+.canvas-page-with-sidebar .insights-stat-update {
+ font-size: 11px;
+ color: var(--canvas-text-3);
+ font-family: var(--canvas-mono);
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
}
-[data-theme="dark"] .clear-canvas-btn:hover:not(:disabled) {
- background: #ef4444;
- color: #ffffff;
- border-color: #ef4444;
+.canvas-page-with-sidebar .insights-filters {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ margin-bottom: 14px;
+ flex-wrap: wrap;
}
-.action-button {
+.canvas-page-with-sidebar .insights-pinned-strip {
display: flex;
align-items: center;
- gap: 0.5rem;
- padding: 10px 16px;
- background: var(--bg-secondary);
- border: 1px solid var(--border-primary);
- border-radius: 12px;
- color: var(--text-primary);
+ gap: 8px;
+ padding: 10px 14px;
+ margin-bottom: 14px;
+ background: var(--canvas-accent-glow);
+ border: 1px solid rgba(99, 102, 241, 0.18);
+ border-radius: 8px;
+ flex-wrap: wrap;
+}
+.canvas-page-with-sidebar .insights-pinned-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 4px 10px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 999px;
+ font-family: inherit;
+ font-size: 12px;
+ color: var(--canvas-text);
cursor: pointer;
- transition: all 0.2s ease;
- font-size: 14px;
- font-weight: 500;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ transition: all .12s;
}
-
-.action-button:hover {
- background: var(--bg-tertiary);
- border-color: var(--accent-primary);
- transform: translateY(-1px);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+.canvas-page-with-sidebar .insights-pinned-pill:hover {
+ border-color: var(--canvas-accent);
+ color: var(--canvas-accent);
}
-.action-button:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- transform: none;
+/* Cards vs Tasks view toggle on the Insights view */
+.canvas-page-with-sidebar .insights-view-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+ margin-bottom: 12px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+ padding: 3px;
+}
+.canvas-page-with-sidebar .insights-view-toggle button {
+ background: transparent;
+ border: none;
+ padding: 5px 12px;
+ border-radius: 5px;
+ font-family: inherit;
+ font-size: 12px;
+ color: var(--canvas-text-3);
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ cursor: pointer;
+ transition: background .12s, color .12s;
}
-
-.action-icon.spinning {
- animation: spin 1s linear infinite;
+.canvas-page-with-sidebar .insights-view-toggle button:hover { color: var(--canvas-text); }
+.canvas-page-with-sidebar .insights-view-toggle button.active {
+ background: var(--canvas-surface-3);
+ color: var(--canvas-text);
+ box-shadow: inset 0 0 0 1px var(--canvas-border-2);
}
-@keyframes spin {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
+/* Flat task list view */
+.canvas-page-with-sidebar .insights-tasks-list {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
}
-
-/* Stats Section */
-.canvas-stats {
+.canvas-page-with-sidebar .insights-task-row {
display: flex;
- gap: 2rem;
- margin-bottom: 2rem;
- justify-content: center;
+ align-items: flex-start;
+ gap: 10px;
+ padding: 10px 12px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+ position: relative;
+ transition: background .12s, border-color .12s;
+}
+.canvas-page-with-sidebar .insights-task-row:hover {
+ border-color: var(--canvas-border-2);
+}
+.canvas-page-with-sidebar .insights-task-row.task-completed { opacity: 0.7; }
+.canvas-page-with-sidebar .insights-task-row.task-abandoned { opacity: 0.5; }
+.canvas-page-with-sidebar .insights-task-row.task-completed:hover,
+.canvas-page-with-sidebar .insights-task-row.task-abandoned:hover { opacity: 0.95; }
+.canvas-page-with-sidebar .insights-task-row .insight-task-check { margin-top: 4px; }
+.canvas-page-with-sidebar .insights-task-body {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
}
-
-.stat-item {
- text-align: center;
- background: var(--bg-primary);
- padding: 1.5rem;
- border-radius: 16px;
- border: 1px solid var(--border-primary);
- box-shadow: var(--shadow-md);
- min-width: 140px;
- transition: all 0.3s ease;
+.canvas-page-with-sidebar .insights-task-section {
+ background: transparent;
+ border: none;
+ color: var(--canvas-text-3);
+ font-family: var(--canvas-mono);
+ font-size: 10.5px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ padding: 0;
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ cursor: pointer;
+ align-self: flex-start;
}
-
-.stat-item:hover {
- transform: translateY(-2px);
- box-shadow: var(--shadow-xl);
+.canvas-page-with-sidebar .insights-task-section:hover { color: var(--canvas-accent); }
+.canvas-page-with-sidebar .insights-task-text {
+ font-size: 13px;
+ color: var(--canvas-text);
+ line-height: 1.5;
}
-
-.print-view .stat-item {
- background: var(--bg-secondary);
- border: 1px solid var(--border-secondary);
+.canvas-page-with-sidebar .insights-task-row.task-completed .insights-task-text,
+.canvas-page-with-sidebar .insights-task-row.task-abandoned .insights-task-text {
+ text-decoration: line-through;
+ color: var(--canvas-text-3);
}
+.canvas-page-with-sidebar .insights-task-text strong { color: var(--canvas-text); }
-.stat-number {
- display: block;
- font-size: 2rem;
+/* Per-card progress bar — replaces the old single status pill */
+.canvas-page-with-sidebar .insight-progress {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding: 0 0 4px;
+}
+.canvas-page-with-sidebar .insight-progress-meta {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-family: var(--canvas-mono);
+ font-size: 10.5px;
+ color: var(--canvas-text-3);
+}
+.canvas-page-with-sidebar .insight-progress-count { font-weight: 600; }
+.canvas-page-with-sidebar .insight-progress-badge {
+ font-family: var(--canvas-mono);
+ font-size: 9px;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
font-weight: 700;
- color: var(--accent-primary);
- margin-bottom: 0.5rem;
+ padding: 1px 6px;
+ border-radius: 3px;
}
+.canvas-page-with-sidebar .insight-progress-badge.done { background: rgba(16,185,129,0.15); color: #10B981; }
+.canvas-page-with-sidebar .insight-progress-badge.inprog { background: rgba(59,130,246,0.15); color: #3B82F6; }
+.canvas-page-with-sidebar .insight-progress-badge.abandoned { background: var(--canvas-surface-2); color: var(--canvas-text-4); }
-.print-view .stat-number {
- color: var(--text-secondary);
+.canvas-page-with-sidebar .insight-progress-bar {
+ height: 5px;
+ background: var(--canvas-surface-2);
+ border-radius: 3px;
+ overflow: hidden;
+ display: flex;
}
-
-.stat-label {
- font-size: 0.9rem;
- color: var(--text-secondary);
+.canvas-page-with-sidebar .insight-progress-bar > i {
+ display: block;
+ height: 100%;
+ transition: width .25s;
}
+.canvas-page-with-sidebar .insight-progress-bar .ip-completed { background: #10B981; }
+.canvas-page-with-sidebar .insight-progress-bar .ip-inprogress { background: #3B82F6; }
+.canvas-page-with-sidebar .insight-progress-bar .ip-abandoned { background: var(--canvas-text-4); }
-/* Content Section */
-.canvas-content {
- max-width: 1200px;
- margin: 0 auto;
+/* Per-bullet tasks inside the body */
+.canvas-page-with-sidebar .insight-tasks {
+ list-style: none;
+ padding: 0;
+ margin: 6px 0 0;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
}
-
-.empty-canvas {
- text-align: center;
- padding: 4rem 2rem;
- color: var(--text-secondary);
- max-width: 600px;
- margin: 0 auto;
+.canvas-page-with-sidebar .insight-task {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ padding: 2px 0;
+ font-size: 13px;
+ color: var(--canvas-text-2);
+ line-height: 1.5;
+ position: relative;
}
-
-.empty-canvas-icon {
- width: 4rem;
- height: 4rem;
- margin-bottom: 1rem;
- color: var(--text-tertiary);
+.canvas-page-with-sidebar .insight-task.task-completed .insight-task-text {
+ text-decoration: line-through;
+ color: var(--canvas-text-3);
+ opacity: 0.7;
}
-
-.empty-canvas h2 {
- margin-bottom: 1rem;
- color: var(--text-primary);
+.canvas-page-with-sidebar .insight-task.task-abandoned .insight-task-text {
+ text-decoration: line-through;
+ color: var(--canvas-text-4);
+ opacity: 0.5;
}
-
-.empty-canvas p {
- margin-bottom: 1.5rem;
- line-height: 1.6;
+.canvas-page-with-sidebar .insight-task.task-in-progress .insight-task-text {
+ color: var(--canvas-text);
+ font-weight: 500;
}
-
-.start-chatting-button {
- padding: 16px 32px;
- background: var(--accent-gradient);
- color: white;
- border: none;
- border-radius: 12px;
- font-size: 1.1rem;
- font-weight: 600;
+.canvas-page-with-sidebar .insight-task-text { flex: 1; }
+.canvas-page-with-sidebar .insight-task-text strong { color: var(--canvas-text); }
+.canvas-page-with-sidebar .insight-task-check {
+ flex-shrink: 0;
+ width: 18px;
+ height: 18px;
+ border-radius: 4px;
+ border: 1.5px solid currentColor;
+ background: transparent;
cursor: pointer;
- transition: all 0.3s ease;
- box-shadow: var(--shadow-md);
+ display: grid;
+ place-items: center;
+ margin-top: 2px;
+ transition: background .12s;
}
-
-.start-chatting-button:hover {
- transform: translateY(-2px);
- box-shadow: var(--shadow-xl);
+.canvas-page-with-sidebar .insight-task-check:hover {
+ background: var(--canvas-surface-2);
}
-
-/* Sections */
-.sections-container {
- display: flex;
- flex-direction: column;
- gap: 1.5rem;
+.canvas-page-with-sidebar .insight-task.task-completed .insight-task-check {
+ background: #10B981;
+ color: #fff;
}
-
-.canvas-section {
- background: var(--bg-primary);
- border-radius: 16px;
- border: 1px solid var(--border-primary);
- box-shadow: var(--shadow-md);
- overflow: hidden;
- transition: all 0.3s ease;
+.canvas-page-with-sidebar .insight-task.task-abandoned .insight-task-check {
+ background: var(--canvas-text-4);
+ color: #fff;
}
-
-.canvas-section:hover {
- transform: translateY(-2px);
- box-shadow: var(--shadow-xl);
+.canvas-page-with-sidebar .task-check-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: currentColor;
}
-.print-view .canvas-section {
- background: white;
- border: 1px solid var(--border-secondary);
- color: black;
- page-break-inside: avoid;
- margin-bottom: 1rem;
+/* Sidebar row for a fully-done section gets struck through */
+.sidebar .csm-row-done .csm-row-label {
+ text-decoration: line-through;
+ color: var(--text-tertiary, #9CA3AF);
+}
+.sidebar .csm-row-done .csm-row-meta {
+ color: #10B981;
+ font-weight: 600;
}
-.section-header {
- display: flex;
- justify-content: space-between;
+/* Insight status pill + menu */
+.canvas-page-with-sidebar .insight-status-wrap {
+ position: relative;
+}
+.canvas-page-with-sidebar .insight-status-pill {
+ display: inline-flex;
align-items: center;
- padding: 1.5rem;
+ gap: 5px;
+ padding: 3px 9px;
+ border-radius: 999px;
+ border: 1px solid transparent;
+ background: transparent;
+ font-family: inherit;
+ font-size: 10.5px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
cursor: pointer;
- transition: background-color 0.2s ease;
+ transition: background .12s;
}
-
-.section-header:hover {
- background: var(--bg-secondary);
+.canvas-page-with-sidebar .insight-status-pill:hover {
+ background: var(--canvas-surface-2);
}
-
-.print-view .section-header:hover {
- background: var(--bg-tertiary);
+.canvas-page-with-sidebar .insight-status-menu {
+ position: absolute;
+ top: calc(100% + 4px);
+ right: 0;
+ z-index: 5;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 7px;
+ box-shadow: var(--canvas-shadow-lg);
+ padding: 4px;
+ min-width: 150px;
+ display: flex;
+ flex-direction: column;
}
-
-.section-header-content {
+.canvas-page-with-sidebar .insight-status-menu button {
display: flex;
align-items: center;
- gap: 1rem;
- flex: 1;
+ gap: 8px;
+ background: transparent;
+ border: none;
+ padding: 6px 10px;
+ border-radius: 4px;
+ font-family: inherit;
+ font-size: 12px;
+ color: var(--canvas-text);
+ cursor: pointer;
+ text-align: left;
}
-
-.section-icon {
- width: 2rem;
- height: 2rem;
- color: var(--feature-icon-color);
+.canvas-page-with-sidebar .insight-status-menu button:hover {
+ background: var(--canvas-surface-2);
}
-
-.print-view .section-icon {
- color: var(--text-secondary);
+.canvas-page-with-sidebar .insight-status-menu button.active {
+ background: var(--canvas-accent-glow);
+ color: var(--canvas-accent);
+ font-weight: 600;
}
-.section-titles {
- flex: 1;
+/* Card-level status mutes the body when completed/abandoned */
+.canvas-page-with-sidebar .insight.status-completed .insight-body,
+.canvas-page-with-sidebar .insight.status-completed .insight-foot {
+ opacity: 0.65;
}
-
-.section-title {
- margin: 0 0 0.25rem 0;
- font-size: 1.3rem;
- font-weight: 600;
- color: var(--text-primary);
+.canvas-page-with-sidebar .insight.status-abandoned {
+ opacity: 0.5;
}
+.canvas-page-with-sidebar .insight.status-abandoned:hover { opacity: 0.85; }
-.section-description {
- margin: 0;
- color: var(--text-secondary);
- font-size: 0.9rem;
+/* Progress bar in stats — segmented for status mix */
+.canvas-page-with-sidebar .insights-stat-progress {
+ flex: 1;
+ min-width: 180px;
+ max-width: 280px;
+ gap: 4px;
}
-
-.section-meta {
+.canvas-page-with-sidebar .insights-progress-bar {
+ height: 6px;
+ width: 100%;
+ background: var(--canvas-surface-2);
+ border-radius: 3px;
+ overflow: hidden;
display: flex;
- align-items: center;
- gap: 1rem;
- font-size: 0.9rem;
}
-
-.insight-count {
- background: var(--feature-bg);
- color: var(--accent-primary);
- padding: 0.25rem 0.75rem;
- border-radius: 20px;
- font-weight: 500;
+.canvas-page-with-sidebar .insights-progress-bar > i {
+ display: block;
+ height: 100%;
+ transition: width .3s;
+}
+.canvas-page-with-sidebar .insights-progress-bar .ip-completed { background: #10B981; }
+.canvas-page-with-sidebar .insights-progress-bar .ip-inprogress { background: #3B82F6; }
+.canvas-page-with-sidebar .insights-progress-bar .ip-abandoned { background: var(--canvas-text-4); }
+
+/* Confidence ring */
+.canvas-page-with-sidebar .confidence-ring {
+ position: relative;
+ width: 32px;
+ height: 32px;
+ display: grid;
+ place-items: center;
+ flex-shrink: 0;
}
-
-[data-theme="dark"] .insight-count {
- background: rgba(255, 255, 255, 0.12);
- color: #ffffff;
+.canvas-page-with-sidebar .confidence-ring span {
+ position: absolute;
+ font-family: var(--canvas-mono);
+ font-size: 9.5px;
+ font-weight: 700;
}
-.print-view .insight-count {
- background: var(--bg-tertiary);
- color: var(--text-secondary);
+/* Insight card refinements */
+.canvas-page-with-sidebar .insight {
+ transition: border-color .15s, transform .15s, box-shadow .15s;
}
-
-.expand-arrow {
- transition: transform 0.3s ease;
- font-size: 0.8rem;
- color: var(--text-secondary);
+.canvas-page-with-sidebar .insight:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 6px 18px rgba(0,0,0,0.08);
}
-
-.expand-arrow.expanded {
- transform: rotate(180deg);
+.canvas-page-with-sidebar .insight.is-pinned {
+ border-color: var(--canvas-accent);
+ background: var(--canvas-surface);
+ box-shadow: 0 0 0 1px var(--canvas-accent-glow);
}
-
-.section-content {
- padding: 0 1.5rem 1.5rem 1.5rem;
- border-top: 1px solid var(--border-primary);
+.canvas-page-with-sidebar[data-canvas-theme="light"] .insight.is-pinned {
+ background: #ffffff;
}
-.print-view .section-content {
- border-top: 1px solid var(--border-secondary);
+/* Insight footer (source count + updated time) */
+.canvas-page-with-sidebar .insight-foot {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 6px 0 0;
+ font-size: 11px;
+ color: var(--canvas-text-3);
}
-
-.empty-section {
- text-align: center;
- padding: 2rem;
- color: var(--text-tertiary);
+.canvas-page-with-sidebar .insight-foot-meta {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-family: var(--canvas-mono);
+}
+.canvas-page-with-sidebar .insight-dot {
+ width: 3px;
+ height: 3px;
+ border-radius: 50%;
+ background: var(--canvas-text-4);
}
-.empty-icon {
- width: 2rem;
- height: 2rem;
- margin-bottom: 1rem;
+/* Expanded source-quotes panel */
+.canvas-page-with-sidebar .insight-detail {
+ margin-top: 8px;
+ padding: 10px 12px;
+ background: var(--canvas-bg-2);
+ border-left: 2px solid var(--canvas-accent);
+ border-radius: 0 6px 6px 0;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ animation: canvas-view-in 180ms var(--canvas-ease);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .insight-detail {
+ background: rgba(99, 102, 241, 0.04);
+}
+.canvas-page-with-sidebar .insight-detail-head {
+ font-size: 10.5px;
+ font-family: var(--canvas-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--canvas-accent);
+ font-weight: 600;
+}
+.canvas-page-with-sidebar .insight-quote {
+ font-size: 12.5px;
+ color: var(--canvas-text-2);
+ line-height: 1.5;
+ font-style: italic;
}
/* Insights */
-.insights-grid {
+.canvas-page-with-sidebar .insight-grid {
display: grid;
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: 1rem;
- margin-top: 1rem;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 14px;
}
-
-.insight-card {
- background: var(--bg-secondary);
- border-radius: 12px;
- padding: 1.25rem;
- border: 1px solid var(--border-primary);
- transition: all 0.3s ease;
+@media (max-width: 1100px) {
+ .canvas-page-with-sidebar .insight-grid { grid-template-columns: 1fr; }
}
-.insight-card:hover {
- background: var(--bg-tertiary);
- transform: translateY(-1px);
- box-shadow: var(--shadow-sm);
+.canvas-page-with-sidebar .insight {
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: var(--canvas-r-md);
+ padding: 16px 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ transition: border-color .15s;
}
+.canvas-page-with-sidebar .insight:hover { border-color: var(--canvas-border-2); }
+.canvas-page-with-sidebar[data-canvas-theme="light"] .insight { box-shadow: 0 1px 2px rgba(20,30,50,0.03); }
-.print-view .insight-card {
- background: var(--bg-tertiary);
- border: 1px solid var(--border-secondary);
- color: black;
- break-inside: avoid;
+.canvas-page-with-sidebar .insight-head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
}
-
-.insight-content {
- margin-bottom: 1rem;
- line-height: 1.5;
- font-size: 0.95rem;
- color: var(--text-primary);
+.canvas-page-with-sidebar .insight-icon {
+ width: 28px;
+ height: 28px;
+ border-radius: 7px;
+ background: var(--canvas-surface-2);
+ display: grid;
+ place-items: center;
+ color: var(--canvas-accent);
}
-
-.empty-canvas-actions {
+.canvas-page-with-sidebar .insight-title { font-weight: 600; font-size: 14px; flex: 1; color: var(--canvas-text); }
+.canvas-page-with-sidebar .confidence {
display: flex;
- gap: 12px;
- justify-content: center;
align-items: center;
- flex-wrap: wrap;
- margin-top: 1.75rem;
+ gap: 6px;
+ font-family: var(--canvas-mono);
+ font-size: 11px;
+ color: var(--canvas-text-3);
+}
+.canvas-page-with-sidebar .conf-bar {
+ width: 36px;
+ height: 4px;
+ background: var(--canvas-surface-3);
+ border-radius: 2px;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .conf-bar { background: #e7ebf2; }
+.canvas-page-with-sidebar .conf-bar > i {
+ display: block;
+ height: 100%;
+ background: var(--canvas-accent);
+ border-radius: 2px;
+}
+
+.canvas-page-with-sidebar .insight-body { color: var(--canvas-text-2); font-size: 13px; }
+.canvas-page-with-sidebar .insight-body ul { margin: 6px 0 0; padding-left: 18px; }
+.canvas-page-with-sidebar .insight-body li { margin-bottom: 4px; }
+.canvas-page-with-sidebar .insight-body li::marker { color: var(--canvas-text-4); }
+.canvas-page-with-sidebar .insight-body strong { color: var(--canvas-text); font-weight: 600; }
+
+.canvas-page-with-sidebar .insight-actions {
+ display: flex;
+ gap: 4px;
+ flex-wrap: wrap;
+ padding-top: 10px;
+ border-top: 1px dashed var(--canvas-border);
+ margin-top: 4px;
+}
+.canvas-page-with-sidebar .chip,
+.canvas-modal-backdrop .chip {
+ font-size: 11px;
+ padding: 4px 9px;
+ border-radius: 5px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-2);
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ transition: background .12s, color .12s;
+ border: none;
+ cursor: pointer;
+}
+.canvas-page-with-sidebar .chip:hover { background: var(--canvas-surface-3); color: var(--canvas-text); }
+.canvas-page-with-sidebar .chip.pinned { background: var(--canvas-accent-glow); color: var(--canvas-accent); }
+
+/* Workspace grid */
+.canvas-page-with-sidebar .workspace {
+ display: grid;
+ grid-template-columns: repeat(6, minmax(0, 1fr));
+ gap: 18px;
+ align-items: start;
+}
+/* Notion-inspired widget treatment: hairline borders, generous padding,
+ no hard chrome — the content is the page, the frame is barely there. */
+.canvas-page-with-sidebar .widget {
+ background: var(--canvas-surface);
+ border: 1px solid transparent;
+ border-radius: 6px;
+ display: flex;
+ flex-direction: column;
+ min-height: 200px;
+ position: relative;
+ overflow: hidden;
+ transition: background .15s, border-color .15s, box-shadow .15s;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .widget {
+ background: #FFFFFF;
+ border-color: rgba(15, 15, 15, 0.06);
+}
+.canvas-page-with-sidebar .widget.size-S { grid-column: span 2; }
+.canvas-page-with-sidebar .widget.size-M { grid-column: span 3; }
+.canvas-page-with-sidebar .widget.size-L { grid-column: span 6; }
+.canvas-page-with-sidebar .widget.dragging { opacity: 0.4; }
+.canvas-page-with-sidebar .widget.drag-over {
+ box-shadow: 0 0 0 2px var(--canvas-accent), 0 4px 14px var(--canvas-accent-glow);
+}
+.canvas-page-with-sidebar .widget:hover {
+ background: var(--canvas-surface-2);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .widget:hover {
+ background: rgba(0, 0, 0, 0.015);
+ border-color: rgba(15, 15, 15, 0.08);
+}
+.canvas-page-with-sidebar .widget.critic { border-color: rgba(167, 139, 250, 0.18); }
+.canvas-page-with-sidebar[data-canvas-theme="light"] .widget.critic { border-color: rgba(139, 92, 246, 0.18); }
+.canvas-page-with-sidebar .widget.critic:hover { border-color: var(--canvas-critic); }
+
+/* Preserve 3-S / 2-M / 1-L per row at every desktop width.
+ On phones (≤768px) just stack everything full-width for readability. */
+@media (max-width: 768px) {
+ .canvas-page-with-sidebar .workspace { grid-template-columns: 1fr; }
+ .canvas-page-with-sidebar .widget.size-S,
+ .canvas-page-with-sidebar .widget.size-M,
+ .canvas-page-with-sidebar .widget.size-L { grid-column: 1 / -1; }
+}
+
+/* Notion-style header: drag grip is hover-only, title is plain text. */
+.canvas-page-with-sidebar .widget-head {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 14px 16px 8px;
+ cursor: grab;
+}
+.canvas-page-with-sidebar .widget-head:active { cursor: grabbing; }
+.canvas-page-with-sidebar .widget-head .drag-grip {
+ color: var(--canvas-text-4);
+ display: grid;
+ place-items: center;
+ width: 14px;
+ opacity: 0;
+ transition: opacity .15s;
+}
+.canvas-page-with-sidebar .widget:hover .widget-head .drag-grip { opacity: 0.6; }
+.canvas-page-with-sidebar .widget-icon {
+ width: 18px;
+ height: 18px;
+ display: grid;
+ place-items: center;
+ color: var(--canvas-text-3);
+}
+.canvas-page-with-sidebar .widget.critic .widget-icon { color: var(--canvas-critic); }
+.canvas-page-with-sidebar .widget-title {
+ font-weight: 600;
+ font-size: 13.5px;
+ flex: 1;
+ letter-spacing: -0.005em;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .widget-tag,
+.canvas-modal-backdrop .widget-tag {
+ font-size: 9.5px;
+ font-family: var(--canvas-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ padding: 2px 6px;
+ border-radius: 3px;
+ background: var(--canvas-critic-glow);
+ color: var(--canvas-critic);
+ font-weight: 600;
+}
+.canvas-modal-backdrop .widget-tag-enhanced {
+ background: rgba(16, 185, 129, 0.15);
+ color: #10B981;
+}
+.canvas-modal-backdrop .widget-tag-chat {
+ background: rgba(59, 130, 246, 0.15);
+ color: #3B82F6;
+}
+.canvas-page-with-sidebar .widget-actions { display: flex; gap: 1px; opacity: 0; transition: opacity .12s; }
+.canvas-page-with-sidebar .widget:hover .widget-actions { opacity: 1; }
+.canvas-page-with-sidebar .widget-actions .icon-btn { width: 24px; height: 24px; }
+.canvas-page-with-sidebar .widget-actions .icon-btn svg { width: 14px; height: 14px; }
+.canvas-page-with-sidebar .size-pill {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-3);
+ padding: 2px 6px;
+ border-radius: 4px;
+ cursor: pointer;
+ user-select: none;
+}
+.canvas-page-with-sidebar .size-pill:hover { background: var(--canvas-surface-3); color: var(--canvas-text); }
+
+.canvas-page-with-sidebar .widget-body {
+ padding: 4px 16px 18px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ min-height: 0;
+}
+
+/* Preset picker (shown above the empty workspace) */
+.canvas-page-with-sidebar .canvas-presets {
+ margin-bottom: 18px;
+}
+.canvas-page-with-sidebar .canvas-presets-head {
+ margin-bottom: 10px;
+}
+.canvas-page-with-sidebar .canvas-presets-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .canvas-presets-sub {
+ font-size: 12.5px;
+ color: var(--canvas-text-3);
+ margin-top: 2px;
+}
+.canvas-page-with-sidebar .canvas-presets-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 10px;
+}
+.canvas-page-with-sidebar .canvas-preset-card {
+ background: transparent;
+ border: 1px solid rgba(15, 15, 15, 0.10);
+ border-radius: 8px;
+ padding: 16px;
+ display: flex;
+ gap: 12px;
+ text-align: left;
+ cursor: pointer;
+ transition: background .15s, border-color .15s;
+ font-family: inherit;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .canvas-preset-card {
+ border-color: rgba(15, 15, 15, 0.12);
+}
+.canvas-page-with-sidebar .canvas-preset-card:hover {
+ background: var(--canvas-surface-2);
+ border-color: rgba(15, 15, 15, 0.15);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .canvas-preset-card:hover {
+ background: rgba(0, 0, 0, 0.025);
+}
+.canvas-page-with-sidebar .canvas-preset-icon {
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-2);
+ display: grid;
+ place-items: center;
+ flex-shrink: 0;
+ font-size: 16px;
+}
+.canvas-page-with-sidebar .canvas-preset-content { flex: 1; min-width: 0; }
+.canvas-page-with-sidebar .canvas-preset-name { font-size: 13.5px; font-weight: 600; color: var(--canvas-text); }
+.canvas-page-with-sidebar .canvas-preset-desc { font-size: 11.5px; color: var(--canvas-text-3); margin-top: 3px; line-height: 1.45; }
+.canvas-page-with-sidebar .canvas-preset-meta {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ color: var(--canvas-text-4);
+ margin-top: 6px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+}
+
+.canvas-page-with-sidebar .empty-cell {
+ border: 1.5px dashed var(--canvas-border-2);
+ border-radius: var(--canvas-r-md);
+ padding: 28px 24px;
+ text-align: center;
+ color: var(--canvas-text-3);
+ font-size: 13px;
+ grid-column: span 6;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ background: rgba(255,255,255,0.01);
+}
+
+/* Inputs / forms (scoped) */
+.canvas-page-with-sidebar .input, .canvas-page-with-sidebar .textarea, .canvas-page-with-sidebar .select,
+.canvas-modal-backdrop .input, .canvas-modal-backdrop .textarea, .canvas-modal-backdrop .select {
+ width: 100%;
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 7px;
+ padding: 8px 10px;
+ font-size: 13px;
+ color: var(--canvas-text);
+ transition: border-color .12s, box-shadow .12s;
+}
+.canvas-page-with-sidebar .input:focus, .canvas-page-with-sidebar .textarea:focus, .canvas-page-with-sidebar .select:focus,
+.canvas-modal-backdrop .input:focus, .canvas-modal-backdrop .textarea:focus, .canvas-modal-backdrop .select:focus {
+ outline: none;
+ border-color: var(--canvas-accent);
+ box-shadow: 0 0 0 3px var(--canvas-accent-glow);
+}
+.canvas-page-with-sidebar .textarea, .canvas-modal-backdrop .textarea { resize: vertical; min-height: 80px; font-family: inherit; line-height: 1.5; }
+.canvas-page-with-sidebar .label, .canvas-modal-backdrop .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--canvas-text-3); font-weight: 600; }
+
+.canvas-page-with-sidebar[data-canvas-theme="light"] .input,
+.canvas-page-with-sidebar[data-canvas-theme="light"] .textarea,
+.canvas-page-with-sidebar[data-canvas-theme="light"] .select,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .input,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .textarea,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .select {
+ background: #fff;
+}
+
+/* Modal backdrop — rendered at the body level, so it gets its own theme vars */
+.canvas-modal-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(17, 24, 39, 0.7);
+ backdrop-filter: blur(6px);
+ z-index: 100;
+ display: grid;
+ place-items: center;
+ padding: 40px 20px;
+ animation: canvas-fade-in .15s ease;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+
+ --canvas-bg: #111827;
+ --canvas-bg-2: #0f172a;
+ --canvas-surface: #1F2937;
+ --canvas-surface-2: #374151;
+ --canvas-surface-3: #4B5563;
+ --canvas-border: #374151;
+ --canvas-border-2: #4B5563;
+ --canvas-text: #F9FAFB;
+ --canvas-text-2: #D1D5DB;
+ --canvas-text-3: #9CA3AF;
+ --canvas-text-4: #6B7280;
+ --canvas-accent: #818CF8;
+ --canvas-accent-dim: #4F46E5;
+ --canvas-accent-glow: rgba(129, 140, 248, 0.20);
+ --canvas-critic: #A78BFA;
+ --canvas-critic-glow: rgba(167, 139, 250, 0.20);
+ --canvas-warn: #F59E0B;
+ --canvas-danger: #DC2626;
+ --canvas-ok: #10B981;
+ --canvas-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
+ --canvas-shadow-lg: 0 24px 60px rgba(0,0,0,0.55), 0 6px 16px rgba(0,0,0,0.4);
+ color: var(--canvas-text);
+}
+body[data-canvas-theme="light"] .canvas-modal-backdrop {
+ background: rgba(17, 24, 39, 0.35);
+ --canvas-bg: #F9FAFB;
+ --canvas-bg-2: #FFFFFF;
+ --canvas-surface: #FFFFFF;
+ --canvas-surface-2: #F3F4F6;
+ --canvas-surface-3: #E5E7EB;
+ --canvas-border: #E5E7EB;
+ --canvas-border-2: #D1D5DB;
+ --canvas-text: #111827;
+ --canvas-text-2: #374151;
+ --canvas-text-3: #6B7280;
+ --canvas-text-4: #9CA3AF;
+ --canvas-accent: #6366F1;
+ --canvas-accent-dim: #C7D2FE;
+ --canvas-accent-glow: rgba(99, 102, 241, 0.15);
+ --canvas-critic: #8B5CF6;
+ --canvas-critic-glow: rgba(139, 92, 246, 0.12);
+ --canvas-shadow-lg: 0 18px 48px rgba(20, 30, 50, 0.16);
+}
+@keyframes canvas-fade-in { from { opacity: 0 } to { opacity: 1 } }
+
+.canvas-modal {
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 14px;
+ width: 100%;
+ max-width: 560px;
+ max-height: 88vh;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ box-shadow: var(--canvas-shadow-lg);
+ animation: canvas-scale-in .18s cubic-bezier(.22,.9,.3,1);
+}
+.canvas-modal.wide { max-width: 760px; }
+.canvas-modal.huge { max-width: 920px; }
+.canvas-modal.canvas-tour { max-width: 480px; }
+@keyframes canvas-scale-in { from { opacity: 0; transform: translateY(8px) scale(0.98) } to { opacity: 1; transform: none } }
+
+.canvas-modal .modal-head {
+ padding: 18px 22px 14px;
+ border-bottom: 1px solid var(--canvas-border);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+.canvas-modal .modal-title { font-weight: 600; font-size: 15px; flex: 1; letter-spacing: -0.005em; color: var(--canvas-text); }
+.canvas-modal .modal-sub { color: var(--canvas-text-3); font-size: 12px; margin-top: 2px; }
+.canvas-modal .modal-body { padding: 18px 22px; overflow-y: auto; flex: 1; }
+.canvas-modal .modal-foot {
+ padding: 14px 22px;
+ border-top: 1px solid var(--canvas-border);
+ display: flex;
+ justify-content: flex-end;
+ gap: 8px;
+ background: var(--canvas-bg-2);
+}
+
+.canvas-modal .modal-icon {
+ width: 36px;
+ height: 36px;
+ border-radius: 9px;
+ background: var(--canvas-accent-glow);
+ color: var(--canvas-accent);
+ display: grid;
+ place-items: center;
+}
+.canvas-modal .modal-icon.critic { background: var(--canvas-critic-glow); color: var(--canvas-critic); }
+
+.canvas-modal-backdrop .form-grid { display: grid; gap: 12px; }
+.canvas-modal-backdrop .form-grid.two { grid-template-columns: 1fr 1fr; }
+.canvas-modal-backdrop .form-row { display: flex; flex-direction: column; gap: 6px; }
+
+/* Widget palette */
+.canvas-modal-backdrop .palette-search { padding: 4px 0 16px; position: sticky; top: 0; background: var(--canvas-surface); z-index: 2; }
+.canvas-modal-backdrop .palette-cats {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+ margin-bottom: 14px;
+}
+.canvas-modal-backdrop .palette-cat {
+ font-size: 12px;
+ padding: 5px 10px;
+ border-radius: 999px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-2);
+ border: 1px solid var(--canvas-border);
+ cursor: pointer;
+ font-family: inherit;
+}
+.canvas-modal-backdrop .palette-cat:hover { color: var(--canvas-text); }
+.canvas-modal-backdrop .palette-cat.active { background: var(--canvas-accent-glow); color: var(--canvas-accent); border-color: var(--canvas-accent); }
+.canvas-modal-backdrop .palette-cat.critic.active { background: var(--canvas-critic-glow); color: var(--canvas-critic); border-color: var(--canvas-critic); }
+.canvas-modal-backdrop .palette-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 8px;
+}
+.canvas-modal-backdrop .palette-item {
+ background: var(--canvas-surface-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 9px;
+ padding: 12px;
+ display: flex;
+ gap: 10px;
+ align-items: flex-start;
+ cursor: pointer;
+ text-align: left;
+ transition: all .12s;
+ font-family: inherit;
+ color: var(--canvas-text);
+}
+.canvas-modal-backdrop .palette-item:hover {
+ background: var(--canvas-surface-3);
+ border-color: var(--canvas-border-2);
+ transform: translateY(-1px);
+}
+.canvas-modal-backdrop .palette-item.added { opacity: 0.4; cursor: default; }
+.canvas-modal-backdrop .palette-item.added:hover { transform: none; }
+.canvas-modal-backdrop .palette-item.critic { border-left: 2px solid var(--canvas-critic); }
+.canvas-modal-backdrop .palette-item .pi-icon {
+ width: 30px;
+ height: 30px;
+ border-radius: 7px;
+ background: var(--canvas-surface-3);
+ display: grid;
+ place-items: center;
+ color: var(--canvas-accent);
+ flex-shrink: 0;
+}
+.canvas-modal-backdrop .palette-item.critic .pi-icon { color: var(--canvas-critic); }
+.canvas-modal-backdrop .palette-item .pi-content { flex: 1; min-width: 0; }
+.canvas-modal-backdrop .palette-item .pi-title { font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 6px; color: var(--canvas-text); }
+.canvas-modal-backdrop .palette-item .pi-desc { font-size: 11.5px; color: var(--canvas-text-3); margin-top: 2px; line-height: 1.4; }
+.canvas-modal-backdrop .palette-item .pi-added {
+ font-size: 10px;
+ font-family: var(--canvas-mono);
+ color: var(--canvas-accent);
+ background: var(--canvas-accent-glow);
+ padding: 2px 6px;
+ border-radius: 3px;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+}
+
+/* Bibliography widget */
+.canvas-page-with-sidebar .bib-list { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
+.canvas-page-with-sidebar .bib-cite { color: var(--canvas-text); font-weight: 500; line-height: 1.4; }
+.canvas-page-with-sidebar .bib-meta { font-family: var(--canvas-mono); font-size: 10px; color: var(--canvas-text-3); display: flex; gap: 8px; }
+.canvas-page-with-sidebar .bib-meta .key { color: var(--canvas-accent); }
+
+.canvas-page-with-sidebar .format-tabs { display: flex; gap: 2px; background: var(--canvas-surface-2); padding: 2px; border-radius: 6px; }
+.canvas-page-with-sidebar .format-tab {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ padding: 3px 7px;
+ border-radius: 4px;
+ color: var(--canvas-text-3);
+ letter-spacing: 0.05em;
+ cursor: pointer;
+}
+.canvas-page-with-sidebar .format-tab.active { background: var(--canvas-surface-3); color: var(--canvas-text); }
+
+/* Kanban widget */
+.canvas-page-with-sidebar .kanban {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 8px;
+ flex: 1;
+ min-height: 0;
+}
+.canvas-page-with-sidebar .kan-col {
+ background: var(--canvas-bg-2);
+ border-radius: 7px;
+ padding: 8px;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ min-width: 0;
+ border: 1px solid var(--canvas-border);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .kan-col { background: #f3f5f9; }
+.canvas-page-with-sidebar .kan-col.drag-target { background: var(--canvas-accent-glow); border-color: var(--canvas-accent); }
+.canvas-page-with-sidebar .kan-col-head {
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--canvas-text-3);
+ display: flex;
+ justify-content: space-between;
+ font-weight: 600;
+ padding: 0 2px 4px;
+}
+.canvas-page-with-sidebar .kan-col-head .count { font-family: var(--canvas-mono); }
+.canvas-page-with-sidebar .kan-card {
+ background: var(--canvas-surface-2);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 6px;
+ padding: 7px 9px;
+ font-size: 12px;
+ cursor: grab;
+ line-height: 1.35;
+ position: relative;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .kan-card { background: #fff; box-shadow: 0 1px 2px rgba(20,30,50,0.04); }
+.canvas-page-with-sidebar .kan-card:active { cursor: grabbing; }
+.canvas-page-with-sidebar .kan-card:hover { background: var(--canvas-surface-3); }
+.canvas-page-with-sidebar .kan-card.dragging { opacity: 0.4; }
+.canvas-page-with-sidebar .kan-card .priority-bar {
+ position: absolute;
+ left: 0; top: 6px; bottom: 6px;
+ width: 2px;
+ border-radius: 2px;
+}
+.canvas-page-with-sidebar .kan-card.high .priority-bar { background: var(--canvas-danger); }
+.canvas-page-with-sidebar .kan-card.med .priority-bar { background: var(--canvas-warn); }
+.canvas-page-with-sidebar .kan-card.low .priority-bar { background: var(--canvas-text-4); }
+.canvas-page-with-sidebar .kan-card .kan-title { padding-left: 6px; }
+.canvas-page-with-sidebar .kan-card .kan-meta {
+ font-family: var(--canvas-mono);
+ font-size: 9.5px;
+ color: var(--canvas-text-3);
+ margin-top: 4px;
+ padding-left: 6px;
+ display: flex;
+ gap: 6px;
+}
+
+.canvas-page-with-sidebar .add-tiny {
+ font-size: 11px;
+ color: var(--canvas-text-3);
+ padding: 4px 6px;
+ border-radius: 4px;
+ text-align: left;
+ border: 1px dashed transparent;
+ cursor: pointer;
+ background: none;
+ font-family: inherit;
+}
+.canvas-page-with-sidebar .add-tiny:hover { color: var(--canvas-accent); border-color: var(--canvas-border); background: var(--canvas-surface); }
+
+/* Pomodoro */
+.canvas-page-with-sidebar .pomo {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ padding: 6px 0;
+ flex: 1;
+ justify-content: center;
+}
+.canvas-page-with-sidebar .pomo-ring {
+ width: 130px;
+ height: 130px;
+ position: relative;
+}
+.canvas-page-with-sidebar .pomo-ring svg { transform: rotate(-90deg); }
+.canvas-page-with-sidebar .pomo-ring .track { stroke: var(--canvas-surface-3); }
+.canvas-page-with-sidebar[data-canvas-theme="light"] .pomo-ring .track { stroke: #e7ebf2; }
+.canvas-page-with-sidebar .pomo-ring .fill {
+ stroke: var(--canvas-accent);
+ stroke-linecap: round;
+ filter: drop-shadow(0 0 6px var(--canvas-accent-glow));
+ transition: stroke-dashoffset .4s ease;
+}
+.canvas-page-with-sidebar .pomo-ring.break .fill { stroke: var(--canvas-ok); filter: drop-shadow(0 0 6px rgba(16,185,129,0.4)); }
+.canvas-page-with-sidebar .pomo-time {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-family: var(--canvas-mono);
+}
+.canvas-page-with-sidebar .pomo-time .t { font-size: 26px; font-weight: 600; color: var(--canvas-text); letter-spacing: -0.02em; }
+.canvas-page-with-sidebar .pomo-time .l { font-size: 10px; color: var(--canvas-text-3); text-transform: uppercase; letter-spacing: 0.08em; margin-top: 2px; }
+.canvas-page-with-sidebar .pomo-controls { display: flex; gap: 6px; }
+.canvas-page-with-sidebar .pomo-stats {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ color: var(--canvas-text-3);
+ display: flex;
+ gap: 14px;
+}
+.canvas-page-with-sidebar .pomo-stats b { color: var(--canvas-accent); font-weight: 500; }
+
+/* Writing tracker */
+.canvas-page-with-sidebar .write-stats {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 8px;
+}
+.canvas-page-with-sidebar .stat {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+ padding: 9px 11px;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .stat { background: #fff; }
+.canvas-page-with-sidebar .stat .stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--canvas-text-3); font-weight: 600; }
+.canvas-page-with-sidebar .stat .stat-value { font-family: var(--canvas-mono); font-size: 18px; font-weight: 600; color: var(--canvas-text); margin-top: 3px; letter-spacing: -0.02em; }
+.canvas-page-with-sidebar .stat .stat-sub { font-size: 10.5px; color: var(--canvas-text-3); margin-top: 2px; }
+.canvas-page-with-sidebar .stat .stat-value.accent { color: var(--canvas-accent); }
+
+.canvas-page-with-sidebar .spark { width: 100%; height: 56px; }
+.canvas-page-with-sidebar .spark .area { fill: var(--canvas-accent-glow); }
+.canvas-page-with-sidebar .spark .line { stroke: var(--canvas-accent); fill: none; stroke-width: 1.5; }
+.canvas-page-with-sidebar .spark .dot-today { fill: var(--canvas-accent); }
+.canvas-page-with-sidebar .spark .grid { stroke: var(--canvas-border); stroke-dasharray: 2 3; }
+
+.canvas-page-with-sidebar .progress {
+ height: 6px;
+ background: var(--canvas-surface-2);
+ border-radius: 3px;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .progress { background: #e7ebf2; }
+.canvas-page-with-sidebar .progress > i {
+ display: block;
+ height: 100%;
+ background: var(--canvas-accent);
+ border-radius: 3px;
+ transition: width .3s;
+}
+
+/* Deadlines */
+.canvas-page-with-sidebar .dl-list { display: flex; flex-direction: column; gap: 6px; }
+.canvas-page-with-sidebar .dl-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 10px;
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .dl-row { background: #fff; }
+.canvas-page-with-sidebar .dl-row:hover { border-color: var(--canvas-border-2); }
+.canvas-page-with-sidebar .dl-day {
+ font-family: var(--canvas-mono);
+ font-size: 11px;
+ font-weight: 600;
+ width: 56px;
+ text-align: center;
+ padding: 6px 0;
+ border-radius: 5px;
+ background: var(--canvas-surface-2);
+ flex-shrink: 0;
+ line-height: 1.1;
+ color: var(--canvas-text-2);
+}
+.canvas-page-with-sidebar .dl-day.urgent { background: rgba(220,38,38,0.15); color: var(--canvas-danger); }
+.canvas-page-with-sidebar .dl-day.warn { background: rgba(245,158,11,0.15); color: var(--canvas-warn); }
+.canvas-page-with-sidebar .dl-day .num { font-size: 16px; display: block; }
+.canvas-page-with-sidebar .dl-day .lbl { font-size: 9px; text-transform: uppercase; letter-spacing: 0.06em; }
+.canvas-page-with-sidebar .dl-info { flex: 1; min-width: 0; }
+.canvas-page-with-sidebar .dl-title { font-size: 12.5px; font-weight: 500; color: var(--canvas-text); }
+.canvas-page-with-sidebar .dl-sub { font-family: var(--canvas-mono); font-size: 10px; color: var(--canvas-text-3); margin-top: 2px; }
+
+/* Budget */
+.canvas-page-with-sidebar .budget-bar {
+ height: 8px;
+ background: var(--canvas-surface-2);
+ border-radius: 4px;
+ overflow: hidden;
+ display: flex;
+ margin: 6px 0 12px;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .budget-bar { background: #e7ebf2; }
+.canvas-page-with-sidebar .budget-bar > i { display: block; height: 100%; }
+.canvas-page-with-sidebar .budget-list { display: flex; flex-direction: column; gap: 6px; }
+.canvas-page-with-sidebar .budget-row {
+ display: grid;
+ grid-template-columns: 12px 1fr auto auto;
+ gap: 10px;
+ align-items: center;
+ font-size: 12px;
+ padding: 5px 0;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .budget-row .swatch { width: 8px; height: 8px; border-radius: 2px; }
+.canvas-page-with-sidebar .budget-row .amt { font-family: var(--canvas-mono); color: var(--canvas-text-2); }
+.canvas-page-with-sidebar .budget-row .pct { font-family: var(--canvas-mono); color: var(--canvas-text-3); font-size: 10.5px; width: 36px; text-align: right; }
+
+/* Reviewer 2 widget */
+.canvas-page-with-sidebar .critic-prompt {
+ font-size: 12px;
+ color: var(--canvas-text-2);
+ background: var(--canvas-bg-2);
+ border: 1px dashed var(--canvas-critic-glow);
+ border-radius: 7px;
+ padding: 10px 12px;
+ font-style: italic;
+ line-height: 1.5;
+}
+.canvas-page-with-sidebar .critic-meter {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-family: var(--canvas-mono);
+ font-size: 10.5px;
+ color: var(--canvas-text-3);
+}
+.canvas-page-with-sidebar .critic-meter .bar {
+ flex: 1;
+ height: 4px;
+ background: var(--canvas-surface-2);
+ border-radius: 2px;
+ position: relative;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .critic-meter .bar { background: #e7ebf2; }
+.canvas-page-with-sidebar .critic-meter .bar > i {
+ position: absolute;
+ left: 0; top: 0; bottom: 0;
+ background: linear-gradient(90deg, var(--canvas-ok), var(--canvas-warn) 50%, var(--canvas-critic));
+ border-radius: 2px;
+}
+
+.canvas-page-with-sidebar .review,
+.canvas-modal-backdrop .review {
+ background: var(--canvas-bg-2);
+ border-left: 3px solid var(--canvas-critic);
+ padding: 10px 12px;
+ border-radius: 0 6px 6px 0;
+ font-size: 12.5px;
+ line-height: 1.55;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .review,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .review { background: #fff; }
+.canvas-page-with-sidebar .review .review-tag,
+.canvas-modal-backdrop .review .review-tag {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ color: var(--canvas-critic);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-weight: 600;
+ margin-bottom: 4px;
+ display: block;
+}
+
+/* Devil's Advocate */
+.canvas-page-with-sidebar .devil-list,
+.canvas-modal-backdrop .devil-list { display: flex; flex-direction: column; gap: 6px; }
+.canvas-page-with-sidebar .devil-item,
+.canvas-modal-backdrop .devil-item {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+ padding: 9px 11px;
+ font-size: 12px;
+ position: relative;
+ padding-left: 30px;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .devil-item,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .devil-item { background: #fff; }
+.canvas-page-with-sidebar .devil-item::before,
+.canvas-modal-backdrop .devil-item::before {
+ content: '◆';
+ position: absolute;
+ left: 11px;
+ top: 9px;
+ color: var(--canvas-critic);
+ font-size: 10px;
+}
+.canvas-page-with-sidebar .devil-item .lbl,
+.canvas-modal-backdrop .devil-item .lbl {
+ font-family: var(--canvas-mono);
+ font-size: 9.5px;
+ color: var(--canvas-critic);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-weight: 600;
+ margin-bottom: 3px;
+}
+
+/* Scope realism */
+.canvas-page-with-sidebar .realism { display: flex; flex-direction: column; gap: 10px; }
+.canvas-page-with-sidebar .realism-verdict {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-critic-glow);
+ border-radius: 9px;
+ padding: 12px 14px;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .realism-verdict { background: #fff; }
+.canvas-page-with-sidebar .verdict-head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 8px;
+}
+.canvas-page-with-sidebar .verdict-score {
+ font-family: var(--canvas-mono);
+ font-size: 22px;
+ font-weight: 600;
+ color: var(--canvas-critic);
+ letter-spacing: -0.02em;
+}
+.canvas-page-with-sidebar .verdict-label {
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--canvas-text-2);
+ font-weight: 600;
+}
+.canvas-page-with-sidebar .realism-row,
+.canvas-modal-backdrop .realism-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ font-size: 12px;
+ padding: 5px 0;
+}
+.canvas-page-with-sidebar .realism-row .label-cell,
+.canvas-modal-backdrop .realism-row .label-cell { width: 100px; color: var(--canvas-text-2); font-size: 11.5px; }
+.canvas-page-with-sidebar .realism-row .gauge,
+.canvas-modal-backdrop .realism-row .gauge {
+ flex: 1;
+ height: 4px;
+ background: var(--canvas-surface-2);
+ border-radius: 2px;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .gauge,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .gauge,
+body[data-canvas-theme="light"] .canvas-modal-backdrop .critic-meter .bar { background: #e7ebf2; }
+.canvas-page-with-sidebar .realism-row .gauge i,
+.canvas-modal-backdrop .realism-row .gauge i { display: block; height: 100%; background: var(--canvas-critic); border-radius: 2px; }
+.canvas-page-with-sidebar .realism-row .val,
+.canvas-modal-backdrop .realism-row .val { font-family: var(--canvas-mono); font-size: 10.5px; color: var(--canvas-text-3); width: 30px; text-align: right; }
+
+/* Notes / generic row */
+.canvas-page-with-sidebar .note-list { display: flex; flex-direction: column; gap: 6px; max-height: 280px; overflow-y: auto; }
+.canvas-page-with-sidebar .note-row {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+ padding: 8px 10px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ position: relative;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .note-row { background: #fff; }
+.canvas-page-with-sidebar .note-row:hover { border-color: var(--canvas-border-2); }
+.canvas-page-with-sidebar .note-row .note-text { font-size: 12.5px; color: var(--canvas-text); line-height: 1.45; word-break: break-word; }
+.canvas-page-with-sidebar .note-row .note-meta { font-family: var(--canvas-mono); font-size: 9.5px; color: var(--canvas-text-3); display: flex; gap: 8px; }
+.canvas-page-with-sidebar .note-row .row-actions,
+.canvas-page-with-sidebar .dl-row .row-actions { position: absolute; top: 6px; right: 6px; display: flex; gap: 1px; opacity: 0; transition: opacity .12s; }
+.canvas-page-with-sidebar .note-row:hover .row-actions,
+.canvas-page-with-sidebar .dl-row:hover .row-actions { opacity: 1; }
+.canvas-page-with-sidebar .row-actions .icon-btn { width: 22px; height: 22px; }
+.canvas-page-with-sidebar .row-actions .icon-btn svg { width: 12px; height: 12px; }
+.canvas-page-with-sidebar .tag-pill { display: inline-block; font-family: var(--canvas-mono); font-size: 9.5px; padding: 1px 6px; border-radius: 3px; background: var(--canvas-accent-glow); color: var(--canvas-accent); text-transform: uppercase; letter-spacing: 0.06em; }
+
+/* ----- Deliverables view ----- */
+
+/* Paper / document mode — three-column layout that collapses on narrow screens */
+.canvas-page-with-sidebar .deliverable-grid {
+ display: grid;
+ grid-template-columns: 180px minmax(0, 1fr) 240px;
+ gap: 16px;
+ align-items: start;
+}
+.canvas-page-with-sidebar .deliverable-nav {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ position: sticky;
+ top: 0;
+}
+
+/* Slides mode — thumbs on left, big canvas in middle, panel on right */
+.canvas-page-with-sidebar .deliverable-slides-grid {
+ display: grid;
+ grid-template-columns: 160px minmax(0, 1fr) 240px;
+ gap: 16px;
+ align-items: start;
+}
+@media (max-width: 1280px) {
+ .canvas-page-with-sidebar .deliverable-slides-grid { grid-template-columns: 140px minmax(0, 1fr); }
+ .canvas-page-with-sidebar .deliverable-slides-grid > .deliverable-insertables { display: none; }
+}
+@media (max-width: 768px) {
+ .canvas-page-with-sidebar .deliverable-slides-grid { grid-template-columns: 1fr; }
+ .canvas-page-with-sidebar .slide-thumbs { display: none; }
+}
+
+/* Slide thumbnails column */
+.canvas-page-with-sidebar .slide-thumbs {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ position: sticky;
+ top: 0;
+ max-height: calc(100vh - 100px);
+ overflow-y: auto;
+ padding-right: 4px;
+}
+.canvas-page-with-sidebar .slide-thumb {
+ display: flex;
+ align-items: stretch;
+ gap: 6px;
+ padding: 0;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ font-family: inherit;
+ text-align: left;
+}
+.canvas-page-with-sidebar .slide-thumb-num {
+ width: 18px;
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ color: var(--canvas-text-3);
+ padding-top: 4px;
+ flex-shrink: 0;
+ text-align: right;
+}
+.canvas-page-with-sidebar .slide-thumb-canvas {
+ flex: 1;
+ background: #fff;
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 4px;
+ aspect-ratio: 16 / 9;
+ padding: 5px 6px;
+ overflow: hidden;
+ color: #111;
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ transition: all .15s;
+}
+.canvas-page-with-sidebar .slide-thumb:hover .slide-thumb-canvas {
+ border-color: var(--canvas-accent);
+ transform: translateY(-1px);
+}
+.canvas-page-with-sidebar .slide-thumb.active .slide-thumb-canvas {
+ border: 2px solid var(--canvas-accent);
+ box-shadow: 0 0 0 2px var(--canvas-accent-glow);
+ padding: 4px 5px;
+}
+.canvas-page-with-sidebar .slide-thumb.active .slide-thumb-num { color: var(--canvas-accent); font-weight: 700; }
+.canvas-page-with-sidebar .slide-thumb-title {
+ font-size: 8px;
+ font-weight: 700;
+ color: #1a1a1a;
+ letter-spacing: -0.01em;
+ line-height: 1.1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.canvas-page-with-sidebar .slide-thumb-body {
+ font-size: 6.5px;
+ color: #555;
+ line-height: 1.25;
+ overflow: hidden;
+ flex: 1;
+}
+
+/* Big slide canvas */
+.canvas-page-with-sidebar .slide-canvas-wrap {
+ background: var(--canvas-surface-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 10px;
+ padding: 18px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.canvas-page-with-sidebar .slide-canvas {
+ position: relative;
+ width: 100%;
+ max-width: 720px;
+ aspect-ratio: 16 / 9;
+ background: #fff;
+ border-radius: 6px;
+ box-shadow: 0 12px 32px rgba(0,0,0,0.18), 0 4px 10px rgba(0,0,0,0.10);
+ padding: 36px 44px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ color: #111;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar .slide-canvas::before {
+ content: '';
+ position: absolute;
+ top: 0; left: 0; right: 0;
+ height: 4px;
+ background: linear-gradient(90deg, var(--canvas-accent), var(--canvas-critic));
+}
+.canvas-page-with-sidebar .slide-canvas-title {
+ font-size: 26px;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: #0f172a;
+ line-height: 1.15;
+}
+.canvas-page-with-sidebar .slide-canvas-body {
+ flex: 1;
+ font-size: 15px;
+ color: #1f2937;
+ line-height: 1.55;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar .slide-canvas-body ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+.canvas-page-with-sidebar .slide-canvas-body ul li {
+ position: relative;
+ padding-left: 22px;
+}
+.canvas-page-with-sidebar .slide-canvas-body ul li::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 9px;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--canvas-accent);
+}
+.canvas-page-with-sidebar .slide-paragraph {
+ font-size: 17px;
+ line-height: 1.6;
+}
+.canvas-page-with-sidebar .slide-placeholder {
+ color: #9ca3af;
+ font-style: italic;
+ font-size: 14px;
+}
+.canvas-page-with-sidebar .slide-canvas-footer {
+ position: absolute;
+ bottom: 14px;
+ right: 18px;
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ color: #9ca3af;
+}
+
+/* Section-check pill (used in both modes) */
+.canvas-page-with-sidebar .check-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 11px;
+ padding: 3px 9px;
+ border-radius: 999px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-3);
+ font-family: var(--canvas-sans);
+}
+.canvas-page-with-sidebar .check-pill[data-passed="true"] {
+ background: rgba(16, 185, 129, 0.15);
+ color: #10B981;
+}
+
+/* Document-page preview (Google Docs feel) */
+.canvas-page-with-sidebar .doc-page {
+ background: #ffffff;
+ color: #1a1a1a;
+ padding: 56px 72px;
+ border-radius: 4px;
+ box-shadow: 0 4px 14px rgba(0,0,0,0.10), 0 1px 4px rgba(0,0,0,0.06);
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
+ max-width: 800px;
+ margin: 0 auto;
+ width: 100%;
+}
+.canvas-page-with-sidebar .doc-title {
+ font-size: 28px;
+ font-weight: 700;
+ margin: 0 0 24px;
+ letter-spacing: -0.02em;
+ color: #0f172a;
+}
+.canvas-page-with-sidebar .doc-h2 {
+ font-size: 18px;
+ font-weight: 700;
+ margin: 24px 0 8px;
+ color: #0f172a;
+}
+.canvas-page-with-sidebar .doc-body { font-size: 14px; line-height: 1.7; color: #1f2937; }
+.canvas-page-with-sidebar .doc-body p { margin: 0 0 12px; }
+.canvas-page-with-sidebar .doc-body strong { color: #0f172a; }
+.canvas-page-with-sidebar .doc-section { margin-bottom: 8px; }
+
+/* Paper-page preview (Overleaf feel — serif, two-column body) */
+.canvas-page-with-sidebar .paper-page {
+ background: #fdfbf7;
+ color: #1a1a1a;
+ padding: 64px 72px;
+ border-radius: 4px;
+ box-shadow: 0 4px 14px rgba(0,0,0,0.10), 0 1px 4px rgba(0,0,0,0.06);
+ font-family: 'Georgia', 'Times New Roman', serif;
+ max-width: 820px;
+ margin: 0 auto;
+ width: 100%;
+}
+.canvas-page-with-sidebar .paper-page-head {
+ text-align: center;
+ border-bottom: 1px solid #d1d5db;
+ padding-bottom: 16px;
+ margin-bottom: 22px;
+}
+.canvas-page-with-sidebar .paper-title {
+ font-size: 22px;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ color: #0f172a;
+}
+.canvas-page-with-sidebar .paper-byline {
+ font-size: 12px;
+ color: #6b7280;
+ font-style: italic;
+ margin-top: 6px;
+}
+.canvas-page-with-sidebar .paper-h2 {
+ font-size: 14px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: #0f172a;
+ margin: 22px 0 8px;
+}
+.canvas-page-with-sidebar .paper-body { font-size: 13px; line-height: 1.65; color: #1f2937; text-align: justify; }
+.canvas-page-with-sidebar .paper-body p { margin: 0 0 10px; text-indent: 1.5em; }
+.canvas-page-with-sidebar .paper-body p:first-child { text-indent: 0; }
+.canvas-page-with-sidebar .paper-empty { color: #9ca3af; font-style: italic; font-size: 12px; }
+.canvas-page-with-sidebar .paper-section { margin-bottom: 4px; }
+
+/* ----- Canvas-mode app sidebar menu (Insights / Workspace / Deliverables) ----- */
+/* These rules live in the *app* sidebar (not the canvas-page-with-sidebar scope)
+ because they apply to .sidebar from components.css when the user is on canvas. */
+.canvas-sidebar-menu {
+ flex: 1;
+ overflow-y: auto;
+ padding: 8px 8px 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+.canvas-sidebar-menu .no-sessions {
+ padding: 18px 12px;
+ font-size: 12px;
+ color: var(--text-tertiary, #9CA3AF);
+ text-align: center;
+}
+
+/* --- Workspace: widget groups --- */
+.canvas-sidebar-menu .csm-group { display: flex; flex-direction: column; gap: 1px; margin-bottom: 4px; }
+.canvas-sidebar-menu .csm-group-head {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 8px;
+ background: transparent;
+ border: none;
+ border-radius: 5px;
+ font-family: inherit;
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ font-weight: 600;
+ color: var(--text-tertiary, #9CA3AF);
+ cursor: pointer;
+}
+.canvas-sidebar-menu .csm-group-head:hover { background: var(--bg-secondary, #f3f4f6); color: var(--text-secondary, #6b7280); }
+.dark .canvas-sidebar-menu .csm-group-head:hover { background: var(--bg-secondary-dark, #374151); }
+.canvas-sidebar-menu .csm-group-name { flex: 1; text-align: left; }
+.canvas-sidebar-menu .csm-group-count {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 10px;
+ color: var(--text-tertiary, #9CA3AF);
+}
+.canvas-sidebar-menu .csm-group-body { display: flex; flex-direction: column; gap: 1px; padding: 2px 0 6px 8px; }
+
+/* --- Deliverables: project blocks --- */
+.canvas-sidebar-menu .csm-project {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ border-radius: 6px;
+ margin-bottom: 4px;
+}
+.canvas-sidebar-menu .csm-project.active { background: rgba(99, 102, 241, 0.06); }
+.dark .canvas-sidebar-menu .csm-project.active { background: rgba(129, 140, 248, 0.10); }
+.canvas-sidebar-menu .csm-project-head {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ padding: 8px 10px;
+ background: transparent;
+ border: none;
+ border-radius: 5px;
+ font-family: inherit;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--text-primary, #111827);
+ cursor: pointer;
+ text-align: left;
+}
+.canvas-sidebar-menu .csm-project-head:hover { background: var(--bg-secondary, #f3f4f6); }
+.dark .canvas-sidebar-menu .csm-project-head { color: var(--text-primary-dark, #f9fafb); }
+.dark .canvas-sidebar-menu .csm-project-head:hover { background: var(--bg-secondary-dark, #374151); }
+.canvas-sidebar-menu .csm-project.active .csm-project-name { color: var(--accent-primary, #6366F1); font-weight: 600; }
+.canvas-sidebar-menu .csm-project-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.canvas-sidebar-menu .csm-versions {
+ display: inline-flex;
+ align-items: center;
+ gap: 3px;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 10px;
+ color: var(--text-tertiary, #9CA3AF);
+ padding: 1px 5px;
+ border-radius: 3px;
+ background: var(--bg-secondary, #f3f4f6);
+}
+.dark .canvas-sidebar-menu .csm-versions { background: var(--bg-secondary-dark, #374151); }
+.canvas-sidebar-menu .csm-project-body {
+ padding: 2px 0 8px 22px;
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+}
+
+/* --- Shared row (used in groups and project bodies) --- */
+.canvas-sidebar-menu .csm-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 5px 10px 5px 6px;
+ background: transparent;
+ border: none;
+ border-radius: 4px;
+ font-family: inherit;
+ font-size: 12.5px;
+ color: var(--text-secondary, #6b7280);
+ text-align: left;
+ cursor: pointer;
+}
+.canvas-sidebar-menu .csm-row:hover {
+ background: var(--bg-secondary, #f3f4f6);
+ color: var(--text-primary, #111827);
+}
+.dark .canvas-sidebar-menu .csm-row { color: var(--text-secondary-dark, #9ca3af); }
+.dark .canvas-sidebar-menu .csm-row:hover { background: var(--bg-secondary-dark, #374151); color: var(--text-primary-dark, #f9fafb); }
+.canvas-sidebar-menu .csm-row.critic { color: #8B5CF6; }
+.dark .canvas-sidebar-menu .csm-row.critic { color: #A78BFA; }
+.canvas-sidebar-menu .csm-row-bullet {
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background: currentColor;
+ opacity: 0.45;
+ flex-shrink: 0;
+ margin-left: 4px;
+}
+.canvas-sidebar-menu .csm-row-icon { font-size: 11px; opacity: 0.85; }
+.canvas-sidebar-menu .csm-row-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.canvas-sidebar-menu .csm-row-meta {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 9.5px;
+ color: var(--text-tertiary, #9CA3AF);
+}
+.canvas-sidebar-menu .csm-row-foot {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 3px 8px;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 9.5px;
+ color: var(--text-tertiary, #9CA3AF);
+ cursor: default;
+}
+.canvas-sidebar-menu .csm-row-foot:hover { background: transparent; color: var(--text-tertiary, #9CA3AF); }
+
+/* Chevron rotates open/closed */
+.canvas-sidebar-menu .csm-chevron {
+ transition: transform 0.15s ease;
+ color: var(--text-tertiary, #9CA3AF);
+}
+.canvas-sidebar-menu .csm-chevron.open { transform: rotate(90deg); }
+
+/* ----- Deliverable project tabs (Overleaf-style multi-draft) ----- */
+.canvas-page-with-sidebar .deliverable-projects {
+ display: flex;
+ gap: 4px;
+ flex-wrap: wrap;
+ align-items: center;
+ margin-bottom: 14px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--canvas-border);
+}
+.canvas-page-with-sidebar .deliverable-project-tab {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 11px;
+ border-radius: 6px;
+ background: transparent;
+ border: 1px solid transparent;
+ color: var(--canvas-text-2);
+ font-family: inherit;
+ font-size: 12.5px;
+ cursor: pointer;
+ transition: background .12s, color .12s, border-color .12s;
+}
+.canvas-page-with-sidebar .deliverable-project-tab:hover {
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .deliverable-project-tab.active {
+ background: var(--canvas-surface-2);
+ border-color: var(--canvas-border);
+ color: var(--canvas-text);
+ font-weight: 600;
+}
+.canvas-page-with-sidebar .deliverable-new-project { position: relative; }
+.canvas-page-with-sidebar .deliverable-new-project summary {
+ list-style: none;
+ cursor: pointer;
+ color: var(--canvas-text-3);
+}
+.canvas-page-with-sidebar .deliverable-new-project summary::-webkit-details-marker { display: none; }
+.canvas-page-with-sidebar .deliverable-new-project[open] summary { background: var(--canvas-surface-2); color: var(--canvas-text); }
+
+/* Auto-save indicator next to the project title */
+.canvas-page-with-sidebar .save-indicator {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 11.5px;
+ color: var(--canvas-text-3);
+ font-family: var(--canvas-mono);
+ white-space: nowrap;
+ padding: 4px 8px;
+ border-radius: 999px;
+ background: var(--canvas-surface-2);
+ transition: background .2s, color .2s;
+}
+.canvas-page-with-sidebar .save-indicator-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--canvas-ok);
+ box-shadow: 0 0 6px rgba(16, 185, 129, 0.4);
+ transition: background .2s;
+}
+.canvas-page-with-sidebar .save-indicator.saving {
+ background: var(--canvas-accent-glow);
+ color: var(--canvas-accent);
+}
+.canvas-page-with-sidebar .save-indicator.saving .save-indicator-dot {
+ background: var(--canvas-accent);
+ animation: save-pulse 0.6s ease infinite;
+}
+@keyframes save-pulse {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.4; transform: scale(0.85); }
+}
+
+/* Editable project title — looks like the page-title but is an input */
+.canvas-page-with-sidebar .page-title-editable {
+ background: transparent;
+ border: none;
+ outline: none;
+ width: 100%;
+ font-family: inherit;
+ font-size: 28px;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ margin: 0;
+ padding: 2px 0;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .page-title-editable:focus {
+ background: rgba(255, 255, 255, 0.03);
+ border-radius: 4px;
+ padding: 2px 6px;
+ margin: 0 -6px;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .page-title-editable:focus {
+ background: rgba(0, 0, 0, 0.025);
+}
+
+/* Version history dropdown panel */
+.canvas-page-with-sidebar .deliverable-history {
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 8px;
+ margin-bottom: 16px;
+ padding: 6px;
+ max-height: 320px;
+ overflow-y: auto;
+}
+.canvas-page-with-sidebar .deliverable-history-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 4px 8px 8px;
+ font-size: 11px;
+ font-family: var(--canvas-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--canvas-text-3);
+ border-bottom: 1px solid var(--canvas-border);
+ margin-bottom: 4px;
+}
+.canvas-page-with-sidebar .deliverable-history-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ width: 100%;
+ padding: 6px 8px;
+ background: transparent;
+ border: none;
+ border-radius: 4px;
+ color: var(--canvas-text-2);
+ font-family: inherit;
+ font-size: 12px;
+ cursor: pointer;
+}
+.canvas-page-with-sidebar .deliverable-history-row:hover {
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text);
}
-.empty-canvas-btn {
- display: inline-flex;
+/* Slash menu (block-insert popover) */
+.canvas-page-with-sidebar .slash-menu {
+ position: absolute;
+ bottom: calc(100% + 4px);
+ left: 0;
+ z-index: 30;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 8px;
+ box-shadow: var(--canvas-shadow-lg);
+ padding: 4px;
+ min-width: 240px;
+ max-height: 280px;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+}
+.canvas-page-with-sidebar .slash-menu-head {
+ font-size: 10px;
+ font-family: var(--canvas-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--canvas-text-4);
+ padding: 6px 8px 4px;
+}
+.canvas-page-with-sidebar .slash-menu button {
+ display: flex;
align-items: center;
- justify-content: center;
- gap: 10px;
- height: 46px;
- padding: 0 22px;
- border-radius: 12px;
- font-size: 15px;
- font-weight: 600;
- letter-spacing: -0.005em;
+ gap: 8px;
+ background: transparent;
+ border: none;
+ padding: 6px 8px;
+ border-radius: 4px;
+ font-family: inherit;
+ font-size: 12.5px;
+ color: var(--canvas-text);
+ text-align: left;
cursor: pointer;
- transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1),
- box-shadow 0.22s ease,
- background 0.18s ease,
- border-color 0.18s ease,
- color 0.18s ease;
- border: 1px solid transparent;
}
-
-.empty-canvas-btn .empty-canvas-btn-arrow {
- transition: transform 0.18s ease;
+.canvas-page-with-sidebar .slash-menu button:hover,
+.canvas-page-with-sidebar .slash-menu button.active {
+ background: var(--canvas-surface-2);
}
-
-.empty-canvas-btn:hover .empty-canvas-btn-arrow {
- transform: translateX(3px);
+.canvas-page-with-sidebar .slash-menu-kind {
+ font-family: var(--canvas-mono);
+ font-size: 9.5px;
+ color: var(--canvas-text-4);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
}
-.empty-canvas-btn:active {
- transform: translateY(1px);
+/* Poster mode — 4-quadrant grid, banners on top and bottom */
+.canvas-page-with-sidebar .poster-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 10px;
+ padding: 18px;
+}
+.canvas-page-with-sidebar .poster-banner { grid-column: 1 / -1; }
+.canvas-page-with-sidebar .poster-panel {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 8px;
+ padding: 14px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
}
-
-/* Primary — Start Chatting */
-.empty-canvas-btn.primary {
- background: var(--accent-gradient);
- color: #ffffff;
- box-shadow: 0 6px 18px -6px rgba(99, 102, 241, 0.55),
- inset 0 1px 0 rgba(255, 255, 255, 0.18);
+.canvas-page-with-sidebar[data-canvas-theme="light"] .poster-panel {
+ background: #ffffff;
+ border-color: rgba(15, 15, 15, 0.08);
}
-
-.empty-canvas-btn.primary:hover {
- transform: translateY(-2px);
- box-shadow: 0 12px 28px -8px rgba(99, 102, 241, 0.7),
- inset 0 1px 0 rgba(255, 255, 255, 0.25);
+.canvas-page-with-sidebar .poster-panel-head {
+ font-size: 13px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--canvas-accent);
+ border-bottom: 2px solid var(--canvas-accent-glow);
+ padding-bottom: 6px;
+}
+.canvas-page-with-sidebar .poster-panel textarea {
+ flex: 1;
+ min-height: 120px;
+ background: transparent;
+ border: none;
+ outline: none;
+ resize: none;
+ font-family: inherit;
+ font-size: 13px;
+ line-height: 1.5;
+ color: var(--canvas-text);
+ padding: 0;
+}
+@media (max-width: 768px) {
+ .canvas-page-with-sidebar .poster-grid { grid-template-columns: 1fr; }
}
-/* Secondary — Process Existing Chats */
-.empty-canvas-btn.secondary {
- background: var(--bg-secondary);
- color: var(--text-primary);
- border-color: var(--border-primary);
+/* ----- Rich editor block (click-to-edit + floating toolbar + LaTeX math) ----- */
+.canvas-page-with-sidebar .rich-block {
+ position: relative;
+ margin-top: 4px;
}
-.empty-canvas-btn.secondary:hover {
- background: var(--bg-tertiary);
- border-color: var(--accent-primary);
- color: var(--accent-primary);
- transform: translateY(-2px);
- box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.15);
+.canvas-page-with-sidebar .rich-toolbar {
+ position: absolute;
+ bottom: calc(100% + 6px);
+ left: 0;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1px;
+ padding: 4px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 8px;
+ box-shadow: var(--canvas-shadow-lg);
+ z-index: 20;
+ animation: canvas-view-in 140ms var(--canvas-ease);
+}
+.canvas-page-with-sidebar .rich-toolbar button {
+ background: transparent;
+ border: none;
+ width: 30px;
+ height: 28px;
+ border-radius: 4px;
+ display: grid;
+ place-items: center;
+ color: var(--canvas-text-2);
+ font-family: inherit;
+ font-size: 13px;
+ cursor: pointer;
+ transition: background .12s, color .12s;
+}
+.canvas-page-with-sidebar .rich-toolbar button:hover {
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text);
+}
+
+/* Rendered block (idle state — looks like real document text) */
+.canvas-page-with-sidebar .rich-rendered {
+ cursor: text;
+ padding: 4px 0;
+ border-radius: 4px;
+ transition: background .12s;
+ outline: none;
+}
+.canvas-page-with-sidebar .rich-rendered:hover {
+ background: rgba(255, 255, 255, 0.02);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .rich-rendered:hover {
+ background: rgba(0, 0, 0, 0.02);
+}
+.canvas-page-with-sidebar .rich-rendered:focus-visible {
+ background: rgba(0, 0, 0, 0.025);
+ box-shadow: 0 0 0 2px var(--canvas-accent-glow);
+}
+.canvas-page-with-sidebar .rich-placeholder {
+ color: var(--canvas-text-4);
+ font-style: italic;
+}
+
+/* Inside the rendered block: typography matching the page */
+.canvas-page-with-sidebar .rich-rendered > * { margin: 0; }
+.canvas-page-with-sidebar .rich-rendered > * + * { margin-top: 12px; }
+.canvas-page-with-sidebar .rich-rendered p {
+ font-size: 16px;
+ line-height: 1.65;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .rich-rendered.serif p {
+ font-family: 'Georgia', 'Times New Roman', serif;
+ font-size: 14.5px;
+ line-height: 1.7;
+ text-align: justify;
+}
+.canvas-page-with-sidebar .rich-rendered h1,
+.canvas-page-with-sidebar .rich-rendered h2,
+.canvas-page-with-sidebar .rich-rendered h3,
+.canvas-page-with-sidebar .rich-rendered h4 {
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ color: var(--canvas-text);
+ margin-top: 16px;
+}
+.canvas-page-with-sidebar .rich-rendered h1 { font-size: 24px; }
+.canvas-page-with-sidebar .rich-rendered h2 { font-size: 19px; }
+.canvas-page-with-sidebar .rich-rendered h3 { font-size: 16px; }
+.canvas-page-with-sidebar .rich-rendered ul,
+.canvas-page-with-sidebar .rich-rendered ol {
+ padding-left: 22px;
+ font-size: 16px;
+ line-height: 1.65;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .rich-rendered.serif ul,
+.canvas-page-with-sidebar .rich-rendered.serif ol {
+ font-family: 'Georgia', serif;
+ font-size: 14.5px;
+}
+.canvas-page-with-sidebar .rich-rendered li { margin-bottom: 4px; }
+.canvas-page-with-sidebar .rich-rendered blockquote {
+ border-left: 3px solid var(--canvas-accent);
+ padding: 4px 14px;
+ color: var(--canvas-text-2);
+ margin-left: 0;
+ background: rgba(99, 102, 241, 0.04);
+ border-radius: 0 4px 4px 0;
+}
+.canvas-page-with-sidebar .rich-rendered code {
+ background: var(--canvas-surface-2);
+ padding: 1px 5px;
+ border-radius: 3px;
+ font-family: var(--canvas-mono);
+ font-size: 13px;
+}
+.canvas-page-with-sidebar .rich-rendered pre {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 6px;
+ padding: 12px;
+ overflow-x: auto;
+ font-family: var(--canvas-mono);
+ font-size: 13px;
+}
+.canvas-page-with-sidebar .rich-rendered pre code { background: none; padding: 0; }
+.canvas-page-with-sidebar .rich-rendered a {
+ color: var(--canvas-accent);
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+.canvas-page-with-sidebar .rich-rendered strong { color: var(--canvas-text); font-weight: 700; }
+.canvas-page-with-sidebar .rich-rendered em { color: var(--canvas-text); }
+.canvas-page-with-sidebar .rich-rendered img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 4px;
+ margin: 8px 0;
+ display: block;
}
-[data-theme="dark"] .empty-canvas-btn.secondary {
- background: rgba(255, 255, 255, 0.05);
- border-color: rgba(255, 255, 255, 0.12);
- color: var(--text-primary);
+/* KaTeX math overrides — match document text size */
+.canvas-page-with-sidebar .rich-rendered .katex {
+ font-size: 1.05em;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .rich-rendered .katex-display {
+ margin: 16px 0;
+ padding: 8px 12px;
+ background: var(--canvas-bg-2);
+ border-radius: 6px;
+ overflow-x: auto;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .rich-rendered .katex-display {
+ background: rgba(0, 0, 0, 0.025);
}
-[data-theme="dark"] .empty-canvas-btn.secondary:hover {
- background: rgba(255, 255, 255, 0.1);
- border-color: var(--accent-primary);
- color: #ffffff;
+/* ----- Real LaTeX editor (CodeMirror + LaTeX.js) ----- */
+.canvas-page-with-sidebar .paper-editor-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 7px;
+ padding: 3px;
+ margin-bottom: 14px;
+}
+.canvas-page-with-sidebar .paper-editor-toggle-label {
+ font-size: 10.5px;
+ color: var(--canvas-text-4);
+ font-family: var(--canvas-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ padding: 0 8px 0 6px;
+}
+.canvas-page-with-sidebar .paper-editor-toggle button {
+ background: transparent;
+ border: none;
+ padding: 4px 11px;
+ border-radius: 5px;
+ font-family: inherit;
+ font-size: 12px;
+ color: var(--canvas-text-3);
+ cursor: pointer;
+ transition: background .12s, color .12s;
+}
+.canvas-page-with-sidebar .paper-editor-toggle button:hover { color: var(--canvas-text); }
+.canvas-page-with-sidebar .paper-editor-toggle button.active {
+ background: var(--canvas-surface-3);
+ color: var(--canvas-text);
+ box-shadow: inset 0 0 0 1px var(--canvas-border-2);
}
-.inline-loading-spinner {
+.canvas-page-with-sidebar .latex-editor {
+ display: flex;
+ flex-direction: column;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 8px;
+ overflow: hidden;
+}
+.canvas-page-with-sidebar .latex-editor-toolbar {
display: flex;
- justify-content: center;
align-items: center;
- width: 100%;
- margin: 1.25rem 0;
+ gap: 8px;
+ padding: 8px 12px;
+ border-bottom: 1px solid var(--canvas-border);
+ background: var(--canvas-bg-2);
}
+.canvas-page-with-sidebar .latex-editor-mode {
+ display: inline-flex;
+ align-items: center;
+ gap: 2px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ border-radius: 5px;
+ padding: 2px;
+}
+.canvas-page-with-sidebar .latex-editor-mode button {
+ background: transparent;
+ border: none;
+ padding: 3px 10px;
+ border-radius: 3px;
+ font-family: inherit;
+ font-size: 11.5px;
+ color: var(--canvas-text-3);
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ cursor: pointer;
+}
+.canvas-page-with-sidebar .latex-editor-mode button.active {
+ background: var(--canvas-surface-3);
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .latex-editor-mode button:hover { color: var(--canvas-text); }
-.inline-loading-spinner .spinning {
- animation: spin 2s linear infinite;
- width: 2rem;
- height: 2rem;
- color: var(--accent-primary);
+.canvas-page-with-sidebar .latex-editor-panes {
+ display: grid;
+ min-height: 520px;
+ height: 70vh;
+ max-height: 800px;
}
+.canvas-page-with-sidebar .latex-editor-panes.mode-split { grid-template-columns: 1fr 1fr; }
+.canvas-page-with-sidebar .latex-editor-panes.mode-source,
+.canvas-page-with-sidebar .latex-editor-panes.mode-preview { grid-template-columns: 1fr; }
-@keyframes spin {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
+.canvas-page-with-sidebar .latex-source-pane {
+ border-right: 1px solid var(--canvas-border);
+ overflow: hidden;
+}
+.canvas-page-with-sidebar .latex-source-pane .cm-editor {
+ height: 100%;
+ font-size: 13px;
+}
+.canvas-page-with-sidebar .latex-source-pane .cm-scroller {
+ font-family: var(--canvas-mono);
+}
+.canvas-page-with-sidebar .latex-editor-panes.mode-preview .latex-source-pane,
+.canvas-page-with-sidebar .latex-editor-panes.mode-source ~ .latex-preview-pane {
+ display: none;
}
-.insight-footer {
+.canvas-page-with-sidebar .latex-preview-pane {
+ background: #fdfbf7;
+ color: #1a1a1a;
+ overflow-y: auto;
+ padding: 40px 56px;
+ font-family: 'Georgia', 'Times New Roman', serif;
+ position: relative;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .latex-preview-pane { background: #fdfbf7; }
+.canvas-page-with-sidebar .latex-preview-body {
+ max-width: 720px;
+ margin: 0 auto;
+}
+.canvas-page-with-sidebar .latex-preview-body h1,
+.canvas-page-with-sidebar .latex-preview-body h2,
+.canvas-page-with-sidebar .latex-preview-body h3 {
+ font-family: 'Georgia', serif;
+ color: #0f172a;
+}
+.canvas-page-with-sidebar .latex-preview-body p {
+ font-size: 14.5px;
+ line-height: 1.65;
+ color: #1f2937;
+ text-align: justify;
+}
+.canvas-page-with-sidebar .latex-preview-error {
display: flex;
- justify-content: space-between;
- font-size: 0.8rem;
- color: var(--text-tertiary);
+ gap: 10px;
+ padding: 10px 14px;
+ margin: 0 auto 14px;
+ max-width: 720px;
+ background: rgba(220, 38, 38, 0.08);
+ border-left: 3px solid var(--canvas-danger);
+ border-radius: 4px;
+ color: var(--canvas-danger);
}
-.insight-source {
- font-weight: 500;
- color: var(--accent-primary);
+@media (max-width: 900px) {
+ .canvas-page-with-sidebar .latex-editor-panes.mode-split { grid-template-columns: 1fr; }
+ .canvas-page-with-sidebar .latex-editor-panes.mode-split .latex-source-pane { border-right: none; border-bottom: 1px solid var(--canvas-border); }
}
-.print-view .insight-source {
- color: var(--text-secondary);
+/* ----- Notion-style single-surface deliverable editor ----- */
+.canvas-page-with-sidebar .notion-deliverable-grid {
+ display: grid;
+ grid-template-columns: 200px minmax(0, 1fr) 240px;
+ gap: 28px;
+ align-items: start;
+}
+@media (max-width: 1280px) {
+ .canvas-page-with-sidebar .notion-deliverable-grid { grid-template-columns: 200px minmax(0, 1fr); }
+ .canvas-page-with-sidebar .notion-deliverable-grid > .deliverable-insertables { display: none; }
+}
+@media (max-width: 768px) {
+ .canvas-page-with-sidebar .notion-deliverable-grid { grid-template-columns: 1fr; }
+ .canvas-page-with-sidebar .notion-toc { display: none; }
}
-/* Loading State */
-.canvas-loading {
+/* Subtle TOC */
+.canvas-page-with-sidebar .notion-toc {
display: flex;
flex-direction: column;
+ gap: 1px;
+ position: sticky;
+ top: 0;
+ padding: 4px 0;
+}
+.canvas-page-with-sidebar .notion-toc-label {
+ font-size: 11px;
+ color: var(--canvas-text-4);
+ font-weight: 500;
+ padding: 4px 6px 8px;
+ letter-spacing: 0;
+ text-transform: none;
+}
+.canvas-page-with-sidebar .notion-toc-link {
+ display: flex;
align-items: center;
+ gap: 8px;
+ background: transparent;
+ border: none;
+ padding: 5px 8px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 13px;
+ color: var(--canvas-text-2);
+ text-align: left;
+ transition: background .12s, color .12s;
+}
+.canvas-page-with-sidebar .notion-toc-link:hover {
+ background: rgba(255, 255, 255, 0.04);
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .notion-toc-link:hover {
+ background: rgba(0, 0, 0, 0.04);
+}
+.canvas-page-with-sidebar .notion-toc-link.active {
+ background: rgba(255, 255, 255, 0.06);
+ color: var(--canvas-text);
+ font-weight: 500;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .notion-toc-link.active {
+ background: rgba(0, 0, 0, 0.05);
+}
+.canvas-page-with-sidebar .notion-toc-link-text {
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.canvas-page-with-sidebar .notion-toc-link-count {
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ color: var(--canvas-text-4);
+}
+
+/* The page itself */
+.canvas-page-with-sidebar .notion-page-wrap {
+ display: flex;
justify-content: center;
- height: 100vh;
- background: var(--bg-gradient);
- color: var(--text-primary);
+}
+.canvas-page-with-sidebar .notion-page {
+ background: var(--canvas-surface);
+ width: 100%;
+ max-width: 760px;
+ padding: 60px 80px;
+ border-radius: 4px;
+ color: var(--canvas-text);
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .notion-page {
+ background: #ffffff;
+ border: 1px solid rgba(15, 15, 15, 0.06);
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
+}
+.canvas-page-with-sidebar .notion-page.serif {
+ font-family: 'Georgia', 'Times New Roman', serif;
+}
+.canvas-page-with-sidebar .notion-page-wrap.paper .notion-page {
+ background: #fdfbf7;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .notion-page-wrap.paper .notion-page {
+ background: #fdfbf7;
+}
+.canvas-page-with-sidebar .notion-page-title {
+ font-size: 40px;
+ font-weight: 700;
+ margin: 0 0 6px;
+ letter-spacing: -0.025em;
+ line-height: 1.15;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .notion-page-meta {
+ font-size: 12.5px;
+ color: var(--canvas-text-3);
+ margin: 0 0 36px;
}
-.loading-spinner {
- width: 3rem;
- height: 3rem;
- border: 3px solid var(--border-primary);
- border-top: 3px solid var(--accent-primary);
- border-radius: 50%;
- animation: spin 1s linear infinite;
- margin-bottom: 1rem;
+.canvas-page-with-sidebar .notion-block {
+ margin: 24px 0;
+ position: relative;
+}
+.canvas-page-with-sidebar .notion-h2 {
+ font-size: 24px;
+ font-weight: 700;
+ letter-spacing: -0.015em;
+ margin: 0 0 8px;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .notion-h2.serif {
+ font-size: 16px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ font-weight: 700;
+}
+.canvas-page-with-sidebar .notion-hint {
+ font-size: 13.5px;
+ color: var(--canvas-text-4);
+ font-style: italic;
+ margin: 4px 0 6px;
}
-/* Print Styles */
-.print-footer {
- position: fixed;
- bottom: 0;
- left: 0;
+/* The textarea, styled as Notion body text */
+.canvas-page-with-sidebar .notion-text {
+ display: block;
+ width: 100%;
+ background: transparent;
+ border: none;
+ outline: none;
+ resize: none;
+ padding: 4px 0;
+ margin: 0;
+ font-family: inherit;
+ font-size: 16px;
+ line-height: 1.6;
+ color: var(--canvas-text);
+ overflow: hidden;
+ min-height: 28px;
+}
+.canvas-page-with-sidebar .notion-text.serif {
+ font-family: 'Georgia', 'Times New Roman', serif;
+ font-size: 14.5px;
+ line-height: 1.7;
+}
+.canvas-page-with-sidebar .notion-text::placeholder {
+ color: var(--canvas-text-4);
+}
+.canvas-page-with-sidebar .notion-text:focus {
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 3px;
+ padding: 4px 6px;
+ margin: 0 -6px;
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .notion-text:focus {
+ background: rgba(0, 0, 0, 0.025);
+}
+
+/* Inline meta (check pills + word count) sits below the block */
+.canvas-page-with-sidebar .notion-block-meta {
+ margin-top: 10px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+/* Notion-style callout box for AI suggestions */
+.canvas-page-with-sidebar .notion-callout {
+ margin-top: 12px;
+ background: rgba(99, 102, 241, 0.06);
+ border-radius: 4px;
+ padding: 14px 16px;
+ display: flex;
+ gap: 10px;
+ font-size: 13.5px;
+ line-height: 1.55;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .notion-callout {
+ background: rgba(99, 102, 241, 0.07);
+}
+.canvas-page-with-sidebar .notion-callout > svg {
+ color: var(--canvas-accent);
+ flex-shrink: 0;
+ margin-top: 2px;
+}
+.canvas-page-with-sidebar .notion-callout-label {
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--canvas-accent);
+ font-weight: 600;
+ margin-bottom: 2px;
+}
+
+/* Export dropdown — uses native for zero-state-management */
+.canvas-page-with-sidebar .canvas-export-menu summary {
+ list-style: none;
+}
+.canvas-page-with-sidebar .canvas-export-menu summary::-webkit-details-marker {
+ display: none;
+}
+.canvas-page-with-sidebar .canvas-export-menu[open] summary {
+ filter: brightness(1.05);
+}
+.canvas-page-with-sidebar .canvas-export-menu-list {
+ position: absolute;
+ top: calc(100% + 4px);
right: 0;
- background: white;
- padding: 1rem;
- text-align: center;
- font-size: 0.8rem;
- color: var(--text-secondary);
- border-top: 1px solid var(--border-primary);
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border-2);
+ border-radius: 6px;
+ box-shadow: var(--canvas-shadow-lg);
+ padding: 4px;
+ min-width: 180px;
+ display: flex;
+ flex-direction: column;
+ z-index: 30;
+}
+.canvas-page-with-sidebar .canvas-export-menu-list button {
+ text-align: left;
+ background: transparent;
+ border: none;
+ padding: 7px 10px;
+ border-radius: 4px;
+ font-size: 13px;
+ cursor: pointer;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .canvas-export-menu-list button:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .canvas-export-menu-list button:hover {
+ background: rgba(0, 0, 0, 0.05);
}
-@media print {
- .canvas-page {
- background: white !important;
- color: black !important;
- padding: 0.5rem !important;
- }
+/* Old paper/doc preview styles can stay below for the slides bottom-info case */
- .back-button,
- .header-actions {
- display: none !important;
- }
+/* Responsive breakpoints for paper/document mode */
+@media (max-width: 1280px) {
+ .canvas-page-with-sidebar .deliverable-grid { grid-template-columns: 160px minmax(0, 1fr); }
+ .canvas-page-with-sidebar .deliverable-grid > .deliverable-insertables { display: none; }
+}
+@media (max-width: 768px) {
+ .canvas-page-with-sidebar .deliverable-grid { grid-template-columns: 1fr; }
+ .canvas-page-with-sidebar .doc-page,
+ .canvas-page-with-sidebar .paper-page { padding: 32px 24px; }
+}
- .canvas-section {
- page-break-inside: avoid;
- margin-bottom: 1rem;
- }
+/* Insertable row in the right rail of Deliverables */
+.canvas-page-with-sidebar .canvas-insert-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 6px 8px;
+ background: transparent;
+ border: none;
+ border-radius: 4px;
+ text-align: left;
+ cursor: pointer;
+ font-family: inherit;
+ color: var(--canvas-text);
+ transition: background .12s;
+}
+.canvas-page-with-sidebar .canvas-insert-row:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+.canvas-page-with-sidebar[data-canvas-theme="light"] .canvas-insert-row:hover {
+ background: rgba(0, 0, 0, 0.04);
+}
- .insight-card {
- break-inside: avoid;
- }
+/* PhD Journey milestone rows */
+.canvas-page-with-sidebar .phd-milestone {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ padding: 8px 4px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background .12s;
+}
+.canvas-page-with-sidebar .phd-milestone:hover { background: var(--canvas-surface-2); }
+.canvas-page-with-sidebar .phd-milestone-check {
+ flex-shrink: 0;
+ width: 18px;
+ height: 18px;
+ border-radius: 4px;
+ border: 1.5px solid var(--canvas-text-4);
+ background: transparent;
+ cursor: pointer;
+ display: grid;
+ place-items: center;
+ margin-top: 2px;
+ color: #fff;
+ transition: background .12s, border-color .12s;
+}
+.canvas-page-with-sidebar .phd-milestone.milestone-in-progress .phd-milestone-check {
+ background: #3B82F6;
+ border-color: #3B82F6;
+}
+.canvas-page-with-sidebar .phd-milestone.milestone-completed .phd-milestone-check {
+ background: #10B981;
+ border-color: #10B981;
+}
+.canvas-page-with-sidebar .phd-milestone-body { flex: 1; min-width: 0; }
+.canvas-page-with-sidebar .phd-milestone-label {
+ font-size: 13px;
+ color: var(--canvas-text);
+ font-weight: 500;
+ line-height: 1.4;
+}
+.canvas-page-with-sidebar .phd-milestone.milestone-completed .phd-milestone-label {
+ text-decoration: line-through;
+ color: var(--canvas-text-3);
+}
+.canvas-page-with-sidebar .phd-milestone-hint {
+ font-size: 11.5px;
+ color: var(--canvas-text-4);
+ font-style: italic;
+ margin-top: 2px;
+}
+.canvas-page-with-sidebar .phd-milestone-note {
+ font-size: 11.5px;
+ color: var(--canvas-text-2);
+ margin-top: 2px;
}
-/* Mobile Responsive */
-@media (max-width: 768px) {
- .canvas-page {
- padding: 1rem;
- }
+/* PhD Resources link rows */
+.canvas-page-with-sidebar .phd-resource-link {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ padding: 6px 8px;
+ background: transparent;
+ border-radius: 5px;
+ text-decoration: none;
+ color: var(--canvas-text);
+ transition: background .12s;
+}
+.canvas-page-with-sidebar .phd-resource-link:hover {
+ background: var(--canvas-surface-2);
+}
+.canvas-page-with-sidebar .phd-resource-name {
+ font-size: 12.5px;
+ font-weight: 600;
+ color: var(--canvas-accent);
+}
+.canvas-page-with-sidebar .phd-resource-desc {
+ font-size: 11px;
+ color: var(--canvas-text-3);
+}
- .canvas-header {
- flex-direction: column;
- gap: 1rem;
- align-items: stretch;
- }
+/* "Coming soon" stub widget — roadmap-preview card, not a dead end */
+.canvas-page-with-sidebar .widget-stub {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ padding: 18px 4px 8px;
+}
+.canvas-page-with-sidebar .widget-stub-icon {
+ width: 32px;
+ height: 32px;
+ border-radius: 7px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-3);
+ display: grid;
+ place-items: center;
+}
+.canvas-page-with-sidebar .widget-stub-tag {
+ font-size: 9.5px;
+ font-family: var(--canvas-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--canvas-text-4);
+ font-weight: 600;
+ margin-top: 8px;
+}
+.canvas-page-with-sidebar .widget-stub-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--canvas-text);
+}
+.canvas-page-with-sidebar .widget-stub-desc {
+ font-size: 12px;
+ color: var(--canvas-text-3);
+ line-height: 1.5;
+}
+.canvas-page-with-sidebar .widget-stub-plan {
+ margin: 8px 0 0;
+ padding-left: 18px;
+ font-size: 12px;
+ color: var(--canvas-text-3);
+ line-height: 1.6;
+}
+.canvas-page-with-sidebar .widget-stub-plan li::marker { color: var(--canvas-accent); }
- .header-left {
- justify-content: center;
- }
+/* Shared empty-state block inside widgets (Notion-style: airy, single CTA) */
+.canvas-page-with-sidebar .widget-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ padding: 28px 16px;
+ text-align: center;
+ color: var(--canvas-text-3);
+ flex: 1;
+}
+.canvas-page-with-sidebar .widget-empty-icon {
+ width: 36px;
+ height: 36px;
+ border-radius: 8px;
+ background: var(--canvas-surface-2);
+ color: var(--canvas-text-3);
+ display: grid;
+ place-items: center;
+ margin-bottom: 4px;
+}
+.canvas-page-with-sidebar .widget-empty-title {
+ font-size: 13.5px;
+ font-weight: 600;
+ color: var(--canvas-text-2);
+}
+.canvas-page-with-sidebar .widget-empty-hint {
+ font-size: 12px;
+ color: var(--canvas-text-3);
+ max-width: 240px;
+ line-height: 1.5;
+}
+.canvas-page-with-sidebar .widget-empty-action {
+ margin-top: 8px;
+}
- .canvas-stats {
- flex-direction: column;
- gap: 1rem;
- }
+/* Cross-widget drop overlay — shows over the target widget body during drag */
+.canvas-page-with-sidebar .canvas-drop-overlay {
+ position: absolute;
+ inset: -4px;
+ background: var(--canvas-accent-glow);
+ border: 2px dashed var(--canvas-accent);
+ border-radius: 9px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--canvas-accent);
+ z-index: 5;
+ pointer-events: none;
+}
+.canvas-page-with-sidebar .canvas-drop-active { outline: none; }
+
+/* Compact markdown rendering inside note rows */
+.canvas-page-with-sidebar .canvas-md p { margin: 0 0 4px; }
+.canvas-page-with-sidebar .canvas-md p:last-child { margin-bottom: 0; }
+.canvas-page-with-sidebar .canvas-md h1,
+.canvas-page-with-sidebar .canvas-md h2,
+.canvas-page-with-sidebar .canvas-md h3 { font-size: 13px; margin: 4px 0 2px; font-weight: 600; }
+.canvas-page-with-sidebar .canvas-md ul,
+.canvas-page-with-sidebar .canvas-md ol { margin: 4px 0; padding-left: 18px; }
+.canvas-page-with-sidebar .canvas-md li { margin-bottom: 2px; }
+.canvas-page-with-sidebar .canvas-md code {
+ background: var(--canvas-surface-2);
+ padding: 1px 5px;
+ border-radius: 3px;
+ font-family: var(--canvas-mono);
+ font-size: 11.5px;
+}
+.canvas-page-with-sidebar .canvas-md pre {
+ background: var(--canvas-bg-2);
+ border: 1px solid var(--canvas-border);
+ border-radius: 5px;
+ padding: 8px 10px;
+ overflow-x: auto;
+ font-family: var(--canvas-mono);
+ font-size: 11.5px;
+ margin: 4px 0;
+}
+.canvas-page-with-sidebar .canvas-md pre code { background: none; padding: 0; }
+.canvas-page-with-sidebar .canvas-md a { color: var(--canvas-accent); text-decoration: underline; text-underline-offset: 2px; }
+.canvas-page-with-sidebar .canvas-md strong { color: var(--canvas-text); font-weight: 600; }
+.canvas-page-with-sidebar .canvas-md blockquote {
+ border-left: 2px solid var(--canvas-accent);
+ margin: 4px 0;
+ padding: 2px 10px;
+ color: var(--canvas-text-2);
+}
+
+/* Edit-in-place */
+.canvas-page-with-sidebar .inline-input {
+ background: transparent;
+ border: 1px solid var(--canvas-accent);
+ border-radius: 4px;
+ padding: 2px 5px;
+ font-size: inherit;
+ color: var(--canvas-text);
+ font-family: inherit;
+ width: 100%;
+ outline: none;
+}
- .insights-grid {
- grid-template-columns: 1fr;
+/* Print: just the Notion page itself, no chrome */
+@media print {
+ .canvas-sidebar,
+ .canvas-page-with-sidebar .floating-header,
+ .canvas-page-with-sidebar .canvas-tabs,
+ .canvas-page-with-sidebar .notion-toc,
+ .canvas-page-with-sidebar .deliverable-insertables,
+ .canvas-page-with-sidebar .canvas-shortcut-hint,
+ .canvas-page-with-sidebar .page-header > div:last-child,
+ .toast-stack,
+ .canvas-modal-backdrop {
+ display: none !important;
}
-
- .canvas-title {
- font-size: 1.5rem;
+ .canvas-page-with-sidebar,
+ .canvas-main-area,
+ .canvas-app-shell,
+ .canvas-content,
+ .canvas-page-with-sidebar .notion-deliverable-grid,
+ .canvas-page-with-sidebar .notion-page-wrap {
+ display: block !important;
+ overflow: visible !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ background: #ffffff !important;
+ color: #000 !important;
}
-
- .stat-item {
- min-width: auto;
+ .canvas-page-with-sidebar .notion-page {
+ background: #ffffff !important;
+ border: none !important;
+ box-shadow: none !important;
+ padding: 0 !important;
+ max-width: none !important;
}
+ .canvas-page-with-sidebar .notion-page-title { font-size: 28px; color: #000 !important; }
+ .canvas-page-with-sidebar .notion-h2 { color: #000 !important; }
+ .canvas-page-with-sidebar .notion-text { color: #000 !important; padding: 0 !important; margin: 0 !important; }
+ .canvas-page-with-sidebar .check-pill,
+ .canvas-page-with-sidebar .notion-callout { display: none !important; }
}
-/* Canvas Copyright Footer */
-.canvas-copyright-footer {
- padding: 24px;
+/* Floating shortcut hint — subtle, hides itself, hover-revealed */
+.canvas-shortcut-hint {
+ position: fixed;
+ bottom: 16px;
+ right: 20px;
+ z-index: 50;
+ display: flex;
+ gap: 14px;
+ padding: 8px 14px;
+ border-radius: 999px;
+ background: var(--canvas-surface);
+ border: 1px solid var(--canvas-border);
+ box-shadow: var(--canvas-shadow);
+ font-size: 11.5px;
+ color: var(--canvas-text-3);
+ font-family: 'Inter', sans-serif;
+ opacity: 0;
+ transform: translateY(8px);
+ transition: opacity 0.25s var(--canvas-ease), transform 0.25s var(--canvas-ease);
+ pointer-events: none;
+}
+.canvas-shortcut-hint.visible {
+ opacity: 1;
+ transform: none;
+ pointer-events: auto;
+}
+.canvas-shortcut-hint:not(.visible):hover {
+ opacity: 0.6;
+ transform: none;
+ pointer-events: auto;
+}
+.canvas-shortcut-hint kbd {
+ display: inline-block;
+ font-family: var(--canvas-mono);
+ font-size: 10px;
+ padding: 1px 5px;
+ border-radius: 3px;
+ background: var(--canvas-surface-2);
+ border: 1px solid var(--canvas-border-2);
+ color: var(--canvas-text-2);
+ margin-right: 2px;
+ min-width: 14px;
text-align: center;
- border-top: 1px solid var(--border-light, #e5e7eb);
- margin-top: 32px;
-}
\ No newline at end of file
+}
+@media (max-width: 768px) {
+ .canvas-shortcut-hint { display: none; }
+}
+
+/* Toast */
+.toast-stack {
+ position: fixed;
+ bottom: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 1000;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ pointer-events: none;
+}
+.toast-stack .toast {
+ background: #374151;
+ border: 1px solid #4B5563;
+ border-radius: 8px;
+ padding: 10px 14px;
+ font-size: 13px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4), 0 2px 6px rgba(0,0,0,0.3);
+ animation: canvas-toast-in .25s cubic-bezier(.22,.9,.3,1);
+ pointer-events: auto;
+ color: #F9FAFB;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
+}
+body[data-canvas-theme="light"] .toast-stack .toast {
+ background: #FFFFFF;
+ color: #111827;
+ border-color: #D1D5DB;
+ box-shadow: 0 18px 48px rgba(20, 30, 50, 0.16);
+}
+.toast-stack .toast.success { border-left: 3px solid #10B981; }
+.toast-stack .toast.critic { border-left: 3px solid #A78BFA; }
+.toast-stack .toast.danger { border-left: 3px solid #DC2626; }
+@keyframes canvas-toast-in { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
+
+/* Spinner */
+.canvas-page-with-sidebar .spinner,
+.canvas-modal-backdrop .spinner {
+ width: 16px;
+ height: 16px;
+ border: 2px solid var(--canvas-surface-3);
+ border-top-color: var(--canvas-accent);
+ border-radius: 50%;
+ animation: canvas-spin .8s linear infinite;
+}
+.canvas-page-with-sidebar .spinner.critic,
+.canvas-modal-backdrop .spinner.critic { border-top-color: var(--canvas-critic); }
+@keyframes canvas-spin { to { transform: rotate(360deg) } }
+
+/* Scrollbar */
+.canvas-page-with-sidebar *::-webkit-scrollbar,
+.canvas-modal-backdrop *::-webkit-scrollbar { width: 8px; height: 8px; }
+.canvas-page-with-sidebar *::-webkit-scrollbar-track,
+.canvas-modal-backdrop *::-webkit-scrollbar-track { background: transparent; }
+.canvas-page-with-sidebar *::-webkit-scrollbar-thumb,
+.canvas-modal-backdrop *::-webkit-scrollbar-thumb { background: var(--canvas-border-2); border-radius: 4px; }
+.canvas-page-with-sidebar *::-webkit-scrollbar-thumb:hover,
+.canvas-modal-backdrop *::-webkit-scrollbar-thumb:hover { background: var(--canvas-text-3); }
+