NeonClary Cursor commited on
Commit
7a97213
·
1 Parent(s): 7413585

Complete Cybersecurity -> MuscleGrowthAI theme swap and fix Profile

Browse files

Retheme the surfaces the config swap couldn't reach and fix the
broken Profile flow so the panel is identical to the base app except
for the fitness theme.

Config-driven wiring:
- Add app.logo_icon to backend AppConfig; use it in AppHeader/Login/Signup.
- Read app.footer_text (author line) in CopyrightNotice; drop the Neon.ai
brand + patents link when a panel supplies its own footer text.

Frontend theme content (cyber/PhD -> fitness):
- User Guide, ProfileWalkthrough labels, ClearDataModal descriptions.
- Canvas subsystem: seed data/insights/presets, Training Roadmap +
Fitness Resources widgets, Documents templates, critic widgets and
sample reviews, welcome tour, onboarding tour defaults.

Backend prompts:
- Neutralize academic "student/dissertation" framing in the live
orchestrator document prompts and reference-detection regexes.

Profile fixes:
- Render ProfileWalkthrough/AccountModal/ClearDataModal in ChatPage.
- Pass correct avatarOptions/currentAvatarId props to UserAvatarPicker.
- Rename theme-coupled localStorage/tour keys to the MuscleGrowth theme.

Co-authored-by: Cursor <cursoragent@cursor.com>

multi_llm_chatbot_backend/app/api/routes/user_profile.py CHANGED
@@ -30,24 +30,25 @@ PROFILE_FIELDS = [
30
  LIST_FIELDS = {"primary_domains", "certifications", "tools_stack"}
31
 
32
  _SELECT_OPTIONS: Dict[str, List[str]] = {
 
 
33
  "cyber_role": [
34
- "Student / Learner",
35
- "Career changer",
36
- "SOC analyst",
37
- "Security engineer",
38
- "Architect / lead",
39
- "Manager / director",
40
- "Consultant",
41
  "Other",
42
  ],
43
  "organization_type": [
44
- "Startup",
45
- "Mid-size company",
46
- "Enterprise",
47
- "Government / public sector",
48
- "Education",
49
- "MSP / MSSP",
50
- "Independent / job seeker",
51
  ],
52
  }
53
 
 
30
  LIST_FIELDS = {"primary_domains", "certifications", "tools_stack"}
31
 
32
  _SELECT_OPTIONS: Dict[str, List[str]] = {
33
+ # NOTE: field keys are kept generic (cyber_role / organization_type) for
34
+ # storage compatibility; the labels/options are themed for the panel.
35
  "cyber_role": [
36
+ "Beginner lifter",
37
+ "General fitness",
38
+ "Bodybuilding / hypertrophy",
39
+ "Powerlifting / strength",
40
+ "Physique / cutting",
41
+ "Athlete / sport-specific",
42
+ "Home-gym / minimal equipment",
43
  "Other",
44
  ],
45
  "organization_type": [
46
+ "Commercial gym",
47
+ "Home gym",
48
+ "University / campus gym",
49
+ "CrossFit box",
50
+ "Outdoor / calisthenics",
51
+ "Hybrid (home + gym)",
 
52
  ],
53
  }
54
 
multi_llm_chatbot_backend/app/config.py CHANGED
@@ -59,6 +59,7 @@ class AppConfig(BaseModel):
59
  title: str = "Advisor Canvas"
60
  subtitle: str = "AI-Powered Guidance"
61
  primary_color: str = "#7C3AED"
 
62
  footer_text: str = ""
63
  user_avatars: List[UserAvatarOption] = []
64
 
 
59
  title: str = "Advisor Canvas"
60
  subtitle: str = "AI-Powered Guidance"
61
  primary_color: str = "#7C3AED"
62
+ logo_icon: str = "Users"
63
  footer_text: str = ""
64
  user_avatars: List[UserAvatarOption] = []
65
 
multi_llm_chatbot_backend/app/core/db.py CHANGED
@@ -10,9 +10,9 @@ party). The replacement is a single Hugging Face Storage Bucket mounted into
10
  the Space at ``$DATA_DIR`` (default ``/data``). Everything durable lives
11
  there:
12
 
13
- ${DATA_DIR}/cybersecurity_panel.db — SQLite database (users, chat
14
  sessions, profiles, onboarding,
15
- PhD canvases)
16
 
17
  A single ``hf buckets sync hf://buckets/<org>/<bucket> ./backup`` therefore
18
  captures the full state and can be restored before the next redeploy.
 
10
  the Space at ``$DATA_DIR`` (default ``/data``). Everything durable lives
11
  there:
12
 
13
+ ${DATA_DIR}/muscle_growth_panel.db — SQLite database (users, chat
14
  sessions, profiles, onboarding,
15
+ canvases)
16
 
17
  A single ``hf buckets sync hf://buckets/<org>/<bucket> ./backup`` therefore
18
  captures the full state and can be restored before the next redeploy.
multi_llm_chatbot_backend/app/core/improved_orchestrator.py CHANGED
@@ -61,11 +61,13 @@ class ImprovedChatOrchestrator:
61
  if not tool_definitions:
62
  return ToolCallResult(text="", used_tool=False)
