File size: 19,850 Bytes
00eebb3
26e5f50
d5f6a93
 
 
26e5f50
d5f6a93
466a383
e4ac365
 
00eebb3
26e5f50
0c2e3cf
26e5f50
b19516b
2f68045
 
b19516b
 
 
16bb456
0c2e3cf
26e5f50
0c2e3cf
26e5f50
 
 
16bb456
 
 
0c2e3cf
16bb456
 
 
46e4f9d
b4a20b2
16bb456
26e5f50
e134339
26e5f50
d5f6a93
46e4f9d
d5f6a93
0c2e3cf
 
d5f6a93
 
 
0c2e3cf
d5f6a93
 
46e4f9d
d5f6a93
0c2e3cf
d5f6a93
 
0c2e3cf
 
d5f6a93
 
46e4f9d
0c2e3cf
d5f6a93
 
 
0c2e3cf
46e4f9d
0c2e3cf
2f68045
d5f6a93
 
 
0c2e3cf
46e4f9d
2f68045
4a1d0c6
d5f6a93
4a1d0c6
d5f6a93
 
 
 
4a1d0c6
d5f6a93
0c2e3cf
2f68045
0c2e3cf
 
6fdae13
 
0c2e3cf
e4ac365
d5f6a93
 
4a1d0c6
981f4cc
 
 
 
 
 
 
f26d6dc
 
981f4cc
 
 
f26d6dc
981f4cc
 
f26d6dc
981f4cc
 
 
 
 
 
 
 
 
 
 
 
 
4a1d0c6
981f4cc
4a1d0c6
 
981f4cc
 
 
 
 
 
 
 
 
 
 
 
0c2e3cf
d5f6a93
 
6cde735
d5f6a93
981f4cc
16bb456
0c2e3cf
 
 
 
16bb456
981f4cc
e134339
0c2e3cf
2f68045
e134339
2f68045
981f4cc
e134339
2f68045
46e4f9d
 
0c2e3cf
 
e134339
981f4cc
d5f6a93
0c2e3cf
16bb456
d5f6a93
2f68045
0c2e3cf
b19516b
 
 
 
981f4cc
4a1d0c6
981f4cc
 
b19516b
 
981f4cc
0c2e3cf
b19516b
0c2e3cf
4a1d0c6
0c2e3cf
6fdae13
 
 
 
 
0c2e3cf
b19516b
 
0c2e3cf
4a1d0c6
6fdae13
26e5f50
 
0c2e3cf
26e5f50
 
e134339
e4ac365
16bb456
2f68045
16bb456
4a1d0c6
 
16bb456
2f68045
 
6fdae13
4a1d0c6
0c2e3cf
4a1d0c6
0c2e3cf
4a1d0c6
 
16bb456
4a1d0c6
 
 
 
 
 
f26d6dc
4a1d0c6
f26d6dc
 
16bb456
46e4f9d
4a1d0c6
 
 
 
f26d6dc
981f4cc
46e4f9d
 
 
f26d6dc
 
 
 
 
 
 
 
 
 
 
 
4a1d0c6
f26d6dc
 
 
46e4f9d
f26d6dc
 
 
 
 
 
 
 
 
 
 
981f4cc
f26d6dc
 
981f4cc
f26d6dc
 
4a1d0c6
2f68045
0c2e3cf
 
16bb456
4a1d0c6
 
f26d6dc
 
 
 
 
 
 
 
 
 
 
 
4a1d0c6
f26d6dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f68045
4a1d0c6
16bb456
 
26e5f50
4a1d0c6
 
26e5f50
 
4a1d0c6
 
b19516b
 
 
0c2e3cf
 
4a1d0c6
 
 
2f68045
4a1d0c6
 
2f68045
4a1d0c6
 
981f4cc
2f68045
4a1d0c6
 
 
 
 
 
6fdae13
26e5f50
4a1d0c6
 
