LakshmiNandaS commited on
Commit
1b072fb
Β·
verified Β·
1 Parent(s): 1b07b12

Update pages/5_ai_coach.py

Browse files
Files changed (1) hide show
  1. pages/5_ai_coach.py +282 -306
pages/5_ai_coach.py CHANGED
@@ -16,115 +16,110 @@ data = st.session_state.user_data
16
 
17
  st.markdown("""
18
  <style>
19
- @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Syne:wght@700;800&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600&display=swap');
20
 
21
- /* ── BACKGROUND ── */
22
  [data-testid="stAppViewContainer"]::before{
23
  content:'';position:fixed;inset:0;z-index:0;
24
- background:url('https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?fm=jpg&w=1600&q=80&fit=crop') center/cover no-repeat;
25
- filter:blur(10px) brightness(0.18) saturate(0.40);transform:scale(1.08);}
26
  [data-testid="stAppViewContainer"]{
27
- background:linear-gradient(160deg,rgba(2,2,6,0.96) 0%,rgba(4,3,10,0.92) 100%)!important;
 
 
28
  position:relative;}
29
  [data-testid="stAppViewContainer"]>section>div.block-container{
30
- position:relative;z-index:2;max-width:920px!important;
31
- margin:0 auto!important;padding:0 20px 100px!important;}
32
 
33
  #MainMenu,footer,header,[data-testid="stToolbar"],[data-testid="stDecoration"],
34
  [data-testid="stSidebarNav"],[data-testid="collapsedControl"],
35
  section[data-testid="stSidebar"],button[kind="header"]{display:none!important;}
36
- html,body,.stApp{background:#020206!important;color:#fff!important;font-family:'DM Sans',sans-serif!important;}
37
 
38
- /* ── NAV BUTTONS ── */
39
  div[data-testid="stHorizontalBlock"]:first-of-type div[data-testid="stButton"]>button{
40
- background:rgba(10,8,20,0.75)!important;border:1.5px solid rgba(229,9,20,0.40)!important;
41
- color:rgba(255,255,255,0.80)!important;border-radius:8px!important;
42
- font-size:0.82rem!important;font-weight:600!important;height:32px!important;
43
- min-height:32px!important;white-space:nowrap!important;transition:all 0.15s!important;}
44
  div[data-testid="stHorizontalBlock"]:first-of-type div[data-testid="stButton"]>button:hover{
45
- background:rgba(229,9,20,0.22)!important;border-color:rgba(229,9,20,0.75)!important;color:#fff!important;}
46
  div[data-testid="stHorizontalBlock"]:first-of-type div[data-testid="stButton"]:last-child>button{
47
- background:rgba(229,9,20,0.30)!important;border-color:#E50914!important;}
48
 
49
- /* ── MAIN SEND BUTTON ── */
50
  .stButton>button{
51
- background:linear-gradient(135deg,#E50914,#c0000c)!important;
52
  border:none!important;color:#fff!important;border-radius:12px!important;
53
  font-family:'DM Sans',sans-serif!important;font-size:1rem!important;font-weight:700!important;
54
- box-shadow:0 4px 20px rgba(229,9,20,0.45)!important;transition:all 0.20s!important;}
55
- .stButton>button:hover{transform:translateY(-2px)!important;box-shadow:0 8px 28px rgba(229,9,20,0.65)!important;}
 
 
 
56
 
57
- /* ── FORM & INPUT ── */
58
  [data-testid="stForm"]{background:transparent!important;border:none!important;}
59
  [data-testid="stFormSubmitButton"] button,.stFormSubmitButton>button{
60
- background:linear-gradient(135deg,#E50914,#b0000a)!important;border:none!important;
61
- color:#fff!important;border-radius:12px!important;font-weight:700!important;
62
- box-shadow:0 4px 20px rgba(229,9,20,0.50)!important;font-size:1rem!important;
63
- letter-spacing:0.5px!important;height:52px!important;}
64
-
65
- /* Chat input β€” dark with glow on focus */
66
- input,textarea,
67
- .stTextInput>div>div>input,
68
- [data-baseweb="input"] input,
69
- [data-testid="stTextInput"] input{
70
- background:#0e0c1a!important;background-color:#0e0c1a!important;
71
- border:1.5px solid rgba(229,9,20,0.35)!important;color:#fff!important;
72
- border-radius:14px!important;font-family:'DM Sans',sans-serif!important;
73
- font-size:1rem!important;
74
- box-shadow:inset 0 0 0 9999px #0e0c1a!important;
75
- -webkit-box-shadow:inset 0 0 0 9999px #0e0c1a!important;
76
- caret-color:#E50914!important;transition:border-color 0.2s!important;}
77
  input:focus,[data-baseweb="input"] input:focus{
78
- border-color:rgba(229,9,20,0.80)!important;
79
- box-shadow:inset 0 0 0 9999px #0e0c1a, 0 0 0 3px rgba(229,9,20,0.18)!important;
80
- -webkit-box-shadow:inset 0 0 0 9999px #0e0c1a!important;outline:none!important;}
81
- [data-baseweb="input"]{
82
- background:#0e0c1a!important;background-color:#0e0c1a!important;
83
- border:1.5px solid rgba(229,9,20,0.35)!important;border-radius:14px!important;}
84
  input:-webkit-autofill,input:-webkit-autofill:focus{
85
- -webkit-box-shadow:0 0 0 9999px #0e0c1a inset!important;
86
- -webkit-text-fill-color:#fff!important;}
87
 
88
- /* ── QUICK BUTTONS ── */
89
  .quick-btn .stButton>button{
90
- background:rgba(255,255,255,0.04)!important;
91
- border:1px solid rgba(255,255,255,0.14)!important;
92
- color:rgba(255,255,255,0.72)!important;font-size:0.82rem!important;
93
- font-weight:500!important;text-transform:none!important;
94
- border-radius:20px!important;padding:5px 12px!important;
95
- height:auto!important;min-height:auto!important;
96
- box-shadow:none!important;letter-spacing:0!important;
97
- font-style:italic!important;backdrop-filter:blur(12px)!important;
98
- animation:none!important;transition:all 0.15s!important;}
99
  .quick-btn .stButton>button:hover{
100
- background:rgba(229,9,20,0.15)!important;border-color:rgba(229,9,20,0.50)!important;
101
- color:#fff!important;transform:translateY(-1px)!important;}
 
102
 
103
- /* ── GLOBAL TEXT ── */
104
  html,body,.stApp,.stMarkdown,p,div,span,label{text-shadow:0 1px 4px rgba(0,0,0,0.95)!important;}
105
  [data-testid="stWidgetLabel"],[data-testid="stWidgetLabel"] p{color:#fff!important;font-weight:600!important;}
106
  .stMarkdown p,.stMarkdown li{color:#fff!important;line-height:1.75!important;}
107
 
108
- /* ── CHAT PULSE ANIMATION ── */
109
- @keyframes msgIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
110
- @keyframes blink{0%,100%{opacity:1}50%{opacity:0.3}}
111
- @keyframes pulse-ring{0%{transform:scale(1);opacity:0.8}100%{transform:scale(1.6);opacity:0}}
112
- @keyframes shimmer{0%{background-position:200% center}100%{background-position:-200% center}}
 
 
 
 
113
  </style>
114
  """, unsafe_allow_html=True)
115
 