63
 
 
64
  system_prompt = (
65
- "You are a helpful cybersecurity assistant with access to external tools. "
 
66
  "Use the available tools when the user's question can be answered by one of them. "
67
  "Call get_current_datetime when the user asks about today, deadlines, timelines, "
68
- "schedules, incident timing, or when accurate date/time context would improve "
69
  "your guidance — then weave the result into your answer. "
70
  "If no tool is relevant, respond with a brief text answer. "
71
  "Format your responses using markdown."
@@ -171,8 +173,8 @@ class ImprovedChatOrchestrator:
171
  # Common document reference patterns
172
  patterns = [
173
  r"(?:my|the|in)\s+([a-zA-Z_\-]+\.(?:pdf|docx|txt))", # specific files
174
- r"(?:my|the)\s+(dissertation|thesis|proposal|chapter|manuscript)", # document types
175
- r"(?:in|from)\s+(?:my\s+)?([a-zA-Z_\-\s]+(?:chapter|section))", # sections
176
  ]
177
 
178
  for pattern in patterns:
@@ -557,8 +559,8 @@ class ImprovedChatOrchestrator:
557
  # Common patterns for document references
558
  document_indicators = [
559
  r"(?:my|the|in|from)\s+([a-zA-Z_\-]+\.(?:pdf|docx|txt|doc))", # specific files
560
- r"(?:my|the)\s+(dissertation|thesis|proposal|chapter|manuscript|paper)", # document types
561
- r"(?:in|from)\s+(?:my\s+)?([a-zA-Z_\-\s]+(?:chapter|section|proposal))", # sections
562
  r"(?:the|my)\s+([a-zA-Z_\-\s]+(?:document|file))", # generic documents
563
  ]
564
 
@@ -706,12 +708,12 @@ When analyzing the document context:
706
  system_message = f"""{persona.system_prompt}
707
 
708
  CURRENT SESSION CONTEXT:
709
- The student has uploaded the following documents: {doc_list}
710
 
711
  DOCUMENT CONTENT:
712
  {document_context}
713
 
714
- IMPORTANT: When the student refers to "my document," "my dissertation," "my proposal," etc., they are referring to one of their uploaded documents. Use the document context above to understand which specific document they mean and reference it by name in your response.
715
 
716
  Always cite your sources when referencing information from their documents using the format: "According to your [document_name]..." or "In your [section_name] from [document_name]..."
717
  """
@@ -719,9 +721,9 @@ When analyzing the document context:
719
  # NO DOCUMENTS - Explicitly tell persona not to reference documents
720
  system_message = f"""{persona.system_prompt}
721
 
722
- IMPORTANT: The student has NOT uploaded any documents yet. Do not reference any specific documents, files, or assume you have access to their research materials.
723
 
724
- If they mention "my document," "my dissertation," "my proposal," etc., you should:
725
  1. Acknowledge that you don't have access to their specific documents
726
  2. Ask them to upload the relevant files for more targeted advice
727
  3. Provide general guidance based on best practices in your area of expertise
 
61
  if not tool_definitions:
62
  return ToolCallResult(text="", used_tool=False)
63
 
64
+ app_cfg = settings.app
65
  system_prompt = (
66
+ f"You are a helpful assistant for {app_cfg.title} — {app_cfg.subtitle}. "
67
+ "You have access to external tools. "
68
  "Use the available tools when the user's question can be answered by one of them. "
69
  "Call get_current_datetime when the user asks about today, deadlines, timelines, "
70
+ "schedules, or when accurate date/time context would improve "
71
  "your guidance — then weave the result into your answer. "
72
  "If no tool is relevant, respond with a brief text answer. "
73
  "Format your responses using markdown."
 
173
  # Common document reference patterns
174
  patterns = [
175
  r"(?:my|the|in)\s+([a-zA-Z_\-]+\.(?:pdf|docx|txt))", # specific files
176
+ r"(?:my|the)\s+(program|plan|routine|split|log|protocol|document|proposal|chapter|report)", # document types
177
+ r"(?:in|from)\s+(?:my\s+)?([a-zA-Z_\-\s]+(?:section|plan))", # sections
178
  ]
179
 
180
  for pattern in patterns:
 
559
  # Common patterns for document references
560
  document_indicators = [
561
  r"(?:my|the|in|from)\s+([a-zA-Z_\-]+\.(?:pdf|docx|txt|doc))", # specific files
562
+ r"(?:my|the)\s+(program|plan|routine|split|log|protocol|document|proposal|chapter|report)", # document types
563
+ r"(?:in|from)\s+(?:my\s+)?([a-zA-Z_\-\s]+(?:section|plan|program))", # sections
564
  r"(?:the|my)\s+([a-zA-Z_\-\s]+(?:document|file))", # generic documents
565
  ]
566
 
 
708
  system_message = f"""{persona.system_prompt}
709
 
710
  CURRENT SESSION CONTEXT:
711
+ The user has uploaded the following documents: {doc_list}
712
 
713
  DOCUMENT CONTENT:
714
  {document_context}
715
 
716
+ IMPORTANT: When the user refers to "my document," "my plan," "my program," etc., they are referring to one of their uploaded documents. Use the document context above to understand which specific document they mean and reference it by name in your response.
717
 
718
  Always cite your sources when referencing information from their documents using the format: "According to your [document_name]..." or "In your [section_name] from [document_name]..."
719
  """
 
721
  # NO DOCUMENTS - Explicitly tell persona not to reference documents
722
  system_message = f"""{persona.system_prompt}
723
 
724
+ IMPORTANT: The user has NOT uploaded any documents yet. Do not reference any specific documents, files, or assume you have access to their materials.
725
 
726
+ If they mention "my document," "my plan," "my program," etc., you should:
727
  1. Acknowledge that you don't have access to their specific documents
728
  2. Ask them to upload the relevant files for more targeted advice
729
  3. Provide general guidance based on best practices in your area of expertise
multi_llm_chatbot_backend/app/core/onboarding_agent.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- OnboardingAgent — conversational profile gathering for cybersecurity advisors.
3
  """
4
 
5
  import json
@@ -12,27 +12,30 @@ from app.core.database import get_database
12
 
13
  LOG = logging.getLogger(__name__)
14
 
 
 
 
15
  PROFILE_FIELDS: List[tuple] = [
16
- ("knowledge_level", "What is your cybersecurity knowledge level?",
17
- "Level such as newcomer, foundational, practitioner, experienced, or expert"),
18
  ("timezone", "What time zone are you usually in?",
19
  "IANA timezone or region such as America/New_York, Europe/London, UTC"),
20
- ("cyber_role", "What best describes your role right now?",
21
- "Job or learning role: student, SOC analyst, engineer, architect, manager, career changer, etc."),
22
- ("organization_type", "What type of organization are you in (or targeting)?",
23
- "Startup, enterprise, government, education, MSP, or independent/job seeker"),
24
- ("primary_domains", "Which security domains do you focus on most?",
25
- "Comma-separated areas such as network, cloud, appsec, GRC, IR, identity, OT"),
26
- ("certifications", "Do you hold or are you pursuing any certifications?",
27
- "List such as Security+, CySA+, CISSP, OSCP, CCSP, or none yet"),
28
- ("tools_stack", "What tools or platforms do you work with regularly?",
29
- "SIEM, EDR, cloud security, ticketing, SOAR, etc."),
30
- ("compliance_focus", "Any compliance or regulatory frameworks you care about?",
31
- "SOC 2, ISO 27001, NIST, HIPAA, PCI-DSS, FedRAMP, or none"),
32
  ("current_goals", "What are you trying to accomplish in the next few months?",
33
- "Incident readiness, certification, job search, architecture review, audit prep, etc."),
34
- ("learning_preferences", "How do you prefer to learn new security concepts?",
35
- "Hands-on labs, reading, videos, mentorship, certifications, capture-the-flag, etc."),
36
  ]
37
 
38
 
@@ -92,8 +95,8 @@ class OnboardingAgent:
92
 
93
  if not missing:
94
  return {
95
- "reply": "Great — your security profile is complete. Your advisors will tailor depth, "
96
- "examples, and next steps to your role, tools, and goals.",
97
  "progress": 100,
98
  "complete": True,
99
  }
@@ -115,7 +118,7 @@ class OnboardingAgent:
115
  )
116
  field_descriptions = "\n".join(f'- "{k}": {desc}' for k, _q, desc in missing_fields)
117
  system = (
118
- "Extract cybersecurity profile fields from the user's message. "
119
  "Return ONLY valid JSON with field names as keys. "
120
  "For list fields return a JSON array. "
121
  f"{skip_instruction}\n"
@@ -149,8 +152,8 @@ class OnboardingAgent:
149
  filled_summary = ", ".join(filled_parts) or "nothing yet"
150
  next_field_key, next_field_q, _ = missing[0]
151
  system = (
152
- "You are a friendly cybersecurity onboarding assistant. "
153
- "You help users build a profile so AI security advisors can personalize answers.\n"
154
  "RULES:\n"
155
  "- Respond in exactly ONE short paragraph (2-3 sentences).\n"
156
  "- Briefly acknowledge what they said, then ask ONE clear question.\n"
@@ -177,4 +180,4 @@ class OnboardingAgent:
177
  return reply
178
  except Exception as e:
179
  LOG.error(f"Question generation failed: {e}")
180
- return missing[0][1] if missing else "Tell me more about your security background!"
 
1
  """
2
+ OnboardingAgent — conversational profile gathering for fitness advisors.
3
  """
4
 
5
  import json
 
12
 
13
  LOG = logging.getLogger(__name__)
14
 
15
+ # NOTE: field keys are kept stable (knowledge_level, cyber_role, ...) for storage
16
+ # compatibility with the user profile schema; the questions/descriptions below
17
+ # are themed for a fitness / bodybuilding advisor panel.
18
  PROFILE_FIELDS: List[tuple] = [
19
+ ("knowledge_level", "What is your fitness experience level?",
20
+ "Level such as beginner, intermediate, or advanced"),
21
  ("timezone", "What time zone are you usually in?",
22
  "IANA timezone or region such as America/New_York, Europe/London, UTC"),
23
+ ("cyber_role", "What best describes your training focus right now?",
24
+ "Focus such as general fitness, bodybuilding/hypertrophy, powerlifting/strength, cutting, or athlete"),
25
+ ("organization_type", "Where do you usually train?",
26
+ "Commercial gym, home gym, campus gym, CrossFit box, outdoor/calisthenics, or hybrid"),
27
+ ("primary_domains", "Which muscle groups or areas do you focus on most?",
28
+ "Comma-separated areas such as chest, back, legs, arms, core, conditioning"),
29
+ ("certifications", "What equipment do you have access to?",
30
+ "List such as dumbbells, barbell, cables, bands, machines, or bodyweight only"),
31
+ ("tools_stack", "What apps or trackers do you use regularly?",
32
+ "MyFitnessPal, Strong, Hevy, Apple Health, a spreadsheet, etc."),
33
+ ("compliance_focus", "Any dietary approach or restrictions we should know about?",
34
+ "High-protein, vegetarian/vegan, cutting, bulking, allergies, or none"),
35
  ("current_goals", "What are you trying to accomplish in the next few months?",
36
+ "Muscle gain, fat loss, strength PRs, first pull-up, recomposition, etc."),
37
+ ("learning_preferences", "How do you prefer to train?",
38
+ "Full-body, upper/lower or bro splits, supersets, progressive overload, etc."),
39
  ]
40
 
41
 
 
95
 
96
  if not missing:
97
  return {
98
+ "reply": "Great — your fitness profile is complete. Your coaches will tailor workouts, "
99
+ "nutrition, and next steps to your goals, equipment, and experience.",
100
  "progress": 100,
101
  "complete": True,
102
  }
 
118
  )
119
  field_descriptions = "\n".join(f'- "{k}": {desc}' for k, _q, desc in missing_fields)
120
  system = (
121
+ "Extract fitness profile fields from the user's message. "
122
  "Return ONLY valid JSON with field names as keys. "
123
  "For list fields return a JSON array. "
124
  f"{skip_instruction}\n"
 
152
  filled_summary = ", ".join(filled_parts) or "nothing yet"
153
  next_field_key, next_field_q, _ = missing[0]
154
  system = (
155
+ "You are a friendly fitness onboarding assistant. "
156
+ "You help users build a profile so AI fitness coaches can personalize workouts and nutrition.\n"
157
  "RULES:\n"
158
  "- Respond in exactly ONE short paragraph (2-3 sentences).\n"
159
  "- Briefly acknowledge what they said, then ask ONE clear question.\n"
 
180
  return reply
181
  except Exception as e:
182
  LOG.error(f"Question generation failed: {e}")
183
+ return missing[0][1] if missing else "Tell me more about your fitness background!"
multi_llm_chatbot_backend/app/tools/current_datetime.py CHANGED
@@ -17,7 +17,7 @@ TOOL_DEFINITION = {
17
  "description": (
18
  "Get the current date and time in UTC and in a configured local timezone. "
19
  "Use when the user asks about today, deadlines, timelines, schedules, "
20
- "or when accurate temporal context improves security guidance."
21
  ),
22
  "parameters": {
23
  "type": "object",
 
17
  "description": (
18
  "Get the current date and time in UTC and in a configured local timezone. "
19
  "Use when the user asks about today, deadlines, timelines, schedules, "
20
+ "or when accurate temporal context improves the guidance."
21
  ),
22
  "parameters": {
23
  "type": "object",
phd-advisor-frontend/src/components/AppHeader.js CHANGED
@@ -22,7 +22,8 @@ const AppHeader = ({
22
  children,
23
  }) => {
24
  const { config, resolveIcon } = useAppConfig();
25
- const BrandIcon = resolveIcon ? resolveIcon('Users') : Users;
 
26
 
27
  const goToCanvas = (view) => {
28
  if (onNavigateToCanvas) onNavigateToCanvas(view);
 
22
  children,
23
  }) => {
24
  const { config, resolveIcon } = useAppConfig();
25
+ const logoIconName = config?.app?.logo_icon || 'Users';
26
+ const BrandIcon = resolveIcon ? resolveIcon(logoIconName) : Users;
27
 
28
  const goToCanvas = (view) => {
29
  if (onNavigateToCanvas) onNavigateToCanvas(view);
phd-advisor-frontend/src/components/ClearDataModal.js CHANGED
@@ -123,7 +123,7 @@ const ClearDataModal = ({ authToken, onClose, onDataCleared }) => {
123
  <div>
124
  <div style={{ fontWeight: 600, fontSize: 14 }}>Profile Information</div>
125
  <div style={{ fontSize: 12, color: isDark ? '#9ca3af' : '#6b7280', marginTop: 2 }}>
126
- Major, GPA, career goals, learning style, etc. Resets "Tell us about yourself."
127
  </div>
128
  </div>
129
  </div>
@@ -143,7 +143,7 @@ const ClearDataModal = ({ authToken, onClose, onDataCleared }) => {
143
  <div>
144
  <div style={{ fontWeight: 600, fontSize: 14 }}>Canvas</div>
145
  <div style={{ fontSize: 12, color: isDark ? '#9ca3af' : '#6b7280', marginTop: 2 }}>
146
- All collected insights and research notes.
147
  </div>
148
  </div>
149
  </div>
 
123
  <div>
124
  <div style={{ fontWeight: 600, fontSize: 14 }}>Profile Information</div>
125
  <div style={{ fontSize: 12, color: isDark ? '#9ca3af' : '#6b7280', marginTop: 2 }}>
126
+ Fitness level, goals, training focus, equipment, etc. Resets "Tell us about yourself."
127
  </div>
128
  </div>
129
  </div>
 
143
  <div>
144
  <div style={{ fontWeight: 600, fontSize: 14 }}>Canvas</div>
145
  <div style={{ fontSize: 12, color: isDark ? '#9ca3af' : '#6b7280', marginTop: 2 }}>
146
+ All saved workout plans, progress notes, and insights.
147
  </div>
148
  </div>
149
  </div>
phd-advisor-frontend/src/components/CopyrightNotice.js CHANGED
@@ -1,11 +1,23 @@
1
  import React from 'react';
 
2
 
3
  const CopyrightNotice = ({ variant = 'footer', className = '' }) => {
 
4
  const isSidebar = variant === 'sidebar';
5
  const textClass = isSidebar ? 'sidebar-copyright-text' : 'footer-text';
6
  const patentsClass = isSidebar ? 'sidebar-patents-link' : 'footer-patents-link';
7
  const combinedClass = className ? `${textClass} ${className}` : textClass;
8
 
 
 
 
 
 
 
 
 
 
 
9
  return (
10
  <p className={combinedClass}>
11
  {'\u00A9 '}
@@ -22,7 +34,8 @@ const CopyrightNotice = ({ variant = 'footer', className = '' }) => {
22
  Neon.ai
23
  </a>
24
  )}
25
- . All rights reserved.{' '}
 
26
  <a
27
  href="https://www.neon.ai/contact"
28
  target="_blank"
 
1
  import React from 'react';
2
+ import { useAppConfig } from '../contexts/AppConfigContext';
3
 
4
  const CopyrightNotice = ({ variant = 'footer', className = '' }) => {
5
+ const { config } = useAppConfig();
6
  const isSidebar = variant === 'sidebar';
7
  const textClass = isSidebar ? 'sidebar-copyright-text' : 'footer-text';
8
  const patentsClass = isSidebar ? 'sidebar-patents-link' : 'footer-patents-link';
9
  const combinedClass = className ? `${textClass} ${className}` : textClass;
10
 
11
+ // Prefer the author/copyright line from config (app.footer_text). This keeps
12
+ // the footer themed per-panel instead of hardcoding a single brand.
13
+ const footerText = config?.app?.footer_text?.trim();
14
+
15
+ // When a panel provides its own footer_text (author/copyright line), show only
16
+ // that and skip the Neon.ai brand + patents link.
17
+ if (footerText) {
18
+ return <p className={combinedClass}>{footerText}</p>;
19
+ }
20
+
21
  return (
22
  <p className={combinedClass}>
23
  {'\u00A9 '}
 
34
  Neon.ai
35
  </a>
36
  )}
37
+ . All rights reserved.
38
+ {' '}
39
  <a
40
  href="https://www.neon.ai/contact"
41
  target="_blank"
phd-advisor-frontend/src/components/Login.js CHANGED
@@ -1,11 +1,12 @@
1
  import React, { useState } from 'react';
2
- import { Eye, EyeOff, Mail, Lock, ArrowRight, BookOpen, Phone } from 'lucide-react';
3
  import { useAppConfig } from '../contexts/AppConfigContext';
4
  import CopyrightNotice from './CopyrightNotice';
5
  import '../styles/Login.css';
6
 
7
  const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
8
- const { config } = useAppConfig();
 
9
  const [showPassword, setShowPassword] = useState(false);
10
  const [formData, setFormData] = useState({
11
  email: '',
@@ -103,7 +104,7 @@ const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
103
  {/* Header */}
104
  <div className="login-header">
105
  <div className="logo-container">
106
- <BookOpen className="logo-icon" />
107
  </div>
108
  <h1 className="login-title">Welcome Back</h1>
109
  <p className="login-subtitle">
 
1
  import React, { useState } from 'react';
2
+ import { Eye, EyeOff, Mail, Lock, ArrowRight, Phone } from 'lucide-react';
3
  import { useAppConfig } from '../contexts/AppConfigContext';
4
  import CopyrightNotice from './CopyrightNotice';
5
  import '../styles/Login.css';
6
 
7
  const Login = ({ onNavigateToSignup, onNavigateToHome }) => {
8
+ const { config, resolveIcon } = useAppConfig();
9
+ const LogoIcon = resolveIcon(config?.app?.logo_icon || 'BookOpen');
10
  const [showPassword, setShowPassword] = useState(false);
11
  const [formData, setFormData] = useState({
12
  email: '',
 
104
  {/* Header */}
105
  <div className="login-header">
106
  <div className="logo-container">
107
+ <LogoIcon className="logo-icon" />
108
  </div>
109
  <h1 className="login-title">Welcome Back</h1>
110
  <p className="login-subtitle">
phd-advisor-frontend/src/components/OnboardingTour.js CHANGED
@@ -16,8 +16,8 @@ const DEFAULT_FEATURES = [
16
  { Icon: BarChart3, label: 'Track your progress on a structured canvas' },
17
  ];
18
  const DEFAULT_CANVAS_STEP = {
19
- title: 'PhD Progress Canvas',
20
- body: 'A dashboard view of your PhD journey — research progress, methodology, next steps, all in one place.',
21
  };
22
 
23
  const buildAdvisorBody = (advisors) => {
@@ -255,7 +255,7 @@ const OnboardingTour = ({ children }) => {
255
  const c = useMemo(() => palette(isDark), [isDark]);
256
 
257
  const stepData = useMemo(() => ({
258
- title: config?.app?.title || 'PhD Advisory Panel',
259
  subtitle: config?.app?.subtitle || 'AI-Powered Guidance',
260
  features: buildFeatures(config, resolveIcon),
261
  advisorBody: buildAdvisorBody(advisors),
 
16
  { Icon: BarChart3, label: 'Track your progress on a structured canvas' },
17
  ];
18
  const DEFAULT_CANVAS_STEP = {
19
+ title: 'Progress Canvas',
20
+ body: 'A dashboard view of your training journey — program progress, nutrition, next steps, all in one place.',
21
  };
22
 
23
  const buildAdvisorBody = (advisors) => {
 
255
  const c = useMemo(() => palette(isDark), [isDark]);
256
 
257
  const stepData = useMemo(() => ({
258
+ title: config?.app?.title || 'Advisor Panel',
259
  subtitle: config?.app?.subtitle || 'AI-Powered Guidance',
260
  features: buildFeatures(config, resolveIcon),
261
  advisorBody: buildAdvisorBody(advisors),
phd-advisor-frontend/src/components/ProfileWalkthrough.js CHANGED
@@ -17,13 +17,14 @@ const buildSteps = (config) => {
17
  fields: [
18
  {
19
  key: 'knowledge_level',
20
- label: 'Cybersecurity knowledge level',
21
  type: 'select',
22
  options: knowledgeLevels.length
23
  ? knowledgeLevels
24
  : [
25
- { value: 'newcomer', label: 'New to cybersecurity' },
26
- { value: 'practitioner', label: 'Practitioner' },
 
27
  ],
28
  },
29
  {
@@ -37,26 +38,26 @@ const buildSteps = (config) => {
37
  ],
38
  },
39
  {
40
- title: 'Role & environment',
41
  fields: [
42
- { key: 'cyber_role', label: 'Your role', type: 'select', options: ['Student / Learner', 'Career changer', 'SOC analyst', 'Security engineer', 'Architect / lead', 'Manager / director', 'Consultant', 'Other'] },
43
- { key: 'organization_type', label: 'Organization type', type: 'select', options: ['Startup', 'Mid-size company', 'Enterprise', 'Government / public sector', 'Education', 'MSP / MSSP', 'Independent / job seeker'] },
44
  ],
45
  },
46
  {
47
- title: 'Focus & tools',
48
  fields: [
49
- { key: 'primary_domains', label: 'Primary domains (comma-separated)', type: 'text', placeholder: 'e.g. cloud, appsec, IR, GRC, identity' },
50
- { key: 'certifications', label: 'Certifications (comma-separated)', type: 'text', placeholder: 'e.g. Security+, CISSP, OSCP, or none yet' },
51
- { key: 'tools_stack', label: 'Tools & platforms (comma-separated)', type: 'text', placeholder: 'e.g. Splunk, CrowdStrike, AWS, Jira' },
52
  ],
53
  },
54
  {
55
- title: 'Goals & learning',
56
  fields: [
57
- { key: 'compliance_focus', label: 'Compliance / frameworks', type: 'text', placeholder: 'e.g. SOC 2, NIST CSF, ISO 27001, HIPAA' },
58
- { key: 'current_goals', label: 'Current goals', type: 'textarea', placeholder: 'Audit prep, cert study, incident readiness, architecture review...' },
59
- { key: 'learning_preferences', label: 'How you learn best', type: 'text', placeholder: 'Labs, reading, CTFs, mentorship, certifications...' },
60
  ],
61
  },
62
  ];
 
17
  fields: [
18
  {
19
  key: 'knowledge_level',
20
+ label: 'Fitness level',
21
  type: 'select',
22
  options: knowledgeLevels.length
23
  ? knowledgeLevels
24
  : [
25
+ { value: 'beginner', label: 'Beginner' },
26
+ { value: 'intermediate', label: 'Intermediate' },
27
+ { value: 'advanced', label: 'Advanced' },
28
  ],
29
  },
30
  {
 
38
  ],
39
  },
40
  {
41
+ title: 'Training focus & setup',
42
  fields: [
43
+ { key: 'cyber_role', label: 'Your training focus', type: 'select', options: ['Beginner lifter', 'General fitness', 'Bodybuilding / hypertrophy', 'Powerlifting / strength', 'Physique / cutting', 'Athlete / sport-specific', 'Home-gym / minimal equipment', 'Other'] },
44
+ { key: 'organization_type', label: 'Where you train', type: 'select', options: ['Commercial gym', 'Home gym', 'University / campus gym', 'CrossFit box', 'Outdoor / calisthenics', 'Hybrid (home + gym)'] },
45
  ],
46
  },
47
  {
48
+ title: 'Focus & equipment',
49
  fields: [
50
+ { key: 'primary_domains', label: 'Focus muscle groups / areas (comma-separated)', type: 'text', placeholder: 'e.g. chest, back, legs, arms, core, conditioning' },
51
+ { key: 'certifications', label: 'Available equipment (comma-separated)', type: 'text', placeholder: 'e.g. dumbbells, barbell, cables, bands, machines' },
52
+ { key: 'tools_stack', label: 'Apps & trackers you use (comma-separated)', type: 'text', placeholder: 'e.g. MyFitnessPal, Strong, Hevy, Apple Health' },
53
  ],
54
  },
55
  {
56
+ title: 'Goals & nutrition',
57
  fields: [
58
+ { key: 'compliance_focus', label: 'Dietary approach / restrictions', type: 'text', placeholder: 'e.g. high-protein, vegetarian, cutting, bulking' },
59
+ { key: 'current_goals', label: 'Current goals', type: 'textarea', placeholder: 'Muscle gain, fat loss, strength PRs, first pull-up...' },
60
+ { key: 'learning_preferences', label: 'How you like to train', type: 'text', placeholder: 'Full-body, splits, supersets, progressive overload...' },
61
  ],
62
  },
63
  ];
phd-advisor-frontend/src/components/Sidebar.js CHANGED
@@ -562,8 +562,8 @@ const Sidebar = ({
562
 
563
  {showAvatarPicker && (
564
  <UserAvatarPicker
565
- options={avatarOptions}
566
- currentId={userAvatarId}
567
  onSelect={(id) => { onAvatarChange?.(id); setShowAvatarPicker(false); }}
568
  onClose={() => setShowAvatarPicker(false)}
569
  />
 
562
 
563
  {showAvatarPicker && (
564
  <UserAvatarPicker
565
+ avatarOptions={avatarOptions}
566
+ currentAvatarId={userAvatarId}
567
  onSelect={(id) => { onAvatarChange?.(id); setShowAvatarPicker(false); }}
568
  onClose={() => setShowAvatarPicker(false)}
569
  />
phd-advisor-frontend/src/components/Signup.js CHANGED
@@ -4,7 +4,8 @@ import { useAppConfig } from '../contexts/AppConfigContext';
4
  import '../styles/Signup.css';
5
 
6
  const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
7
- const { config } = useAppConfig();
 
8
  const [showPassword, setShowPassword] = useState(false);
9
  const [showConfirmPassword, setShowConfirmPassword] = useState(false);
10
  const [formData, setFormData] = useState({
@@ -24,12 +25,12 @@ const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
24
  : config?.login?.academic_stages?.length
25
  ? config.login.academic_stages
26
  : [
27
- { value: '', label: 'Select your cybersecurity knowledge level' },
28
- { value: 'newcomer', label: 'New to cybersecurity' },
29
- { value: 'foundational', label: 'Foundational' },
30
- { value: 'practitioner', label: 'Practitioner' },
31
- { value: 'experienced', label: 'Experienced' },
32
- { value: 'expert', label: 'Expert / specialist' },
33
  ];
34
 
35
  const timezones = config?.login?.timezones?.length
@@ -83,7 +84,7 @@ const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
83
  }
84
 
85
  if (!formData.academicStage) {
86
- newErrors.academicStage = 'Please select your cybersecurity knowledge level';
87
  }
88
 
89
  setErrors(newErrors);
@@ -148,7 +149,7 @@ const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
148
  {/* Header */}
149
  <div className="signup-header">
150
  <div className="logo-container">
151
- <Shield className="logo-icon" />
152
  </div>
153
  <h1 className="signup-title">Join Our Community</h1>
154
  <p className="signup-subtitle">
@@ -293,10 +294,10 @@ const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
293
  </div>
294
  </div>
295
 
296
- {/* Cybersecurity knowledge level */}
297
  <div className="form-group">
298
  <label htmlFor="academicStage" className="form-label">
299
- Cybersecurity knowledge level
300
  </label>
301
  <div className="input-container">
302
  <Shield className="input-icon" />
 
4
  import '../styles/Signup.css';
5
 
6
  const Signup = ({ onNavigateToLogin, onNavigateToHome }) => {
7
+ const { config, resolveIcon } = useAppConfig();
8
+ const LogoIcon = resolveIcon(config?.app?.logo_icon || 'Shield');
9
  const [showPassword, setShowPassword] = useState(false);
10
  const [showConfirmPassword, setShowConfirmPassword] = useState(false);
11
  const [formData, setFormData] = useState({
 
25
  : config?.login?.academic_stages?.length
26
  ? config.login.academic_stages
27
  : [
28
+ { value: '', label: 'Select your fitness level' },
29
+ { value: 'beginner', label: 'Beginner' },
30
+ { value: 'intermediate', label: 'Intermediate' },
31
+ { value: 'advanced', label: 'Advanced' },
32
+ { value: 'strength_focus', label: 'Strength training' },
33
+ { value: 'cardio_focus', label: 'Run & cardio' },
34
  ];
35
 
36
  const timezones = config?.login?.timezones?.length
 
84
  }
85
 
86
  if (!formData.academicStage) {
87
+ newErrors.academicStage = 'Please select your fitness level';
88
  }
89
 
90
  setErrors(newErrors);
 
149
  {/* Header */}
150
  <div className="signup-header">
151
  <div className="logo-container">
152
+ <LogoIcon className="logo-icon" />
153
  </div>
154
  <h1 className="signup-title">Join Our Community</h1>
155
  <p className="signup-subtitle">
 
294
  </div>
295
  </div>
296
 
297
+ {/* Fitness level */}
298
  <div className="form-group">
299
  <label htmlFor="academicStage" className="form-label">
300
+ Fitness level
301
  </label>
302
  <div className="input-container">
303
  <Shield className="input-icon" />
phd-advisor-frontend/src/components/canvas/CanvasCriticWidgets.js CHANGED
@@ -97,7 +97,7 @@ export function DevilsAdvocateWidget({ state, setState, openModal }) {
97
  className="btn"
98
  title="Open Devil's Advocate in the main chat (history lives there)"
99
  onClick={() => handoffToChat("Devil's Advocate",
100
- `Take the position of devil's advocate on my claim: "${state.claim || 'my current hypothesis'}". Be ruthless.`)}
101
  >
102
  <Icon name="message" size={13}/>Open in chat
103
  </button>
@@ -142,7 +142,7 @@ export function ScopeRealismWidget({ state, openModal }) {
142
  className="btn"
143
  title="Open Scope Realism in the main chat (history lives there)"
144
  onClick={() => handoffToChat('Scope Realism',
145
- `Run a brutal feasibility check on my goal: "${state.target || 'my current research scope'}". Be specific about what's at risk.`)}
146
  >
147
  <Icon name="message" size={13}/>Open in chat
148
  </button>
@@ -158,43 +158,43 @@ export function ScopeRealismWidget({ state, openModal }) {
158
  const REVIEW_TEMPLATES = [
159
  {
160
  severity: 8,
161
- major: 'The hypothesis is presented before its operationalization. You write that L2/3 spiking "encodes" prediction error without specifying what spike-pattern feature you will measure (rate? latency? variance?), what range of values would count as "encoding," or what would falsify the claim.',
162
  minor: [
163
- 'Sample size of n=4 animals is described as "preliminary" without a power calculation or a stopping rule.',
164
- 'GLM with history kernel is presented as the analysis but no mention of how its outputs map to the proposed predictive-coding interpretation.',
165
- 'No engagement with adaptation as a confound — the obvious alternative explanation for any oddball-driven decrease in firing.',
166
- 'Reference to "predictive coding" is loose. Rao & Ballard, Bastos, and Friston make different commitments. Which one are you testing?',
167
  ],
168
  suggestions: [
169
- 'Add a single sentence specifying the measurable signature you predict, with directionality.',
170
- 'Either control for arousal/pupil or acknowledge it as a limit upfront.',
171
- 'Add a stopping rule and target effect size before scaling beyond n=4.',
172
  ],
173
  },
174
  {
175
  severity: 7,
176
- major: 'You claim the GLM analyses are "consistent with" the hypothesis. This phrase is doing too much work. Consistency with PE encoding is also consistency with at least three alternative explanations (adaptation, arousal, attention). Without a positive test that PE encoding predicts but the alternatives do not, "consistent with" is unfalsifiable.',
177
  minor: [
178
- 'Mouse V1 is justified by convention rather than by what makes it the right model for this question.',
179
- 'No statement of what would change your mind.',
180
- 'Figure-free abstract for an empirical claim is a red flag for reviewers.',
181
  ],
182
  suggestions: [
183
- 'Replace "consistent with" with a specific signed prediction the data either matches or doesn\'t.',
184
- 'List 1-2 results that, if observed, would refute the hypothesis.',
185
  ],
186
  },
187
  {
188
  severity: 9,
189
- major: 'This reads like an introduction, not an abstract. There is no result. There is no number. The strongest claim is that your "preliminary analyses are consistent" with your hypothesis — which is the lowest possible bar in empirical neuroscience. If the actual finding is interesting, lead with the finding, not with the framing.',
190
  minor: [
191
- 'Word "preliminary" appears three times in three sentences. Cut two.',
192
- '"Oddball stimulus paradigm" is jargon-without-citation; one sentence of definition or one citation, not zero.',
193
- 'No mention of layer-specificity, despite L2/3 being the core claim.',
194
  ],
195
  suggestions: [
196
- 'Lead sentence: "We find that <effect>" — even if the effect is small, name it.',
197
- 'Cut "we hypothesize that" entirely. Hypotheses go in the intro of the paper, not the abstract.',
198
  ],
199
  },
200
  ];
@@ -313,10 +313,10 @@ export function ReviewerModal({ data, onClose }) {
313
  }
314
 
315
  const HARDER_COUNTERS = [
316
- { lbl: 'Reverse causation', text: 'You assume PE drives spike changes. The opposite mapping — that some intrinsic cortical state drives both the spike pattern and the perceived "surprise" — is observationally indistinguishable in your design.' },
317
- { lbl: 'Definition shift', text: 'You will be tempted, when results don\'t fit, to redefine "prediction error" until they do. Pre-register your operationalization or you cannot honestly claim to have tested PC.' },
318
- { lbl: 'Wrong layer', text: 'Most predictive-coding accounts place PE signaling in L4 or L5b, not L2/3. Your prior for finding PE in L2/3 should be lower than you\'re writing.' },
319
- { lbl: 'Mouse vs. theory', text: 'Predictive coding theories were built on primate visual hierarchies with rich top-down attention. Mouse V1 lacks several of the assumed circuits. You may be testing the theory on a substrate it doesn\'t apply to.' },
320
  ];
321
 
322
  export function DevilsModal({ data, onClose }) {
@@ -333,7 +333,7 @@ export function DevilsModal({ data, onClose }) {
333
  data.onUpdate({ counters: nc });
334
  fireToast('Stronger counter added: "' + next.lbl + '"', 'critic');
335
  } else {
336
- fireToast('No more counters — your hypothesis is more robust than I thought.');
337
  }
338
  setPushing(false);
339
  }, 800);
@@ -345,7 +345,7 @@ export function DevilsModal({ data, onClose }) {
345
  <div className="modal-icon critic"><Icon name="scale" size={18}/></div>
346
  <div style={{ flex: 1 }}>
347
  <div className="modal-title">Devil's Advocate</div>
348
- <div className="modal-sub">The strongest counter-arguments to your hypothesis, ranked by how much they should worry you.</div>
349
  </div>
350
  <button className="icon-btn" onClick={onClose}><Icon name="x" size={16}/></button>
351
  </div>
@@ -414,10 +414,10 @@ export function ScopeModal({ data, onClose }) {
414
  <div style={{ marginTop: 16 }}>
415
  <div className="label" style={{ marginBottom: 8 }}>Recommended actions</div>
416
  <ul style={{ margin: 0, paddingLeft: 18, fontSize: 13, color: 'var(--canvas-text-2)', lineHeight: 1.6 }}>
417
- <li><strong style={{ color: 'var(--canvas-text)' }}>Commit to a PC formulation by May 31.</strong> Theory clarity is your bottleneck, not data.</li>
418
- <li>Build a writing buffer. Your 9-day streak is great; 90 days is the minimum to absorb the inevitable lab/family/health setbacks.</li>
419
- <li>Cut a chapter. A 5-chapter dissertation that ships beats a 6-chapter one that doesn't.</li>
420
- <li>Calibrate against your cohort: median time-to-defense in your program is 5.8 years. You are projecting 5.2.</li>
421
  </ul>
422
  </div>
423
  </div>
 
97
  className="btn"
98
  title="Open Devil's Advocate in the main chat (history lives there)"
99
  onClick={() => handoffToChat("Devil's Advocate",
100
+ `Take the position of devil's advocate on my claim: "${state.claim || 'my current training plan'}". Be ruthless.`)}
101
  >
102
  <Icon name="message" size={13}/>Open in chat
103
  </button>
 
142
  className="btn"
143
  title="Open Scope Realism in the main chat (history lives there)"
144
  onClick={() => handoffToChat('Scope Realism',
145
+ `Run a brutal feasibility check on my goal: "${state.target || 'my current training goal'}". Be specific about what's at risk.`)}
146
  >
147
  <Icon name="message" size={13}/>Open in chat
148
  </button>
 
158
  const REVIEW_TEMPLATES = [
159
  {
160
  severity: 8,
161
+ major: 'The program states a goal ("get bigger and stronger") before defining what success looks like. You never specify the measurable target — which lifts, how much, by when — so there is no way to tell if the plan is working or when to change it.',
162
  minor: [
163
+ 'Weekly volume is described as "high" without a set count per muscle group.',
164
+ 'Progression is listed as "add weight when you can" with no concrete rule (reps in reserve? double progression?).',
165
+ 'Legs get one day while chest gets two, with no rationale for the imbalance.',
166
+ 'No deload is scheduled across a 12-week block — fatigue will accumulate unmanaged.',
167
  ],
168
  suggestions: [
169
+ 'Add one sentence naming the target: e.g. "bench 100kg for 5, add 5cm on arms in 12 weeks."',
170
+ 'Pick an explicit progression rule and write it down.',
171
+ 'Schedule a deload every 5–6 weeks and rebalance leg volume.',
172
  ],
173
  },
174
  {
175
  severity: 7,
176
+ major: 'You claim this split is "optimal for hypertrophy." That word is doing too much work. The layout is also compatible with several worse outcomes (junk volume, under-recovery, skipped legs). Without a way to check whether volume actually drives growth for you, "optimal" is untestable.',
177
  minor: [
178
+ 'The split is justified by "that\'s what most lifters do" rather than by your schedule and recovery.',
179
+ 'No statement of what would make you change the plan.',
180
+ 'A program with no logged numbers is a red flag — you can\'t audit it later.',
181
  ],
182
  suggestions: [
183
+ 'Replace "optimal" with a specific target the plan either hits or misses.',
184
+ 'List 1–2 signals (stalled lifts, poor sleep) that would trigger a change.',
185
  ],
186
  },
187
  {
188
  severity: 9,
189
+ major: 'This reads like a wish list, not a plan. There is no number. There is no timeframe. The strongest claim is that you want to "tone up and feel better" — which is the lowest possible bar. If you actually care about the result, lead with the measurable goal, not the vibe.',
190
  minor: [
191
+ 'The word "consistent" appears three times in three sentences. Show it with a logged history instead.',
192
+ '"Functional training" is jargon-without-definition; say what you\'ll actually do.',
193
+ 'No mention of nutrition, despite it driving most of the outcome you want.',
194
  ],
195
  suggestions: [
196
+ 'Lead sentence: "In 12 weeks I will <specific, measurable result>."',
197
+ 'Cut "I want to get in shape" entirely. Replace it with a lift, a weight, and a date.',
198
  ],
199
  },
200
  ];
 
313
  }
314
 
315
  const HARDER_COUNTERS = [
316
+ { lbl: 'Reverse causation', text: 'You assume the program drove your gains. The opposite is just as likely early on — newbie gains and better sleep would have driven progress on almost any plan. You may be crediting the split for something else.' },
317
+ { lbl: 'Goalpost shift', text: 'When the scale doesn\'t move you\'ll be tempted to redefine "progress" until it does (now it\'s "recomp," now it\'s "strength"). Pick your primary metric up front or you can\'t honestly say the plan worked.' },
318
+ { lbl: 'Wrong lever', text: 'You\'re tweaking exercise selection when the real bottleneck is total volume and calories. You\'re optimizing the variable that matters least.' },
319
+ { lbl: 'Copied context', text: 'This program was built for a lifter training 5 days a week with years of experience. On your schedule and recovery it may be testing a plan that doesn\'t apply to you.' },
320
  ];
321
 
322
  export function DevilsModal({ data, onClose }) {
 
333
  data.onUpdate({ counters: nc });
334
  fireToast('Stronger counter added: "' + next.lbl + '"', 'critic');
335
  } else {
336
+ fireToast('No more counters — your plan is more robust than I thought.');
337
  }
338
  setPushing(false);
339
  }, 800);
 
345
  <div className="modal-icon critic"><Icon name="scale" size={18}/></div>
346
  <div style={{ flex: 1 }}>
347
  <div className="modal-title">Devil's Advocate</div>
348
+ <div className="modal-sub">The strongest counter-arguments to your plan, ranked by how much they should worry you.</div>
349
  </div>
350
  <button className="icon-btn" onClick={onClose}><Icon name="x" size={16}/></button>
351
  </div>
 
414
  <div style={{ marginTop: 16 }}>
415
  <div className="label" style={{ marginBottom: 8 }}>Recommended actions</div>
416
  <ul style={{ margin: 0, paddingLeft: 18, fontSize: 13, color: 'var(--canvas-text-2)', lineHeight: 1.6 }}>
417
+ <li><strong style={{ color: 'var(--canvas-text)' }}>Commit to one primary goal for this block.</strong> Trying to bulk and cut at once is your bottleneck, not effort.</li>
418
+ <li>Build a consistency buffer. Your 9-day streak is great; 90 days is the minimum to absorb the inevitable work/family/illness setbacks.</li>
419
+ <li>Cut a training day. A 3-day plan you actually run beats a 5-day one you skip.</li>
420
+ <li>Calibrate against reality: a natural lifter gains ~0.25–0.5% bodyweight per week when bulking. Your projected rate is faster than that.</li>
421
  </ul>
422
  </div>
423
  </div>
phd-advisor-frontend/src/components/canvas/CanvasDeliverables.js CHANGED
@@ -1,7 +1,7 @@
1
- // Deliverables view — multi-project PhD deliverable center.
2
- // Each "project" is a draft of a template (paper, slides, poster, CV, etc.).
3
  // You can keep many projects in flight, switch between them, version-rollback,
4
- // drag in citations from your Bibliography or arXiv, embed images, render math,
5
  // and export to Markdown / LaTeX / HTML / Print.
6
  // AI passes are stubbed (need LLM endpoint) but every static signal works today.
7
  import React, { useState, useMemo, useEffect, useRef } from 'react';
@@ -32,193 +32,193 @@ const newId = (p) => p + Math.random().toString(36).slice(2, 8);
32
  export const TEMPLATES = [
33
  {
34
  id: 'research-paper',
35
- name: 'Security Assessment Report',
36
- desc: 'Executive summary → Scope → Findings → Risk rating → Remediation → Appendix',
37
  icon: 'book',
38
  mode: 'paper',
39
  sections: [
40
- { id: 'abstract', name: 'Abstract', target: 250, hint: 'One paragraph: question, method, finding, implication.', checks: ['hasNumber', 'hasFinding'] },
41
- { id: 'intro', name: 'Introduction', target: 1000, hint: 'Frame the problem, state the gap, name your contribution.', checks: ['hasGap', 'hasCitation'] },
42
- { id: 'methods', name: 'Methods', target: 800, hint: 'Reproducibility-first: subjects, materials, procedure, analysis.', checks: ['hasCitation', 'hasNumber'] },
43
- { id: 'results', name: 'Results', target: 800, hint: 'Lead with the effect. Numbers + figure refs. No interpretation here.', checks: ['hasNumber', 'hasFigure'] },
44
- { id: 'discussion', name: 'Discussion', target: 1000, hint: 'What it means, what it doesn\'t, limits, future work.', checks: ['hasLimit', 'hasCitation'] },
45
- { id: 'refs', name: 'References', target: 0, hint: 'Bibliography list. Drop @keys here from the Bibliography widget.', checks: [] },
46
  ],
47
  },
48
  {
49
  id: 'thesis-chapter',
50
- name: 'Incident Report',
51
- desc: 'Timeline → Impact → Root cause → Containment → Lessons learned',
52
  icon: 'book',
53
  mode: 'paper',
54
  sections: [
55
- { id: 'overview', name: 'Overview', target: 200, hint: 'What this chapter does and why it\'s here.', checks: [] },
56
- { id: 'background', name: 'Background', target: 1500, hint: 'Lit review focused on this chapter\'s question.', checks: ['hasCitation'] },
57
- { id: 'methods', name: 'Methods', target: 1500, hint: 'Reproducibility-first.', checks: ['hasCitation', 'hasNumber'] },
58
- { id: 'results', name: 'Results', target: 2000, hint: 'Findings + figures.', checks: ['hasFigure', 'hasNumber'] },
59
- { id: 'discussion', name: 'Discussion', target: 1500, hint: 'How it fits the larger thesis.', checks: ['hasLimit'] },
60
  ],
61
  },
62
  {
63
  id: 'nsf-grfp',
64
- name: 'NSF GRFP',
65
- desc: 'Personal Statement (3 pages) + Research Plan (2 pages).',
66
  icon: 'award',
67
  mode: 'document',
68
  sections: [
69
- { id: 'personal', name: 'Personal Statement', target: 1500, hint: 'Background, experiences, broader impacts. Write as a story.', checks: ['hasBroaderImpacts'] },
70
- { id: 'research', name: 'Research Plan', target: 1000, hint: 'Question, hypothesis, approach, intellectual merit.', checks: ['hasHypothesis', 'hasMerit'] },
71
  ],
72
  },
73
  {
74
  id: 'conference-abstract',
75
- name: 'Conference Abstract',
76
- desc: 'Single section, 250 words. Lead with the result.',
77
  icon: 'send',
78
  mode: 'document',
79
  sections: [
80
- { id: 'abs', name: 'Abstract', target: 250, hint: 'One paragraph. Lead with finding, end with implication.', checks: ['hasFinding', 'hasNumber'] },
81
  ],
82
  },
83
  {
84
  id: 'defense-slides',
85
- name: 'Defense Slides',
86
- desc: 'Title → Outline → Background → Question → Methods → Results → Discussion → Q&A',
87
  icon: 'kanban',
88
  mode: 'slides',
89
  sections: [
90
- { id: 'title', name: 'Title slide', target: 30, hint: 'Title, your name, advisor, date.', checks: [] },
91
- { id: 'outline', name: 'Outline', target: 60, hint: '5–7 bullet points covering the talk arc.', checks: [] },
92
- { id: 'background', name: 'Background', target: 200, hint: 'Just enough context to follow the question.', checks: ['hasCitation'] },
93
- { id: 'question', name: 'Question', target: 80, hint: 'Single sentence, falsifiable.', checks: [] },
94
- { id: 'methods', name: 'Methods', target: 200, hint: 'High-level. Save details for backup slides.', checks: ['hasNumber'] },
95
- { id: 'results', name: 'Results', target: 300, hint: 'One slide per finding. Lead with the headline.', checks: ['hasFigure', 'hasNumber'] },
96
- { id: 'discussion', name: 'Discussion', target: 200, hint: 'Implications + limits + next steps.', checks: ['hasLimit'] },
97
  { id: 'qa', name: 'Anticipated Q&A', target: 300, hint: 'Hardest 5 questions and your answers.', checks: [] },
98
  ],
99
  },
100
  {
101
  id: 'poster',
102
- name: 'Conference Poster',
103
- desc: '4-quadrant scientific poster: Intro · Methods · Results · Discussion.',
104
  icon: 'layout',
105
  mode: 'poster',
106
  sections: [
107
- { id: 'title', name: 'Title & Authors', target: 30, hint: 'Title, your name, advisor, affiliation.', checks: [] },
108
- { id: 'intro', name: 'Introduction', target: 200, hint: 'Question, gap, why-care.', checks: ['hasGap'] },
109
- { id: 'methods', name: 'Methods', target: 200, hint: 'High-level: subjects, design, analysis.', checks: ['hasNumber'] },
110
- { id: 'results', name: 'Results', target: 250, hint: 'Headline finding + 1–2 figures.', checks: ['hasFigure', 'hasNumber'] },
111
- { id: 'discussion', name: 'Discussion', target: 200, hint: 'What it means + next steps.', checks: ['hasLimit'] },
112
- { id: 'refs', name: 'References / Acks', target: 80, hint: '5–10 citations + funding + contact.', checks: ['hasCitation'] },
113
  ],
114
  },
115
  {
116
  id: 'cv',
117
- name: 'Academic CV',
118
- desc: 'Standard sections: Education · Pubs · Talks · Awards · Service · Skills.',
119
  icon: 'user',
120
  mode: 'document',
121
  sections: [
122
- { id: 'header', name: 'Header', target: 40, hint: 'Name, position, affiliation, contact.', checks: [] },
123
- { id: 'education', name: 'Education', target: 100, hint: 'Most recent first. Degree · Year · Institution.', checks: [] },
124
- { id: 'publications', name: 'Publications', target: 300, hint: 'Drop @keys from Bibliography. Group by type if needed.', checks: ['hasCitation'] },
125
- { id: 'talks', name: 'Invited talks & posters', target: 150, hint: 'Title · Venue · Year.', checks: [] },
126
- { id: 'awards', name: 'Awards & funding', target: 100, hint: 'Most recent first. Amount + year if applicable.', checks: [] },
127
- { id: 'service', name: 'Service & teaching', target: 100, hint: 'Reviewing, mentorship, TA roles.', checks: [] },
128
- { id: 'skills', name: 'Skills', target: 60, hint: 'Methods, languages, software.', checks: [] },
129
  ],
130
  },
131
  {
132
  id: 'cover-letter',
133
- name: 'Cover Letter',
134
- desc: 'For job applications, journal submissions, or postdoc inquiries.',
135
  icon: 'send',
136
  mode: 'document',
137
  sections: [
138
- { id: 'header', name: 'Header', target: 40, hint: 'Date, recipient, salutation.', checks: [] },
139
- { id: 'opener', name: 'Opening paragraph', target: 100, hint: 'Why you\'re writing + the position/journal.', checks: [] },
140
- { id: 'body', name: 'Why me', target: 250, hint: 'Specific achievements that match the call. Numbers > adjectives.', checks: ['hasNumber'] },
141
- { id: 'fit', name: 'Why this place', target: 150, hint: 'What about this group / journal makes it the right fit.', checks: [] },
142
  { id: 'close', name: 'Close', target: 60, hint: 'Thanks + next step + signature.', checks: [] },
143
  ],
144
  },
145
  {
146
  id: 'irb-protocol',
147
- name: 'IRB Protocol',
148
- desc: 'Standard sections for human-subjects research approval.',
149
  icon: 'shield',
150
  mode: 'document',
151
  sections: [
152
- { id: 'overview', name: 'Study overview', target: 200, hint: 'One-paragraph summary of purpose and procedures.', checks: [] },
153
- { id: 'background', name: 'Background & significance', target: 400, hint: 'Why this study? What gap does it close?', checks: ['hasGap', 'hasCitation'] },
154
- { id: 'aims', name: 'Specific aims & hypotheses', target: 250, hint: '2–3 aims. Each falsifiable.', checks: ['hasHypothesis'] },
155
- { id: 'participants', name: 'Participants & recruitment', target: 300, hint: 'Inclusion/exclusion criteria, sample size, recruitment plan.', checks: ['hasNumber'] },
156
- { id: 'procedures', name: 'Procedures', target: 500, hint: 'Step-by-step what subjects experience. Time burden in minutes.', checks: ['hasNumber'] },
157
- { id: 'risks', name: 'Risks & mitigation', target: 200, hint: 'Anticipated risks (physical, psychological, privacy) + mitigations.', checks: ['hasLimit'] },
158
- { id: 'benefits', name: 'Benefits', target: 100, hint: 'Direct + societal benefits. Be honest about minimal direct benefits.', checks: [] },
159
- { id: 'consent', name: 'Consent process', target: 200, hint: 'Who consents, when, written or verbal, capacity considerations.', checks: [] },
160
- { id: 'data', name: 'Data handling & confidentiality', target: 200, hint: 'Storage, access, identifiers, retention period.', checks: [] },
161
  ],
162
  },
163
  {
164
  id: 'meeting-prep',
165
- name: 'Advisor Meeting Prep',
166
- desc: 'Bring this to your 1:1 — agenda, updates, decisions needed, follow-ups.',
167
  icon: 'message',
168
  mode: 'document',
169
  sections: [
170
  { id: 'agenda', name: 'Agenda', target: 80, hint: '3–5 bullets ranked by priority.', checks: [] },
171
- { id: 'progress', name: 'Progress since last meeting', target: 200, hint: 'What you actually did, with numbers when possible.', checks: ['hasNumber'] },
172
- { id: 'blockers', name: 'Blockers', target: 150, hint: 'What you need from them to move forward.', checks: ['hasLimit'] },
173
- { id: 'decisions', name: 'Decisions needed', target: 200, hint: 'Frame as A/B options with your recommendation.', checks: [] },
174
- { id: 'questions', name: 'Questions', target: 150, hint: 'Open questions you genuinely want their take on.', checks: [] },
175
- { id: 'followup', name: 'Action items (post-meeting)', target: 100, hint: 'Fill in during/after. Owner + due date for each.', checks: [] },
176
  ],
177
  },
178
  {
179
  id: 'dissertation-formatting',
180
- name: 'Dissertation Formatting Checklist',
181
- desc: 'Catch-everything pass before ProQuest submission.',
182
  icon: 'shield',
183
  mode: 'document',
184
  sections: [
185
- { id: 'frontmatter', name: 'Front matter', target: 0, hint: 'Title page, copyright, abstract, dedication, acknowledgements, ToC, list of figures/tables.', checks: [] },
186
- { id: 'margins', name: 'Margins & spacing', target: 0, hint: 'Verify school requirements. Most: 1" margins, double-spaced body, single-spaced quotes/captions.', checks: ['hasNumber'] },
187
- { id: 'fonts', name: 'Fonts & typography', target: 0, hint: 'One body font (Times/Garamond/Cambria) at 12pt. Captions 10–11pt. Headings consistent.', checks: ['hasNumber'] },
188
- { id: 'pagenumbers', name: 'Page numbering', target: 0, hint: 'Roman for front matter, Arabic from Intro onward. Check section breaks.', checks: [] },
189
- { id: 'figures', name: 'Figures & tables', target: 0, hint: 'Captions below figures, above tables. Numbered. Cited in text before they appear.', checks: ['hasFigure'] },
190
- { id: 'citations', name: 'Citations & references', target: 0, hint: 'Consistent style throughout. Every cite has a reference; every reference is cited.', checks: ['hasCitation'] },
191
- { id: 'appendices', name: 'Appendices', target: 0, hint: 'Lettered (A, B, C). Each cited in the body at least once.', checks: [] },
192
- { id: 'proquest', name: 'ProQuest submission', target: 0, hint: 'PDF/A format, embedded fonts, no broken links, abstract under word limit.', checks: [] },
193
  ],
194
  },
195
  {
196
  id: 'faculty-hunt',
197
- name: 'Faculty / Advisor Hunt',
198
- desc: 'For prospective PhDs or finding committee members — research the people.',
199
  icon: 'user',
200
  mode: 'document',
201
  sections: [
202
- { id: 'criteria', name: 'What you\'re looking for', target: 150, hint: 'Research area, methodology, working style, mentorship reputation.', checks: [] },
203
- { id: 'shortlist', name: 'Shortlist (5–10 names)', target: 400, hint: 'For each: name, institution, 2–3 representative papers, why they fit.', checks: ['hasCitation'] },
204
- { id: 'pubs', name: 'Recent publications', target: 300, hint: 'What have they published in the last 2 years? Drop @keys from Bibliography.', checks: ['hasCitation'] },
205
- { id: 'students', name: 'Current/recent students', target: 200, hint: 'Lab size, time-to-defense, where students go after.', checks: ['hasNumber'] },
206
- { id: 'reachout', name: 'Outreach plan', target: 200, hint: 'When to email, what to send, who to mention.', checks: [] },
207
- { id: 'notes', name: 'Conversation notes', target: 0, hint: 'After meetings/emails — vibes, fit signals, red flags.', checks: [] },
208
  ],
209
  },
210
  {
211
  id: 'research-statement',
212
- name: 'Research Statement',
213
- desc: 'For faculty applications: past work, current direction, future arc.',
214
  icon: 'sparkles',
215
  mode: 'document',
216
  sections: [
217
- { id: 'overview', name: 'Overview', target: 200, hint: 'One paragraph: your research identity in 2–3 sentences.', checks: [] },
218
- { id: 'past', name: 'Past work', target: 600, hint: 'What you\'ve done. Lead with results, cite your own papers.', checks: ['hasCitation', 'hasFinding'] },
219
- { id: 'current', name: 'Current direction', target: 400, hint: 'What you\'re working on now and why it matters.', checks: ['hasGap'] },
220
- { id: 'future', name: 'Future research', target: 600, hint: '3–5 year arc. 1–2 funding-ready specific aims.', checks: ['hasHypothesis'] },
221
- { id: 'broader', name: 'Broader impacts', target: 200, hint: 'Outreach, mentorship, what your group will look like.', checks: ['hasBroaderImpacts'] },
222
  ],
223
  },
224
  ];
@@ -466,7 +466,7 @@ const DeliverablesView = ({ allStates }) => {
466
  <div>
467
  <h1 className="page-title">Documents</h1>
468
  <div className="page-sub">
469
- Security deliverables hub — policies, IR reports, audit evidence, and architecture briefs. Drafts auto-save. Versions kept for rollback.
470
  {projectList.length > 0 && ` · ${projectList.length} draft${projectList.length === 1 ? '' : 's'} in flight.`}
471
  </div>
472
  </div>
@@ -504,7 +504,7 @@ const DeliverablesView = ({ allStates }) => {
504
  <div className="canvas-presets">
505
  <div className="canvas-presets-head">
506
  <div className="canvas-presets-title">{projectList.length > 0 ? 'Or start a new draft' : 'Pick a template'}</div>
507
- <div className="canvas-presets-sub">9 templates · paper, slides, poster, CV, cover letter, and more.</div>
508
  </div>
509
  <div className="canvas-presets-grid">
510
  {TEMPLATES.map(t => (
@@ -877,7 +877,7 @@ const DeliverablesView = ({ allStates }) => {
877
  <div className={`notion-page ${paperLike ? 'serif' : ''}`}>
878
  <h1 className="notion-page-title">{project.name}</h1>
879
  <div className="notion-page-meta">
880
- {totalWords} words · ~{readingMinutes(totalWords)} min read · {template.sections.length} sections{paperLike ? ' · academic paper' : ''}
881
  </div>
882
  {template.sections.map(s => {
883
  const text = sections[s.id] || '';
 
1
+ // Deliverables view — multi-project document center.
2
+ // Each "project" is a draft of a template (program plan, check-in, poster, résumé, etc.).
3
  // You can keep many projects in flight, switch between them, version-rollback,
4
+ // drag in references from your Bibliography, embed images, render math,
5
  // and export to Markdown / LaTeX / HTML / Print.
6
  // AI passes are stubbed (need LLM endpoint) but every static signal works today.
7
  import React, { useState, useMemo, useEffect, useRef } from 'react';
 
32
  export const TEMPLATES = [
33
  {
34
  id: 'research-paper',
35
+ name: 'Training Program Plan',
36
+ desc: 'Overview → Goals & assessment → Weekly split → Progression → Nutrition → Resources',
37
  icon: 'book',
38
  mode: 'paper',
39
  sections: [
40
+ { id: 'abstract', name: 'Overview', target: 250, hint: 'One paragraph: goal, timeframe, split, and expected outcome.', checks: ['hasNumber', 'hasFinding'] },
41
+ { id: 'intro', name: 'Goals & assessment', target: 1000, hint: 'Current lifts and measurements, target, and any limitations.', checks: ['hasNumber'] },
42
+ { id: 'methods', name: 'Weekly split', target: 800, hint: 'Day-by-day: exercises, sets, reps, and rest.', checks: ['hasNumber'] },
43
+ { id: 'results', name: 'Progression scheme', target: 800, hint: 'How you add load or volume week to week. Deload timing.', checks: ['hasNumber'] },
44
+ { id: 'discussion', name: 'Nutrition plan', target: 1000, hint: 'Calorie and protein targets, meal timing, supplements.', checks: ['hasNumber'] },
45
+ { id: 'refs', name: 'Resources', target: 0, hint: 'Links to programs, exercise demos, and references.', checks: [] },
46
  ],
47
  },
48
  {
49
  id: 'thesis-chapter',
50
+ name: 'Training Block Review',
51
+ desc: 'Overview → Starting point → What you did → Results → What to change',
52
  icon: 'book',
53
  mode: 'paper',
54
  sections: [
55
+ { id: 'overview', name: 'Overview', target: 200, hint: 'What block this covers and its goal.', checks: [] },
56
+ { id: 'background', name: 'Starting point', target: 1500, hint: 'Lifts, bodyweight, and condition at the start of the block.', checks: ['hasNumber'] },
57
+ { id: 'methods', name: 'What you did', target: 1500, hint: 'Training approach, volume, and adherence.', checks: ['hasNumber'] },
58
+ { id: 'results', name: 'Results', target: 2000, hint: 'Strength, size, and body-composition changes with numbers.', checks: ['hasNumber'] },
59
+ { id: 'discussion', name: 'What to change', target: 1500, hint: 'What worked, what didn\'t, and adjustments for next block.', checks: ['hasLimit'] },
60
  ],
61
  },
62
  {
63
  id: 'nsf-grfp',
64
+ name: 'Coaching Intake',
65
+ desc: 'Athlete background + goal plan for a new coaching relationship.',
66
  icon: 'award',
67
  mode: 'document',
68
  sections: [
69
+ { id: 'personal', name: 'Athlete background', target: 1500, hint: 'Training history, injuries, lifestyle, and motivation. Write as a story.', checks: [] },
70
+ { id: 'research', name: 'Goal plan', target: 1000, hint: 'Specific goal, timeframe, and how you plan to get there.', checks: ['hasNumber'] },
71
  ],
72
  },
73
  {
74
  id: 'conference-abstract',
75
+ name: 'Weekly Check-in',
76
+ desc: 'Single section, ~250 words. Lead with the wins and the numbers.',
77
  icon: 'send',
78
  mode: 'document',
79
  sections: [
80
+ { id: 'abs', name: 'Check-in', target: 250, hint: 'One paragraph: weight, training, nutrition, and how you feel.', checks: ['hasFinding', 'hasNumber'] },
81
  ],
82
  },
83
  {
84
  id: 'defense-slides',
85
+ name: 'Program Presentation',
86
+ desc: 'Title → Outline → Background → Goal → Approach → Results → Discussion → Q&A',
87
  icon: 'kanban',
88
  mode: 'slides',
89
  sections: [
90
+ { id: 'title', name: 'Title slide', target: 30, hint: 'Program name, your name, date.', checks: [] },
91
+ { id: 'outline', name: 'Outline', target: 60, hint: '5–7 bullet points covering the plan.', checks: [] },
92
+ { id: 'background', name: 'Background', target: 200, hint: 'Starting point and why this program.', checks: [] },
93
+ { id: 'question', name: 'Goal', target: 80, hint: 'Single, measurable goal.', checks: [] },
94
+ { id: 'methods', name: 'Approach', target: 200, hint: 'High-level: split, volume, progression.', checks: ['hasNumber'] },
95
+ { id: 'results', name: 'Expected results', target: 300, hint: 'Realistic targets per milestone.', checks: ['hasNumber'] },
96
+ { id: 'discussion', name: 'Discussion', target: 200, hint: 'Risks, recovery, and next steps.', checks: ['hasLimit'] },
97
  { id: 'qa', name: 'Anticipated Q&A', target: 300, hint: 'Hardest 5 questions and your answers.', checks: [] },
98
  ],
99
  },
100
  {
101
  id: 'poster',
102
+ name: 'Progress Poster',
103
+ desc: '4-quadrant progress board: Goal · Approach · Results · Next.',
104
  icon: 'layout',
105
  mode: 'poster',
106
  sections: [
107
+ { id: 'title', name: 'Title & Athlete', target: 30, hint: 'Program name, your name, phase.', checks: [] },
108
+ { id: 'intro', name: 'Goal', target: 200, hint: 'Where you started and where you\'re headed.', checks: [] },
109
+ { id: 'methods', name: 'Approach', target: 200, hint: 'High-level: split, volume, nutrition.', checks: ['hasNumber'] },
110
+ { id: 'results', name: 'Results', target: 250, hint: 'Headline numbers + before/after.', checks: ['hasNumber'] },
111
+ { id: 'discussion', name: 'Next steps', target: 200, hint: 'What it means + what\'s next.', checks: ['hasLimit'] },
112
+ { id: 'refs', name: 'Notes / Credits', target: 80, hint: 'Program source, coach, contact.', checks: [] },
113
  ],
114
  },
115
  {
116
  id: 'cv',
117
+ name: 'Lifting Résumé',
118
+ desc: 'Standard sections: Stats · PRs · Competitions · Certifications · Skills.',
119
  icon: 'user',
120
  mode: 'document',
121
  sections: [
122
+ { id: 'header', name: 'Header', target: 40, hint: 'Name, bodyweight, training age, contact.', checks: [] },
123
+ { id: 'education', name: 'Training history', target: 100, hint: 'Most recent first. Program · Duration · Focus.', checks: [] },
124
+ { id: 'publications', name: 'Personal records', target: 300, hint: 'Big lifts and key numbers. Group by movement.', checks: ['hasNumber'] },
125
+ { id: 'talks', name: 'Competitions & events', target: 150, hint: 'Meet · Placing · Year.', checks: [] },
126
+ { id: 'awards', name: 'Achievements', target: 100, hint: 'Most recent first. Milestone + date.', checks: [] },
127
+ { id: 'service', name: 'Coaching & mentorship', target: 100, hint: 'Anyone you\'ve trained or helped.', checks: [] },
128
+ { id: 'skills', name: 'Skills', target: 60, hint: 'Lifts, sports, mobility, and specialties.', checks: [] },
129
  ],
130
  },
131
  {
132
  id: 'cover-letter',
133
+ name: 'Coach Outreach Letter',
134
+ desc: 'For reaching out to a coach, gym, or training partner.',
135
  icon: 'send',
136
  mode: 'document',
137
  sections: [
138
+ { id: 'header', name: 'Header', target: 40, hint: 'Date, recipient, greeting.', checks: [] },
139
+ { id: 'opener', name: 'Opening paragraph', target: 100, hint: 'Why you\'re reaching out + your goal.', checks: [] },
140
+ { id: 'body', name: 'Where you\'re at', target: 250, hint: 'Current stats and training background. Numbers > adjectives.', checks: ['hasNumber'] },
141
+ { id: 'fit', name: 'Why this coach', target: 150, hint: 'What about them makes it the right fit.', checks: [] },
142
  { id: 'close', name: 'Close', target: 60, hint: 'Thanks + next step + signature.', checks: [] },
143
  ],
144
  },
145
  {
146
  id: 'irb-protocol',
147
+ name: 'Nutrition Protocol',
148
+ desc: 'A structured plan for a diet phase — targets, meals, and tracking.',
149
  icon: 'shield',
150
  mode: 'document',
151
  sections: [
152
+ { id: 'overview', name: 'Diet overview', target: 200, hint: 'One-paragraph summary of the phase (bulk, cut, maintain).', checks: [] },
153
+ { id: 'background', name: 'Goal & rationale', target: 400, hint: 'Why this phase? What are you optimizing for?', checks: [] },
154
+ { id: 'aims', name: 'Targets', target: 250, hint: 'Calories, protein, carbs, fats, and rate of change.', checks: ['hasNumber'] },
155
+ { id: 'participants', name: 'Your stats', target: 300, hint: 'Bodyweight, measurements, and maintenance estimate.', checks: ['hasNumber'] },
156
+ { id: 'procedures', name: 'Meal plan', target: 500, hint: 'Day structure, meal timing, and go-to foods.', checks: ['hasNumber'] },
157
+ { id: 'risks', name: 'Risks & adjustments', target: 200, hint: 'Hunger, energy, plateaus — and how you\'ll adjust.', checks: ['hasLimit'] },
158
+ { id: 'benefits', name: 'Expected outcome', target: 100, hint: 'Realistic result over the phase.', checks: [] },
159
+ { id: 'consent', name: 'Adherence plan', target: 200, hint: 'How you\'ll stay consistent: prep, flexibility, refeeds.', checks: [] },
160
+ { id: 'data', name: 'Tracking', target: 200, hint: 'What you log, how often, and how you review it.', checks: [] },
161
  ],
162
  },
163
  {
164
  id: 'meeting-prep',
165
+ name: 'Coach Check-in Prep',
166
+ desc: 'Bring this to your check-in — agenda, updates, decisions needed, follow-ups.',
167
  icon: 'message',
168
  mode: 'document',
169
  sections: [
170
  { id: 'agenda', name: 'Agenda', target: 80, hint: '3–5 bullets ranked by priority.', checks: [] },
171
+ { id: 'progress', name: 'Progress since last check-in', target: 200, hint: 'What you actually did, with numbers when possible.', checks: ['hasNumber'] },
172
+ { id: 'blockers', name: 'Blockers', target: 150, hint: 'What\'s getting in the way of progress.', checks: ['hasLimit'] },
173
+ { id: 'decisions', name: 'Decisions needed', target: 200, hint: 'Frame as A/B options with your preference.', checks: [] },
174
+ { id: 'questions', name: 'Questions', target: 150, hint: 'Open questions for your coach.', checks: [] },
175
+ { id: 'followup', name: 'Action items (post check-in)', target: 100, hint: 'Fill in during/after. What to do + by when.', checks: [] },
176
  ],
177
  },
178
  {
179
  id: 'dissertation-formatting',
180
+ name: 'Meet Prep Checklist',
181
+ desc: 'Catch-everything pass in the weeks before a competition or photoshoot.',
182
  icon: 'shield',
183
  mode: 'document',
184
  sections: [
185
+ { id: 'frontmatter', name: 'Logistics', target: 0, hint: 'Registration, weigh-in time, venue, travel, and gear list.', checks: [] },
186
+ { id: 'margins', name: 'Weight & conditioning', target: 0, hint: 'Target weight class and how you\'ll get there safely.', checks: ['hasNumber'] },
187
+ { id: 'fonts', name: 'Peak week plan', target: 0, hint: 'Training taper, carbs, sodium, and water strategy.', checks: ['hasNumber'] },
188
+ { id: 'pagenumbers', name: 'Attempt / posing plan', target: 0, hint: 'Opener/second/third attempts, or posing routine.', checks: [] },
189
+ { id: 'figures', name: 'Gear & equipment', target: 0, hint: 'Singlet/belt/sleeves/shoes or posing suit and tan.', checks: [] },
190
+ { id: 'citations', name: 'Nutrition & timing', target: 0, hint: 'Pre-event meals and intra-event fueling.', checks: [] },
191
+ { id: 'appendices', name: 'Recovery & warm-up', target: 0, hint: 'Warm-up sequence and between-attempt recovery.', checks: [] },
192
+ { id: 'proquest', name: 'Day-of checklist', target: 0, hint: 'Final pass: bag packed, alarms set, food prepped.', checks: [] },
193
  ],
194
  },
195
  {
196
  id: 'faculty-hunt',
197
+ name: 'Coach / Gym Hunt',
198
+ desc: 'For finding a coach or gym — research the options before committing.',
199
  icon: 'user',
200
  mode: 'document',
201
  sections: [
202
+ { id: 'criteria', name: 'What you\'re looking for', target: 150, hint: 'Specialty, coaching style, location, and budget.', checks: [] },
203
+ { id: 'shortlist', name: 'Shortlist (5–10 options)', target: 400, hint: 'For each: name, location, focus, why they fit.', checks: [] },
204
+ { id: 'pubs', name: 'Reputation & results', target: 300, hint: 'Reviews, client transformations, credentials.', checks: [] },
205
+ { id: 'students', name: 'Current/recent clients', target: 200, hint: 'Who they train and the results they get.', checks: ['hasNumber'] },
206
+ { id: 'reachout', name: 'Outreach plan', target: 200, hint: 'When to contact, what to ask, trial options.', checks: [] },
207
+ { id: 'notes', name: 'Conversation notes', target: 0, hint: 'After calls/visits — vibes, fit signals, red flags.', checks: [] },
208
  ],
209
  },
210
  {
211
  id: 'research-statement',
212
+ name: 'Training Philosophy',
213
+ desc: 'Your approach to training: where you\'ve been, where you are, where you\'re going.',
214
  icon: 'sparkles',
215
  mode: 'document',
216
  sections: [
217
+ { id: 'overview', name: 'Overview', target: 200, hint: 'One paragraph: your training identity in 2–3 sentences.', checks: [] },
218
+ { id: 'past', name: 'Where you\'ve been', target: 600, hint: 'Your training history. Lead with the results you got.', checks: ['hasFinding'] },
219
+ { id: 'current', name: 'Where you are now', target: 400, hint: 'Current focus and why it matters to you.', checks: [] },
220
+ { id: 'future', name: 'Where you\'re going', target: 600, hint: '1–3 year arc with concrete milestones.', checks: ['hasNumber'] },
221
+ { id: 'broader', name: 'Beyond the gym', target: 200, hint: 'How training fits your life, health, and community.', checks: [] },
222
  ],
223
  },
224
  ];
 
466
  <div>
467
  <h1 className="page-title">Documents</h1>
468
  <div className="page-sub">
469
+ Documents hub — program plans, check-ins, nutrition protocols, and progress boards. Drafts auto-save. Versions kept for rollback.
470
  {projectList.length > 0 && ` · ${projectList.length} draft${projectList.length === 1 ? '' : 's'} in flight.`}
471
  </div>
472
  </div>
 
504
  <div className="canvas-presets">
505
  <div className="canvas-presets-head">
506
  <div className="canvas-presets-title">{projectList.length > 0 ? 'Or start a new draft' : 'Pick a template'}</div>
507
+ <div className="canvas-presets-sub">13 templates · program plans, check-ins, posters, résumé, and more.</div>
508
  </div>
509
  <div className="canvas-presets-grid">
510
  {TEMPLATES.map(t => (
 
877
  <div className={`notion-page ${paperLike ? 'serif' : ''}`}>
878
  <h1 className="notion-page-title">{project.name}</h1>
879
  <div className="notion-page-meta">
880
+ {totalWords} words · ~{readingMinutes(totalWords)} min read · {template.sections.length} sections{paperLike ? ' · long-form doc' : ''}
881
  </div>
882
  {template.sections.map(s => {
883
  const text = sections[s.id] || '';
phd-advisor-frontend/src/components/canvas/CanvasLatexEditor.js CHANGED
@@ -147,7 +147,7 @@ function PdfPanel({ source, title, onClose }) {
147
  <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--canvas-text)', marginBottom: 6 }}>Compile failed</div>
148
  {error}
149
  <div style={{ marginTop: 10, color: 'var(--canvas-text-3)', fontSize: 11.5, fontFamily: 'var(--canvas-sans)' }}>
150
- The free latex-online service can be flaky and doesn't support every package. The HTML preview to the left should still work for most thesis-shaped documents.
151
  </div>
152
  </div>
153
  )}
 
147
  <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--canvas-text)', marginBottom: 6 }}>Compile failed</div>
148
  {error}
149
  <div style={{ marginTop: 10, color: 'var(--canvas-text-3)', fontSize: 11.5, fontFamily: 'var(--canvas-sans)' }}>
150
+ The free latex-online service can be flaky and doesn't support every package. The HTML preview to the left should still work for most long-form documents.
151
  </div>
152
  </div>
153
  )}
phd-advisor-frontend/src/components/canvas/CanvasModals.js CHANGED
@@ -720,7 +720,7 @@ export function GoalModal({ data, onClose }) {
720
  <div className="modal-icon"><Icon name="bullseye" size={18}/></div>
721
  <div style={{ flex: 1 }}>
722
  <div className="modal-title">{editing ? 'Edit goal' : 'New goal'}</div>
723
- <div className="modal-sub">Quarterly OKR or dissertation milestone.</div>
724
  </div>
725
  <button className="icon-btn" onClick={onClose}><Icon name="x" size={16}/></button>
726
  </div>
 
720
  <div className="modal-icon"><Icon name="bullseye" size={18}/></div>
721
  <div style={{ flex: 1 }}>
722
  <div className="modal-title">{editing ? 'Edit goal' : 'New goal'}</div>
723
+ <div className="modal-sub">Strength target or body-composition milestone.</div>
724
  </div>
725
  <button className="icon-btn" onClick={onClose}><Icon name="x" size={16}/></button>
726
  </div>
phd-advisor-frontend/src/components/canvas/CanvasWelcomeTour.js CHANGED
@@ -2,18 +2,18 @@
2
  import Icon from './CanvasIcon';
3
  import { MOD } from './platform';
4
 
5
- const TOUR_KEY = 'canvas-tour-seen-cyber-v1';
6
 
7
  const STEPS = [
8
  {
9
- title: 'Welcome to your Security Canvas',
10
  icon: 'shield',
11
- body: 'This is your security operations workspace. Two views ΓÇö Insights (highlights from your chats) and Workspace (a customizable dashboard of widgets). It starts empty so you can build it the way you want.',
12
  },
13
  {
14
  title: 'Add widgets from the palette',
15
  icon: 'plus',
16
- body: `Click "Add widget" on the Workspace view, or hit ${MOD}+K and search. There are 30+ widgets ΓÇö incidents, deadlines, controls, reading, plus challenge widgets that push back on weak assumptions.`,
17
  },
18
  {
19
  title: 'Make it yours',
@@ -23,7 +23,7 @@ const STEPS = [
23
  {
24
  title: 'Try the challenge widgets',
25
  icon: 'gavel',
26
- body: 'Reviewer 2, Devil\'s Advocate, and Scope Realism are tuned to push back, not validate. They\'re where the real work gets sharpened. Add them last ΓÇö when you\'re ready for honest feedback.',
27
  },
28
  ];
29
 
 
2
  import Icon from './CanvasIcon';
3
  import { MOD } from './platform';
4
 
5
+ const TOUR_KEY = 'canvas-tour-seen-musclegrowth-v1';
6
 
7
  const STEPS = [
8
  {
9
+ title: 'Welcome to your Fitness Canvas',
10
  icon: 'shield',
11
+ body: 'This is your training workspace. Two views — Insights (highlights from your chats) and Workspace (a customizable dashboard of widgets). It starts empty so you can build it the way you want.',
12
  },
13
  {
14
  title: 'Add widgets from the palette',
15
  icon: 'plus',
16
+ body: `Click "Add widget" on the Workspace view, or hit ${MOD}+K and search. There are 30+ widgets — workouts, deadlines, tracking, notes, plus challenge widgets that push back on weak plans.`,
17
  },
18
  {
19
  title: 'Make it yours',
 
23
  {
24
  title: 'Try the challenge widgets',
25
  icon: 'gavel',
26
+ body: 'Reviewer 2, Devil\'s Advocate, and Scope Realism are tuned to push back, not validate. They\'re where your program gets sharpened. Add them last — when you\'re ready for honest feedback.',
27
  },
28
  ];
29
 
phd-advisor-frontend/src/components/canvas/CanvasWidgets.js CHANGED
@@ -837,7 +837,7 @@ export function HabitsWidget({ state, setState, openModal }) {
837
  );
838
  })}
839
  {habits.length === 0 && (
840
- <EmptyState icon="flame" title="No habits yet" hint="Track daily research practices. Read 1 paper, write 30 min, lab notebook entry."/>
841
  )}
842
  </div>
843
  <button className="add-tiny" onClick={add}>+ New habit</button>
@@ -876,7 +876,7 @@ export function GoalsWidget({ state, setState, openModal }) {
876
  </div>
877
  ))}
878
  {goals.length === 0 && (
879
- <EmptyState icon="bullseye" title="No goals yet" hint="Quarterly OKRs, dissertation milestones, anything you want to track."/>
880
  )}
881
  </div>
882
  <button className="add-tiny" onClick={add}>+ New goal</button>
@@ -1439,23 +1439,23 @@ export function DocumenterWidget({ state, setState }) {
1439
  );
1440
  }
1441
 
1442
- // ===== PhD Journey — standard milestones with status + notes =====
1443
- // Captures the "General Journey" list: courses, prelim, lit review, IRB,
1444
- // committee, topic, comps, defense, ProQuest. Each milestone has a status
1445
- // (open/in-progress/completed) and an inline note.
1446
  const PHD_MILESTONES = [
1447
- { id: 'courses', label: 'Course selection & dissertation credits', hint: 'Plan term-by-term. Check funding constraints.' },
1448
- { id: 'prelim', label: 'Preliminary exam', hint: 'Department format varies — consult your handbook.' },
1449
- { id: 'lit-review', label: 'Literature review', hint: 'Coverage + critique. Bibliography widget pairs well here.' },
1450
- { id: 'topic', label: 'Pick dissertation topic', hint: 'Narrow until your advisor pushes back.' },
1451
- { id: 'committee', label: 'Select committee', hint: 'Pros/cons of a co-chair: more buy-in, more scheduling.' },
1452
- { id: 'irb', label: 'IRB approval', hint: 'Allow 6–12 weeks. Pre-fill paperwork early.' },
1453
- { id: 'data', label: 'Data collection', hint: 'Pilot first. Plan for the inevitable instrument failure.' },
1454
- { id: 'comps', label: 'Comprehensive exam', hint: 'Department format varies.' },
1455
- { id: 'analysis', label: 'Data analysis & visualization', hint: 'Make the figures before the prose.' },
1456
- { id: 'writing', label: 'Write dissertation', hint: 'One chapter at a time. Aim for "good enough to defend".' },
1457
- { id: 'defense', label: 'Oral defense', hint: 'Slides + practice Q&A. Use the Defense Slides template.' },
1458
- { id: 'proquest', label: 'Final admin (ProQuest upload)', hint: 'Read the formatting checklist before you start formatting.' },
1459
  ];
1460
 
1461
  export function PhdJourneyWidget({ state, setState }) {
@@ -1527,33 +1527,33 @@ export function PhdJourneyWidget({ state, setState }) {
1527
  );
1528
  }
1529
 
1530
- // ===== PhD Resources — curated links + open-source apps =====
1531
- // Static curated list of useful PhD tools and resources, plus user-added links.
1532
  const PHD_RESOURCE_GROUPS = [
1533
  {
1534
- label: 'Open-source PhD tools',
1535
  items: [
1536
- { name: 'Zotero', href: 'https://www.zotero.org/', desc: 'Reference manager — free and open source' },
1537
- { name: 'Obsidian', href: 'https://obsidian.md/', desc: 'Local-first knowledge graph for notes' },
1538
- { name: 'JabRef', href: 'https://www.jabref.org/', desc: 'BibTeX-native reference manager' },
1539
- { name: 'Pandoc', href: 'https://pandoc.org/', desc: 'Universal document converter' },
1540
- { name: 'Quarto', href: 'https://quarto.org/', desc: 'Scientific publishing with R/Python/Julia' },
1541
  ],
1542
  },
1543
  {
1544
- label: 'Writing & formatting',
1545
  items: [
1546
- { name: 'Overleaf', href: 'https://www.overleaf.com/', desc: 'Browser LaTeX editor with templates' },
1547
- { name: 'LaTeX Templates', href: 'https://www.latextemplates.com/', desc: 'Thesis, CV, poster templates' },
1548
- { name: 'Hemingway Editor', href: 'https://hemingwayapp.com/', desc: 'Plain-language readability check' },
1549
  ],
1550
  },
1551
  {
1552
- label: 'Community & career',
1553
  items: [
1554
- { name: 'Academic Twitter / #PhDChat', href: 'https://twitter.com/search?q=%23PhDChat', desc: 'Peers + advisors discussing the grind' },
1555
- { name: 'ORCID', href: 'https://orcid.org/', desc: 'Permanent researcher ID for citations + grants' },
1556
- { name: 'Conferences & CFPs (WikiCFP)', href: 'http://www.wikicfp.com/', desc: 'Upcoming deadlines across fields' },
1557
  ],
1558
  },
1559
  ];
@@ -1629,7 +1629,7 @@ const STUB_PLANS = {
1629
  'outline': ['Collapsible tree', 'Drop Insights into slots', 'Promote to Deliverable section'],
1630
  'latex': ['Render math as you type', 'Snippet library', 'Copy as image / TeX'],
1631
  'draft-locker': ['Versioned chapter drafts', 'Diff between versions', 'Roll back any change'],
1632
- 'gantt': ['Proposal → IRB → defense timeline', 'Critical-path highlighting', 'Drag to reschedule'],
1633
  'mood': ['Daily slider', 'Trend graph', 'Correlate with productive days'],
1634
  'sleep': ['Sleep duration vs. word output', 'Energy heatmap', 'Apple Health import'],
1635
  'focus': ['Curated ambient playlists', 'Focus session timer', 'Auto-pause on Pomodoro break'],
@@ -1640,7 +1640,7 @@ const STUB_PLANS = {
1640
  'datasets': ['Public datasets by domain', 'Saved searches', 'License + access notes'],
1641
  'methods': ['When to use what test', 'Examples + citations', 'Saved templates per chapter'],
1642
  'discounts': ['Software & services with edu pricing', 'Discount expiration tracking'],
1643
- 'assumption': ['Names hidden assumptions', 'Asks "what if wrong?"', 'Logs to a hypothesis tree'],
1644
  'whats-missing': ['Gap analysis on lit review', 'Compares to top venues', 'Suggests targeted reads'],
1645
  'calibrator': ['Challenges every "results show" claim', 'Asks for the prior', 'Flags p-hacking patterns'],
1646
  };
 
837
  );
838
  })}
839
  {habits.length === 0 && (
840
+ <EmptyState icon="flame" title="No habits yet" hint="Track daily training habits. Hit protein target, 8k steps, 10 min mobility."/>
841
  )}
842
  </div>
843
  <button className="add-tiny" onClick={add}>+ New habit</button>
 
876
  </div>
877
  ))}
878
  {goals.length === 0 && (
879
+ <EmptyState icon="bullseye" title="No goals yet" hint="Strength targets, body-composition goals, anything you want to track."/>
880
  )}
881
  </div>
882
  <button className="add-tiny" onClick={add}>+ New goal</button>
 
1439
  );
1440
  }
1441
 
1442
+ // ===== Training Roadmap — standard milestones with status + notes =====
1443
+ // Captures the training journey: assessment, base building, technique, program
1444
+ // design, nutrition, progression, peak, and maintenance. Each milestone has a
1445
+ // status (open/in-progress/completed) and an inline note.
1446
  const PHD_MILESTONES = [
1447
+ { id: 'courses', label: 'Baseline assessment & goal setting', hint: 'Record current lifts, measurements, and a clear goal.' },
1448
+ { id: 'prelim', label: 'Movement screen', hint: 'Check mobility and any injury history before loading.' },
1449
+ { id: 'lit-review', label: 'Learn the fundamentals', hint: 'Progressive overload, rep ranges, and form basics.' },
1450
+ { id: 'topic', label: 'Pick a training split', hint: 'Full-body, upper/lower, or PPL — match it to your schedule.' },
1451
+ { id: 'committee', label: 'Set up your gym & equipment', hint: 'Home vs. commercial: trade-offs in equipment and cost.' },
1452
+ { id: 'irb', label: 'Dial in nutrition', hint: 'Set protein and calorie targets for your goal.' },
1453
+ { id: 'data', label: 'Start logging workouts', hint: 'Track every session. Pilot the plan for 2 weeks first.' },
1454
+ { id: 'comps', label: 'First progress check', hint: 'Reassess lifts and measurements at week 4–6.' },
1455
+ { id: 'analysis', label: 'Adjust volume & progression', hint: 'Add sets or load where progress has stalled.' },
1456
+ { id: 'writing', label: 'Run the hypertrophy block', hint: 'One block at a time. Aim for consistent progressive overload.' },
1457
+ { id: 'defense', label: 'Deload & peak', hint: 'Plan a deload every 5–6 weeks. Use the Deload template.' },
1458
+ { id: 'proquest', label: 'Transition to maintenance', hint: 'Lock in gains before starting the next block.' },
1459
  ];
1460
 
1461
  export function PhdJourneyWidget({ state, setState }) {
 
1527
  );
1528
  }
1529
 
1530
+ // ===== Fitness Resources — curated links + apps =====
1531
+ // Static curated list of useful training/nutrition tools, plus user-added links.
1532
  const PHD_RESOURCE_GROUPS = [
1533
  {
1534
+ label: 'Training & tracking apps',
1535
  items: [
1536
+ { name: 'Boostcamp', href: 'https://www.boostcamp.app/', desc: 'Free proven programs + workout logging' },
1537
+ { name: 'Strong', href: 'https://www.strong.app/', desc: 'Simple, popular workout logger' },
1538
+ { name: 'Hevy', href: 'https://www.hevyapp.com/', desc: 'Workout tracker with routines and history' },
1539
+ { name: 'Liftosaur', href: 'https://www.liftosaur.com/', desc: 'Scriptable, open-source workout planner' },
1540
+ { name: 'ExRx.net', href: 'https://exrx.net/', desc: 'Exercise library with form instructions' },
1541
  ],
1542
  },
1543
  {
1544
+ label: 'Nutrition & recovery',
1545
  items: [
1546
+ { name: 'Cronometer', href: 'https://cronometer.com/', desc: 'Detailed calorie and micronutrient tracker' },
1547
+ { name: 'MyFitnessPal', href: 'https://www.myfitnesspal.com/', desc: 'Large food database for macro tracking' },
1548
+ { name: 'TDEE Calculator', href: 'https://tdeecalculator.net/', desc: 'Estimate maintenance calories fast' },
1549
  ],
1550
  },
1551
  {
1552
+ label: 'Learn & community',
1553
  items: [
1554
+ { name: 'Stronger by Science', href: 'https://www.strongerbyscience.com/', desc: 'Evidence-based training and nutrition articles' },
1555
+ { name: 'r/Fitness Wiki', href: 'https://thefitness.wiki/', desc: 'Beginner-friendly guides and program picks' },
1556
+ { name: 'Jeff Nippard (YouTube)', href: 'https://www.youtube.com/@JeffNippard', desc: 'Science-based hypertrophy breakdowns' },
1557
  ],
1558
  },
1559
  ];
 
1629
  'outline': ['Collapsible tree', 'Drop Insights into slots', 'Promote to Deliverable section'],
1630
  'latex': ['Render math as you type', 'Snippet library', 'Copy as image / TeX'],
1631
  'draft-locker': ['Versioned chapter drafts', 'Diff between versions', 'Roll back any change'],
1632
+ 'gantt': ['Base → hypertrophy → peak timeline', 'Critical-path highlighting', 'Drag to reschedule'],
1633
  'mood': ['Daily slider', 'Trend graph', 'Correlate with productive days'],
1634
  'sleep': ['Sleep duration vs. word output', 'Energy heatmap', 'Apple Health import'],
1635
  'focus': ['Curated ambient playlists', 'Focus session timer', 'Auto-pause on Pomodoro break'],
 
1640
  'datasets': ['Public datasets by domain', 'Saved searches', 'License + access notes'],
1641
  'methods': ['When to use what test', 'Examples + citations', 'Saved templates per chapter'],
1642
  'discounts': ['Software & services with edu pricing', 'Discount expiration tracking'],
1643
+ 'assumption': ['Names hidden assumptions', 'Asks "what if wrong?"', 'Logs to an assumptions list'],
1644
  'whats-missing': ['Gap analysis on lit review', 'Compares to top venues', 'Suggests targeted reads'],
1645
  'calibrator': ['Challenges every "results show" claim', 'Asks for the prior', 'Flags p-hacking patterns'],
1646
  };
phd-advisor-frontend/src/components/canvas/canvasData.js CHANGED
@@ -1,8 +1,8 @@
1
- // Demo data for a security program lead preparing for a SOC 2 audit.
2
 
3
  export const DEMO_PROJECT = {
4
- title: "Zero Trust Rollout — Production SaaS",
5
- meta: "Security Engineer · Q2 audit prep",
6
  };
7
 
8
  export const INSIGHTS = [
@@ -12,75 +12,75 @@ export const INSIGHTS = [
12
  icon: 'graph',
13
  category: 'progress',
14
  confidence: 82,
15
- summary: 'Zero Trust Phase 2 is 78% complete. MFA enforced for workforce; service accounts and legacy VPN exceptions remain the main gaps before audit sampling.',
16
  bullets: [
17
- 'Identity: <strong>MFA 94%</strong> workforce · service accounts in remediation',
18
- 'Network: micro-segmentation pilot on <strong>3 app tiers</strong>',
19
- '<strong>Risk:</strong> 12 VPN exceptions still lack compensating controls',
20
  ],
21
  pinned: true,
22
  sources: 18,
23
  updatedMinutesAgo: 5,
24
  quotes: [
25
- '"MFA rollout blocked on two legacy HR integrations." — IAM workstream notes',
26
- '"Auditors will sample VPN exception register first." — GRC advisor chat',
27
  ],
28
  },
29
  {
30
  id: 'i-method',
31
- title: 'Controls posture',
32
  icon: 'flask',
33
  category: 'theory',
34
  confidence: 71,
35
- summary: 'SOC 2 CC6/CC7 mappings are drafted. Detection use cases cover ransomware and cred theft; log retention and IR tabletop evidence are still thin.',
36
  bullets: [
37
- 'Mapped: <strong>CC6.1–CC6.7</strong> access controls with Okta + AWS',
38
- 'Open: centralized logging retention proof for <strong>365 days</strong>',
39
- 'Open: tabletop scenario for <strong>ransomware + exfil</strong> not yet run',
40
  ],
41
  sources: 14,
42
  updatedMinutesAgo: 14,
43
  quotes: [
44
- '"Need SIEM retention screenshots before fieldwork." — compliance advisor',
45
- '"Tabletop scheduled but not executed." — IR lead notes',
46
  ],
47
  },
48
  {
49
  id: 'i-lit',
50
- title: 'Threat landscape',
51
  icon: 'book',
52
  category: 'literature',
53
  confidence: 76,
54
- summary: 'Strong coverage of identity attacks, SaaS misconfigurations, and supply-chain risks for your stack. Weaker on OT exposure and insider threat playbooks.',
55
  bullets: [
56
- '<strong>Coverage:</strong> MITRE techniques for cloud identity & SaaS',
57
- '<strong>Gap:</strong> limited intel on <strong>OAuth consent phishing</strong> variants',
58
- '<strong>Gap:</strong> no formal insider-threat escalation path documented',
59
  ],
60
  sources: 32,
61
  updatedMinutesAgo: 28,
62
  quotes: [
63
- '"OAuth abuse is the fastest-moving thread in your sector." — threat intel advisor',
64
- '"Insider playbook is a one-pager — not enough for audit." — GRC advisor',
65
  ],
66
  },
67
  {
68
  id: 'i-questions',
69
- title: 'Open security questions',
70
  icon: 'sparkles',
71
  category: 'theory',
72
  confidence: 63,
73
- summary: 'Three live threads. Q1 (scope of zero trust for contractors) gates architecture sign-off. Q2–Q3 affect detection engineering priorities.',
74
  bullets: [
75
- '<strong>Q1:</strong> Do contractors get full ZTNA or bastion-only access?',
76
- '<strong>Q2:</strong> Which SIEM detections are in-scope for SOC 2 evidence?',
77
- '<strong>Q3:</strong> Is customer data in EU regions in scope for DPA addendum?',
78
  ],
79
  sources: 9,
80
  updatedMinutesAgo: 41,
81
  quotes: [
82
- '"Contractor access model blocks network design." — architect advisor',
83
- '"EU data residency may expand audit scope." — privacy advisor',
84
  ],
85
  },
86
  {
@@ -89,18 +89,18 @@ export const INSIGHTS = [
89
  icon: 'arrow',
90
  category: 'action',
91
  confidence: 85,
92
- summary: 'Near-term actions tied to audit date and production cutover. Two items have slipped one sprint.',
93
  bullets: [
94
- 'Close <strong>12 VPN exceptions</strong> or document compensating controls',
95
- 'Run ransomware tabletop & upload minutes to evidence locker',
96
- 'Ship <strong>5 high-fidelity detections</strong> to production SIEM',
97
- 'Finalize vendor SOC 2 bridge letter for subprocessors',
98
  ],
99
  sources: 7,
100
  updatedMinutesAgo: 9,
101
  quotes: [
102
- '"VPN exceptions are the #1 audit finding risk." — GRC advisor',
103
- '"Detections without tuning will false-positive in week one." — SOC advisor',
104
  ],
105
  },
106
  {
@@ -109,16 +109,16 @@ export const INSIGHTS = [
109
  icon: 'alert',
110
  category: 'risk',
111
  confidence: 74,
112
- summary: 'One technical blocker (legacy logging), one governance blocker (exception approvals). Governance is the higher audit risk.',
113
  bullets: [
114
- '<strong>Technical:</strong> legacy app logs not reaching SIEM — 18% of prod traffic',
115
- '<strong>Governance:</strong> exception approval SLA &gt; 10 days — auditors will flag',
116
  ],
117
  sources: 6,
118
  updatedMinutesAgo: 20,
119
  quotes: [
120
- '"Without those logs you cannot prove detective controls." — detection engineer',
121
- '"Exception backlog reads as control failure." — devil\'s advocate advisor',
122
  ],
123
  },
124
  ];
@@ -139,14 +139,14 @@ export const WIDGET_CATALOG = [
139
  { type: 'kanban', name: 'Task Board', desc: 'Drag-to-move with priority filter chips and due-date sort', icon: 'kanban', cat: 'project', defaultSize: 'L', enhanced: true },
140
  { type: 'deadlines', name: 'Deadlines', desc: 'Countdown plus per-deadline .ics calendar export', icon: 'calendar', cat: 'project', defaultSize: 'S', enhanced: true },
141
  { type: 'pomodoro', name: 'Pomodoro', desc: 'Real timer with break cycle and session counter', icon: 'timer', cat: 'project', defaultSize: 'S', enhanced: true },
142
- { type: 'gantt', name: 'Milestone Timeline', desc: 'Proposal → IRB → defense', icon: 'flag', cat: 'project', defaultSize: 'L', stub: true },
143
  { type: 'meeting-log', name: 'Meeting Log', desc: 'Per-stakeholder, last contact, actions', icon: 'message', cat: 'project', defaultSize: 'M' },
144
  { type: 'goals', name: 'Goals / OKRs', desc: 'Quarterly milestones with progress sliders', icon: 'bullseye', cat: 'project', defaultSize: 'M' },
145
  { type: 'calendar', name: 'Calendar', desc: 'Month grid with deadlines and writing days', icon: 'calendar', cat: 'project', defaultSize: 'M', enhanced: true },
146
  { type: 'activity', name: 'Activity Feed', desc: 'Chronological log of edits across widgets', icon: 'graph', cat: 'project', defaultSize: 'M', enhanced: true },
147
  { type: 'documenter', name: 'Daily Documenter', desc: 'Date-stamped journal · AI weekly summary (LLM stub)', icon: 'pencil', cat: 'project', defaultSize: 'M', enhanced: true },
148
- { type: 'phd-journey', name: 'Security Program Roadmap', desc: 'Milestones from assessment → hardening → audit → steady state', icon: 'flag', cat: 'project', defaultSize: 'M', enhanced: true },
149
- { type: 'phd-resources', name: 'Security Resources', desc: 'Frameworks, tools, training, and community references', icon: 'star', cat: 'research', defaultSize: 'M', enhanced: true },
150
 
151
  { type: 'mood', name: 'Mood / Burnout Check-in', desc: 'Daily slider, trend graph', icon: 'smile', cat: 'wellness', defaultSize: 'S', stub: true },
152
  { type: 'sleep', name: 'Sleep & Energy', desc: 'Correlate with productive days', icon: 'heart', cat: 'wellness', defaultSize: 'S', stub: true },
@@ -164,21 +164,21 @@ export const WIDGET_CATALOG = [
164
  { type: 'budget', name: 'Budget Tracker', desc: 'Research spend vs. cap', icon: 'wallet', cat: 'practical', defaultSize: 'S' },
165
  { type: 'discounts', name: 'Student Discounts', desc: 'Software & services with edu pricing', icon: 'star', cat: 'practical', defaultSize: 'S', stub: true },
166
 
167
- { type: 'reviewer-2', name: 'Reviewer 2 Simulator', desc: 'Paste a draft → harsh peer-review-style critique', icon: 'gavel', cat: 'critic', defaultSize: 'M', critic: true },
168
- { type: 'devils-advocate', name: 'Devil\'s Advocate', desc: 'Strongest counter-arguments to your hypothesis', icon: 'scale', cat: 'critic', defaultSize: 'M', critic: true },
169
  { type: 'scope-realism', name: 'Scope Realism Check', desc: 'Brutal feasibility verdict given timeline', icon: 'bullseye', cat: 'critic', defaultSize: 'M', critic: true },
170
  { type: 'assumption', name: 'Assumption Excavator', desc: 'Names hidden assumptions, asks "what if wrong?"', icon: 'brain', cat: 'critic', defaultSize: 'M', critic: true, stub: true },
171
- { type: 'whats-missing', name: '"What\'s Missing"', desc: 'Gap analysis on lit review or method', icon: 'alert', cat: 'critic', defaultSize: 'S', critic: true, stub: true },
172
- { type: 'calibrator', name: 'Confidence Calibrator', desc: 'Challenges every "results show X" claim', icon: 'scale', cat: 'critic', defaultSize: 'S', critic: true, stub: true },
173
  ];
174
 
175
  export const CATEGORIES = [
176
  { id: 'all', label: 'All' },
177
- { id: 'research', label: 'Threat Intel' },
178
- { id: 'writing', label: 'Writing' },
179
  { id: 'project', label: 'Project' },
180
  { id: 'wellness', label: 'Wellness' },
181
- { id: 'career', label: 'Career' },
182
  { id: 'data', label: 'Data' },
183
  { id: 'practical', label: 'Practical' },
184
  { id: 'critic', label: 'Anti-yes-man', critic: true },
@@ -193,8 +193,8 @@ const presetIds = (types) => types.map((t, i) => ({ id: `pre-${t.type}-${i}`, ..
193
  export const WORKSPACE_PRESETS = [
194
  {
195
  id: 'day1-soc',
196
- name: 'SOC Starter',
197
- desc: 'Get oriented: reading queue, notes, deadlines, kanban, pomodoro.',
198
  icon: 'sparkles',
199
  layout: presetIds([
200
  { type: 'reading-queue', size: 'M' },
@@ -207,8 +207,8 @@ export const WORKSPACE_PRESETS = [
207
  },
208
  {
209
  id: 'writing-sprint',
210
- name: 'Writing Sprint',
211
- desc: 'Focus mode for drafting: writing pad, outline, LaTeX, highlights, pomodoro.',
212
  icon: 'pencil',
213
  layout: presetIds([
214
  { type: 'writing', size: 'M' },
@@ -221,8 +221,8 @@ export const WORKSPACE_PRESETS = [
221
  },
222
  {
223
  id: 'audit-prep',
224
- name: 'Audit Prep',
225
- desc: 'Evidence-heavy: bibliography, reading queue, notes, highlights, kanban.',
226
  icon: 'book',
227
  layout: presetIds([
228
  { type: 'bibliography', size: 'L' },
@@ -234,8 +234,8 @@ export const WORKSPACE_PRESETS = [
234
  },
235
  {
236
  id: 'incident-mode',
237
- name: 'Incident Mode',
238
- desc: 'Active response: kanban, deadlines, challenge widgets, meeting log.',
239
  icon: 'gavel',
240
  layout: presetIds([
241
  { type: 'writing', size: 'M' },
@@ -291,7 +291,7 @@ export const EMPTY_STATE = {
291
  documenter: { entries: [], lastSummary: null },
292
  'phd-journey': {
293
  // Status per milestone: 'open' | 'in-progress' | 'completed'
294
- // Milestones come from the standard PhD journey (course selection → ProQuest)
295
  statuses: {},
296
  notes: {},
297
  },
 
1
+ // Demo data for a lifter running a 12-week hypertrophy block.
2
 
3
  export const DEMO_PROJECT = {
4
+ title: "12-Week Hypertrophy Block — Upper/Lower",
5
+ meta: "Intermediate lifter · muscle gain phase",
6
  };
7
 
8
  export const INSIGHTS = [
 
12
  icon: 'graph',
13
  category: 'progress',
14
  confidence: 82,
15
+ summary: 'Week 6 of a 12-week upper/lower block. Upper-body volume and pressing strength are trending up; leg volume and step count are the main gaps before the next deload.',
16
  bullets: [
17
+ 'Strength: <strong>bench +7.5kg</strong> · overhead press +5kg since week 1',
18
+ 'Volume: <strong>16 sets/week</strong> chest & back · legs lagging at 10',
19
+ '<strong>Risk:</strong> right shoulder tightness on incline pressing',
20
  ],
21
  pinned: true,
22
  sources: 18,
23
  updatedMinutesAgo: 5,
24
  quotes: [
25
+ '"Add a second lower day before increasing upper volume." — Program Planner chat',
26
+ '"Warm up the rotator cuff before pressing." — Form & Safety Coach',
27
  ],
28
  },
29
  {
30
  id: 'i-method',
31
+ title: 'Nutrition & recovery',
32
  icon: 'flask',
33
  category: 'theory',
34
  confidence: 71,
35
+ summary: 'Protein target is being hit most days; total calories are slightly under the surplus goal. Sleep is the biggest recovery limiter this block.',
36
  bullets: [
37
+ 'Protein: <strong>1.9 g/kg</strong> avg · target 2.0–2.2 g/kg',
38
+ 'Calories: <strong>~250 kcal under</strong> the lean-bulk surplus',
39
+ 'Sleep: <strong>6.2 h avg</strong> — recovery and gym energy suffering',
40
  ],
41
  sources: 14,
42
  updatedMinutesAgo: 14,
43
  quotes: [
44
+ '"Add a shake + carbs post-workout to close the calorie gap." — Nutrition Strategist',
45
+ '"Prioritize 7.5h sleep before adding volume." — Recovery Specialist',
46
  ],
47
  },
48
  {
49
  id: 'i-lit',
50
+ title: 'Training knowledge',
51
  icon: 'book',
52
  category: 'literature',
53
  confidence: 76,
54
+ summary: 'Solid grasp of progressive overload and rep ranges for chest and back. Weaker on leg-day exercise selection and how to program a deload.',
55
  bullets: [
56
+ '<strong>Coverage:</strong> hypertrophy rep ranges & progressive overload',
57
+ '<strong>Gap:</strong> quad vs. hip-hinge <strong>exercise selection</strong> for legs',
58
+ '<strong>Gap:</strong> no clear <strong>deload</strong> protocol planned',
59
  ],
60
  sources: 32,
61
  updatedMinutesAgo: 28,
62
  quotes: [
63
+ '"Rotate hack squats and RDLs to balance quads and hamstrings." — Hypertrophy Coach',
64
+ '"Plan a deload every 5–6 weeks." — Program Planner',
65
  ],
66
  },
67
  {
68
  id: 'i-questions',
69
+ title: 'Open training questions',
70
  icon: 'sparkles',
71
  category: 'theory',
72
  confidence: 63,
73
+ summary: 'Three live threads. Q1 (adding a third training day for legs) gates the next block. Q2–Q3 affect nutrition and recovery choices.',
74
  bullets: [
75
+ '<strong>Q1:</strong> Move to a 4-day upper/lower or keep 3 full-body days?',
76
+ '<strong>Q2:</strong> Lean-bulk surplus size — 200 or 400 kcal over maintenance?',
77
+ '<strong>Q3:</strong> Cardio on rest days without hurting recovery?',
78
  ],
79
  sources: 9,
80
  updatedMinutesAgo: 41,
81
  quotes: [
82
+ '"A 4th day lets you bring legs up without cutting upper volume." — Program Planner',
83
+ '"Keep the surplus small to limit fat gain." — Nutrition Strategist',
84
  ],
85
  },
86
  {
 
89
  icon: 'arrow',
90
  category: 'action',
91
  confidence: 85,
92
+ summary: 'Near-term actions tied to the next training week and the upcoming deload. Two items have slipped a week.',
93
  bullets: [
94
+ 'Add a <strong>second lower-body day</strong> and rebalance leg volume',
95
+ 'Hit <strong>2.0 g/kg protein</strong> daily and log every meal for a week',
96
+ 'Add <strong>10 min mobility</strong> pre-lift for the shoulder',
97
+ 'Schedule a <strong>deload</strong> for week 7',
98
  ],
99
  sources: 7,
100
  updatedMinutesAgo: 9,
101
  quotes: [
102
+ '"Under-recovered legs are the #1 progress blocker right now." — Recovery Specialist',
103
+ '"Track protein before adding training volume." — Nutrition Strategist',
104
  ],
105
  },
106
  {
 
109
  icon: 'alert',
110
  category: 'risk',
111
  confidence: 74,
112
+ summary: 'One physical limiter (shoulder tightness), one lifestyle limiter (sleep). Sleep is the higher risk to progress this block.',
113
  bullets: [
114
+ '<strong>Physical:</strong> shoulder tightness limits incline pressing volume',
115
+ '<strong>Lifestyle:</strong> sleep under 6.5h is capping recovery and gym energy',
116
  ],
117
  sources: 6,
118
  updatedMinutesAgo: 20,
119
  quotes: [
120
+ '"Without recovery you cannot express the strength you are building." — Recovery Specialist',
121
+ '"Fix sleep before chasing bigger lifts." — Form & Safety Coach',
122
  ],
123
  },
124
  ];
 
139
  { type: 'kanban', name: 'Task Board', desc: 'Drag-to-move with priority filter chips and due-date sort', icon: 'kanban', cat: 'project', defaultSize: 'L', enhanced: true },
140
  { type: 'deadlines', name: 'Deadlines', desc: 'Countdown plus per-deadline .ics calendar export', icon: 'calendar', cat: 'project', defaultSize: 'S', enhanced: true },
141
  { type: 'pomodoro', name: 'Pomodoro', desc: 'Real timer with break cycle and session counter', icon: 'timer', cat: 'project', defaultSize: 'S', enhanced: true },
142
+ { type: 'gantt', name: 'Milestone Timeline', desc: 'Base building → hypertrophy → peak', icon: 'flag', cat: 'project', defaultSize: 'L', stub: true },
143
  { type: 'meeting-log', name: 'Meeting Log', desc: 'Per-stakeholder, last contact, actions', icon: 'message', cat: 'project', defaultSize: 'M' },
144
  { type: 'goals', name: 'Goals / OKRs', desc: 'Quarterly milestones with progress sliders', icon: 'bullseye', cat: 'project', defaultSize: 'M' },
145
  { type: 'calendar', name: 'Calendar', desc: 'Month grid with deadlines and writing days', icon: 'calendar', cat: 'project', defaultSize: 'M', enhanced: true },
146
  { type: 'activity', name: 'Activity Feed', desc: 'Chronological log of edits across widgets', icon: 'graph', cat: 'project', defaultSize: 'M', enhanced: true },
147
  { type: 'documenter', name: 'Daily Documenter', desc: 'Date-stamped journal · AI weekly summary (LLM stub)', icon: 'pencil', cat: 'project', defaultSize: 'M', enhanced: true },
148
+ { type: 'phd-journey', name: 'Training Roadmap', desc: 'Milestones from assessment → base building → hypertrophy → peak → maintain', icon: 'flag', cat: 'project', defaultSize: 'M', enhanced: true },
149
+ { type: 'phd-resources', name: 'Fitness Resources', desc: 'Programs, exercise libraries, nutrition guides, and community references', icon: 'star', cat: 'research', defaultSize: 'M', enhanced: true },
150
 
151
  { type: 'mood', name: 'Mood / Burnout Check-in', desc: 'Daily slider, trend graph', icon: 'smile', cat: 'wellness', defaultSize: 'S', stub: true },
152
  { type: 'sleep', name: 'Sleep & Energy', desc: 'Correlate with productive days', icon: 'heart', cat: 'wellness', defaultSize: 'S', stub: true },
 
164
  { type: 'budget', name: 'Budget Tracker', desc: 'Research spend vs. cap', icon: 'wallet', cat: 'practical', defaultSize: 'S' },
165
  { type: 'discounts', name: 'Student Discounts', desc: 'Software & services with edu pricing', icon: 'star', cat: 'practical', defaultSize: 'S', stub: true },
166
 
167
+ { type: 'reviewer-2', name: 'Reviewer 2 Simulator', desc: 'Paste a plan → harsh, honest critique', icon: 'gavel', cat: 'critic', defaultSize: 'M', critic: true },
168
+ { type: 'devils-advocate', name: 'Devil\'s Advocate', desc: 'Strongest counter-arguments to your plan', icon: 'scale', cat: 'critic', defaultSize: 'M', critic: true },
169
  { type: 'scope-realism', name: 'Scope Realism Check', desc: 'Brutal feasibility verdict given timeline', icon: 'bullseye', cat: 'critic', defaultSize: 'M', critic: true },
170
  { type: 'assumption', name: 'Assumption Excavator', desc: 'Names hidden assumptions, asks "what if wrong?"', icon: 'brain', cat: 'critic', defaultSize: 'M', critic: true, stub: true },
171
+ { type: 'whats-missing', name: '"What\'s Missing"', desc: 'Gap analysis on your program or diet', icon: 'alert', cat: 'critic', defaultSize: 'S', critic: true, stub: true },
172
+ { type: 'calibrator', name: 'Confidence Calibrator', desc: 'Challenges every "this always works" claim', icon: 'scale', cat: 'critic', defaultSize: 'S', critic: true, stub: true },
173
  ];
174
 
175
  export const CATEGORIES = [
176
  { id: 'all', label: 'All' },
177
+ { id: 'research', label: 'Training' },
178
+ { id: 'writing', label: 'Logging' },
179
  { id: 'project', label: 'Project' },
180
  { id: 'wellness', label: 'Wellness' },
181
+ { id: 'career', label: 'Goals' },
182
  { id: 'data', label: 'Data' },
183
  { id: 'practical', label: 'Practical' },
184
  { id: 'critic', label: 'Anti-yes-man', critic: true },
 
193
  export const WORKSPACE_PRESETS = [
194
  {
195
  id: 'day1-soc',
196
+ name: 'Beginner Starter',
197
+ desc: 'Get oriented: reading queue, notes, deadlines, task board, pomodoro.',
198
  icon: 'sparkles',
199
  layout: presetIds([
200
  { type: 'reading-queue', size: 'M' },
 
207
  },
208
  {
209
  id: 'writing-sprint',
210
+ name: 'Program Builder',
211
+ desc: 'Plan your split: writing pad, outline, highlights, task board, pomodoro.',
212
  icon: 'pencil',
213
  layout: presetIds([
214
  { type: 'writing', size: 'M' },
 
221
  },
222
  {
223
  id: 'audit-prep',
224
+ name: 'Cut Prep',
225
+ desc: 'Dial in nutrition and tracking: notes, reading queue, highlights, task board.',
226
  icon: 'book',
227
  layout: presetIds([
228
  { type: 'bibliography', size: 'L' },
 
234
  },
235
  {
236
  id: 'incident-mode',
237
+ name: 'Plateau Buster',
238
+ desc: 'Break through: planning widgets plus challenge widgets to pressure-test your program.',
239
  icon: 'gavel',
240
  layout: presetIds([
241
  { type: 'writing', size: 'M' },
 
291
  documenter: { entries: [], lastSummary: null },
292
  'phd-journey': {
293
  // Status per milestone: 'open' | 'in-progress' | 'completed'
294
+ // Milestones come from the standard training roadmap (assessment → maintenance)
295
  statuses: {},
296
  notes: {},
297
  },
phd-advisor-frontend/src/data/userGuide.js CHANGED
@@ -1,4 +1,4 @@
1
- // User Guide content for Cybersecurity Advisor.
2
  // Use {{appName}} as a placeholder — replaced at render time.
3
 
4
  export const userGuideTopics = [
@@ -8,12 +8,12 @@ export const userGuideTopics = [
8
  icon: 'Sparkles',
9
  content: `# Welcome to {{appName}}
10
 
11
- {{appName}} is your AI-powered cybersecurity guidance system. A panel of specialized advisors gives you diverse perspectives on threats, controls, incidents, compliance, architecture, and career growth.
12
 
13
  ## Your first steps
14
  1. **Start a new chat** using the pencil icon next to the search bar
15
- 2. **Type a question** about security risks, tools, policies, incidents, or your career path
16
- 3. **Read multiple advisor responses** — each persona brings a different lens
17
  4. **Reply to a specific advisor** to go deeper on their angle
18
 
19
  ## Need help?
@@ -21,11 +21,11 @@ Return to this guide anytime via the **?** icon in the header.`,
21
  },
22
  {
23
  id: 'advisors',
24
- title: 'Your Advisors',
25
- icon: 'Shield',
26
- content: `# Your Advisors
27
 
28
- {{appName}} includes {{advisorCount}} specialized cybersecurity personas, powered by Neon BrainForge Security (4090 x1-3) with GPT-5.4 fallback when needed.
29
 
30
  ## Available advisors
31
  {{advisorList}}
@@ -40,15 +40,15 @@ Click the **advisors** dropdown in the top right of the chat to see the full pan
40
  content: `# Conversations & Replies
41
 
42
  ## Asking a question
43
- Type into the chat box at the bottom. All advisors respond with their unique perspective.
44
 
45
- ## Replying to a specific advisor
46
- Click an advisor's response to **reply directly to them** and continue one-on-one.
47
 
48
  ## Tips
49
- - Include environment context (cloud, on-prem, SaaS, regulated industry)
50
- - Paste log snippets, policy excerpts, or architecture notes for sharper advice
51
- - Different advisors may disagree — use that tension to stress-test decisions`,
52
  },
53
  {
54
  id: 'documents',
@@ -56,18 +56,18 @@ Click an advisor's response to **reply directly to them** and continue one-on-on
56
  icon: 'Paperclip',
57
  content: `# Uploading Documents
58
 
59
- Attach **PDFs, Word documents, and text files** so advisors can reference your materials.
60
 
61
  ## How it works
62
  1. Click the paperclip icon in the chat input
63
  2. Select your file
64
  3. Wait for processing
65
- 4. Ask a question — advisors use **RAG** to pull relevant sections
66
 
67
  ## Good uploads
68
- - Incident reports and postmortems
69
- - Architecture diagrams (exported as PDF)
70
- - Policy drafts, audit findings, pen-test summaries`,
71
  },
72
  {
73
  id: 'sessions',
@@ -79,18 +79,18 @@ Every conversation is saved as a session. Use the sidebar search to find past ch
79
  },
80
  {
81
  id: 'canvas',
82
- title: 'Security Canvas',
83
  icon: 'BarChart3',
84
  content: `# {{appName}} Canvas
85
 
86
- The Canvas is a **structured workspace** for your security program. Insights from chats can inform widgets such as:
87
 
88
- - Threat landscape
89
- - Controls posture
90
- - Open incidents & IR actions
91
- - Compliance gaps
92
- - Architecture decisions
93
- - Skill development & certifications
94
 
95
  Open Canvas from the sidebar. Layout and widgets auto-save in your browser.`,
96
  },
@@ -101,10 +101,10 @@ Open Canvas from the sidebar. Layout and widgets auto-save in your browser.`,
101
  content: `# Tips & Shortcuts
102
 
103
  ## Useful workflows
104
- - **Incident triage:** Incident Response Lead + Threat Modeling Analyst
105
- - **Audit prep:** Compliance Advisor + Security Architect
106
- - **Career planning:** Jerry Huaute Advisor + Security Career Mentor
107
- - **Red-team mindset:** Use anti-yes-man Canvas widgets for challenge and scope checks
108
 
109
  ## Theme
110
  Switch light/dark mode from the toggle in the header.`,
 
1
+ // User Guide content for MuscleGrowthAI.
2
  // Use {{appName}} as a placeholder — replaced at render time.
3
 
4
  export const userGuideTopics = [
 
8
  icon: 'Sparkles',
9
  content: `# Welcome to {{appName}}
10
 
11
+ {{appName}} is your AI-powered fitness and bodybuilding guidance system. A panel of specialized coaches gives you diverse perspectives on training, hypertrophy, nutrition, recovery, form, and progress tracking.
12
 
13
  ## Your first steps
14
  1. **Start a new chat** using the pencil icon next to the search bar
15
+ 2. **Type a question** about workouts, muscle groups, nutrition, recovery, or your goals
16
+ 3. **Read multiple advisor responses** — each coach brings a different lens
17
  4. **Reply to a specific advisor** to go deeper on their angle
18
 
19
  ## Need help?
 
21
  },
22
  {
23
  id: 'advisors',
24
+ title: 'Your Coaches',
25
+ icon: 'Dumbbell',
26
+ content: `# Your Coaches
27
 
28
+ {{appName}} includes {{advisorCount}} specialized fitness personas, each focused on a different part of building muscle safely and effectively.
29
 
30
  ## Available advisors
31
  {{advisorList}}
 
40
  content: `# Conversations & Replies
41
 
42
  ## Asking a question
43
+ Type into the chat box at the bottom. All coaches respond with their unique perspective.
44
 
45
+ ## Replying to a specific coach
46
+ Click a coach's response to **reply directly to them** and continue one-on-one.
47
 
48
  ## Tips
49
+ - Include context (goal, experience level, equipment, days per week, injuries)
50
+ - Paste your current program, macros, or a training log for sharper advice
51
+ - Different coaches may prioritize differently — use that to balance gains, recovery, and safety`,
52
  },
53
  {
54
  id: 'documents',
 
56
  icon: 'Paperclip',
57
  content: `# Uploading Documents
58
 
59
+ Attach **PDFs, Word documents, and text files** so coaches can reference your materials.
60
 
61
  ## How it works
62
  1. Click the paperclip icon in the chat input
63
  2. Select your file
64
  3. Wait for processing
65
+ 4. Ask a question — coaches use **RAG** to pull relevant sections
66
 
67
  ## Good uploads
68
+ - Current training programs and splits
69
+ - Nutrition plans, macro breakdowns, and food logs
70
+ - Progress spreadsheets or bloodwork summaries (exported as PDF)`,
71
  },
72
  {
73
  id: 'sessions',
 
79
  },
80
  {
81
  id: 'canvas',
82
+ title: 'Fitness Canvas',
83
  icon: 'BarChart3',
84
  content: `# {{appName}} Canvas
85
 
86
+ The Canvas is a **structured workspace** for your training. Insights from chats can inform widgets such as:
87
 
88
+ - Training split & weekly plan
89
+ - Target muscle groups & volume
90
+ - Nutrition & protein goals
91
+ - Recovery, sleep & mobility
92
+ - Progress metrics & PRs
93
+ - Program adjustments over time
94
 
95
  Open Canvas from the sidebar. Layout and widgets auto-save in your browser.`,
96
  },
 
101
  content: `# Tips & Shortcuts
102
 
103
  ## Useful workflows
104
+ - **Program design:** Hypertrophy Coach + Program Planner
105
+ - **Eating for your goal:** Nutrition Strategist + Hypertrophy Coach
106
+ - **Staying injury-free:** Form & Safety Coach + Recovery Specialist
107
+ - **Breaking a plateau:** Ask multiple coaches to compare volume, intensity, and recovery
108
 
109
  ## Theme
110
  Switch light/dark mode from the toggle in the header.`,
phd-advisor-frontend/src/pages/ChatPage.js CHANGED
@@ -18,7 +18,7 @@ import ProfileWalkthrough from '../components/ProfileWalkthrough';
18
  import ClearDataModal from '../components/ClearDataModal';
19
  import AccountModal from '../components/AccountModal';
20
 
21
- const ACTIVE_ADVISORS_STORAGE_KEY = 'cybersecurityActiveAdvisorIds';
22
 
23
  const ChatPage = ({ user, authToken, onNavigateToHome, onNavigateToCanvas, onSignOut }) => {
24
  const { config, advisors, getAdvisorColors } = useAppConfig();
@@ -1081,6 +1081,32 @@ const handleNewChat = async (sessionId = null) => {
1081
  </div>
1082
  </div>
1083
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1084
  </div>
1085
  );
1086
  };
 
18
  import ClearDataModal from '../components/ClearDataModal';
19
  import AccountModal from '../components/AccountModal';
20
 
21
+ const ACTIVE_ADVISORS_STORAGE_KEY = 'muscleGrowthActiveAdvisorIds';
22
 
23
  const ChatPage = ({ user, authToken, onNavigateToHome, onNavigateToCanvas, onSignOut }) => {
24
  const { config, advisors, getAdvisorColors } = useAppConfig();
 
1081
  </div>
1082
  </div>
1083
  </div>
1084
+
1085
+ {showProfileForm && (
1086
+ <ProfileWalkthrough
1087
+ authToken={authToken}
1088
+ existingProfile={userProfile}
1089
+ onClose={() => { setShowProfileForm(false); loadProfile(); }}
1090
+ />
1091
+ )}
1092
+
1093
+ {showAccount && (
1094
+ <AccountModal
1095
+ user={user}
1096
+ authToken={authToken}
1097
+ onClose={() => setShowAccount(false)}
1098
+ onAccountUpdated={() => setShowAccount(false)}
1099
+ onAccountDeleted={() => { setShowAccount(false); onSignOut?.(); }}
1100
+ />
1101
+ )}
1102
+
1103
+ {showClearData && (
1104
+ <ClearDataModal
1105
+ authToken={authToken}
1106
+ onClose={() => setShowClearData(false)}
1107
+ onDataCleared={() => loadProfile()}
1108
+ />
1109
+ )}
1110
  </div>
1111
  );
1112
  };