b19516b
f26d6dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a1d0c6
0c2e3cf
b19516b
 
6fdae13
4a1d0c6
b19516b
6fdae13
0c2e3cf
b19516b
2f68045
0c2e3cf
2f68045
 
f26d6dc
0c2e3cf
 
 
 
 
4a1d0c6
f26d6dc
4a1d0c6
f26d6dc
 
4a1d0c6
 
 
2f68045
 
0c2e3cf
 
4a1d0c6
 
0c2e3cf
4a1d0c6
6fdae13
0c2e3cf
6fdae13
b19516b
6fdae13
b19516b
 
 
 
 
6fdae13
4a1d0c6
b19516b
 
4a1d0c6
b19516b
 
 
 
 
4a1d0c6
b19516b
 
6fdae13
 
e4ac365
26e5f50
0c2e3cf
26e5f50
16bb456
2f68045
0c2e3cf
16bb456
6fdae13
b19516b
 
6fdae13
16bb456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
import streamlit as st
import os
import sqlite3
import hashlib
import google.generativeai as genai
from PIL import Image
from datetime import datetime
import time
import requests
from streamlit_lottie import st_lottie

# -----------------------------------------------------------------------------
# 0. CONFIGURATION
# -----------------------------------------------------------------------------
st.set_page_config(
    page_title="Medivio | Medical Clarity", 
    page_icon="⚕️", 
    layout="wide", 
    initial_sidebar_state="expanded"
)

# Fix Upload
config_dir = ".streamlit"
if not os.path.exists(config_dir): os.makedirs(config_dir)
with open(os.path.join(config_dir, "config.toml"), "w") as f:
    f.write("[server]\nenableXsrfProtection=false\nenableCORS=false\nmaxUploadSize=200\n")

# API Setup
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
if not GOOGLE_API_KEY:
    st.error("⚠️ API Key Missing. Add GOOGLE_API_KEY to Secrets.")
    st.stop()

genai.configure(api_key=GOOGLE_API_KEY)
# FIXED: Use the correct model name
model = genai.GenerativeModel('gemini-2.5-pro')

# -----------------------------------------------------------------------------
# 1. DATABASE SYSTEM
# -----------------------------------------------------------------------------
def init_db():
    conn = sqlite3.connect('medivio_final_v10.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS users (email TEXT PRIMARY KEY, password TEXT, joined_date TEXT)''')
    c.execute('''CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT, type TEXT, summary TEXT, risk_level TEXT, date TEXT)''')
    conn.commit()
    conn.close()

def hash_pass(password): return hashlib.sha256(str.encode(password)).hexdigest()

def register_user(email, password):
    conn = sqlite3.connect('medivio_final_v10.db')
    try:
        conn.execute("INSERT INTO users VALUES (?, ?, ?)", (email, hash_pass(password), datetime.now().strftime("%Y-%m-%d")))
        conn.commit()
        return True
    except: return False
    finally: conn.close()

def login_user(email, password):
    conn = sqlite3.connect('medivio_final_v10.db')
    data = conn.execute("SELECT * FROM users WHERE email=? AND password=?", (email, hash_pass(password))).fetchall()
    conn.close()
    return data

def add_history(email, type, summary, risk):
    conn = sqlite3.connect('medivio_final_v10.db')
    conn.execute("INSERT INTO history (email, type, summary, risk_level, date) VALUES (?, ?, ?, ?, ?)", 
                 (email, type, summary[:100], risk, datetime.now().strftime("%Y-%m-%d")))
    conn.commit()
    conn.close()

def get_user_stats(email):
    conn = sqlite3.connect('medivio_final_v10.db')
    history = conn.execute("SELECT type, date FROM history WHERE email=? ORDER BY id DESC LIMIT 10", (email,)).fetchall()
    last_scan = history[0][1] if history else "New User"
    conn.close()
    return history, last_scan

init_db()