116
  apply_bg(
117
- "https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?fm=jpg&w=1600&q=80&fit=crop",
118
- overlay="rgba(4,8,14,0.82)"
119
  )
120
 
121
  # ── NAV ───────────────────────────────────────────────────────────────────────
122
- st.markdown("<div style='padding:6px 0;margin-bottom:14px'>", unsafe_allow_html=True)
123
  _n = st.columns([1.6,1,1,1,1,1,1,1.2])
124
  with _n[0]:
125
  st.markdown(
126
  "<div style='font-family:Bebas Neue,sans-serif;font-size:1.4rem;letter-spacing:5px;"
127
- "color:#E50914;text-shadow:0 0 20px rgba(229,9,20,0.55);line-height:1;padding-top:4px'>"
128
  "&#9889; FITPLAN PRO</div>", unsafe_allow_html=True)
129
  nav_pages = [("Home","pages/2_Dashboard.py","ac_db"),("Workout","pages/3_Workout_Plan.py","ac_wp"),
130
  ("Diet","pages/4_Diet_Plan.py","ac_dp"),("AI Coach","pages/5_ai_coach.py","ac_ai"),
@@ -145,73 +140,82 @@ with _n[7]:
145
  st.switch_page("app.py")
146
  st.markdown("</div>", unsafe_allow_html=True)
147
 
148
- # ── HERO ──────────────────────────────────────────────────────────────────────
149
- sdays = st.session_state.get("structured_days", [])
150
- diet_lbl = "Vegetarian" if st.session_state.get("dietary_type")=="veg" else \
151
- ("Non-Veg" if st.session_state.get("dietary_type")=="nonveg" else "Flexible")
152
-
153
- st.markdown(
154
- "<div style='background:linear-gradient(135deg,rgba(229,9,20,0.12),rgba(10,6,20,0.80));"
155
- "border:1.5px solid rgba(229,9,20,0.30);border-radius:20px;padding:24px 32px;"
156
- "margin-bottom:20px;position:relative;overflow:hidden'>"
157
- "<div style='position:absolute;top:0;left:0;right:0;height:2px;"
158
- "background:linear-gradient(90deg,transparent,#E50914,rgba(229,9,20,0.30),transparent)'></div>"
159
- "<div style='position:absolute;top:-40px;right:-20px;font-size:8rem;opacity:0.04;line-height:1'>πŸ€–</div>"
160
- "<div style='display:flex;align-items:center;gap:16px'>"
161
- # Avatar with pulse
162
- "<div style='position:relative;flex-shrink:0'>"
163
- "<div style='width:56px;height:56px;border-radius:50%;"
164
- "background:linear-gradient(135deg,#E50914,#7c000a);"
165
- "display:flex;align-items:center;justify-content:center;font-size:1.6rem;"
166
- "box-shadow:0 0 0 3px rgba(229,9,20,0.25),0 0 20px rgba(229,9,20,0.40)'>"
167
- "πŸ€–</div>"
168
- "<div style='position:absolute;inset:-4px;border-radius:50%;"
169
- "border:2px solid rgba(229,9,20,0.40);animation:pulse-ring 2s ease-out infinite'></div>"
170
- "</div>"
171
- "<div>"
172
- "<div style='font-family:Syne,sans-serif;font-size:1.5rem;font-weight:800;"
173
- "color:#fff;letter-spacing:1px;line-height:1;margin-bottom:5px'>"
174
- "ATLAS β€” Your AI Fitness Coach</div>"
175
- "<div style='font-size:0.85rem;color:rgba(255,255,255,0.55);line-height:1.5'>"
176
- "Trained on your profile &nbsp;Β·&nbsp; "
177
- + str(len(sdays)) + "-day plan &nbsp;Β·&nbsp; "
178
- + diet_lbl + " diet &nbsp;Β·&nbsp; "
179
- + data.get("goal","Fitness") + "</div>"
180
- "</div>"
181
- "<div style='margin-left:auto;display:flex;align-items:center;gap:6px'>"
182
- "<div style='width:8px;height:8px;border-radius:50%;background:#22c55e;"
183
- "box-shadow:0 0 8px #22c55e;animation:blink 2s ease-in-out infinite'></div>"
184
- "<span style='font-size:0.75rem;color:rgba(34,197,94,0.90);font-weight:600;letter-spacing:1px'>ONLINE</span>"
185
- "</div>"
186
- "</div></div>",
187
- unsafe_allow_html=True
188
- )
189
-
190
- # ── SYSTEM PROMPT ─────────────────────────────────────────────────────────────
191
- plan_ctx = ""
192
- if sdays:
193
- plan_ctx = str(len(sdays)) + "-day fitness plan. Diet: " + diet_lbl + ". "
194
 
195
  system_prompt = (
196
- "You are ATLAS, a professional AI fitness coach inside FitPlan Pro. You are warm, direct, "
197
- "knowledgeable, and motivating. You speak like a real coach β€” not a robot. "
198
- "User profile: Name=" + uname +
199
- ", Age=" + str(data.get("age","?")) +
200
  ", Weight=" + str(data.get("weight","?")) + "kg"
201
  ", Height=" + str(data.get("height","?")) + "cm"
202
  ", Goal=" + data.get("goal","Fitness") +
203
  ", Level=" + data.get("level","Beginner") +
204
- ", Equipment=" + (", ".join(data.get("equipment",[])) or "None") + ". " +
205
- plan_ctx +
206
- "STRICT RULES: "
207
- "1. Only answer questions about fitness, gym, nutrition, body, health, lifestyle, motivation, sleep, stress, and general wellness. "
208
- "2. For completely off-topic questions (politics, religion, coding, finance etc) politely redirect: say you're a fitness coach and steer back to health topics. "
209
- "3. NEVER output JSON, code blocks, or curly braces. Plain conversational English only. "
210
- "4. Keep replies under 130 words. Be punchy and actionable. "
211
- "5. Use a dash (-) on a new line for lists. Max 4 list items. "
212
- "6. Never start with 'Assistant:', 'ATLAS:', or repeat the user's question. "
213
- "7. Add ONE motivational nudge at the end when appropriate. "
214
- "8. Remember context from earlier in the conversation and refer back to it naturally."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  )
216
 
217
  # ── CHAT HISTORY ──────────────────────────────────────────────────────────────
@@ -224,154 +228,169 @@ if "chat_messages" not in st.session_state:
224
  st.session_state.chat_messages = []
225
 
226
  # ── QUICK QUESTIONS ───────────────────────────────────────────────────────────
227
- quick_qs = [
228
- "πŸ’ͺ Best post-workout meal?",
229
- "πŸ”₯ How to lose fat fast?",
230
- "😴 How much sleep do I need?",
231
- "πŸ₯© Daily protein target?",
232
- "⚑ How to boost energy?",
233
- "🧘 Recovery tips for sore muscles?",
234
- ]
235
-
236
  st.markdown(
237
- "<div style='font-size:0.70rem;font-weight:700;letter-spacing:3px;text-transform:uppercase;"
238
- "color:rgba(229,9,20,0.75);margin-bottom:8px;display:flex;align-items:center;gap:8px'>"
239
- "<span style='width:14px;height:1.5px;background:#E50914;display:block'></span>"
240
- "Quick Questions</div>",
241
- unsafe_allow_html=True
242
- )
 
243
  qc = st.columns(3)
244
- for i, q in enumerate(quick_qs):
245
- with qc[i % 3]:
246
  st.markdown("<div class='quick-btn'>", unsafe_allow_html=True)
247
- if st.button(q, key="qq_"+str(i), use_container_width=True):
248
- clean_q = q.split(" ",1)[1] if q[0] in "πŸ’ͺπŸ”₯😴πŸ₯©βš‘🧘" else q
249
- st.session_state.chat_messages.append({"role":"user","content":clean_q})
250
  st.rerun()
251
  st.markdown("</div>", unsafe_allow_html=True)
252
 
253
  st.markdown("<div style='height:8px'></div>", unsafe_allow_html=True)
254
 
255
- # ── CHAT WINDOW ───────────────────────────────────────────────────────────────
256
- st.markdown("<div style='height:4px'></div>", unsafe_allow_html=True)
257
-
258
  if not st.session_state.chat_messages:
259
  st.markdown(
260
- "<div style='text-align:center;padding:40px 20px'>"
261
- "<div style='font-size:3rem;margin-bottom:12px;opacity:0.30'>πŸ’¬</div>"
262
- "<div style='font-family:Syne,sans-serif;font-size:1.1rem;font-weight:700;"
263
- "color:rgba(255,255,255,0.30);letter-spacing:1px'>Start the conversation</div>"
264
- "<div style='font-size:0.85rem;color:rgba(255,255,255,0.20);margin-top:6px'>"
265
- "Ask ATLAS anything about your fitness journey</div>"
266
- "</div>",
267
- unsafe_allow_html=True
268
- )
 
 
 
 
 
 
 
 
 
 
 
 
269
  else:
270
  for mi, msg in enumerate(st.session_state.chat_messages):
 
271
  if msg["role"] == "user":
272
  st.markdown(
273
- "<div style='display:flex;justify-content:flex-end;margin:10px 0;"
274
- "animation:msgIn 0.3s ease'>"
275
- "<div style='max-width:78%;background:linear-gradient(135deg,rgba(229,9,20,0.28),rgba(180,0,8,0.20));"
276
- "border:1.5px solid rgba(229,9,20,0.40);border-radius:18px 18px 4px 18px;"
277
- "padding:12px 18px;position:relative'>"
278
- "<div style='font-size:0.65rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;"
279
- "color:rgba(229,9,20,0.75);margin-bottom:5px'>YOU</div>"
280
- "<div style='font-size:0.95rem;color:#fff;line-height:1.60'>" + msg["content"] + "</div>"
281
- "</div>"
282
- "<div style='width:34px;height:34px;border-radius:50%;background:rgba(229,9,20,0.20);"
283
- "border:1.5px solid rgba(229,9,20,0.40);display:flex;align-items:center;"
284
- "justify-content:center;margin-left:10px;flex-shrink:0;align-self:flex-end;font-size:1rem'>πŸ‘€</div>"
285
- "</div>",
286
- unsafe_allow_html=True
287
- )
 
288
  else:
289
- # Format AI message
290
  _lines = []
291
  for _ln in msg["content"].splitlines():
292
  _s = _ln.strip()
293
  if _s.startswith("- ") or _s.startswith("β€’ "):
294
  _lines.append(
295
- "<div style='display:flex;gap:8px;margin:4px 0;align-items:flex-start'>"
296
- "<span style='color:#E50914;font-size:0.75rem;margin-top:3px;flex-shrink:0'>β–Ά</span>"
297
- "<span style='font-size:0.95rem;color:rgba(255,255,255,0.90)'>" + _s[2:] + "</span></div>")
 
298
  elif _s:
299
  _lines.append(
300
- "<div style='font-size:0.95rem;color:rgba(255,255,255,0.90);"
301
- "line-height:1.65;margin-bottom:3px'>" + _s + "</div>")
302
  else:
303
- _lines.append("<div style='height:5px'></div>")
304
 
305
  st.markdown(
306
- "<div style='display:flex;margin:10px 0;animation:msgIn 0.3s ease'>"
307
- "<div style='width:34px;height:34px;border-radius:50%;"
308
- "background:linear-gradient(135deg,#E50914,#7c000a);"
309
- "display:flex;align-items:center;justify-content:center;"
310
- "margin-right:10px;flex-shrink:0;align-self:flex-end;font-size:1rem;"
311
- "box-shadow:0 0 12px rgba(229,9,20,0.35)'>πŸ€–</div>"
312
- "<div style='max-width:80%;background:rgba(14,12,26,0.85);"
313
- "border:1.5px solid rgba(255,255,255,0.10);border-radius:18px 18px 18px 4px;"
314
- "padding:14px 18px;backdrop-filter:blur(20px)'>"
315
- "<div style='font-size:0.65rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;"
316
- "color:rgba(229,9,20,0.70);margin-bottom:6px;"
317
- "background:linear-gradient(90deg,#E50914,#ff6b6b);-webkit-background-clip:text;"
318
- "-webkit-text-fill-color:transparent;background-clip:text'>ATLAS</div>"
 
319
  + "".join(_lines) +
320
- "</div></div>",
321
- unsafe_allow_html=True
322
- )
323
 
324
- st.markdown("<div style='height:8px'></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
  # ── PROCESS AI REPLY ──────────────────────────────────────────────────────────
327
  if st.session_state.chat_messages and st.session_state.chat_messages[-1]["role"] == "user":
328
- with st.spinner("ATLAS is thinking..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  try:
330
- from model_api import query_model
331
- history = st.session_state.chat_messages[-10:]
332
- conv = ""
333
- for m in history[:-1]:
334
- conv += ("User" if m["role"]=="user" else "Coach") + ": " + m["content"] + "\n"
335
- full_prompt = (system_prompt + "\n\nConversation history:\n" + conv +
336
- "\nUser: " + history[-1]["content"] + "\nCoach:")
337
- reply = query_model(full_prompt, max_tokens=320).strip()
338
- # Clean unwanted prefixes
339
- for _pfx in ["Assistant:","ATLAS:","AI Coach:","Coach:"]:
340
- if reply.startswith(_pfx): reply = reply[len(_pfx):].strip()
341
- import re as _re
342
- # Remove any JSON-like blobs
343
- _clean = [ln for ln in reply.splitlines()
344
- if not (ln.strip().startswith("{") or ln.strip().startswith("[")
345
- or (ln.strip().startswith('"') and ln.strip().endswith('"},'))) ]
346
- reply = _re.sub(r"\{[^}]*\}", "", "\n".join(_clean))
347
- reply = _re.sub(r"\[[^\]]*\]", "", reply)
348
- reply = _re.sub(r"\n{3,}", "\n\n", reply).strip()
349
- if not reply: reply = "Great question! Ask me again and I'll give you the full breakdown."
350
- st.session_state.chat_messages.append({"role":"assistant","content":reply})
351
- try:
352
- from utils.db import save_chat_history
353
- save_chat_history(uname, st.session_state.chat_messages[-30:])
354
- except Exception: pass
355
- st.rerun()
356
- except Exception as e:
357
- st.error("ATLAS error: " + str(e))
358
 
359
  # ── INPUT BAR ─────────────────────────────────────────────────────────────────
360
  st.markdown(
361
- "<div style='background:rgba(6,4,14,0.85);border:1.5px solid rgba(229,9,20,0.28);"
362
- "border-radius:16px;padding:12px 16px;backdrop-filter:blur(20px);"
363
- "position:relative;overflow:hidden'>"
364
- "<div style='position:absolute;top:0;left:0;right:0;height:1px;"
365
- "background:linear-gradient(90deg,transparent,rgba(229,9,20,0.30),transparent)'></div>",
366
- unsafe_allow_html=True
367
- )
368
  with st.form("chat_form", clear_on_submit=True):
369
- inp_col, btn_col = st.columns([5, 1])
370
  with inp_col:
371
  user_input = st.text_input(
372
- "", placeholder="Ask ATLAS about workouts, nutrition, recovery...",
373
- key="chat_input_field", label_visibility="collapsed"
374
- )
375
  with btn_col:
376
  send = st.form_submit_button("Send ➀", use_container_width=True)
377
  if send and user_input.strip():
@@ -379,84 +398,41 @@ with st.form("chat_form", clear_on_submit=True):
379
  st.rerun()
380
  st.markdown("</div>", unsafe_allow_html=True)
381
 
382
- # ── BOTTOM ACTIONS ────────────────────────────────────────────────────────────
383
- st.markdown("<div style='height:10px'></div>", unsafe_allow_html=True)
384
-
385
- ba1, ba2, ba3 = st.columns([2, 2, 6])
386
  with ba1:
387
  if st.session_state.chat_messages:
388
  if st.button("πŸ—‘οΈ Clear Chat", key="clear_chat", use_container_width=True):
389
  st.session_state.chat_messages = []
390
  st.rerun()
391
  with ba2:
392
- msg_count = len(st.session_state.chat_messages)
393
- if msg_count > 0:
394
- ai_count = sum(1 for m in st.session_state.chat_messages if m["role"]=="assistant")
395
  st.markdown(
396
- f"<div style='background:rgba(229,9,20,0.08);border:1px solid rgba(229,9,20,0.22);"
397
- f"border-radius:10px;padding:8px 14px;text-align:center;font-size:0.78rem;"
398
- f"color:rgba(255,255,255,0.55)'>"
399
- f"πŸ’¬ {msg_count} messages &nbsp;Β·&nbsp; πŸ€– {ai_count} replies</div>",
400
- unsafe_allow_html=True
401
- )
402
 
403
- # ── COACH CAPABILITIES ────────────────────────────────────────────────────────
404
  if not st.session_state.chat_messages:
405
- st.markdown("<div style='height:16px'></div>", unsafe_allow_html=True)
406
- st.markdown(
407
- "<div style='font-size:0.70rem;font-weight:700;letter-spacing:3px;text-transform:uppercase;"
408
- "color:rgba(229,9,20,0.65);margin-bottom:12px;display:flex;align-items:center;gap:8px'>"
409
- "<span style='width:14px;height:1.5px;background:#E50914;display:block'></span>"
410
- "What ATLAS Knows About You</div>",
411
- unsafe_allow_html=True
412
- )
413
- caps = [
414
- ("πŸ‹οΈ", "Your Workout Plan", str(len(sdays)) + "-day personalised plan"),
415
- ("πŸ₯—", "Your Diet", diet_lbl + " Β· " + data.get("goal","Fitness")),
416
- ("πŸ“Š", "Your Body Stats", str(data.get("weight","?")) + "kg Β· " + str(data.get("height","?")) + "cm Β· " + data.get("level","Beginner")),
417
- ("βš™οΈ", "Your Equipment", ", ".join(data.get("equipment",[])) if data.get("equipment") else "Bodyweight"),
418
- ]
419
  cc = st.columns(4)
420
  for i,(icon,title,sub) in enumerate(caps):
421
  with cc[i]:
422
  st.markdown(
423
- f"<div style='background:rgba(10,8,20,0.75);border:1px solid rgba(229,9,20,0.18);"
424
- f"border-radius:14px;padding:14px 16px;text-align:center'>"
425
- f"<div style='font-size:1.6rem;margin-bottom:6px'>{icon}</div>"
426
- f"<div style='font-size:0.72rem;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;"
427
- f"color:rgba(229,9,20,0.75);margin-bottom:4px'>{title}</div>"
428
- f"<div style='font-size:0.78rem;color:rgba(255,255,255,0.50);line-height:1.4'>{sub}</div>"
 
429
  f"</div>",
430
- unsafe_allow_html=True
431
- )
432
-
433
- st.markdown("<div style='height:14px'></div>", unsafe_allow_html=True)
434
- st.markdown(
435
- "<div style='font-size:0.70rem;font-weight:700;letter-spacing:3px;text-transform:uppercase;"
436
- "color:rgba(229,9,20,0.65);margin-bottom:12px;display:flex;align-items:center;gap:8px'>"
437
- "<span style='width:14px;height:1.5px;background:#E50914;display:block'></span>"
438
- "Topics ATLAS Can Help With</div>",
439
- unsafe_allow_html=True
440
- )
441
- topics = [
442
- ("πŸ’ͺ","Workout Form & Technique","Exercise breakdowns, form cues, safer alternatives"),
443
- ("πŸ₯¦","Nutrition & Diet","Meal timing, macros, supplements, foods to eat"),
444
- ("😴","Recovery & Sleep","Rest days, sleep quality, muscle soreness relief"),
445
- ("πŸ”₯","Fat Loss & Muscle Gain","Cutting, bulking, body recomposition strategies"),
446
- ("🧠","Motivation & Mindset","Staying consistent, beating plateaus, mental game"),
447
- ("πŸ“ˆ","Progress & Goals","Tracking progress, adjusting your plan, setting targets"),
448
- ]
449
- tc = st.columns(3)
450
- for i,(icon,title,sub) in enumerate(topics):
451
- with tc[i % 3]:
452
- st.markdown(
453
- f"<div style='background:rgba(10,8,20,0.65);border:1px solid rgba(255,255,255,0.07);"
454
- f"border-radius:12px;padding:12px 14px;margin-bottom:10px;"
455
- f"display:flex;align-items:flex-start;gap:10px'>"
456
- f"<div style='font-size:1.3rem;flex-shrink:0'>{icon}</div>"
457
- f"<div>"
458
- f"<div style='font-size:0.82rem;font-weight:700;color:#fff;margin-bottom:3px'>{title}</div>"
459
- f"<div style='font-size:0.73rem;color:rgba(255,255,255,0.42);line-height:1.4'>{sub}</div>"
460
- f"</div></div>",
461
- unsafe_allow_html=True
462
- )
 
16
 
17
  st.markdown("""
18
  <style>
19
+ @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Syne:wght@600;700;800&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600&display=swap');
20
 
 
21
  [data-testid="stAppViewContainer"]::before{
22
  content:'';position:fixed;inset:0;z-index:0;
23
+ background:url('https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?fm=jpg&w=1920&q=80&fit=crop') center/cover no-repeat;
24
+ filter:blur(12px) brightness(0.15) saturate(0.35);transform:scale(1.10);}
25
  [data-testid="stAppViewContainer"]{
26
+ background:radial-gradient(ellipse at 20% 50%,rgba(229,9,20,0.08) 0%,transparent 60%),
27
+ radial-gradient(ellipse at 80% 20%,rgba(99,60,220,0.06) 0%,transparent 50%),
28
+ linear-gradient(160deg,rgba(2,2,8,0.97) 0%,rgba(5,3,12,0.95) 100%)!important;
29
  position:relative;}
30
  [data-testid="stAppViewContainer"]>section>div.block-container{
31
+ position:relative;z-index:2;max-width:940px!important;
32
+ margin:0 auto!important;padding:0 20px 120px!important;}
33
 
34
  #MainMenu,footer,header,[data-testid="stToolbar"],[data-testid="stDecoration"],
35
  [data-testid="stSidebarNav"],[data-testid="collapsedControl"],
36
  section[data-testid="stSidebar"],button[kind="header"]{display:none!important;}
37
+ html,body,.stApp{background:#020208!important;color:#fff!important;font-family:'DM Sans',sans-serif!important;}
38
 
 
39
  div[data-testid="stHorizontalBlock"]:first-of-type div[data-testid="stButton"]>button{
40
+ background:rgba(8,6,18,0.80)!important;border:1px solid rgba(229,9,20,0.28)!important;
41
+ color:rgba(255,255,255,0.70)!important;border-radius:8px!important;
42
+ font-size:0.80rem!important;font-weight:600!important;height:30px!important;
43
+ min-height:30px!important;white-space:nowrap!important;transition:all 0.16s!important;}
44
  div[data-testid="stHorizontalBlock"]:first-of-type div[data-testid="stButton"]>button:hover{
45
+ background:rgba(229,9,20,0.18)!important;border-color:rgba(229,9,20,0.60)!important;color:#fff!important;}
46
  div[data-testid="stHorizontalBlock"]:first-of-type div[data-testid="stButton"]:last-child>button{
47
+ background:rgba(229,9,20,0.22)!important;border-color:rgba(229,9,20,0.50)!important;}
48
 
 
49
  .stButton>button{
50
+ background:linear-gradient(135deg,#E50914 0%,#b0000a 100%)!important;
51
  border:none!important;color:#fff!important;border-radius:12px!important;
52
  font-family:'DM Sans',sans-serif!important;font-size:1rem!important;font-weight:700!important;
53
+ box-shadow:0 4px 20px rgba(229,9,20,0.48),0 0 0 1px rgba(229,9,20,0.18)!important;
54
+ transition:all 0.22s cubic-bezier(0.34,1.56,0.64,1)!important;}
55
+ .stButton>button:hover{
56
+ transform:translateY(-3px) scale(1.03)!important;
57
+ box-shadow:0 10px 32px rgba(229,9,20,0.70),0 0 50px rgba(229,9,20,0.22)!important;}
58
 
 
59
  [data-testid="stForm"]{background:transparent!important;border:none!important;}
60
  [data-testid="stFormSubmitButton"] button,.stFormSubmitButton>button{
61
+ background:linear-gradient(135deg,#E50914,#8b0000)!important;border:none!important;
62
+ color:#fff!important;border-radius:14px!important;font-weight:800!important;
63
+ font-size:1rem!important;letter-spacing:1px!important;height:56px!important;
64
+ box-shadow:0 6px 24px rgba(229,9,20,0.55),0 0 50px rgba(229,9,20,0.12)!important;}
65
+
66
+ input,textarea,.stTextInput>div>div>input,
67
+ [data-baseweb="input"] input,[data-testid="stTextInput"] input{
68
+ background:#0a0814!important;background-color:#0a0814!important;
69
+ border:1.5px solid rgba(229,9,20,0.28)!important;color:#fff!important;
70
+ border-radius:14px!important;font-family:'DM Sans',sans-serif!important;font-size:1rem!important;
71
+ box-shadow:inset 0 0 0 9999px #0a0814!important;
72
+ -webkit-box-shadow:inset 0 0 0 9999px #0a0814!important;
73
+ caret-color:#E50914!important;transition:border-color 0.25s!important;}
 
 
 
 
74
  input:focus,[data-baseweb="input"] input:focus{
75
+ border-color:rgba(229,9,20,0.72)!important;
76
+ box-shadow:inset 0 0 0 9999px #0a0814,0 0 0 3px rgba(229,9,20,0.14)!important;
77
+ -webkit-box-shadow:inset 0 0 0 9999px #0a0814!important;outline:none!important;}
78
+ [data-baseweb="input"]{background:#0a0814!important;background-color:#0a0814!important;
79
+ border:1.5px solid rgba(229,9,20,0.28)!important;border-radius:14px!important;}
 
80
  input:-webkit-autofill,input:-webkit-autofill:focus{
81
+ -webkit-box-shadow:0 0 0 9999px #0a0814 inset!important;-webkit-text-fill-color:#fff!important;}
 
82
 
 
83
  .quick-btn .stButton>button{
84
+ background:rgba(255,255,255,0.03)!important;border:1px solid rgba(255,255,255,0.09)!important;
85
+ color:rgba(255,255,255,0.62)!important;font-size:0.80rem!important;font-weight:500!important;
86
+ text-transform:none!important;border-radius:22px!important;padding:6px 14px!important;
87
+ height:auto!important;min-height:auto!important;box-shadow:none!important;
88
+ letter-spacing:0!important;font-style:italic!important;animation:none!important;
89
+ transition:all 0.18s ease!important;}
 
 
 
90
  .quick-btn .stButton>button:hover{
91
+ background:rgba(229,9,20,0.12)!important;border-color:rgba(229,9,20,0.42)!important;
92
+ color:#fff!important;transform:translateY(-2px)!important;
93
+ box-shadow:0 4px 14px rgba(229,9,20,0.18)!important;}
94
 
 
95
  html,body,.stApp,.stMarkdown,p,div,span,label{text-shadow:0 1px 4px rgba(0,0,0,0.95)!important;}
96
  [data-testid="stWidgetLabel"],[data-testid="stWidgetLabel"] p{color:#fff!important;font-weight:600!important;}
97
  .stMarkdown p,.stMarkdown li{color:#fff!important;line-height:1.75!important;}
98
 
99
+ @keyframes fadeSlideUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}
100
+ @keyframes fadeSlideRight{from{opacity:0;transform:translateX(-14px)}to{opacity:1;transform:translateX(0)}}
101
+ @keyframes fadeSlideLeft{from{opacity:0;transform:translateX(14px)}to{opacity:1;transform:translateX(0)}}
102
+ @keyframes pulseRing{0%{transform:scale(1);opacity:0.70}100%{transform:scale(1.80);opacity:0}}
103
+ @keyframes onlineBlink{0%,100%{opacity:1;box-shadow:0 0 8px #22c55e}50%{opacity:0.35;box-shadow:0 0 3px #22c55e}}
104
+ @keyframes typing{0%,100%{transform:translateY(0);opacity:0.90}50%{transform:translateY(-5px);opacity:0.40}}
105
+ @keyframes borderPulse{0%,100%{box-shadow:0 0 0 1px rgba(229,9,20,0.18),0 8px 32px rgba(0,0,0,0.60)}
106
+ 50%{box-shadow:0 0 0 1px rgba(229,9,20,0.42),0 8px 40px rgba(229,9,20,0.10)}}
107
+ @keyframes scanLine{0%{top:-2px;opacity:0}10%{opacity:1}90%{opacity:1}100%{top:102%;opacity:0}}
108
  </style>
109
  """, unsafe_allow_html=True)
110
 
111
  apply_bg(
112
+ "https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?fm=jpg&w=1920&q=80&fit=crop",
113
+ overlay="rgba(2,2,10,0.85)"
114
  )
115
 
116
  # ── NAV ───────────────────────────────────────────────────────────────────────
117
+ st.markdown("<div style='padding:6px 0;margin-bottom:12px'>", unsafe_allow_html=True)
118
  _n = st.columns([1.6,1,1,1,1,1,1,1.2])
119
  with _n[0]:
120
  st.markdown(
121
  "<div style='font-family:Bebas Neue,sans-serif;font-size:1.4rem;letter-spacing:5px;"
122
+ "color:#E50914;text-shadow:0 0 24px rgba(229,9,20,0.60);line-height:1;padding-top:4px'>"
123
  "&#9889; FITPLAN PRO</div>", unsafe_allow_html=True)
124
  nav_pages = [("Home","pages/2_Dashboard.py","ac_db"),("Workout","pages/3_Workout_Plan.py","ac_wp"),
125
  ("Diet","pages/4_Diet_Plan.py","ac_dp"),("AI Coach","pages/5_ai_coach.py","ac_ai"),
 
140
  st.switch_page("app.py")
141
  st.markdown("</div>", unsafe_allow_html=True)
142
 
143
+ # ── CONTEXT ───────────────────────────────────────────────────────────────────
144
+ sdays = st.session_state.get("structured_days", [])
145
+ diet_lbl = ("Vegetarian" if st.session_state.get("dietary_type")=="veg"
146
+ else ("Non-Veg" if st.session_state.get("dietary_type")=="nonveg" else "Flexible"))
147
+ plan_ctx = (str(len(sdays)) + "-day fitness plan. Diet: " + diet_lbl + ". ") if sdays else ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  system_prompt = (
150
+ "You are ATLAS, a professional AI fitness coach inside FitPlan Pro. "
151
+ "Warm, motivating, direct. Speak like a real coach. "
152
+ "User: " + uname + ", Age=" + str(data.get("age","?")) +
 
153
  ", Weight=" + str(data.get("weight","?")) + "kg"
154
  ", Height=" + str(data.get("height","?")) + "cm"
155
  ", Goal=" + data.get("goal","Fitness") +
156
  ", Level=" + data.get("level","Beginner") +
157
+ ", Equipment=" + (", ".join(data.get("equipment",[])) or "Bodyweight") + ". " + plan_ctx +
158
+ "RULES: Only answer fitness, gym, nutrition, body, health, lifestyle, motivation, sleep, recovery. "
159
+ "Redirect off-topic warmly. NO JSON/code blocks. Max 130 words. "
160
+ "Dash (-) for lists, max 4 items. Don't start with 'Assistant:' or 'ATLAS:'. "
161
+ "Reference earlier conversation. End with short motivational line when fitting."
162
+ )
163
+
164
+ # ── HERO ──────────────────────────────────────────────────────────────────────
165
+ st.markdown(
166
+ "<div style='position:relative;overflow:hidden;border-radius:22px;margin-bottom:16px;"
167
+ "background:linear-gradient(135deg,rgba(18,6,6,0.94) 0%,rgba(8,4,20,0.90) 60%,rgba(18,6,6,0.94) 100%);"
168
+ "border:1px solid rgba(229,9,20,0.22);animation:borderPulse 4s ease-in-out infinite;"
169
+ "padding:20px 26px'>"
170
+ # Scan line
171
+ "<div style='position:absolute;left:0;right:0;height:1px;"
172
+ "background:linear-gradient(90deg,transparent,rgba(229,9,20,0.35),transparent);"
173
+ "animation:scanLine 4s linear infinite;pointer-events:none;z-index:1'></div>"
174
+ # Top line
175
+ "<div style='position:absolute;top:0;left:0;right:0;height:2px;"
176
+ "background:linear-gradient(90deg,transparent,#E50914 35%,rgba(229,9,20,0.35) 65%,transparent)'></div>"
177
+ # Glow orb
178
+ "<div style='position:absolute;top:-50px;right:-50px;width:200px;height:200px;border-radius:50%;"
179
+ "background:radial-gradient(circle,rgba(229,9,20,0.16) 0%,transparent 70%);pointer-events:none'></div>"
180
+ "<div style='display:flex;align-items:center;gap:18px;position:relative;z-index:2'>"
181
+ # Avatar
182
+ "<div style='position:relative;flex-shrink:0;width:60px;height:60px'>"
183
+ "<div style='width:60px;height:60px;border-radius:50%;"
184
+ "background:linear-gradient(135deg,#E50914 0%,#5c000a 100%);"
185
+ "display:flex;align-items:center;justify-content:center;font-size:1.7rem;"
186
+ "box-shadow:0 0 0 3px rgba(229,9,20,0.28),0 0 28px rgba(229,9,20,0.42)'>πŸ€–</div>"
187
+ "<div style='position:absolute;inset:-6px;border-radius:50%;"
188
+ "border:2px solid rgba(229,9,20,0.45);animation:pulseRing 2s ease-out infinite'></div>"
189
+ "<div style='position:absolute;inset:-12px;border-radius:50%;"
190
+ "border:1px solid rgba(229,9,20,0.20);animation:pulseRing 2s ease-out infinite 0.6s'></div>"
191
+ "</div>"
192
+ # Title
193
+ "<div style='flex:1'>"
194
+ "<div style='font-family:Syne,sans-serif;font-size:1.55rem;font-weight:800;"
195
+ "background:linear-gradient(90deg,#ffffff 55%,rgba(229,9,20,0.85));background-size:200% auto;"
196
+ "-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;"
197
+ "line-height:1.1;margin-bottom:6px'>ATLAS &mdash; Your AI Fitness Coach</div>"
198
+ "<div style='display:flex;flex-wrap:wrap;gap:6px;align-items:center'>"
199
+ + " ".join([
200
+ f"<span style='font-size:0.76rem;color:rgba(255,255,255,0.40)'>{t}</span>"
201
+ + (f"<span style='color:rgba(229,9,20,0.40);font-size:0.76rem'>&middot;</span>" if i<3 else "")
202
+ for i,t in enumerate([
203
+ f"Trained on your profile",
204
+ f"{len(sdays)}-day plan",
205
+ f"{diet_lbl} diet",
206
+ data.get("goal","Fitness")
207
+ ])
208
+ ]) +
209
+ "</div></div>"
210
+ # Online
211
+ "<div style='display:flex;align-items:center;gap:6px;background:rgba(34,197,94,0.09);"
212
+ "border:1px solid rgba(34,197,94,0.25);border-radius:20px;padding:5px 12px;flex-shrink:0'>"
213
+ "<div style='width:7px;height:7px;border-radius:50%;background:#22c55e;"
214
+ "animation:onlineBlink 2s ease-in-out infinite'></div>"
215
+ "<span style='font-size:0.70rem;font-weight:700;letter-spacing:2px;"
216
+ "color:rgba(34,197,94,0.88);text-transform:uppercase'>ONLINE</span>"
217
+ "</div></div></div>",
218
+ unsafe_allow_html=True
219
  )
220
 
221
  # ── CHAT HISTORY ──────────────────────────────────────────────────────────────
 
228
  st.session_state.chat_messages = []
229
 
230
  # ── QUICK QUESTIONS ───────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
231
  st.markdown(
232
+ "<div style='font-size:0.66rem;font-weight:700;letter-spacing:3px;text-transform:uppercase;"
233
+ "color:rgba(229,9,20,0.58);margin-bottom:7px;display:flex;align-items:center;gap:8px'>"
234
+ "<span style='width:12px;height:1px;background:#E50914;display:block'></span>Quick Ask</div>",
235
+ unsafe_allow_html=True)
236
+ quick_qs = [("πŸ’ͺ","Best post-workout meal?"),("πŸ”₯","How to lose fat faster?"),
237
+ ("😴","How much sleep do I need?"),("πŸ₯©","Daily protein target?"),
238
+ ("⚑","How to boost energy?"),("🧘","Recovery tips for soreness?")]
239
  qc = st.columns(3)
240
+ for i,(emoji,q) in enumerate(quick_qs):
241
+ with qc[i%3]:
242
  st.markdown("<div class='quick-btn'>", unsafe_allow_html=True)
243
+ if st.button(f"{emoji} {q}", key=f"qq_{i}", use_container_width=True):
244
+ st.session_state.chat_messages.append({"role":"user","content":q})
 
245
  st.rerun()
246
  st.markdown("</div>", unsafe_allow_html=True)
247
 
248
  st.markdown("<div style='height:8px'></div>", unsafe_allow_html=True)
249
 
250
+ # ── CHAT MESSAGES ─────────────────────────────────────────────────────────────
 
 
251
  if not st.session_state.chat_messages:
252
  st.markdown(
253
+ f"<div style='text-align:center;padding:52px 20px;animation:fadeSlideUp 0.6s ease both'>"
254
+ f"<div style='font-size:3.8rem;margin-bottom:14px;"
255
+ f"filter:drop-shadow(0 0 24px rgba(229,9,20,0.65))'>πŸ€–</div>"
256
+ f"<div style='font-family:Syne,sans-serif;font-size:1.5rem;font-weight:800;"
257
+ f"background:linear-gradient(135deg,#fff 40%,#E50914);"
258
+ f"-webkit-background-clip:text;-webkit-text-fill-color:transparent;"
259
+ f"background-clip:text;letter-spacing:0.5px;margin-bottom:10px'>"
260
+ f"Hey {uname}, I'm ATLAS</div>"
261
+ f"<div style='font-size:0.92rem;color:rgba(255,255,255,0.42);max-width:420px;"
262
+ f"margin:0 auto;line-height:1.75'>"
263
+ f"Your personal AI fitness coach. Ask me anything about workouts, "
264
+ f"nutrition, recovery β€” I know your full {len(sdays)}-day plan.</div>"
265
+ f"<div style='margin-top:22px;display:flex;justify-content:center;gap:10px;flex-wrap:wrap'>"
266
+ f"<div style='background:rgba(229,9,20,0.09);border:1px solid rgba(229,9,20,0.22);"
267
+ f"border-radius:20px;padding:5px 14px;font-size:0.76rem;color:rgba(255,255,255,0.50)'>πŸ’¬ Ongoing conversations</div>"
268
+ f"<div style='background:rgba(229,9,20,0.09);border:1px solid rgba(229,9,20,0.22);"
269
+ f"border-radius:20px;padding:5px 14px;font-size:0.76rem;color:rgba(255,255,255,0.50)'>🧠 Knows your full plan</div>"
270
+ f"<div style='background:rgba(229,9,20,0.09);border:1px solid rgba(229,9,20,0.22);"
271
+ f"border-radius:20px;padding:5px 14px;font-size:0.76rem;color:rgba(255,255,255,0.50)'>⚑ Instant answers</div>"
272
+ f"</div></div>",
273
+ unsafe_allow_html=True)
274
  else:
275
  for mi, msg in enumerate(st.session_state.chat_messages):
276
+ delay = f"{min(mi*0.04,0.30):.2f}s"
277
  if msg["role"] == "user":
278
  st.markdown(
279
+ f"<div style='display:flex;justify-content:flex-end;margin:8px 0;"
280
+ f"animation:fadeSlideLeft 0.32s ease {delay} both'>"
281
+ f"<div style='max-width:76%'>"
282
+ f"<div style='text-align:right;font-size:0.60rem;font-weight:700;letter-spacing:2px;"
283
+ f"text-transform:uppercase;color:rgba(229,9,20,0.60);margin-bottom:4px;padding-right:6px'>YOU</div>"
284
+ f"<div style='background:linear-gradient(135deg,rgba(229,9,20,0.22),rgba(140,0,8,0.16));"
285
+ f"border:1.5px solid rgba(229,9,20,0.35);border-radius:18px 18px 4px 18px;"
286
+ f"padding:13px 17px;box-shadow:0 4px 20px rgba(229,9,20,0.16)'>"
287
+ f"<div style='font-size:0.95rem;color:#fff;line-height:1.65'>{msg['content']}</div>"
288
+ f"</div></div>"
289
+ f"<div style='width:34px;height:34px;border-radius:50%;"
290
+ f"background:rgba(229,9,20,0.14);border:1.5px solid rgba(229,9,20,0.32);"
291
+ f"display:flex;align-items:center;justify-content:center;"
292
+ f"margin-left:9px;flex-shrink:0;align-self:flex-end;font-size:1rem'>πŸ‘€</div>"
293
+ f"</div>",
294
+ unsafe_allow_html=True)
295
  else:
 
296
  _lines = []
297
  for _ln in msg["content"].splitlines():
298
  _s = _ln.strip()
299
  if _s.startswith("- ") or _s.startswith("β€’ "):
300
  _lines.append(
301
+ f"<div style='display:flex;gap:9px;margin:5px 0;align-items:flex-start'>"
302
+ f"<span style='color:#E50914;font-size:0.68rem;margin-top:4px;flex-shrink:0;"
303
+ f"text-shadow:0 0 8px rgba(229,9,20,0.55)'>β–Ά</span>"
304
+ f"<span style='font-size:0.95rem;color:rgba(255,255,255,0.88);line-height:1.62'>{_s[2:]}</span></div>")
305
  elif _s:
306
  _lines.append(
307
+ f"<div style='font-size:0.95rem;color:rgba(255,255,255,0.88);"
308
+ f"line-height:1.68;margin-bottom:3px'>{_s}</div>")
309
  else:
310
+ _lines.append("<div style='height:4px'></div>")
311
 
312
  st.markdown(
313
+ f"<div style='display:flex;margin:8px 0;"
314
+ f"animation:fadeSlideRight 0.32s ease {delay} both'>"
315
+ f"<div style='width:34px;height:34px;border-radius:50%;"
316
+ f"background:linear-gradient(135deg,#E50914,#7c000a);"
317
+ f"display:flex;align-items:center;justify-content:center;font-size:1rem;"
318
+ f"flex-shrink:0;align-self:flex-end;margin-right:9px;"
319
+ f"box-shadow:0 0 16px rgba(229,9,20,0.40)'>πŸ€–</div>"
320
+ f"<div style='max-width:80%'>"
321
+ f"<div style='font-size:0.60rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;"
322
+ f"color:rgba(229,9,20,0.60);margin-bottom:4px;padding-left:4px'>ATLAS</div>"
323
+ f"<div style='background:rgba(12,10,24,0.90);"
324
+ f"border:1px solid rgba(255,255,255,0.08);border-radius:18px 18px 18px 4px;"
325
+ f"padding:14px 18px;backdrop-filter:blur(24px);"
326
+ f"box-shadow:0 4px 24px rgba(0,0,0,0.45),inset 0 1px 0 rgba(255,255,255,0.04)'>"
327
  + "".join(_lines) +
328
+ f"</div></div></div>",
329
+ unsafe_allow_html=True)
 
330
 
331
+ # ── TYPING DOTS ───────────────────────────────────────────────────────────────
332
+ if st.session_state.chat_messages and st.session_state.chat_messages[-1]["role"] == "user":
333
+ st.markdown(
334
+ "<div style='display:flex;align-items:center;gap:10px;margin:8px 0;padding-left:43px'>"
335
+ "<div style='display:flex;gap:5px;align-items:center;"
336
+ "background:rgba(12,10,24,0.85);border:1px solid rgba(255,255,255,0.07);"
337
+ "border-radius:20px;padding:9px 16px'>"
338
+ "<div style='width:7px;height:7px;border-radius:50%;background:#E50914;"
339
+ "animation:typing 1.1s ease-in-out infinite 0s'></div>"
340
+ "<div style='width:7px;height:7px;border-radius:50%;background:#E50914;"
341
+ "animation:typing 1.1s ease-in-out infinite 0.22s'></div>"
342
+ "<div style='width:7px;height:7px;border-radius:50%;background:#E50914;"
343
+ "animation:typing 1.1s ease-in-out infinite 0.44s'></div>"
344
+ "<span style='font-size:0.76rem;color:rgba(255,255,255,0.38);margin-left:8px;"
345
+ "font-style:italic'>ATLAS is thinking...</span>"
346
+ "</div></div>",
347
+ unsafe_allow_html=True)
348
 
349
  # ── PROCESS AI REPLY ──────────────────────────────────────────────────────────
350
  if st.session_state.chat_messages and st.session_state.chat_messages[-1]["role"] == "user":
351
+ try:
352
+ from model_api import query_model
353
+ history = st.session_state.chat_messages[-10:]
354
+ conv = "".join(
355
+ ("User" if m["role"]=="user" else "Coach") + ": " + m["content"] + "\n"
356
+ for m in history[:-1])
357
+ full_prompt = (system_prompt + "\n\nConversation:\n" + conv +
358
+ "\nUser: " + history[-1]["content"] + "\nCoach:")
359
+ reply = query_model(full_prompt, max_tokens=320).strip()
360
+ for _pfx in ["Assistant:","ATLAS:","AI Coach:","Coach:"]:
361
+ if reply.startswith(_pfx): reply = reply[len(_pfx):].strip()
362
+ import re as _re
363
+ _clean = [ln for ln in reply.splitlines()
364
+ if not (ln.strip().startswith("{") or ln.strip().startswith("[")
365
+ or (ln.strip().startswith('"') and ln.strip().endswith('",')))]
366
+ reply = _re.sub(r"\{[^}]*\}","","\n".join(_clean))
367
+ reply = _re.sub(r"\[[^\]]*\]","",reply)
368
+ reply = _re.sub(r"\n{3,}","\n\n",reply).strip()
369
+ if not reply: reply = "Great question! Try asking me again."
370
+ st.session_state.chat_messages.append({"role":"assistant","content":reply})
371
  try:
372
+ from utils.db import save_chat_history
373
+ save_chat_history(uname, st.session_state.chat_messages[-30:])
374
+ except Exception: pass
375
+ st.rerun()
376
+ except Exception as e:
377
+ st.error("ATLAS error: " + str(e))
378
+
379
+ st.markdown("<div style='height:10px'></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
 
381
  # ── INPUT BAR ─────────────────────────────────────────────────────────────────
382
  st.markdown(
383
+ "<div style='background:linear-gradient(135deg,rgba(10,8,22,0.94),rgba(14,6,8,0.90));"
384
+ "border:1.5px solid rgba(229,9,20,0.20);border-radius:18px;"
385
+ "padding:13px 16px;backdrop-filter:blur(28px);"
386
+ "box-shadow:0 8px 36px rgba(0,0,0,0.55),inset 0 1px 0 rgba(255,255,255,0.03)'>",
387
+ unsafe_allow_html=True)
 
 
388
  with st.form("chat_form", clear_on_submit=True):
389
+ inp_col, btn_col = st.columns([5,1])
390
  with inp_col:
391
  user_input = st.text_input(
392
+ "", placeholder="Ask ATLAS about workouts, nutrition, recovery, goals...",
393
+ key="chat_input_field", label_visibility="collapsed")
 
394
  with btn_col:
395
  send = st.form_submit_button("Send ➀", use_container_width=True)
396
  if send and user_input.strip():
 
398
  st.rerun()
399
  st.markdown("</div>", unsafe_allow_html=True)
400
 
401
+ # ── BOTTOM BAR ────────────────────────────────────────────────────────────────
402
+ st.markdown("<div style='height:8px'></div>", unsafe_allow_html=True)
403
+ ba1, ba2, ba3 = st.columns([2,3,5])
 
404
  with ba1:
405
  if st.session_state.chat_messages:
406
  if st.button("πŸ—‘οΈ Clear Chat", key="clear_chat", use_container_width=True):
407
  st.session_state.chat_messages = []
408
  st.rerun()
409
  with ba2:
410
+ mc = len(st.session_state.chat_messages)
411
+ if mc > 0:
412
+ ai_c = sum(1 for m in st.session_state.chat_messages if m["role"]=="assistant")
413
  st.markdown(
414
+ f"<div style='background:rgba(229,9,20,0.06);border:1px solid rgba(229,9,20,0.15);"
415
+ f"border-radius:10px;padding:8px 14px;text-align:center;font-size:0.76rem;"
416
+ f"color:rgba(255,255,255,0.40)'>πŸ’¬ {mc} messages &nbsp;Β·&nbsp; πŸ€– {ai_c} replies</div>",
417
+ unsafe_allow_html=True)
 
 
418
 
419
+ # ── WHAT ATLAS KNOWS (empty state only) ───────────────────────────────────────
420
  if not st.session_state.chat_messages:
421
+ st.markdown("<div style='height:18px'></div>", unsafe_allow_html=True)
422
+ caps = [("πŸ‹οΈ","Workout Plan",f"{len(sdays)}-day personalised"),
423
+ ("πŸ₯—","Your Diet",f"{diet_lbl} Β· {data.get('goal','Fitness')}"),
424
+ ("πŸ“Š","Body Stats",f"{data.get('weight','?')}kg Β· {data.get('height','?')}cm"),
425
+ ("βš™οΈ","Equipment",", ".join(data.get("equipment",[])) if data.get("equipment") else "Bodyweight")]
 
 
 
 
 
 
 
 
 
426
  cc = st.columns(4)
427
  for i,(icon,title,sub) in enumerate(caps):
428
  with cc[i]:
429
  st.markdown(
430
+ f"<div style='background:rgba(10,8,20,0.68);border:1px solid rgba(229,9,20,0.12);"
431
+ f"border-radius:14px;padding:14px 14px;text-align:center;"
432
+ f"animation:fadeSlideUp 0.5s ease {0.08*i:.2f}s both'>"
433
+ f"<div style='font-size:1.5rem;margin-bottom:6px'>{icon}</div>"
434
+ f"<div style='font-size:0.66rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;"
435
+ f"color:rgba(229,9,20,0.60);margin-bottom:4px'>{title}</div>"
436
+ f"<div style='font-size:0.74rem;color:rgba(255,255,255,0.38);line-height:1.4'>{sub}</div>"
437
  f"</div>",
438
+ unsafe_allow_html=True)