# -----------------------------------------------------------------------------
# 2. SESSION STATE & CSS
# -----------------------------------------------------------------------------
if 'page' not in st.session_state: st.session_state.page = 'landing'
if 'auth_mode' not in st.session_state: st.session_state.auth_mode = 'login'
if 'logged_in' not in st.session_state: st.session_state.logged_in = False
if 'user_email' not in st.session_state: st.session_state.user_email = ""
if 'analysis_result' not in st.session_state: st.session_state.analysis_result = None
if 'analysis_images' not in st.session_state: st.session_state.analysis_images = []
if 'chat_history' not in st.session_state: st.session_state.chat_history = []

st.markdown("""
<style>
    @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;800&display=swap');
    
    .stApp {
        background: radial-gradient(circle at 50% 0%, #1e293b 0%, #020617 100%);
        font-family: 'Plus Jakarta Sans', sans-serif;
        color: #f8fafc;
    }

    /* Clean Card Style */
    .stContainer {
        background: rgba(255, 255, 255, 0.03);
        border: 1px solid rgba(255, 255, 255, 0.05);
        border-radius: 16px;
        padding: 20px;
    }
    
    /* Result Cards */
    .result-card {
        background: #0f172a;
        border-radius: 12px;
        padding: 20px;
        margin-bottom: 15px;
        border-left: 4px solid #334155;
    }

    div.stButton > button {
        background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
        color: white; border: none; border-radius: 8px; font-weight: 600;
        padding: 12px 20px; width: 100%; transition: transform 0.2s;
    }
    div.stButton > button:hover { transform: scale(1.02); }

    .chat-user { background: #334155; padding: 12px; border-radius: 12px 12px 0 12px; margin-bottom: 10px; text-align: right; margin-left: 20%; }
    .chat-ai { background: #1e293b; border: 1px solid #3b82f6; padding: 12px; border-radius: 12px 12px 12px 0; margin-bottom: 10px; text-align: left; margin-right: 20%; }

    .history-item {
        font-size: 0.9rem;
        padding: 12px;
        border-bottom: 1px solid rgba(255,255,255,0.05);
        display: flex;
        justify-content: space-between;
        align-items: center;
        color: #e2e8f0;
    }
    .history-date { color: #64748b; font-size: 0.7rem; margin-left: 10px;}

    #MainMenu, footer, header {visibility: hidden;}
</style>
""", unsafe_allow_html=True)

# -----------------------------------------------------------------------------
# 3. LOGIC
# -----------------------------------------------------------------------------
def get_gemini_analysis(images, text_context, mode):
    role = "a helpful medical assistant"
    if mode == "Radiologist Expert": role = "a senior radiologist. Use precise technical terminology."
    elif mode == "Simple Explanation": role = "a compassionate doctor explaining to a patient. Use simple analogies."
    
    base_instruction = f"Analyze these medical images and the following patient context: '{text_context}'." if images else f"Analyze the following patient symptoms/notes: '{text_context}'."

    prompt = [
        f"You are Medivio, {role}.",
        base_instruction,
        "Strictly format your output into 5 parts separated by '|||'.",
        "Part 1: A Short, Descriptive Title (Max 4 words, e.g. 'Chest X-Ray Normal', 'Flu Symptoms')",
        "Part 2: Clinical Findings (What is seen or described?)",
        "Part 3: Risk Assessment (Low/Medium/High)",
        "Part 4: Severity Score (Just the word: Low, Medium, or High)",
        "Part 5: Recommended Actions",
        "Do not use Markdown headers. Just raw text for each section."
    ]
    
    content = prompt + images if images else prompt
    try:
        response = model.generate_content(content)
        return response.text
    except Exception as e:
        return f"Error|||System Error|||Low|||Low|||{str(e)}"

def chat_with_scan(user_query):
    prev_findings = st.session_state.analysis_result
    images = st.session_state.analysis_images
    context_prompt = [
        "You are Medivio. You have ALREADY analyzed this patient's scan.",
        f"Here is your previous analysis summary: {prev_findings}",
        "The user is asking a follow-up question. Do NOT re-analyze the whole image from scratch unless asked.",
        "Answer the specific question based on the findings above.",
        f"User Question: {user_query}"
    ]
    full_content = context_prompt + images if images else context_prompt
    try:
        response = model.generate_content(full_content)
        return response.text
    except: return "I couldn't process that. Please try again."

def go_to(page):
    st.session_state.page = page
    st.rerun()

def do_sign_out():
    st.session_state.logged_in=False
    st.session_state.analysis_result=None
    st.session_state.analysis_images=[]
    st.session_state.chat_history=[]
    st.session_state.auth_mode = 'login'
    go_to('landing')

# -----------------------------------------------------------------------------
# 4. PAGES
# -----------------------------------------------------------------------------

# --- LANDING ---
def show_landing():
    c1, c2 = st.columns([1, 6])
    with c1: st.markdown("<h3 style='color:#3b82f6; margin:0;'>MEDIVIO</h3>", unsafe_allow_html=True)
    st.markdown("<br><br>", unsafe_allow_html=True)
    
    col1, col2 = st.columns([1.3, 1])
    with col1:
        st.markdown("<h1 style='font-size: 4rem; line-height: 1.1; margin-bottom:20px;'>Upload your medical report.<br><span style='background: -webkit-linear-gradient(0deg, #3b82f6, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent;'>Get answers in seconds.</span></h1>", unsafe_allow_html=True)
        st.markdown("<p style='color:#94a3b8; font-size:1.2rem; line-height:1.6;'>AI that translates scans, labs, and clinical notes into language you understand.</p>", unsafe_allow_html=True)
        
        b1, b2 = st.columns([1,1.5])
        with b1: 
            if st.button("Start Now"): go_to('login')
        with b2: 
            if st.button("How it Works"): go_to('about')

    with col2:
        st.markdown("""
        <div style='text-align:center; animation: float 6s ease-in-out infinite;'>
            <div style='font-size: 7rem;'>🧬</div>
        </div>
        """, unsafe_allow_html=True)

    # PURE MARKDOWN FOOTER (No HTML Tags)
    st.markdown("<br><br><br><br>", unsafe_allow_html=True)
    st.divider()
    st.caption("Disclaimer: Medivio is an AI-powered analysis tool. It does not provide medical diagnosis. Always consult a qualified healthcare professional for medical advice.")

# --- ABOUT (CLEAN TEXT ONLY) ---
def show_about():
    if st.button("← Back"): go_to('landing')
    st.markdown("<br>", unsafe_allow_html=True)
    
    st.title("About Medivio")
    
    st.markdown("""
    Medivio is an intelligent interface for your health. It acts as a secure bridge between 
    complex medical data—like X-Rays, MRI scans, and doctor's notes—and clear human understanding.
    """)
    
    st.divider()
    
    col_a, col_b = st.columns(2)
    
    with col_a:
        st.subheader("Our Mission")
        st.markdown("""
        To democratize access to medical information. We believe everyone should be able to 
        understand their own health records instantly, without waiting days for an appointment.
        """)
        
    with col_b:
        st.subheader("Technology")
        st.markdown("""
        Medivio leverages **Google Gemini 1.5 Pro**, a multimodal AI model capable of processing 
        vision (images) and text simultaneously. This allows for context-aware analysis that 
        mimics the reasoning of a medical professional.
        """)
        
    st.divider()
    
    st.subheader("How It Works")
    f1, f2, f3 = st.columns(3)
    
    with f1:
        st.info("**1. Upload**\n\nDrag & Drop medical scans or paste clinical notes securely.")
        
    with f2:
        st.info("**2. Analyze**\n\nOur AI engine detects patterns, risks, and anomalies instantly.")
        
    with f3:
        st.success("**3. Understand**\n\nReceive a clear, jargon-free explanation of your health data.")

# --- AUTH ---
def show_auth():
    c1, c2, c3 = st.columns([1,1,1])
    with c2:
        st.markdown("<br><br>", unsafe_allow_html=True)
        
        with st.container():
            if st.session_state.auth_mode == 'login':
                st.markdown("<h2 style='text-align:center; margin-bottom:10px;'>Member Login</h2>", unsafe_allow_html=True)
                email = st.text_input("Email", key="l_e")
                pw = st.text_input("Password", type="password", key="l_p")
                st.markdown("<br>", unsafe_allow_html=True)
                if st.button("Sign In"):
                    if login_user(email, pw):
                        st.session_state.logged_in=True
                        st.session_state.user_email=email
                        go_to('dashboard')
                    else: st.error("Invalid Credentials")
                
                st.markdown("---")
                if st.button("Create Account", type="secondary"):
                    st.session_state.auth_mode = 'register'
                    st.rerun()
                    
            else: 
                st.markdown("<h2 style='text-align:center; margin-bottom:10px;'>Create Account</h2>", unsafe_allow_html=True)
                re = st.text_input("Email", key="r_e")
                rp = st.text_input("Password", type="password", key="r_p")
                st.markdown("<br>", unsafe_allow_html=True)
                if st.button("Sign Up"):
                    if register_user(re, rp): 
                        st.success("Account created! Please sign in.")
                        time.sleep(1.5)
                        st.session_state.auth_mode = 'login'
                        st.rerun()
                    else: st.error("Email already used.")
                
                st.markdown("---")
                if st.button("Back to Login", type="secondary"):
                    st.session_state.auth_mode = 'login'
                    st.rerun()

# --- DASHBOARD ---
def show_dashboard():
    history_data, last_active = get_user_stats(st.session_state.user_email)
    
    with st.sidebar:
        st.markdown(f"### 👤 {st.session_state.user_email}")
        st.markdown("<div style='color:#10b981; margin-bottom:20px'>● Online</div>", unsafe_allow_html=True)
        
        if st.button("Start New Analysis"):
            st.session_state.analysis_result = None
            st.session_state.analysis_images = []
            st.session_state.chat_history = []
            st.rerun()
            
        st.markdown("---")
        st.markdown(f"<div style='font-size:0.75rem; color:#64748b; margin-bottom:10px;'>Last active: {last_active}</div>", unsafe_allow_html=True)
        st.markdown("##### History")
        
        if history_data:
            for h_title, h_date in history_data:
                st.markdown(f"""
                <div class='history-item'>
                    <b>{h_title}</b>
                    <span class='history-date'>{h_date}</span>
                </div>
                """, unsafe_allow_html=True)
        else:
            st.caption("No scans yet.")
            
        st.markdown("---")
        if st.button("Sign Out"): do_sign_out()

    st.title("Diagnostic Interface")
    
    if st.session_state.analysis_result is None:
        with st.container():
            st.write("Upload your medical data below.")
            img_files = st.file_uploader("Upload Medical Scans (X-Ray, MRI, CT)", type=['png','jpg','jpeg'], accept_multiple_files=True)
            c1, c2 = st.columns([2, 1])
            with c1: txt_context = st.text_area("Patient Symptoms / Clinical Context", height=100, placeholder="E.g. Patient has chest pain for 3 weeks...")
            with c2: mode = st.selectbox("Analysis Mode", ["Radiologist Expert", "Simple Explanation"])
            
            st.markdown("<br>", unsafe_allow_html=True)
            if st.button("Run Analysis"):
                if not img_files and not txt_context: st.error("Please upload an image or provide text.")
                else:
                    with st.spinner("Processing Multi-Modal Data..."):
                        pil_images = [Image.open(x) for x in img_files] if img_files else []
                        res = get_gemini_analysis(pil_images, txt_context, mode)
                        st.session_state.analysis_result = res
                        st.session_state.analysis_images = pil_images
                        
                        parts = res.split("|||")
                        if len(parts) >= 5:
                            title = parts[0].strip().replace("Part 1:", "").replace("**", "").strip()
                            risk_lvl = parts[3].strip()
                            add_history(st.session_state.user_email, title, res, risk_lvl)
                        else:
                            add_history(st.session_state.user_email, "Analysis", res, "Unknown")
                        st.rerun()
    
    else:
        res = st.session_state.analysis_result
        images = st.session_state.analysis_images
        
        col_act1, col_act2 = st.columns([1, 4])
        with col_act1:
            if st.button("💬 Chat with Scan"): go_to('chat')
        
        st.markdown("---")
        
        parts = res.split("|||")
        if len(parts) >= 5:
            title, obs, risks, severity, actions = parts[0], parts[1], parts[2], parts[3], parts[4]
            
            sev_clean = severity.strip().lower()
            bar_class = "risk-fill-low"
            color = "#22c55e"
            if "medium" in sev_clean: bar_class = "risk-fill-med"; color = "#eab308"
            if "high" in sev_clean: bar_class = "risk-fill-high"; color = "#ef4444"
            
            # Severity Bar (HTML is required here for custom CSS visuals, but kept simple)
            st.markdown(f"""
            <div style='background:rgba(255,255,255,0.05); padding:20px; border-radius:10px; border-left:5px solid {color};'>
                <h4 style='margin:0; color:{color}'>SEVERITY: {severity.upper()}</h4>
            </div>
            """, unsafe_allow_html=True)
            
            st.markdown(f"### {title}")
            
            c1, c2 = st.columns(2)
            with c1:
                st.markdown(f"<div class='result-card' style='border-color:#3b82f6'><h4 style='color:#3b82f6'>🔍 Findings</h4>{obs}</div>", unsafe_allow_html=True)
                st.markdown(f"<div class='result-card' style='border-color:#f59e0b'><h4 style='color:#f59e0b'>⚠️ Risks</h4>{risks}</div>", unsafe_allow_html=True)
            with c2:
                st.markdown(f"<div class='result-card' style='border-color:#10b981'><h4 style='color:#10b981'>✅ Protocol</h4>{actions}</div>", unsafe_allow_html=True)
                if images: st.image(images[0], caption="Primary Scan Reference", use_container_width=True)

# --- CHAT PAGE ---
def show_chat():
    if st.button("← Back to Results"): go_to('dashboard')
    st.title("💬 Chat with Scan")
    if st.session_state.analysis_images:
        with st.expander("View Scans Reference"):
            cols = st.columns(len(st.session_state.analysis_images))
            for idx, img in enumerate(st.session_state.analysis_images):
                with cols[idx]: st.image(img, width=150)
    
    chat_container = st.container()
    user_input = st.chat_input("Ask a follow-up question about the findings...")
    
    if user_input:
        st.session_state.chat_history.append({"role": "user", "content": user_input})
        with st.spinner("Consulting Analysis..."):
            ai_reply = chat_with_scan(user_input)
            st.session_state.chat_history.append({"role": "ai", "content": ai_reply})
            
    with chat_container:
        for msg in st.session_state.chat_history:
            if msg['role'] == 'user': st.markdown(f"<div class='chat-user'>{msg['content']}</div>", unsafe_allow_html=True)
            else: st.markdown(f"<div class='chat-ai'>{msg['content']}</div>", unsafe_allow_html=True)

# -----------------------------------------------------------------------------
# 5. ROUTING
# -----------------------------------------------------------------------------
if st.session_state.page == 'landing': show_landing()
elif st.session_state.page == 'login': show_auth()
elif st.session_state.page == 'dashboard': 
    if st.session_state.logged_in: show_dashboard()
    else: go_to('login')
elif st.session_state.page == 'chat':
    if st.session_state.logged_in: show_chat()
    else: go_to('login')
elif st.session_state.page == 'about': show_about()