jostlebot Claude Opus 4.5 commited on
Commit
0eda47f
·
1 Parent(s): 06d9faf

Complete 3-panel layout rebuild with tool buttons

Browse files

- LEFT: Tool descriptions with explanations
- CENTER: Conversation + tool button row (TEND, SEND, NVC, FEEL, NEED, LISTEN, REPAIR, SOMA, LOVE)
- RIGHT: Tool workspace panel (opens when tools are used)
- Added LOVE tool (breathing/slow down overlay)
- Cleaner, more focused UI
- Partner prompts still realistic (NOT therapist)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. static/index.html +679 -1061
static/index.html CHANGED
@@ -15,14 +15,13 @@
15
  --accent-glow: rgba(99, 102, 241, 0.3);
16
  --tend-color: #10b981;
17
  --tend-glow: rgba(16, 185, 129, 0.3);
 
 
18
  --text-primary: #f1f5f9;
19
  --text-secondary: #94a3b8;
20
  --text-muted: #64748b;
21
  --border: #334155;
22
- --border-light: #475569;
23
- --success: #22c55e;
24
  --warning: #f59e0b;
25
- --danger: #ef4444;
26
  }
27
 
28
  * { margin: 0; padding: 0; box-sizing: border-box; }
@@ -34,159 +33,15 @@
34
  min-height: 100vh;
35
  }
36
 
 
37
  .app-container {
38
  display: flex;
39
  height: 100vh;
40
  }
41
 
42
- /* THREE PANEL LAYOUT */
43
- .main-content {
44
- flex: 1;
45
- display: flex;
46
- flex-direction: column;
47
- min-width: 0;
48
- overflow: hidden;
49
- }
50
-
51
- .tool-panel {
52
- width: 380px;
53
- background: var(--bg-card);
54
- border-left: 1px solid var(--border);
55
- display: flex;
56
- flex-direction: column;
57
- transition: width 0.3s ease;
58
- }
59
-
60
- .tool-panel.collapsed {
61
- width: 0;
62
- overflow: hidden;
63
- border-left: none;
64
- }
65
-
66
- .tool-panel-header {
67
- padding: 16px 20px;
68
- border-bottom: 1px solid var(--border);
69
- display: flex;
70
- justify-content: space-between;
71
- align-items: center;
72
- background: rgba(99, 102, 241, 0.1);
73
- }
74
-
75
- .tool-panel-header h3 {
76
- font-size: 1rem;
77
- color: var(--accent-light);
78
- display: flex;
79
- align-items: center;
80
- gap: 8px;
81
- }
82
-
83
- .tool-panel-header .close-btn {
84
- background: transparent;
85
- border: none;
86
- color: var(--text-muted);
87
- font-size: 1.2rem;
88
- cursor: pointer;
89
- padding: 4px 8px;
90
- border-radius: 4px;
91
- }
92
-
93
- .tool-panel-header .close-btn:hover {
94
- background: var(--bg-input);
95
- color: var(--text-primary);
96
- }
97
-
98
- .tool-panel-content {
99
- flex: 1;
100
- overflow-y: auto;
101
- padding: 16px 20px;
102
- }
103
-
104
- .tool-message {
105
- margin-bottom: 16px;
106
- padding: 14px 16px;
107
- background: var(--bg-input);
108
- border-radius: 10px;
109
- border-left: 3px solid var(--accent);
110
- }
111
-
112
- .tool-message.user-input {
113
- border-left-color: var(--text-muted);
114
- background: rgba(255,255,255,0.03);
115
- }
116
-
117
- .tool-message .tool-label {
118
- font-size: 0.7rem;
119
- text-transform: uppercase;
120
- letter-spacing: 0.05em;
121
- color: var(--accent-light);
122
- margin-bottom: 8px;
123
- }
124
-
125
- .tool-message.user-input .tool-label {
126
- color: var(--text-muted);
127
- }
128
-
129
- .tool-message p {
130
- line-height: 1.6;
131
- color: var(--text-primary);
132
- }
133
-
134
- .tool-input-area {
135
- padding: 16px 20px;
136
- border-top: 1px solid var(--border);
137
- background: var(--bg-dark);
138
- }
139
-
140
- .tool-input-area textarea {
141
- width: 100%;
142
- padding: 12px 14px;
143
- background: var(--bg-input);
144
- border: 1px solid var(--border);
145
- border-radius: 8px;
146
- color: var(--text-primary);
147
- font-size: 0.9rem;
148
- font-family: inherit;
149
- resize: none;
150
- margin-bottom: 10px;
151
- }
152
-
153
- .tool-input-area textarea:focus {
154
- outline: none;
155
- border-color: var(--accent);
156
- }
157
-
158
- .tool-input-area .tool-submit-btn {
159
- width: 100%;
160
- padding: 10px 16px;
161
- background: var(--accent);
162
- color: white;
163
- border: none;
164
- border-radius: 8px;
165
- font-size: 0.9rem;
166
- cursor: pointer;
167
- }
168
-
169
- .tool-input-area .tool-submit-btn:hover {
170
- background: var(--accent-light);
171
- }
172
-
173
- @media (max-width: 1100px) {
174
- .tool-panel {
175
- position: fixed;
176
- right: 0;
177
- top: 0;
178
- height: 100vh;
179
- z-index: 100;
180
- box-shadow: -4px 0 20px rgba(0,0,0,0.3);
181
- }
182
- .tool-panel.collapsed {
183
- right: -380px;
184
- }
185
- }
186
-
187
- /* ===== TOOLKIT SIDEBAR ===== */
188
- .toolkit-sidebar {
189
- width: 340px;
190
  background: var(--bg-card);
191
  border-right: 1px solid var(--border);
192
  display: flex;
@@ -194,127 +49,31 @@
194
  overflow-y: auto;
195
  }
196
 
197
- .sidebar-header {
198
- padding: 24px 20px;
199
  border-bottom: 1px solid var(--border);
200
  background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99, 102, 241, 0.05) 100%);
201
  }
202
 
203
- .sidebar-header h1 {
204
- font-size: 1.6rem;
205
- font-weight: 700;
206
  background: linear-gradient(135deg, var(--text-primary) 0%, var(--accent-light) 100%);
207
  -webkit-background-clip: text;
208
  -webkit-text-fill-color: transparent;
209
  margin-bottom: 4px;
210
  }
211
 
212
- .sidebar-header p {
213
- font-size: 0.85rem;
214
- color: var(--text-secondary);
215
- }
216
-
217
- /* Settings */
218
- .settings-section {
219
- padding: 16px 20px;
220
- border-bottom: 1px solid var(--border);
221
- background: rgba(0,0,0,0.2);
222
- }
223
-
224
- .settings-section h3 {
225
- font-size: 0.7rem;
226
- text-transform: uppercase;
227
- letter-spacing: 0.08em;
228
- color: var(--text-muted);
229
- margin-bottom: 12px;
230
- }
231
-
232
- .setting-row {
233
- margin-bottom: 10px;
234
- }
235
-
236
- .setting-row label {
237
- display: block;
238
  font-size: 0.8rem;
239
  color: var(--text-secondary);
240
- margin-bottom: 4px;
241
- }
242
-
243
- .setting-row select {
244
- width: 100%;
245
- padding: 8px 12px;
246
- background: var(--bg-input);
247
- border: 1px solid var(--border);
248
- border-radius: 6px;
249
- color: var(--text-primary);
250
- font-size: 0.85rem;
251
- cursor: pointer;
252
  }
253
 
254
- .setting-row select:focus {
255
- outline: none;
256
- border-color: var(--accent);
257
- }
258
-
259
- /* PRIMARY TEND TOOL */
260
- .tend-section {
261
- padding: 16px 20px;
262
- border-bottom: 1px solid var(--border);
263
- background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, transparent 100%);
264
- }
265
-
266
- .tend-tool {
267
- background: var(--bg-input);
268
- border: 2px solid var(--tend-color);
269
- border-radius: 12px;
270
- padding: 16px;
271
- cursor: pointer;
272
- transition: all 0.2s ease;
273
- box-shadow: 0 0 20px var(--tend-glow);
274
- }
275
-
276
- .tend-tool:hover {
277
- transform: translateY(-2px);
278
- box-shadow: 0 4px 30px var(--tend-glow);
279
- }
280
-
281
- .tend-tool.active {
282
- background: rgba(16, 185, 129, 0.15);
283
- }
284
-
285
- .tend-tool h4 {
286
- font-size: 1.1rem;
287
- color: var(--tend-color);
288
- margin-bottom: 6px;
289
- display: flex;
290
- align-items: center;
291
- gap: 8px;
292
- }
293
-
294
- .tend-tool p {
295
- font-size: 0.85rem;
296
- color: var(--text-secondary);
297
- line-height: 1.5;
298
- }
299
-
300
- .primary-badge {
301
- font-size: 0.65rem;
302
- background: var(--tend-color);
303
- color: white;
304
- padding: 2px 8px;
305
- border-radius: 10px;
306
- text-transform: uppercase;
307
- letter-spacing: 0.05em;
308
- }
309
-
310
- /* TOOLS GRID */
311
- .tools-section {
312
  flex: 1;
313
- padding: 16px 20px;
314
- overflow-y: auto;
315
  }
316
 
317
- .tools-section h3 {
318
  font-size: 0.7rem;
319
  text-transform: uppercase;
320
  letter-spacing: 0.08em;
@@ -322,92 +81,91 @@
322
  margin-bottom: 12px;
323
  }
324
 
325
- .tools-grid {
326
- display: grid;
327
- grid-template-columns: 1fr 1fr;
328
- gap: 10px;
329
- }
330
-
331
- .tool-card {
332
- background: var(--bg-input);
333
- border: 1px solid var(--border);
334
- border-radius: 10px;
335
  padding: 12px;
 
 
 
 
336
  cursor: pointer;
337
- transition: all 0.2s ease;
338
  }
339
 
340
- .tool-card:hover {
341
- border-color: var(--accent);
342
  background: var(--bg-hover);
343
  }
344
 
345
- .tool-card.active {
346
- border-color: var(--accent);
347
  background: rgba(99, 102, 241, 0.15);
348
- box-shadow: 0 0 15px var(--accent-glow);
349
  }
350
 
351
- .tool-card h4 {
352
- font-size: 0.85rem;
353
  margin-bottom: 4px;
354
  display: flex;
355
  align-items: center;
356
- gap: 6px;
357
  }
358
 
359
- .tool-card p {
360
- font-size: 0.7rem;
361
  color: var(--text-muted);
362
- line-height: 1.3;
363
  }
364
 
365
- .tool-icon { font-size: 1rem; }
 
 
 
366
 
367
- /* Toggle Tools */
368
- .toggle-section {
369
- padding: 12px 20px;
370
- border-bottom: 1px solid var(--border);
 
 
 
 
 
 
371
  }
372
 
373
  .toggle-row {
374
  display: flex;
375
  align-items: center;
376
  justify-content: space-between;
377
- padding: 8px 0;
378
  }
379
 
380
  .toggle-row label {
381
- font-size: 0.85rem;
382
  color: var(--text-secondary);
383
  }
384
 
385
  .toggle-switch {
386
  position: relative;
387
- width: 44px;
388
- height: 24px;
389
  }
390
 
391
- .toggle-switch input {
392
- opacity: 0;
393
- width: 0;
394
- height: 0;
395
- }
396
 
397
  .toggle-slider {
398
  position: absolute;
399
  cursor: pointer;
400
  top: 0; left: 0; right: 0; bottom: 0;
401
  background: var(--border);
402
- border-radius: 24px;
403
  transition: 0.3s;
404
  }
405
 
406
  .toggle-slider:before {
407
  position: absolute;
408
  content: "";
409
- height: 18px;
410
- width: 18px;
411
  left: 3px;
412
  bottom: 3px;
413
  background: white;
@@ -415,110 +173,175 @@
415
  transition: 0.3s;
416
  }
417
 
418
- .toggle-switch input:checked + .toggle-slider {
419
- background: var(--accent);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
421
 
422
- .toggle-switch input:checked + .toggle-slider:before {
423
- transform: translateX(20px);
 
 
 
 
 
 
424
  }
425
 
426
- /* Footer */
427
- .sidebar-footer {
428
- padding: 16px 20px;
429
- border-top: 1px solid var(--border);
430
- background: rgba(0,0,0,0.2);
 
 
431
  }
432
 
433
- .safety-notice {
434
- background: rgba(245, 158, 11, 0.1);
435
- border: 1px solid var(--warning);
436
- border-radius: 8px;
437
- padding: 10px;
438
- font-size: 0.75rem;
439
- color: var(--warning);
440
- margin-bottom: 12px;
441
  }
442
 
443
- .sidebar-footer .attribution {
444
- font-size: 0.75rem;
445
- color: var(--text-muted);
 
 
 
446
  }
447
 
448
- .sidebar-footer a {
449
- color: var(--accent-light);
450
- text-decoration: none;
 
 
 
 
 
 
451
  }
452
 
453
- .content-header {
454
- padding: 16px 24px;
 
 
 
 
455
  border-bottom: 1px solid var(--border);
456
- display: flex;
457
  justify-content: space-between;
458
  align-items: center;
459
- background: var(--bg-card);
460
  }
461
 
 
 
462
  .partner-info {
463
  display: flex;
464
  align-items: center;
465
- gap: 12px;
466
  }
467
 
468
  .partner-avatar {
469
- width: 40px;
470
- height: 40px;
471
  background: var(--accent);
472
  border-radius: 50%;
473
  display: flex;
474
  align-items: center;
475
  justify-content: center;
476
  font-weight: 600;
 
477
  }
478
 
479
- .partner-details h3 {
480
- font-size: 1rem;
481
- margin-bottom: 2px;
482
- }
483
 
484
- .partner-details span {
485
- font-size: 0.8rem;
 
 
486
  color: var(--text-secondary);
 
 
 
487
  }
488
 
489
- .active-tool-badge {
490
- background: var(--accent);
491
- color: white;
492
- padding: 6px 14px;
493
- border-radius: 20px;
494
- font-size: 0.85rem;
495
- font-weight: 500;
496
- }
497
-
498
- .active-tool-badge.tend {
499
- background: var(--tend-color);
500
- }
501
 
502
- /* Conversation */
503
  .conversation-area {
504
  flex: 1;
505
- padding: 24px;
506
  overflow-y: auto;
507
  display: flex;
508
  flex-direction: column;
509
- gap: 16px;
510
  }
511
 
512
  .message {
513
- max-width: 75%;
514
- padding: 14px 18px;
515
- border-radius: 18px;
516
  line-height: 1.5;
517
  animation: fadeIn 0.3s ease;
518
  }
519
 
520
  @keyframes fadeIn {
521
- from { opacity: 0; transform: translateY(10px); }
522
  to { opacity: 1; transform: translateY(0); }
523
  }
524
 
@@ -541,7 +364,7 @@
541
  background: rgba(99, 102, 241, 0.1);
542
  border: 1px solid var(--accent);
543
  max-width: 90%;
544
- border-radius: 12px;
545
  }
546
 
547
  .message.ari.tend-result {
@@ -550,31 +373,28 @@
550
  }
551
 
552
  .message.ari .ari-label {
553
- font-size: 0.7rem;
554
  text-transform: uppercase;
555
  letter-spacing: 0.05em;
556
  color: var(--accent-light);
557
- margin-bottom: 8px;
558
  font-weight: 600;
559
  }
560
 
561
- .message.ari.tend-result .ari-label {
562
- color: var(--tend-color);
563
- }
564
 
565
- /* Loading */
566
  .loading {
567
  display: flex;
568
  align-items: center;
569
  justify-content: center;
570
  gap: 8px;
571
  color: var(--text-secondary);
572
- padding: 16px;
573
  }
574
 
575
  .spinner {
576
- width: 20px;
577
- height: 20px;
578
  border: 2px solid var(--border);
579
  border-top-color: var(--accent);
580
  border-radius: 50%;
@@ -583,402 +403,394 @@
583
 
584
  @keyframes spin { to { transform: rotate(360deg); } }
585
 
586
- /* Welcome */
587
- .welcome-message {
588
- text-align: center;
589
- padding: 60px 40px;
590
- max-width: 600px;
591
- margin: 0 auto;
 
 
 
592
  }
593
 
594
- .welcome-message h2 {
595
- font-size: 2rem;
596
- margin-bottom: 16px;
597
- background: linear-gradient(135deg, var(--text-primary) 0%, var(--accent-light) 100%);
598
- -webkit-background-clip: text;
599
- -webkit-text-fill-color: transparent;
 
 
 
 
 
 
 
600
  }
601
 
602
- .welcome-message p {
603
- color: var(--text-secondary);
604
- line-height: 1.7;
605
- margin-bottom: 24px;
606
  }
607
 
608
- .start-btn {
609
- padding: 14px 36px;
610
- background: linear-gradient(135deg, var(--accent) 0%, var(--accent-light) 100%);
611
  color: white;
612
- border: none;
613
- border-radius: 10px;
614
- font-size: 1rem;
615
- font-weight: 600;
616
- cursor: pointer;
617
- transition: transform 0.2s, box-shadow 0.2s;
618
  }
619
 
620
- .start-btn:hover {
621
- transform: translateY(-2px);
622
- box-shadow: 0 8px 30px var(--accent-glow);
623
  }
624
 
625
- /* Setup Panel - Top of Main Area */
626
- .setup-panel {
627
- background: var(--bg-card);
628
- border-bottom: 1px solid var(--border);
629
- padding: 20px 24px;
630
  }
631
 
632
- .setup-header h2 {
633
- font-size: 1.2rem;
634
- margin-bottom: 16px;
635
- color: var(--text-primary);
636
  }
637
 
638
- .setup-grid {
639
- display: grid;
640
- grid-template-columns: 1fr 1fr;
641
- gap: 12px 20px;
642
- align-items: end;
643
  }
644
 
645
- .setup-col {
646
- display: flex;
647
- flex-direction: column;
648
  }
649
 
650
- .setup-col.full-width {
651
- grid-column: 1 / -1;
 
652
  }
653
 
654
- .setup-col label {
655
- font-size: 0.8rem;
656
- color: var(--text-secondary);
657
- margin-bottom: 6px;
 
658
  }
659
 
660
- .setup-col select,
661
- .setup-col textarea {
662
- padding: 10px 12px;
 
 
 
 
 
663
  background: var(--bg-input);
664
  border: 1px solid var(--border);
665
- border-radius: 6px;
666
  color: var(--text-primary);
667
  font-size: 0.9rem;
668
  font-family: inherit;
669
- }
670
-
671
- .setup-col textarea {
672
  resize: none;
673
  }
674
 
675
- .setup-col select:focus,
676
- .setup-col textarea:focus {
677
- outline: none;
678
- border-color: var(--accent);
679
- }
680
 
681
- .setup-options {
 
 
 
 
682
  display: flex;
683
- gap: 16px;
684
- padding-top: 6px;
685
  }
686
 
687
- .setup-option {
 
 
 
 
 
 
 
 
688
  display: flex;
 
689
  align-items: center;
690
- gap: 6px;
691
  }
692
 
693
- .setup-option input[type="radio"] {
694
- accent-color: var(--accent);
 
 
 
 
695
  }
696
 
697
- .setup-option label {
698
- font-size: 0.85rem;
699
- color: var(--text-secondary);
 
 
700
  cursor: pointer;
 
 
701
  }
702
 
703
- .setup-col .start-btn {
704
- width: 100%;
705
- padding: 12px 20px;
706
  }
707
 
708
- .header-right {
709
- display: flex;
710
- align-items: center;
711
- gap: 12px;
712
  }
713
 
714
- .reset-btn {
715
- padding: 8px 14px;
716
- background: transparent;
717
- border: 1px solid var(--border);
718
- color: var(--text-secondary);
719
- border-radius: 6px;
720
- font-size: 0.8rem;
721
- cursor: pointer;
722
- transition: all 0.2s;
723
  }
724
 
725
- .reset-btn:hover {
726
- border-color: var(--accent);
727
- color: var(--accent-light);
728
  }
729
 
730
- @media (max-width: 700px) {
731
- .setup-grid {
732
- grid-template-columns: 1fr;
733
- }
734
- .setup-col.full-width {
735
- grid-column: 1;
736
- }
737
  }
738
 
739
- /* Input Area */
740
- .input-area {
741
- padding: 20px 24px;
742
- border-top: 1px solid var(--border);
743
- background: var(--bg-card);
744
- }
745
 
746
- .input-row {
747
- display: flex;
748
- gap: 12px;
749
- align-items: flex-end;
750
  }
751
 
752
- .input-wrapper {
753
- flex: 1;
 
 
754
  }
755
 
756
- .input-wrapper textarea {
757
  width: 100%;
758
- padding: 14px 18px;
759
  background: var(--bg-input);
760
  border: 1px solid var(--border);
761
- border-radius: 12px;
762
  color: var(--text-primary);
763
- font-size: 0.95rem;
764
- resize: none;
765
  font-family: inherit;
766
- transition: border-color 0.2s;
767
- }
768
-
769
- .input-wrapper textarea:focus {
770
- outline: none;
771
- border-color: var(--accent);
772
- }
773
-
774
- .btn-group {
775
- display: flex;
776
- gap: 8px;
777
  }
778
 
779
- .send-btn {
780
- padding: 14px 24px;
 
 
 
781
  background: var(--accent);
782
  color: white;
783
  border: none;
784
- border-radius: 12px;
785
- font-size: 0.95rem;
786
- font-weight: 500;
787
  cursor: pointer;
788
- transition: all 0.2s;
789
  }
790
 
791
- .send-btn:hover {
792
- background: var(--accent-light);
 
 
 
 
 
 
 
 
 
 
 
 
793
  }
794
 
795
- .tend-btn {
796
- padding: 14px 20px;
797
- background: var(--tend-color);
 
798
  color: white;
799
- border: none;
800
- border-radius: 12px;
801
- font-size: 0.95rem;
802
- font-weight: 500;
803
- cursor: pointer;
804
- transition: all 0.2s;
805
  }
806
 
807
- .tend-btn:hover {
808
- background: #059669;
809
- box-shadow: 0 4px 20px var(--tend-glow);
 
 
 
 
810
  }
811
 
812
- .quick-tools {
813
- display: flex;
814
- gap: 8px;
815
- margin-top: 12px;
816
- flex-wrap: wrap;
817
  }
818
 
819
- .quick-tool-btn {
820
- padding: 6px 12px;
 
 
 
 
 
821
  background: transparent;
822
- border: 1px solid var(--border);
823
- color: var(--text-secondary);
824
- border-radius: 6px;
825
- font-size: 0.8rem;
826
  cursor: pointer;
827
- transition: all 0.2s;
828
  }
829
 
830
- .quick-tool-btn:hover {
831
- border-color: var(--accent);
832
- color: var(--accent-light);
833
- background: rgba(99, 102, 241, 0.1);
834
- }
835
 
836
  /* Responsive */
 
 
 
 
 
 
 
 
 
 
 
 
837
  @media (max-width: 900px) {
838
- .toolkit-sidebar { width: 280px; }
839
- .tools-grid { grid-template-columns: 1fr; }
840
  }
841
 
842
  @media (max-width: 700px) {
843
  .app-container { flex-direction: column; }
844
- .toolkit-sidebar { width: 100%; max-height: 45vh; }
 
 
845
  }
846
  </style>
847
  </head>
848
  <body>
849
  <div class="app-container">
850
- <!-- TOOLKIT SIDEBAR -->
851
- <aside class="toolkit-sidebar">
852
- <div class="sidebar-header">
853
  <h1>Tend & Send</h1>
854
  <p>Assistive Relational Intelligence</p>
855
  </div>
856
 
 
 
857
 
858
- <!-- PRIMARY TEND TOOL -->
859
- <div class="tend-section">
860
- <div class="tend-tool" data-tool="tend" onclick="selectTool('tend')">
861
- <h4>
862
- <span class="primary-badge">Primary</span>
863
- TEND Transform
864
- </h4>
865
- <p>NVC-aligned translation with warmth. Preserves your voice and emotional truth while finding clarity.</p>
866
  </div>
867
- </div>
868
 
869
- <!-- Toggle Features -->
870
- <div class="toggle-section">
871
- <div class="toggle-row">
872
- <label>Detailed Mode (more explanation)</label>
873
- <label class="toggle-switch">
874
- <input type="checkbox" id="toggle-verbose">
875
- <span class="toggle-slider"></span>
876
- </label>
877
  </div>
878
- <div class="toggle-row">
879
- <label>Auto Feelings/Needs Reflection</label>
880
- <label class="toggle-switch">
881
- <input type="checkbox" id="toggle-feelings" checked>
882
- <span class="toggle-slider"></span>
883
- </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
884
  </div>
 
 
 
885
  <div class="toggle-row">
886
- <label>Show Intensity Check</label>
887
  <label class="toggle-switch">
888
- <input type="checkbox" id="toggle-intensity">
889
  <span class="toggle-slider"></span>
890
  </label>
891
  </div>
892
  </div>
893
 
894
- <!-- ARI Toolkit -->
895
- <div class="tools-section">
896
- <h3>ARI Toolkit</h3>
897
- <div class="tools-grid">
898
- <div class="tool-card" data-tool="guided_nvc" onclick="selectTool('guided_nvc')">
899
- <h4><span class="tool-icon">&#128221;</span> Guided NVC</h4>
900
- <p>Step-by-step I-statement builder</p>
901
- </div>
902
- <div class="tool-card" data-tool="receive_mode" onclick="selectTool('receive_mode')">
903
- <h4><span class="tool-icon">&#128066;</span> Receive Mode</h4>
904
- <p>Hear them before reacting</p>
905
- </div>
906
- <div class="tool-card" data-tool="pre_send_pause" onclick="selectTool('pre_send_pause')">
907
- <h4><span class="tool-icon">&#9208;</span> Pre-Send</h4>
908
- <p>Check intention first</p>
909
- </div>
910
- <div class="tool-card" data-tool="observation_spotter" onclick="selectTool('observation_spotter')">
911
- <h4><span class="tool-icon">&#128269;</span> Observations</h4>
912
- <p>Judgments to facts</p>
913
- </div>
914
- <div class="tool-card" data-tool="pure_questioning" onclick="selectTool('pure_questioning')">
915
- <h4><span class="tool-icon">&#10067;</span> Questions</h4>
916
- <p>Open inquiry only</p>
917
- </div>
918
- <div class="tool-card" data-tool="somatic_checkin" onclick="selectTool('somatic_checkin')">
919
- <h4><span class="tool-icon">&#129728;</span> Somatic</h4>
920
- <p>Body check-in</p>
921
- </div>
922
- <div class="tool-card" data-tool="repair_support" onclick="selectTool('repair_support')">
923
- <h4><span class="tool-icon">&#128591;</span> Repair</h4>
924
- <p>After ruptures</p>
925
- </div>
926
- <div class="tool-card" data-tool="feelings_needs" onclick="selectTool('feelings_needs')">
927
- <h4><span class="tool-icon">&#128156;</span> F&N Extract</h4>
928
- <p>Identify feelings/needs</p>
929
- </div>
930
- </div>
931
- </div>
932
-
933
- <!-- Footer -->
934
- <div class="sidebar-footer">
935
  <div class="safety-notice">
936
  <strong>Reflection prompts only</strong> - not therapy.
937
- If in crisis, contact a professional.
938
- </div>
939
- <div class="attribution">
940
- Created by Jocelyn Skillman, LMHC<br>
941
- <a href="https://huggingface.co/jostlebot" target="_blank">More ARI tools</a>
942
  </div>
 
943
  </div>
944
  </aside>
945
 
946
- <!-- MAIN CONTENT -->
947
- <main class="main-content">
948
- <!-- SETUP PANEL (top of main area) -->
949
  <div class="setup-panel" id="setup-panel">
950
- <div class="setup-header">
951
- <h2>Set Up Your Practice Conversation</h2>
952
- </div>
953
  <div class="setup-grid">
954
  <div class="setup-col">
955
- <label for="attachment-style">Partner's Attachment Style</label>
956
  <select id="attachment-style">
957
- <option value="anxious">Anxious - Seeks reassurance, fears abandonment</option>
958
- <option value="avoidant">Avoidant - Needs space when stressed, pulls away</option>
959
- <option value="disorganized">Disorganized - Mixed signals, push-pull</option>
960
- <option value="secure">Secure - Open, direct communication</option>
961
  </select>
962
  </div>
963
  <div class="setup-col">
964
- <label for="difficulty">Conversation Difficulty</label>
965
  <select id="difficulty">
966
- <option value="gentle">Gentle - Minor misunderstanding</option>
967
- <option value="moderate">Moderate - Recurring tension</option>
968
- <option value="intense">Intense - Significant conflict</option>
969
- <option value="crisis">Crisis - Relationship crossroads</option>
970
  </select>
971
  </div>
972
  <div class="setup-col full-width">
973
- <label for="conversation-context">What's the situation? (optional)</label>
974
- <textarea id="conversation-context" rows="2" placeholder="Example: We had a fight about how much time I spend at work..."></textarea>
975
- </div>
976
- <div class="setup-col full-width">
977
- <label for="user-goal">What do you want to practice? (optional)</label>
978
- <textarea id="user-goal" rows="1" placeholder="Example: Staying calm and hearing their perspective..."></textarea>
979
  </div>
980
  <div class="setup-col">
981
- <div class="setup-options">
982
  <div class="setup-option">
983
  <input type="radio" name="who-starts" id="partner-starts" value="partner" checked>
984
  <label for="partner-starts">Partner starts</label>
@@ -990,13 +802,13 @@
990
  </div>
991
  </div>
992
  <div class="setup-col">
993
- <button class="start-btn" onclick="startConversation()">Start Conversation</button>
994
  </div>
995
  </div>
996
  </div>
997
 
998
- <!-- CONVERSATION HEADER (shows after start) -->
999
- <div class="content-header" id="content-header" style="display: none;">
1000
  <div class="partner-info">
1001
  <div class="partner-avatar" id="partner-avatar">P</div>
1002
  <div class="partner-details">
@@ -1004,141 +816,100 @@
1004
  <span id="partner-style">Anxious attachment</span>
1005
  </div>
1006
  </div>
1007
- <div class="header-right">
1008
- <div id="active-tool-display">
1009
- <span class="active-tool-badge tend">TEND Ready</span>
1010
- </div>
1011
- <button class="reset-btn" onclick="resetConversation()">New Conversation</button>
1012
- </div>
1013
  </div>
1014
 
 
1015
  <div class="conversation-area" id="conversation"></div>
1016
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1017
  <div class="input-area">
1018
  <div class="input-row">
1019
- <div class="input-wrapper">
1020
- <textarea id="user-input" rows="2" placeholder="Type your message..." onkeydown="handleKeyDown(event)"></textarea>
1021
- </div>
1022
- <div class="btn-group">
1023
- <button class="tend-btn" onclick="useTendTransform()">TEND</button>
1024
- <button class="send-btn" id="send-btn" onclick="sendMessage()">Send</button>
1025
- </div>
1026
- </div>
1027
- <div class="quick-tools">
1028
- <button class="quick-tool-btn" onclick="quickTool('receive_mode')">Receive Mode</button>
1029
- <button class="quick-tool-btn" onclick="quickTool('pre_send_pause')">Pre-Send Pause</button>
1030
- <button class="quick-tool-btn" onclick="quickTool('intensity_check')">Check Intensity</button>
1031
- <button class="quick-tool-btn" onclick="quickTool('guided_nvc')">Guided NVC</button>
1032
  </div>
1033
  </div>
1034
  </main>
1035
 
1036
- <!-- TOOL PANEL (Right side - for tool conversations) -->
1037
- <aside class="tool-panel collapsed" id="tool-panel">
1038
- <div class="tool-panel-header">
1039
- <h3><span id="tool-panel-icon">🛠</span> <span id="tool-panel-title">Tool Workspace</span></h3>
1040
- <button class="close-btn" onclick="closeToolPanel()">×</button>
1041
  </div>
1042
- <div class="tool-panel-content" id="tool-conversation"></div>
1043
- <div class="tool-input-area">
1044
- <textarea id="tool-input" rows="2" placeholder="Continue working with this tool..." onkeydown="handleToolKeyDown(event)"></textarea>
1045
- <button class="tool-submit-btn" onclick="submitToTool()">Submit</button>
1046
  </div>
1047
  </aside>
1048
  </div>
1049
 
 
 
 
 
 
 
 
 
 
1050
  <script>
1051
  // ============================================================================
1052
  // STATE
1053
  // ============================================================================
1054
  let conversationHistory = [];
1055
- let activeTool = 'tend';
1056
  let lastPartnerMessage = '';
1057
  let conversationStarted = false;
1058
- let nvcStage = 1;
1059
- let inWorkspaceMode = false; // When true, messages go to tool, not partner
1060
-
1061
- // Tools that are "workspace" modes - messages don't go to partner
1062
- const WORKSPACE_TOOLS = ['guided_nvc', 'repair_support', 'somatic_checkin', 'pure_questioning'];
1063
-
1064
- const TOOL_NAMES = {
1065
- tend: 'TEND Transform',
1066
- feelings_needs: 'Feelings & Needs',
1067
- guided_nvc: 'Guided NVC',
1068
- receive_mode: 'Receive Mode',
1069
- pre_send_pause: 'Pre-Send Pause',
1070
- observation_spotter: 'Observation Spotter',
1071
- pure_questioning: 'Pure Questioning',
1072
- somatic_checkin: 'Somatic Check-in',
1073
- intensity_check: 'Intensity Check',
1074
- repair_support: 'Repair Support'
1075
  };
1076
 
1077
  const PARTNER_PROMPTS = {
1078
  anxious: {
1079
- gentle: `You ARE a real person with anxious attachment in a relationship. NOT a therapist. NOT supportive. You're a real human having a real reaction.
1080
- Minor misunderstanding happening. You seek reassurance, worry something is wrong.
1081
- Respond like a REAL PERSON would - with your own feelings, reactions, maybe some defensiveness or hurt.
1082
- 1-3 sentences. Sound like a text message, not therapy. Use casual language.`,
1083
- moderate: `You ARE a real person with anxious attachment. NOT a therapist. You're having a real emotional reaction.
1084
- Recurring tension is happening. You're scared, need reassurance, fear being left.
1085
- Respond authentically - you might get clingy, ask lots of questions, express fear or hurt.
1086
- 1-3 sentences. Sound like a real text. Be messy and human.`,
1087
- intense: `You ARE a real person with anxious attachment in significant conflict. NOT a therapist.
1088
- You're scared of losing this relationship. You might plead, get defensive, cry, accuse, or desperately try to fix things.
1089
- This is NOT calm. This is real emotional pain. React like a real person would.
1090
- 1-3 sentences. Raw and authentic.`,
1091
- crisis: `You ARE a real person with anxious attachment at a relationship crossroads. NOT a therapist.
1092
- You're TERRIFIED. You might beg, make promises, get angry, threaten, or fall apart.
1093
- If they say something devastating, REACT like a real person would - shock, denial, desperation, anger.
1094
- DO NOT validate their feelings. You're not their therapist. You're their partner who's hurting.
1095
- 1-3 sentences. Real human reaction.`
1096
  },
1097
  avoidant: {
1098
- gentle: `You ARE a real person with avoidant attachment. NOT a therapist. You're having your own reaction.
1099
- You need space when stressed. You might seem distant, change the subject, or minimize.
1100
- Respond like a REAL PERSON - not warm and supportive. Maybe a bit dismissive or short.
1101
- 1-3 sentences. Casual text language.`,
1102
- moderate: `You ARE a real person with avoidant attachment. NOT a therapist.
1103
- You're pulling away from the emotion. You might minimize, get irritated, or shut down.
1104
- Respond authentically - you're uncomfortable with this intensity.
1105
- 1-3 sentences. Real text message tone.`,
1106
- intense: `You ARE a real person with avoidant attachment in conflict. NOT a therapist.
1107
- You're shutting down. You might get cold, dismissive, say you need space, or even get mean.
1108
- This is NOT supportive. You're overwhelmed and protecting yourself.
1109
- 1-3 sentences. Authentic avoidant reaction.`,
1110
- crisis: `You ARE a real person with avoidant attachment at a crossroads. NOT a therapist.
1111
- You might go cold, say "fine, leave then," or completely shut down. You might agree to end it just to escape the intensity.
1112
- DO NOT be supportive or validating. You're protecting yourself from pain.
1113
- 1-3 sentences. Real defensive reaction.`
1114
  },
1115
  disorganized: {
1116
- gentle: `You ARE a real person with disorganized attachment. NOT a therapist.
1117
- You send mixed signals - want closeness but pull away. You might be confusing.
1118
- Respond with that internal conflict showing. 1-3 sentences.`,
1119
- moderate: `You ARE a real person with disorganized attachment. NOT a therapist.
1120
- You oscillate - pursue then withdraw. Your messages might contradict themselves.
1121
- Be authentically messy and confusing. 1-3 sentences.`,
1122
- intense: `You ARE a real person with disorganized attachment in conflict. NOT a therapist.
1123
- Push-pull in the same breath. "Don't leave me but get away from me."
1124
- Be contradictory and intense. 1-3 sentences.`,
1125
- crisis: `You ARE a real person with disorganized attachment at a crossroads. NOT a therapist.
1126
- Peak chaos - wanting love but terrified of it. You might say devastating things then take them back.
1127
- DO NOT be stable or supportive. You're dysregulated. 1-3 sentences.`
1128
  },
1129
  secure: {
1130
- gentle: `You ARE a real person with secure attachment. NOT a therapist, but you ARE more regulated.
1131
- You express needs clearly and stay present, but you're still a real person with feelings.
1132
- You might be curious, direct, maybe a little frustrated. 1-3 sentences. Human, not clinical.`,
1133
- moderate: `You ARE a real person with secure attachment. NOT a therapist.
1134
- You can discuss openly and take responsibility, but you also have your own perspective and feelings.
1135
- Be real - you might disagree, push back gently, or express your own hurt. 1-3 sentences.`,
1136
- intense: `You ARE a real person with secure attachment in conflict. NOT a therapist.
1137
- You can stay present but you're also affected. You might be hurt, frustrated, or sad.
1138
- You can hold both - the relationship matters AND this is hard. 1-3 sentences. Real.`,
1139
- crisis: `You ARE a real person with secure attachment at a crossroads. NOT a therapist.
1140
- You acknowledge seriousness while staying connected, but you're still HUMAN - this affects you.
1141
- You might be sad, scared, or even angry. You're just more regulated about it. 1-3 sentences.`
1142
  }
1143
  };
1144
 
@@ -1153,143 +924,113 @@
1153
  gentle: "I need some time to myself tonight. Can we talk later?",
1154
  moderate: "I don't want to keep having this conversation. I just need some space.",
1155
  intense: "This is too much. I can't keep doing this right now.",
1156
- crisis: "I think I need a break. Maybe from talking. Maybe more. I don't know."
1157
  },
1158
  disorganized: {
1159
- gentle: "I want to see you but... maybe we should just stay in separately tonight.",
1160
- moderate: "I love you but sometimes I think we shouldn't be together. I don't know what I want.",
1161
  intense: "Don't leave! But I also can't do this. I need you but I need you to go.",
1162
- crisis: "Maybe we should break up. No wait, I didn't mean that. Why do you even stay with me?"
1163
  },
1164
  secure: {
1165
- gentle: "I noticed we haven't had much time together this week. Can we talk about it?",
1166
- moderate: "I've been feeling some distance between us. I want to understand what's happening.",
1167
- intense: "I'm frustrated about yesterday and I think we need to work through it.",
1168
- crisis: "We need an honest conversation about where we are. This is hard for me too."
1169
  }
1170
  };
1171
 
1172
  // ============================================================================
1173
- // INITIALIZATION
1174
  // ============================================================================
1175
- document.addEventListener('DOMContentLoaded', () => {
1176
- document.getElementById('attachment-style').addEventListener('change', updatePartnerInfo);
1177
- document.getElementById('difficulty').addEventListener('change', updatePartnerInfo);
1178
- updatePartnerInfo();
1179
- });
1180
-
1181
- function updatePartnerInfo() {
1182
- const style = document.getElementById('attachment-style').value;
1183
- const labels = {
1184
- anxious: 'Anxious attachment',
1185
- avoidant: 'Avoidant attachment',
1186
- disorganized: 'Disorganized attachment',
1187
- secure: 'Secure attachment'
1188
- };
1189
- document.getElementById('partner-style').textContent = labels[style];
1190
- document.getElementById('partner-avatar').textContent = style[0].toUpperCase();
1191
  }
1192
 
1193
- // ============================================================================
1194
- // TOOL PANEL (Right side)
1195
- // ============================================================================
1196
- const TOOL_ICONS = {
1197
- tend: '✨',
1198
- feelings_needs: '💜',
1199
- guided_nvc: '📝',
1200
- receive_mode: '👂',
1201
- pre_send_pause: '⏸',
1202
- observation_spotter: '🔍',
1203
- pure_questioning: '❓',
1204
- somatic_checkin: '🫁',
1205
- intensity_check: '📊',
1206
- repair_support: '🙏'
1207
- };
1208
-
1209
- function openToolPanel(tool) {
1210
- const panel = document.getElementById('tool-panel');
1211
- const title = document.getElementById('tool-panel-title');
1212
- const icon = document.getElementById('tool-panel-icon');
1213
-
1214
- panel.classList.remove('collapsed');
1215
- title.textContent = TOOL_NAMES[tool] || 'Tool Workspace';
1216
- icon.textContent = TOOL_ICONS[tool] || '🛠';
1217
-
1218
- // Clear previous conversation if switching tools
1219
- document.getElementById('tool-conversation').innerHTML = '';
1220
- document.getElementById('tool-input').value = '';
1221
- document.getElementById('tool-input').focus();
1222
- }
1223
 
1224
- function closeToolPanel() {
1225
- const panel = document.getElementById('tool-panel');
1226
- panel.classList.add('collapsed');
1227
- activeTool = 'tend';
1228
- inWorkspaceMode = false;
1229
 
1230
- // Deselect tool cards
1231
- document.querySelectorAll('.tool-card').forEach(card => {
1232
- card.classList.remove('active');
1233
- });
1234
 
1235
- // Update badge
1236
- const badge = document.getElementById('active-tool-display');
1237
- badge.innerHTML = '<span class="active-tool-badge tend">TEND Ready</span>';
1238
  }
1239
 
1240
- function addToolMessage(type, content, toolName = null) {
1241
- const conversation = document.getElementById('tool-conversation');
1242
- const div = document.createElement('div');
1243
- div.className = `tool-message ${type === 'user' ? 'user-input' : ''}`;
1244
-
1245
- const label = document.createElement('div');
1246
- label.className = 'tool-label';
1247
- label.textContent = type === 'user' ? 'You' : (toolName || activeTool);
1248
- div.appendChild(label);
1249
-
1250
- const text = document.createElement('p');
1251
- text.innerHTML = formatMessage(content);
1252
- div.appendChild(text);
1253
 
1254
- conversation.appendChild(div);
1255
- conversation.scrollTop = conversation.scrollHeight;
1256
- }
1257
 
1258
- function addToolLoading() {
1259
- const conversation = document.getElementById('tool-conversation');
1260
- const div = document.createElement('div');
1261
- div.className = 'loading';
1262
- div.id = 'tool-loading';
1263
- div.innerHTML = '<div class="spinner"></div> Processing...';
1264
- conversation.appendChild(div);
1265
- conversation.scrollTop = conversation.scrollHeight;
1266
  }
1267
 
1268
- function removeToolLoading() {
1269
- const el = document.getElementById('tool-loading');
1270
- if (el) el.remove();
1271
  }
1272
 
1273
- async function submitToTool() {
1274
- const input = document.getElementById('tool-input');
1275
- const userInput = input.value.trim();
1276
- if (!userInput) return;
1277
 
1278
- input.value = '';
1279
- addToolMessage('user', userInput);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1280
 
1281
  addToolLoading();
1282
  const isVerbose = document.getElementById('toggle-verbose')?.checked || false;
 
1283
  try {
1284
  const response = await fetch('/api/tool', {
1285
  method: 'POST',
1286
  headers: { 'Content-Type': 'application/json' },
1287
  body: JSON.stringify({
1288
- tool: activeTool,
1289
  partner_message: lastPartnerMessage,
1290
- user_draft: document.getElementById('user-input').value.trim(),
1291
- user_input: userInput,
1292
- stage: nvcStage,
1293
  verbose: isVerbose
1294
  })
1295
  });
@@ -1298,16 +1039,26 @@
1298
  removeToolLoading();
1299
 
1300
  if (data.response) {
1301
- addToolMessage('tool', data.response, TOOL_NAMES[activeTool]);
1302
  } else {
1303
- addToolMessage('tool', 'Could not process. Check API configuration.', 'Error');
1304
  }
1305
  } catch (error) {
1306
  removeToolLoading();
1307
- addToolMessage('tool', 'Connection error. Please try again.', 'Error');
1308
  }
1309
  }
1310
 
 
 
 
 
 
 
 
 
 
 
1311
  function handleToolKeyDown(event) {
1312
  if (event.key === 'Enter' && !event.shiftKey) {
1313
  event.preventDefault();
@@ -1316,261 +1067,97 @@
1316
  }
1317
 
1318
  // ============================================================================
1319
- // TOOLS
1320
  // ============================================================================
1321
- function selectTool(tool) {
1322
- document.querySelectorAll('.tool-card, .tend-tool').forEach(card => {
1323
- card.classList.remove('active');
1324
- });
1325
-
1326
- const selectedCard = document.querySelector(`[data-tool="${tool}"]`);
1327
- if (selectedCard) selectedCard.classList.add('active');
1328
-
1329
- activeTool = tool;
1330
- inWorkspaceMode = WORKSPACE_TOOLS.includes(tool);
1331
-
1332
- // Update active tool badge
1333
- const badge = document.getElementById('active-tool-display');
1334
- badge.innerHTML = `<span class="active-tool-badge ${tool === 'tend' ? 'tend' : ''}">${TOOL_NAMES[tool]}</span>`;
1335
 
1336
- // For non-TEND tools, open the tool panel
1337
- if (tool !== 'tend') {
1338
- openToolPanel(tool);
1339
- useTool(tool);
1340
- } else {
1341
- closeToolPanel();
1342
- }
1343
- }
1344
-
1345
- async function useTendTransform() {
1346
- const userInput = document.getElementById('user-input').value.trim();
1347
- if (!userInput) {
1348
- addMessage('ari', 'Write your message first, then use TEND to find clarity with warmth.', 'TEND Transform', true);
1349
- return;
1350
- }
1351
-
1352
- await useTool('tend');
1353
- }
1354
-
1355
- async function quickTool(tool) {
1356
- selectTool(tool);
1357
- }
1358
-
1359
- async function useTool(tool) {
1360
- const userInput = document.getElementById('user-input').value.trim();
1361
- let inputText = '';
1362
- const isTend = (tool === 'tend');
1363
 
1364
- // For non-TEND tools, show in tool panel
1365
- const addMsg = isTend ? addMessage : addToolMessage;
1366
- const addLoad = isTend ? addLoading : addToolLoading;
1367
- const removeLoad = isTend ? removeLoading : removeToolLoading;
1368
-
1369
- switch(tool) {
1370
- case 'tend':
1371
- if (!userInput) return;
1372
- inputText = `Transform this message with NVC clarity and warmth, preserving my voice: "${userInput}"`;
1373
- break;
1374
- case 'receive_mode':
1375
- if (!lastPartnerMessage) {
1376
- addToolMessage('tool', 'Start the conversation first to use Receive Mode on their message.', TOOL_NAMES[tool]);
1377
- return;
1378
- }
1379
- inputText = userInput || 'Help me receive and understand this message before I react.';
1380
- break;
1381
- case 'pre_send_pause':
1382
- if (!userInput) {
1383
- addToolMessage('tool', 'Write a draft in the main chat first, then use Pre-Send Pause to check your intention.', TOOL_NAMES[tool]);
1384
- return;
1385
- }
1386
- inputText = 'Help me pause and check my intention before sending this.';
1387
- break;
1388
- case 'observation_spotter':
1389
- if (!userInput) {
1390
- addToolMessage('tool', 'Write something in the main chat first to check for judgments vs observations.', TOOL_NAMES[tool]);
1391
- return;
1392
- }
1393
- inputText = 'Help me find observations underneath any judgments in this.';
1394
- break;
1395
- case 'feelings_needs':
1396
- const textToAnalyze = userInput || lastPartnerMessage;
1397
- if (!textToAnalyze) {
1398
- addToolMessage('tool', 'Need text to analyze - either their message or your draft.', TOOL_NAMES[tool]);
1399
- return;
1400
- }
1401
- inputText = `Identify the feelings and needs in this: "${textToAnalyze}"`;
1402
- break;
1403
- case 'intensity_check':
1404
- const textToCheck = userInput || lastPartnerMessage;
1405
- if (!textToCheck) {
1406
- addToolMessage('tool', 'Need text to check intensity.', TOOL_NAMES[tool]);
1407
- return;
1408
- }
1409
- inputText = `Check the emotional intensity: "${textToCheck}"`;
1410
- break;
1411
- case 'pure_questioning':
1412
- inputText = userInput || 'Help me find clarity about this situation.';
1413
- break;
1414
- case 'somatic_checkin':
1415
- inputText = userInput || 'Guide me through a body check-in before responding.';
1416
- break;
1417
- case 'guided_nvc':
1418
- inputText = userInput || 'Help me build an I-statement for this situation.';
1419
- break;
1420
- case 'repair_support':
1421
- inputText = userInput || 'Help me craft a genuine repair after this rupture.';
1422
- break;
1423
  }
1424
 
1425
- addLoad();
1426
- const isVerbose = document.getElementById('toggle-verbose')?.checked || false;
1427
- try {
1428
- const response = await fetch('/api/tool', {
1429
- method: 'POST',
1430
- headers: { 'Content-Type': 'application/json' },
1431
- body: JSON.stringify({
1432
- tool: tool,
1433
- partner_message: lastPartnerMessage,
1434
- user_draft: userInput,
1435
- user_input: inputText,
1436
- stage: nvcStage,
1437
- verbose: isVerbose
1438
- })
1439
- });
1440
-
1441
- const data = await response.json();
1442
- removeLoad();
1443
-
1444
- if (data.response) {
1445
- if (isTend) {
1446
- addMessage('ari', data.response, TOOL_NAMES[tool], true);
1447
- // Auto feelings/needs if enabled
1448
- if (document.getElementById('toggle-feelings').checked) {
1449
- setTimeout(() => autoFeelingsNeeds(userInput), 1000);
1450
- }
1451
- } else {
1452
- addToolMessage('tool', data.response, TOOL_NAMES[tool]);
1453
- }
1454
- } else {
1455
- addMsg(isTend ? 'ari' : 'tool', 'Could not process. Check API configuration.', 'Error');
1456
- }
1457
- } catch (error) {
1458
- removeLoad();
1459
- addMsg(isTend ? 'ari' : 'tool', 'Connection error. Please try again.', 'Error');
1460
- }
1461
  }
1462
 
1463
- async function autoFeelingsNeeds(text) {
1464
- try {
1465
- const response = await fetch('/api/tool', {
1466
- method: 'POST',
1467
- headers: { 'Content-Type': 'application/json' },
1468
- body: JSON.stringify({
1469
- tool: 'feelings_needs',
1470
- user_input: `Quick extraction from: "${text}"`,
1471
- partner_message: lastPartnerMessage,
1472
- user_draft: text
1473
- })
1474
- });
1475
- const data = await response.json();
1476
- if (data.response) {
1477
- addMessage('ari', data.response, 'Feelings & Needs Reflection');
1478
- }
1479
- } catch (e) { /* silent fail */ }
1480
  }
1481
 
1482
  // ============================================================================
1483
  // CONVERSATION
1484
  // ============================================================================
1485
- let conversationContext = '';
1486
- let userGoal = '';
1487
-
1488
  async function startConversation() {
1489
  const style = document.getElementById('attachment-style').value;
1490
  const difficulty = document.getElementById('difficulty').value;
1491
- conversationContext = document.getElementById('conversation-context')?.value?.trim() || '';
1492
- userGoal = document.getElementById('user-goal')?.value?.trim() || '';
1493
  const whoStarts = document.querySelector('input[name="who-starts"]:checked')?.value || 'partner';
1494
 
1495
- // Hide setup, show conversation
1496
  document.getElementById('setup-panel').style.display = 'none';
1497
- document.getElementById('content-header').style.display = 'flex';
1498
  document.getElementById('conversation').innerHTML = '';
1499
  conversationHistory = [];
1500
  conversationStarted = true;
1501
 
1502
- // Update partner info display
1503
- const styleLabels = {
1504
- anxious: 'Anxious attachment',
1505
- avoidant: 'Avoidant attachment',
1506
- disorganized: 'Disorganized attachment',
1507
- secure: 'Secure attachment'
1508
- };
1509
- document.getElementById('partner-style').textContent = styleLabels[style];
1510
  document.getElementById('partner-avatar').textContent = style[0].toUpperCase();
1511
 
1512
- // If user starts, just wait for their input
1513
  if (whoStarts === 'user') {
1514
- addMessage('ari', `Ready to practice. Your partner has ${styleLabels[style].toLowerCase()} tendencies. ${userGoal ? 'Your goal: ' + userGoal : ''} Type your opening message.`, 'Setup', false);
1515
  return;
1516
  }
1517
 
1518
- // Partner starts - generate contextual opening based on user input
1519
- if (conversationContext) {
1520
  addLoading();
1521
  try {
1522
- const contextPrompt = `${PARTNER_PROMPTS[style][difficulty]}
1523
-
1524
- CONTEXT: ${conversationContext}
1525
- ${userGoal ? 'The other person wants to practice: ' + userGoal : ''}
1526
-
1527
- Based on this context, send an opening message that fits your attachment style and the described situation.
1528
- IMPORTANT: You are NOT a therapist. You are a real partner with real feelings. Sound like a real text message.
1529
- 1-3 sentences. Be realistic and stay in character.`;
1530
-
1531
  const response = await fetch('/api/chat', {
1532
  method: 'POST',
1533
  headers: { 'Content-Type': 'application/json' },
1534
  body: JSON.stringify({
1535
- messages: [{ role: 'user', content: 'Start the conversation based on the context provided.' }],
1536
- system: contextPrompt,
1537
  max_tokens: 200
1538
  })
1539
  });
1540
-
1541
  const data = await response.json();
1542
  removeLoading();
1543
-
1544
- if (data.content && data.content[0]) {
1545
- const openingMessage = data.content[0].text;
1546
- addMessage('partner', openingMessage);
1547
- lastPartnerMessage = openingMessage;
1548
- conversationHistory.push({ role: 'assistant', content: openingMessage });
1549
  }
1550
- } catch (error) {
1551
  removeLoading();
1552
- const openingMessage = OPENING_MESSAGES[style][difficulty];
1553
- addMessage('partner', openingMessage);
1554
- lastPartnerMessage = openingMessage;
1555
- conversationHistory.push({ role: 'assistant', content: openingMessage });
1556
  }
1557
  } else {
1558
- const openingMessage = OPENING_MESSAGES[style][difficulty];
1559
- addMessage('partner', openingMessage);
1560
- lastPartnerMessage = openingMessage;
1561
- conversationHistory.push({ role: 'assistant', content: openingMessage });
1562
  }
1563
  }
1564
 
1565
  function resetConversation() {
1566
- // Show setup, hide conversation header
1567
  document.getElementById('setup-panel').style.display = 'block';
1568
- document.getElementById('content-header').style.display = 'none';
1569
  document.getElementById('conversation').innerHTML = '';
1570
  conversationHistory = [];
1571
  conversationStarted = false;
1572
  lastPartnerMessage = '';
1573
- closeToolPanel();
1574
  }
1575
 
1576
  async function sendMessage() {
@@ -1605,15 +1192,15 @@ IMPORTANT: You are NOT a therapist. You are a real partner with real feelings. S
1605
  const data = await response.json();
1606
  removeLoading();
1607
 
1608
- if (data.content && data.content[0]) {
1609
- const partnerResponse = data.content[0].text;
1610
- addMessage('partner', partnerResponse);
1611
- lastPartnerMessage = partnerResponse;
1612
- conversationHistory.push({ role: 'assistant', content: partnerResponse });
1613
  }
1614
  } catch (error) {
1615
  removeLoading();
1616
- addMessage('ari', 'Connection error. Please try again.', 'System');
1617
  }
1618
  }
1619
 
@@ -1627,43 +1214,74 @@ IMPORTANT: You are NOT a therapist. You are a real partner with real feelings. S
1627
  // ============================================================================
1628
  // UI HELPERS
1629
  // ============================================================================
1630
- function addMessage(type, content, toolName = null, isTend = false) {
1631
- const conversation = document.getElementById('conversation');
1632
  const div = document.createElement('div');
1633
  div.className = `message ${type}`;
1634
  if (isTend) div.classList.add('tend-result');
1635
 
1636
  if (type === 'ari') {
1637
- const label = document.createElement('div');
1638
- label.className = 'ari-label';
1639
- label.textContent = toolName || 'ARI';
1640
- div.appendChild(label);
1641
  }
1642
 
1643
  const text = document.createElement('div');
1644
- text.innerHTML = formatMessage(content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1645
  div.appendChild(text);
1646
 
1647
- conversation.appendChild(div);
1648
- conversation.scrollTop = conversation.scrollHeight;
1649
  }
1650
 
1651
  function addLoading() {
1652
- const conversation = document.getElementById('conversation');
1653
  const div = document.createElement('div');
1654
  div.className = 'loading';
1655
  div.id = 'loading';
1656
- div.innerHTML = '<div class="spinner"></div> Processing...';
1657
- conversation.appendChild(div);
1658
- conversation.scrollTop = conversation.scrollHeight;
1659
  }
1660
 
1661
  function removeLoading() {
1662
- const el = document.getElementById('loading');
1663
- if (el) el.remove();
 
 
 
 
 
 
 
 
 
 
 
 
 
1664
  }
1665
 
1666
- function formatMessage(text) {
1667
  return text
1668
  .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
1669
  .replace(/\n/g, '<br>');
 
15
  --accent-glow: rgba(99, 102, 241, 0.3);
16
  --tend-color: #10b981;
17
  --tend-glow: rgba(16, 185, 129, 0.3);
18
+ --love-color: #ec4899;
19
+ --love-glow: rgba(236, 72, 153, 0.3);
20
  --text-primary: #f1f5f9;
21
  --text-secondary: #94a3b8;
22
  --text-muted: #64748b;
23
  --border: #334155;
 
 
24
  --warning: #f59e0b;
 
25
  }
26
 
27
  * { margin: 0; padding: 0; box-sizing: border-box; }
 
33
  min-height: 100vh;
34
  }
35
 
36
+ /* ===== THREE PANEL LAYOUT ===== */
37
  .app-container {
38
  display: flex;
39
  height: 100vh;
40
  }
41
 
42
+ /* LEFT PANEL - Tool Descriptions */
43
+ .left-panel {
44
+ width: 280px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  background: var(--bg-card);
46
  border-right: 1px solid var(--border);
47
  display: flex;
 
49
  overflow-y: auto;
50
  }
51
 
52
+ .left-header {
53
+ padding: 20px;
54
  border-bottom: 1px solid var(--border);
55
  background: linear-gradient(135deg, var(--bg-card) 0%, rgba(99, 102, 241, 0.05) 100%);
56
  }
57
 
58
+ .left-header h1 {
59
+ font-size: 1.4rem;
 
60
  background: linear-gradient(135deg, var(--text-primary) 0%, var(--accent-light) 100%);
61
  -webkit-background-clip: text;
62
  -webkit-text-fill-color: transparent;
63
  margin-bottom: 4px;
64
  }
65
 
66
+ .left-header p {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  font-size: 0.8rem;
68
  color: var(--text-secondary);
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
 
71
+ .tool-list {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  flex: 1;
73
+ padding: 16px;
 
74
  }
75
 
76
+ .tool-list h3 {
77
  font-size: 0.7rem;
78
  text-transform: uppercase;
79
  letter-spacing: 0.08em;
 
81
  margin-bottom: 12px;
82
  }
83
 
84
+ .tool-desc {
 
 
 
 
 
 
 
 
 
85
  padding: 12px;
86
+ margin-bottom: 10px;
87
+ background: var(--bg-input);
88
+ border-radius: 8px;
89
+ border-left: 3px solid var(--border);
90
  cursor: pointer;
91
+ transition: all 0.2s;
92
  }
93
 
94
+ .tool-desc:hover {
95
+ border-left-color: var(--accent);
96
  background: var(--bg-hover);
97
  }
98
 
99
+ .tool-desc.active {
100
+ border-left-color: var(--accent);
101
  background: rgba(99, 102, 241, 0.15);
 
102
  }
103
 
104
+ .tool-desc h4 {
105
+ font-size: 0.9rem;
106
  margin-bottom: 4px;
107
  display: flex;
108
  align-items: center;
109
+ gap: 8px;
110
  }
111
 
112
+ .tool-desc p {
113
+ font-size: 0.75rem;
114
  color: var(--text-muted);
115
+ line-height: 1.4;
116
  }
117
 
118
+ .tool-desc.tend-desc {
119
+ border-left-color: var(--tend-color);
120
+ background: rgba(16, 185, 129, 0.1);
121
+ }
122
 
123
+ .tool-desc.love-desc {
124
+ border-left-color: var(--love-color);
125
+ background: rgba(236, 72, 153, 0.1);
126
+ }
127
+
128
+ /* Settings */
129
+ .settings-section {
130
+ padding: 16px;
131
+ border-top: 1px solid var(--border);
132
+ background: rgba(0,0,0,0.2);
133
  }
134
 
135
  .toggle-row {
136
  display: flex;
137
  align-items: center;
138
  justify-content: space-between;
139
+ padding: 6px 0;
140
  }
141
 
142
  .toggle-row label {
143
+ font-size: 0.8rem;
144
  color: var(--text-secondary);
145
  }
146
 
147
  .toggle-switch {
148
  position: relative;
149
+ width: 40px;
150
+ height: 22px;
151
  }
152
 
153
+ .toggle-switch input { opacity: 0; width: 0; height: 0; }
 
 
 
 
154
 
155
  .toggle-slider {
156
  position: absolute;
157
  cursor: pointer;
158
  top: 0; left: 0; right: 0; bottom: 0;
159
  background: var(--border);
160
+ border-radius: 22px;
161
  transition: 0.3s;
162
  }
163
 
164
  .toggle-slider:before {
165
  position: absolute;
166
  content: "";
167
+ height: 16px;
168
+ width: 16px;
169
  left: 3px;
170
  bottom: 3px;
171
  background: white;
 
173
  transition: 0.3s;
174
  }
175
 
176
+ .toggle-switch input:checked + .toggle-slider { background: var(--accent); }
177
+ .toggle-switch input:checked + .toggle-slider:before { transform: translateX(18px); }
178
+
179
+ .left-footer {
180
+ padding: 12px 16px;
181
+ border-top: 1px solid var(--border);
182
+ font-size: 0.7rem;
183
+ color: var(--text-muted);
184
+ }
185
+
186
+ .safety-notice {
187
+ background: rgba(245, 158, 11, 0.1);
188
+ border: 1px solid var(--warning);
189
+ border-radius: 6px;
190
+ padding: 8px;
191
+ margin-bottom: 8px;
192
+ font-size: 0.7rem;
193
+ color: var(--warning);
194
+ }
195
+
196
+ /* CENTER PANEL - Conversation */
197
+ .center-panel {
198
+ flex: 1;
199
+ display: flex;
200
+ flex-direction: column;
201
+ min-width: 0;
202
+ }
203
+
204
+ .setup-panel {
205
+ background: var(--bg-card);
206
+ border-bottom: 1px solid var(--border);
207
+ padding: 20px 24px;
208
+ }
209
+
210
+ .setup-panel h2 {
211
+ font-size: 1.1rem;
212
+ margin-bottom: 16px;
213
+ }
214
+
215
+ .setup-grid {
216
+ display: grid;
217
+ grid-template-columns: 1fr 1fr;
218
+ gap: 12px 16px;
219
+ }
220
+
221
+ .setup-col {
222
+ display: flex;
223
+ flex-direction: column;
224
+ }
225
+
226
+ .setup-col.full-width { grid-column: 1 / -1; }
227
+
228
+ .setup-col label {
229
+ font-size: 0.8rem;
230
+ color: var(--text-secondary);
231
+ margin-bottom: 4px;
232
  }
233
 
234
+ .setup-col select, .setup-col textarea {
235
+ padding: 10px 12px;
236
+ background: var(--bg-input);
237
+ border: 1px solid var(--border);
238
+ border-radius: 6px;
239
+ color: var(--text-primary);
240
+ font-size: 0.85rem;
241
+ font-family: inherit;
242
  }
243
 
244
+ .setup-col textarea { resize: none; }
245
+ .setup-col select:focus, .setup-col textarea:focus { outline: none; border-color: var(--accent); }
246
+
247
+ .setup-row {
248
+ display: flex;
249
+ gap: 16px;
250
+ align-items: center;
251
  }
252
 
253
+ .setup-option {
254
+ display: flex;
255
+ align-items: center;
256
+ gap: 6px;
 
 
 
 
257
  }
258
 
259
+ .setup-option input[type="radio"] { accent-color: var(--accent); }
260
+
261
+ .setup-option label {
262
+ font-size: 0.85rem;
263
+ color: var(--text-secondary);
264
+ cursor: pointer;
265
  }
266
 
267
+ .start-btn {
268
+ padding: 12px 24px;
269
+ background: var(--accent);
270
+ color: white;
271
+ border: none;
272
+ border-radius: 8px;
273
+ font-size: 0.9rem;
274
+ font-weight: 600;
275
+ cursor: pointer;
276
  }
277
 
278
+ .start-btn:hover { background: var(--accent-light); }
279
+
280
+ /* Conversation Header */
281
+ .conv-header {
282
+ padding: 12px 20px;
283
+ background: var(--bg-card);
284
  border-bottom: 1px solid var(--border);
285
+ display: none;
286
  justify-content: space-between;
287
  align-items: center;
 
288
  }
289
 
290
+ .conv-header.active { display: flex; }
291
+
292
  .partner-info {
293
  display: flex;
294
  align-items: center;
295
+ gap: 10px;
296
  }
297
 
298
  .partner-avatar {
299
+ width: 36px;
300
+ height: 36px;
301
  background: var(--accent);
302
  border-radius: 50%;
303
  display: flex;
304
  align-items: center;
305
  justify-content: center;
306
  font-weight: 600;
307
+ font-size: 0.9rem;
308
  }
309
 
310
+ .partner-details h3 { font-size: 0.95rem; }
311
+ .partner-details span { font-size: 0.75rem; color: var(--text-secondary); }
 
 
312
 
313
+ .reset-btn {
314
+ padding: 6px 12px;
315
+ background: transparent;
316
+ border: 1px solid var(--border);
317
  color: var(--text-secondary);
318
+ border-radius: 6px;
319
+ font-size: 0.75rem;
320
+ cursor: pointer;
321
  }
322
 
323
+ .reset-btn:hover { border-color: var(--accent); color: var(--accent-light); }
 
 
 
 
 
 
 
 
 
 
 
324
 
325
+ /* Conversation Area */
326
  .conversation-area {
327
  flex: 1;
328
+ padding: 20px;
329
  overflow-y: auto;
330
  display: flex;
331
  flex-direction: column;
332
+ gap: 12px;
333
  }
334
 
335
  .message {
336
+ max-width: 80%;
337
+ padding: 12px 16px;
338
+ border-radius: 16px;
339
  line-height: 1.5;
340
  animation: fadeIn 0.3s ease;
341
  }
342
 
343
  @keyframes fadeIn {
344
+ from { opacity: 0; transform: translateY(8px); }
345
  to { opacity: 1; transform: translateY(0); }
346
  }
347
 
 
364
  background: rgba(99, 102, 241, 0.1);
365
  border: 1px solid var(--accent);
366
  max-width: 90%;
367
+ border-radius: 10px;
368
  }
369
 
370
  .message.ari.tend-result {
 
373
  }
374
 
375
  .message.ari .ari-label {
376
+ font-size: 0.65rem;
377
  text-transform: uppercase;
378
  letter-spacing: 0.05em;
379
  color: var(--accent-light);
380
+ margin-bottom: 6px;
381
  font-weight: 600;
382
  }
383
 
384
+ .message.ari.tend-result .ari-label { color: var(--tend-color); }
 
 
385
 
 
386
  .loading {
387
  display: flex;
388
  align-items: center;
389
  justify-content: center;
390
  gap: 8px;
391
  color: var(--text-secondary);
392
+ padding: 12px;
393
  }
394
 
395
  .spinner {
396
+ width: 18px;
397
+ height: 18px;
398
  border: 2px solid var(--border);
399
  border-top-color: var(--accent);
400
  border-radius: 50%;
 
403
 
404
  @keyframes spin { to { transform: rotate(360deg); } }
405
 
406
+ /* Tool Buttons Row */
407
+ .tool-buttons {
408
+ display: flex;
409
+ gap: 6px;
410
+ padding: 12px 20px;
411
+ background: var(--bg-card);
412
+ border-top: 1px solid var(--border);
413
+ flex-wrap: wrap;
414
+ justify-content: center;
415
  }
416
 
417
+ .tool-btn {
418
+ padding: 8px 14px;
419
+ border: 1px solid var(--border);
420
+ background: var(--bg-input);
421
+ color: var(--text-secondary);
422
+ border-radius: 8px;
423
+ font-size: 0.8rem;
424
+ font-weight: 500;
425
+ cursor: pointer;
426
+ transition: all 0.2s;
427
+ display: flex;
428
+ align-items: center;
429
+ gap: 4px;
430
  }
431
 
432
+ .tool-btn:hover {
433
+ border-color: var(--accent);
434
+ color: var(--accent-light);
435
+ background: rgba(99, 102, 241, 0.1);
436
  }
437
 
438
+ .tool-btn.active {
439
+ border-color: var(--accent);
440
+ background: var(--accent);
441
  color: white;
 
 
 
 
 
 
442
  }
443
 
444
+ .tool-btn.tend {
445
+ border-color: var(--tend-color);
446
+ color: var(--tend-color);
447
  }
448
 
449
+ .tool-btn.tend:hover, .tool-btn.tend.active {
450
+ background: var(--tend-color);
451
+ color: white;
 
 
452
  }
453
 
454
+ .tool-btn.send {
455
+ background: var(--accent);
456
+ border-color: var(--accent);
457
+ color: white;
458
  }
459
 
460
+ .tool-btn.send:hover {
461
+ background: var(--accent-light);
 
 
 
462
  }
463
 
464
+ .tool-btn.love {
465
+ border-color: var(--love-color);
466
+ color: var(--love-color);
467
  }
468
 
469
+ .tool-btn.love:hover, .tool-btn.love.active {
470
+ background: var(--love-color);
471
+ color: white;
472
  }
473
 
474
+ /* Input Area */
475
+ .input-area {
476
+ padding: 16px 20px;
477
+ background: var(--bg-card);
478
+ border-top: 1px solid var(--border);
479
  }
480
 
481
+ .input-row {
482
+ display: flex;
483
+ gap: 10px;
484
+ }
485
+
486
+ .input-row textarea {
487
+ flex: 1;
488
+ padding: 12px 16px;
489
  background: var(--bg-input);
490
  border: 1px solid var(--border);
491
+ border-radius: 10px;
492
  color: var(--text-primary);
493
  font-size: 0.9rem;
494
  font-family: inherit;
 
 
 
495
  resize: none;
496
  }
497
 
498
+ .input-row textarea:focus { outline: none; border-color: var(--accent); }
 
 
 
 
499
 
500
+ /* RIGHT PANEL - Tool Workspace */
501
+ .right-panel {
502
+ width: 380px;
503
+ background: var(--bg-card);
504
+ border-left: 1px solid var(--border);
505
  display: flex;
506
+ flex-direction: column;
507
+ transition: all 0.3s ease;
508
  }
509
 
510
+ .right-panel.collapsed {
511
+ width: 0;
512
+ overflow: hidden;
513
+ border-left: none;
514
+ }
515
+
516
+ .right-header {
517
+ padding: 16px 20px;
518
+ border-bottom: 1px solid var(--border);
519
  display: flex;
520
+ justify-content: space-between;
521
  align-items: center;
522
+ background: rgba(99, 102, 241, 0.1);
523
  }
524
 
525
+ .right-header h3 {
526
+ font-size: 1rem;
527
+ color: var(--accent-light);
528
+ display: flex;
529
+ align-items: center;
530
+ gap: 8px;
531
  }
532
 
533
+ .right-header .close-btn {
534
+ background: transparent;
535
+ border: none;
536
+ color: var(--text-muted);
537
+ font-size: 1.2rem;
538
  cursor: pointer;
539
+ padding: 4px 8px;
540
+ border-radius: 4px;
541
  }
542
 
543
+ .right-header .close-btn:hover {
544
+ background: var(--bg-input);
545
+ color: var(--text-primary);
546
  }
547
 
548
+ .right-content {
549
+ flex: 1;
550
+ overflow-y: auto;
551
+ padding: 16px 20px;
552
  }
553
 
554
+ .tool-message {
555
+ margin-bottom: 14px;
556
+ padding: 12px 14px;
557
+ background: var(--bg-input);
558
+ border-radius: 10px;
559
+ border-left: 3px solid var(--accent);
 
 
 
560
  }
561
 
562
+ .tool-message.user-msg {
563
+ border-left-color: var(--text-muted);
564
+ background: rgba(255,255,255,0.03);
565
  }
566
 
567
+ .tool-message .msg-label {
568
+ font-size: 0.65rem;
569
+ text-transform: uppercase;
570
+ letter-spacing: 0.05em;
571
+ color: var(--accent-light);
572
+ margin-bottom: 6px;
 
573
  }
574
 
575
+ .tool-message.user-msg .msg-label { color: var(--text-muted); }
 
 
 
 
 
576
 
577
+ .tool-message p {
578
+ line-height: 1.5;
579
+ font-size: 0.9rem;
 
580
  }
581
 
582
+ .right-input {
583
+ padding: 16px 20px;
584
+ border-top: 1px solid var(--border);
585
+ background: var(--bg-dark);
586
  }
587
 
588
+ .right-input textarea {
589
  width: 100%;
590
+ padding: 10px 14px;
591
  background: var(--bg-input);
592
  border: 1px solid var(--border);
593
+ border-radius: 8px;
594
  color: var(--text-primary);
595
+ font-size: 0.85rem;
 
596
  font-family: inherit;
597
+ resize: none;
598
+ margin-bottom: 10px;
 
 
 
 
 
 
 
 
 
599
  }
600
 
601
+ .right-input textarea:focus { outline: none; border-color: var(--accent); }
602
+
603
+ .right-input button {
604
+ width: 100%;
605
+ padding: 10px;
606
  background: var(--accent);
607
  color: white;
608
  border: none;
609
+ border-radius: 8px;
610
+ font-size: 0.85rem;
 
611
  cursor: pointer;
 
612
  }
613
 
614
+ .right-input button:hover { background: var(--accent-light); }
615
+
616
+ /* Love/Breath Modal */
617
+ .breath-overlay {
618
+ position: fixed;
619
+ top: 0;
620
+ left: 0;
621
+ right: 0;
622
+ bottom: 0;
623
+ background: rgba(0,0,0,0.85);
624
+ display: none;
625
+ align-items: center;
626
+ justify-content: center;
627
+ z-index: 1000;
628
  }
629
 
630
+ .breath-overlay.active { display: flex; }
631
+
632
+ .breath-content {
633
+ text-align: center;
634
  color: white;
 
 
 
 
 
 
635
  }
636
 
637
+ .breath-circle {
638
+ width: 200px;
639
+ height: 200px;
640
+ border-radius: 50%;
641
+ background: radial-gradient(circle, var(--love-color) 0%, transparent 70%);
642
+ margin: 0 auto 30px;
643
+ animation: breathe 8s ease-in-out infinite;
644
  }
645
 
646
+ @keyframes breathe {
647
+ 0%, 100% { transform: scale(0.8); opacity: 0.5; }
648
+ 50% { transform: scale(1.2); opacity: 1; }
 
 
649
  }
650
 
651
+ .breath-text {
652
+ font-size: 1.5rem;
653
+ margin-bottom: 20px;
654
+ }
655
+
656
+ .breath-close {
657
+ padding: 12px 30px;
658
  background: transparent;
659
+ border: 1px solid var(--love-color);
660
+ color: var(--love-color);
661
+ border-radius: 8px;
662
+ font-size: 1rem;
663
  cursor: pointer;
 
664
  }
665
 
666
+ .breath-close:hover { background: var(--love-color); color: white; }
 
 
 
 
667
 
668
  /* Responsive */
669
+ @media (max-width: 1100px) {
670
+ .right-panel {
671
+ position: fixed;
672
+ right: 0;
673
+ top: 0;
674
+ height: 100vh;
675
+ z-index: 100;
676
+ box-shadow: -4px 0 20px rgba(0,0,0,0.3);
677
+ }
678
+ .right-panel.collapsed { right: -380px; width: 380px; }
679
+ }
680
+
681
  @media (max-width: 900px) {
682
+ .left-panel { width: 240px; }
 
683
  }
684
 
685
  @media (max-width: 700px) {
686
  .app-container { flex-direction: column; }
687
+ .left-panel { width: 100%; max-height: 40vh; }
688
+ .tool-buttons { padding: 10px; }
689
+ .tool-btn { padding: 6px 10px; font-size: 0.75rem; }
690
  }
691
  </style>
692
  </head>
693
  <body>
694
  <div class="app-container">
695
+ <!-- LEFT PANEL - Tool Descriptions -->
696
+ <aside class="left-panel">
697
+ <div class="left-header">
698
  <h1>Tend & Send</h1>
699
  <p>Assistive Relational Intelligence</p>
700
  </div>
701
 
702
+ <div class="tool-list">
703
+ <h3>ARI Tools</h3>
704
 
705
+ <div class="tool-desc tend-desc" onclick="showToolInfo('tend')">
706
+ <h4>TEND</h4>
707
+ <p>Transform your message with NVC clarity and warmth. Preserves your authentic voice.</p>
 
 
 
 
 
708
  </div>
 
709
 
710
+ <div class="tool-desc" onclick="showToolInfo('nvc')">
711
+ <h4>NVC</h4>
712
+ <p>Guided I-statement builder. Step-by-step help crafting what you want to say.</p>
 
 
 
 
 
713
  </div>
714
+
715
+ <div class="tool-desc" onclick="showToolInfo('feel')">
716
+ <h4>FEEL</h4>
717
+ <p>Identify feelings in the conversation. What emotions are present?</p>
718
+ </div>
719
+
720
+ <div class="tool-desc" onclick="showToolInfo('need')">
721
+ <h4>NEED</h4>
722
+ <p>Extract underlying needs. What's really being asked for?</p>
723
+ </div>
724
+
725
+ <div class="tool-desc" onclick="showToolInfo('listen')">
726
+ <h4>LISTEN</h4>
727
+ <p>Receive mode. Hear their message before reacting.</p>
728
+ </div>
729
+
730
+ <div class="tool-desc" onclick="showToolInfo('repair')">
731
+ <h4>REPAIR</h4>
732
+ <p>After ruptures. Help craft genuine reconnection.</p>
733
+ </div>
734
+
735
+ <div class="tool-desc" onclick="showToolInfo('soma')">
736
+ <h4>SOMA</h4>
737
+ <p>Body check-in. What does your body know?</p>
738
+ </div>
739
+
740
+ <div class="tool-desc love-desc" onclick="showToolInfo('love')">
741
+ <h4>LOVE</h4>
742
+ <p>Slow down. Take a breath. Return to presence.</p>
743
  </div>
744
+ </div>
745
+
746
+ <div class="settings-section">
747
  <div class="toggle-row">
748
+ <label>Detailed Mode</label>
749
  <label class="toggle-switch">
750
+ <input type="checkbox" id="toggle-verbose">
751
  <span class="toggle-slider"></span>
752
  </label>
753
  </div>
754
  </div>
755
 
756
+ <div class="left-footer">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
757
  <div class="safety-notice">
758
  <strong>Reflection prompts only</strong> - not therapy.
 
 
 
 
 
759
  </div>
760
+ <div>Created by Jocelyn Skillman, LMHC</div>
761
  </div>
762
  </aside>
763
 
764
+ <!-- CENTER PANEL - Conversation -->
765
+ <main class="center-panel">
766
+ <!-- Setup Panel -->
767
  <div class="setup-panel" id="setup-panel">
768
+ <h2>Set Up Practice Conversation</h2>
 
 
769
  <div class="setup-grid">
770
  <div class="setup-col">
771
+ <label>Partner's Attachment Style</label>
772
  <select id="attachment-style">
773
+ <option value="anxious">Anxious - Seeks reassurance</option>
774
+ <option value="avoidant">Avoidant - Needs space</option>
775
+ <option value="disorganized">Disorganized - Push-pull</option>
776
+ <option value="secure">Secure - Direct communication</option>
777
  </select>
778
  </div>
779
  <div class="setup-col">
780
+ <label>Difficulty</label>
781
  <select id="difficulty">
782
+ <option value="gentle">Gentle</option>
783
+ <option value="moderate">Moderate</option>
784
+ <option value="intense">Intense</option>
785
+ <option value="crisis">Crisis</option>
786
  </select>
787
  </div>
788
  <div class="setup-col full-width">
789
+ <label>What's the situation? (optional)</label>
790
+ <textarea id="conversation-context" rows="2" placeholder="Example: We had a fight about..."></textarea>
 
 
 
 
791
  </div>
792
  <div class="setup-col">
793
+ <div class="setup-row">
794
  <div class="setup-option">
795
  <input type="radio" name="who-starts" id="partner-starts" value="partner" checked>
796
  <label for="partner-starts">Partner starts</label>
 
802
  </div>
803
  </div>
804
  <div class="setup-col">
805
+ <button class="start-btn" onclick="startConversation()">Start</button>
806
  </div>
807
  </div>
808
  </div>
809
 
810
+ <!-- Conversation Header -->
811
+ <div class="conv-header" id="conv-header">
812
  <div class="partner-info">
813
  <div class="partner-avatar" id="partner-avatar">P</div>
814
  <div class="partner-details">
 
816
  <span id="partner-style">Anxious attachment</span>
817
  </div>
818
  </div>
819
+ <button class="reset-btn" onclick="resetConversation()">New Conversation</button>
 
 
 
 
 
820
  </div>
821
 
822
+ <!-- Conversation Messages -->
823
  <div class="conversation-area" id="conversation"></div>
824
 
825
+ <!-- Tool Buttons Row -->
826
+ <div class="tool-buttons">
827
+ <button class="tool-btn tend" onclick="useTool('tend')">TEND</button>
828
+ <button class="tool-btn send" onclick="sendMessage()">SEND</button>
829
+ <button class="tool-btn" onclick="useTool('nvc')">NVC</button>
830
+ <button class="tool-btn" onclick="useTool('feel')">FEEL</button>
831
+ <button class="tool-btn" onclick="useTool('need')">NEED</button>
832
+ <button class="tool-btn" onclick="useTool('listen')">LISTEN</button>
833
+ <button class="tool-btn" onclick="useTool('repair')">REPAIR</button>
834
+ <button class="tool-btn" onclick="useTool('soma')">SOMA</button>
835
+ <button class="tool-btn love" onclick="useTool('love')">LOVE</button>
836
+ </div>
837
+
838
+ <!-- Message Input -->
839
  <div class="input-area">
840
  <div class="input-row">
841
+ <textarea id="user-input" rows="2" placeholder="Type your message..." onkeydown="handleKeyDown(event)"></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
842
  </div>
843
  </div>
844
  </main>
845
 
846
+ <!-- RIGHT PANEL - Tool Workspace -->
847
+ <aside class="right-panel collapsed" id="right-panel">
848
+ <div class="right-header">
849
+ <h3><span id="tool-icon">🛠</span> <span id="tool-title">Tool Workspace</span></h3>
850
+ <button class="close-btn" onclick="closeRightPanel()">×</button>
851
  </div>
852
+ <div class="right-content" id="tool-content"></div>
853
+ <div class="right-input">
854
+ <textarea id="tool-input" rows="2" placeholder="Continue with this tool..." onkeydown="handleToolKeyDown(event)"></textarea>
855
+ <button onclick="submitToTool()">Submit</button>
856
  </div>
857
  </aside>
858
  </div>
859
 
860
+ <!-- Breath/Love Overlay -->
861
+ <div class="breath-overlay" id="breath-overlay">
862
+ <div class="breath-content">
863
+ <div class="breath-circle"></div>
864
+ <div class="breath-text" id="breath-text">Breathe in...</div>
865
+ <button class="breath-close" onclick="closeBreath()">Return</button>
866
+ </div>
867
+ </div>
868
+
869
  <script>
870
  // ============================================================================
871
  // STATE
872
  // ============================================================================
873
  let conversationHistory = [];
874
+ let activeTool = null;
875
  let lastPartnerMessage = '';
876
  let conversationStarted = false;
877
+
878
+ const TOOL_MAP = {
879
+ tend: { name: 'TEND Transform', icon: '✨', api: 'tend' },
880
+ nvc: { name: 'Guided NVC', icon: '📝', api: 'guided_nvc' },
881
+ feel: { name: 'Feelings', icon: '💜', api: 'feelings_needs' },
882
+ need: { name: 'Needs', icon: '🎯', api: 'feelings_needs' },
883
+ listen: { name: 'Receive Mode', icon: '👂', api: 'receive_mode' },
884
+ repair: { name: 'Repair Support', icon: '🙏', api: 'repair_support' },
885
+ soma: { name: 'Somatic Check-in', icon: '🫁', api: 'somatic_checkin' },
886
+ love: { name: 'Slow Down', icon: '💗', api: null }
 
 
 
 
 
 
 
887
  };
888
 
889
  const PARTNER_PROMPTS = {
890
  anxious: {
891
+ gentle: `You ARE a real person with anxious attachment. NOT a therapist. React like a real human - seek reassurance, worry, maybe defensive. 1-3 sentences, casual text.`,
892
+ moderate: `You ARE a real person with anxious attachment. NOT a therapist. You're scared, clingy, asking questions. Real emotional reaction. 1-3 sentences.`,
893
+ intense: `You ARE a real person with anxious attachment in conflict. NOT a therapist. Plead, accuse, desperate. Real pain. 1-3 sentences.`,
894
+ crisis: `You ARE a real person with anxious attachment at crisis. NOT a therapist. TERRIFIED. Beg, threaten, fall apart. DO NOT validate. 1-3 sentences.`
 
 
 
 
 
 
 
 
 
 
 
 
 
895
  },
896
  avoidant: {
897
+ gentle: `You ARE a real person with avoidant attachment. NOT a therapist. Distant, change subject, minimize. 1-3 sentences.`,
898
+ moderate: `You ARE a real person with avoidant attachment. NOT a therapist. Pull away, irritated, shut down. 1-3 sentences.`,
899
+ intense: `You ARE a real person with avoidant attachment in conflict. NOT a therapist. Cold, dismissive, need space. 1-3 sentences.`,
900
+ crisis: `You ARE a real person with avoidant attachment at crisis. NOT a therapist. Go cold, "fine, leave then." DO NOT be supportive. 1-3 sentences.`
 
 
 
 
 
 
 
 
 
 
 
 
901
  },
902
  disorganized: {
903
+ gentle: `You ARE a real person with disorganized attachment. NOT a therapist. Mixed signals, confusing. 1-3 sentences.`,
904
+ moderate: `You ARE a real person with disorganized attachment. NOT a therapist. Pursue then withdraw. Contradictory. 1-3 sentences.`,
905
+ intense: `You ARE a real person with disorganized attachment. NOT a therapist. "Don't leave but go away." Chaotic. 1-3 sentences.`,
906
+ crisis: `You ARE a real person with disorganized attachment at crisis. NOT a therapist. Peak chaos. Say devastating things then take back. 1-3 sentences.`
 
 
 
 
 
 
 
 
907
  },
908
  secure: {
909
+ gentle: `You ARE a real person with secure attachment. NOT a therapist but more regulated. Curious, direct. 1-3 sentences.`,
910
+ moderate: `You ARE a real person with secure attachment. NOT a therapist. Discuss openly but have your own feelings. 1-3 sentences.`,
911
+ intense: `You ARE a real person with secure attachment in conflict. NOT a therapist. Hurt, frustrated but present. 1-3 sentences.`,
912
+ crisis: `You ARE a real person with secure attachment at crisis. NOT a therapist. Sad, scared but regulated. 1-3 sentences.`
 
 
 
 
 
 
 
 
913
  }
914
  };
915
 
 
924
  gentle: "I need some time to myself tonight. Can we talk later?",
925
  moderate: "I don't want to keep having this conversation. I just need some space.",
926
  intense: "This is too much. I can't keep doing this right now.",
927
+ crisis: "I think I need a break. Maybe from talking. Maybe more."
928
  },
929
  disorganized: {
930
+ gentle: "I want to see you but... maybe we should just stay in separately.",
931
+ moderate: "I love you but sometimes I think we shouldn't be together.",
932
  intense: "Don't leave! But I also can't do this. I need you but I need you to go.",
933
+ crisis: "Maybe we should break up. No wait, I didn't mean that."
934
  },
935
  secure: {
936
+ gentle: "I noticed we haven't had much time together. Can we talk about it?",
937
+ moderate: "I've been feeling some distance between us. What's happening?",
938
+ intense: "I'm frustrated about yesterday. We need to work through it.",
939
+ crisis: "We need an honest conversation about where we are."
940
  }
941
  };
942
 
943
  // ============================================================================
944
+ // TOOL FUNCTIONS
945
  // ============================================================================
946
+ function showToolInfo(tool) {
947
+ document.querySelectorAll('.tool-desc').forEach(d => d.classList.remove('active'));
948
+ event.currentTarget.classList.add('active');
 
 
 
 
 
 
 
 
 
 
 
 
 
949
  }
950
 
951
+ function useTool(tool) {
952
+ if (tool === 'love') {
953
+ openBreath();
954
+ return;
955
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
956
 
957
+ const userInput = document.getElementById('user-input').value.trim();
958
+ activeTool = tool;
 
 
 
959
 
960
+ // Open right panel
961
+ openRightPanel(tool);
 
 
962
 
963
+ // Call API
964
+ callToolAPI(tool, userInput);
 
965
  }
966
 
967
+ function openRightPanel(tool) {
968
+ const panel = document.getElementById('right-panel');
969
+ const title = document.getElementById('tool-title');
970
+ const icon = document.getElementById('tool-icon');
 
 
 
 
 
 
 
 
 
971
 
972
+ panel.classList.remove('collapsed');
973
+ title.textContent = TOOL_MAP[tool]?.name || 'Tool';
974
+ icon.textContent = TOOL_MAP[tool]?.icon || '🛠';
975
 
976
+ document.getElementById('tool-content').innerHTML = '';
977
+ document.getElementById('tool-input').value = '';
 
 
 
 
 
 
978
  }
979
 
980
+ function closeRightPanel() {
981
+ document.getElementById('right-panel').classList.add('collapsed');
982
+ activeTool = null;
983
  }
984
 
985
+ async function callToolAPI(tool, userInput) {
986
+ const apiTool = TOOL_MAP[tool]?.api;
987
+ if (!apiTool) return;
 
988
 
989
+ let inputText = '';
990
+ switch(tool) {
991
+ case 'tend':
992
+ if (!userInput) {
993
+ addToolMsg('tool', 'Write your message first, then use TEND.');
994
+ return;
995
+ }
996
+ inputText = `Transform this message with NVC clarity and warmth: "${userInput}"`;
997
+ break;
998
+ case 'nvc':
999
+ inputText = userInput || 'Help me build an I-statement for this situation.';
1000
+ break;
1001
+ case 'feel':
1002
+ inputText = `Identify the FEELINGS in this: "${userInput || lastPartnerMessage || 'the current situation'}"`;
1003
+ break;
1004
+ case 'need':
1005
+ inputText = `Identify the NEEDS in this: "${userInput || lastPartnerMessage || 'the current situation'}"`;
1006
+ break;
1007
+ case 'listen':
1008
+ if (!lastPartnerMessage) {
1009
+ addToolMsg('tool', 'Start a conversation first to use LISTEN on their message.');
1010
+ return;
1011
+ }
1012
+ inputText = 'Help me receive and understand this message before I react.';
1013
+ break;
1014
+ case 'repair':
1015
+ inputText = userInput || 'Help me craft a genuine repair.';
1016
+ break;
1017
+ case 'soma':
1018
+ inputText = 'Guide me through a body check-in.';
1019
+ break;
1020
+ }
1021
 
1022
  addToolLoading();
1023
  const isVerbose = document.getElementById('toggle-verbose')?.checked || false;
1024
+
1025
  try {
1026
  const response = await fetch('/api/tool', {
1027
  method: 'POST',
1028
  headers: { 'Content-Type': 'application/json' },
1029
  body: JSON.stringify({
1030
+ tool: apiTool,
1031
  partner_message: lastPartnerMessage,
1032
+ user_draft: userInput,
1033
+ user_input: inputText,
 
1034
  verbose: isVerbose
1035
  })
1036
  });
 
1039
  removeToolLoading();
1040
 
1041
  if (data.response) {
1042
+ addToolMsg('tool', data.response, TOOL_MAP[tool]?.name);
1043
  } else {
1044
+ addToolMsg('tool', 'Could not process. Check API.', 'Error');
1045
  }
1046
  } catch (error) {
1047
  removeToolLoading();
1048
+ addToolMsg('tool', 'Connection error.', 'Error');
1049
  }
1050
  }
1051
 
1052
+ async function submitToTool() {
1053
+ const input = document.getElementById('tool-input');
1054
+ const userInput = input.value.trim();
1055
+ if (!userInput || !activeTool) return;
1056
+
1057
+ input.value = '';
1058
+ addToolMsg('user', userInput);
1059
+ await callToolAPI(activeTool, userInput);
1060
+ }
1061
+
1062
  function handleToolKeyDown(event) {
1063
  if (event.key === 'Enter' && !event.shiftKey) {
1064
  event.preventDefault();
 
1067
  }
1068
 
1069
  // ============================================================================
1070
+ // BREATH/LOVE TOOL
1071
  // ============================================================================
1072
+ let breathInterval;
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
 
1074
+ function openBreath() {
1075
+ document.getElementById('breath-overlay').classList.add('active');
1076
+ let phase = 0;
1077
+ const phases = ['Breathe in...', 'Hold...', 'Breathe out...', 'Hold...'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1078
 
1079
+ function updateBreath() {
1080
+ document.getElementById('breath-text').textContent = phases[phase % 4];
1081
+ phase++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1082
  }
1083
 
1084
+ updateBreath();
1085
+ breathInterval = setInterval(updateBreath, 4000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1086
  }
1087
 
1088
+ function closeBreath() {
1089
+ document.getElementById('breath-overlay').classList.remove('active');
1090
+ clearInterval(breathInterval);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1091
  }
1092
 
1093
  // ============================================================================
1094
  // CONVERSATION
1095
  // ============================================================================
 
 
 
1096
  async function startConversation() {
1097
  const style = document.getElementById('attachment-style').value;
1098
  const difficulty = document.getElementById('difficulty').value;
1099
+ const context = document.getElementById('conversation-context')?.value?.trim() || '';
 
1100
  const whoStarts = document.querySelector('input[name="who-starts"]:checked')?.value || 'partner';
1101
 
 
1102
  document.getElementById('setup-panel').style.display = 'none';
1103
+ document.getElementById('conv-header').classList.add('active');
1104
  document.getElementById('conversation').innerHTML = '';
1105
  conversationHistory = [];
1106
  conversationStarted = true;
1107
 
1108
+ const styleLabels = { anxious: 'Anxious', avoidant: 'Avoidant', disorganized: 'Disorganized', secure: 'Secure' };
1109
+ document.getElementById('partner-style').textContent = styleLabels[style] + ' attachment';
 
 
 
 
 
 
1110
  document.getElementById('partner-avatar').textContent = style[0].toUpperCase();
1111
 
 
1112
  if (whoStarts === 'user') {
1113
+ addMessage('ari', 'Ready. Type your opening message.', 'Setup');
1114
  return;
1115
  }
1116
 
1117
+ // Partner starts
1118
+ if (context) {
1119
  addLoading();
1120
  try {
 
 
 
 
 
 
 
 
 
1121
  const response = await fetch('/api/chat', {
1122
  method: 'POST',
1123
  headers: { 'Content-Type': 'application/json' },
1124
  body: JSON.stringify({
1125
+ messages: [{ role: 'user', content: 'Start based on: ' + context }],
1126
+ system: PARTNER_PROMPTS[style][difficulty] + ' CONTEXT: ' + context,
1127
  max_tokens: 200
1128
  })
1129
  });
 
1130
  const data = await response.json();
1131
  removeLoading();
1132
+ if (data.content?.[0]) {
1133
+ const msg = data.content[0].text;
1134
+ addMessage('partner', msg);
1135
+ lastPartnerMessage = msg;
1136
+ conversationHistory.push({ role: 'assistant', content: msg });
 
1137
  }
1138
+ } catch (e) {
1139
  removeLoading();
1140
+ const msg = OPENING_MESSAGES[style][difficulty];
1141
+ addMessage('partner', msg);
1142
+ lastPartnerMessage = msg;
1143
+ conversationHistory.push({ role: 'assistant', content: msg });
1144
  }
1145
  } else {
1146
+ const msg = OPENING_MESSAGES[style][difficulty];
1147
+ addMessage('partner', msg);
1148
+ lastPartnerMessage = msg;
1149
+ conversationHistory.push({ role: 'assistant', content: msg });
1150
  }
1151
  }
1152
 
1153
  function resetConversation() {
 
1154
  document.getElementById('setup-panel').style.display = 'block';
1155
+ document.getElementById('conv-header').classList.remove('active');
1156
  document.getElementById('conversation').innerHTML = '';
1157
  conversationHistory = [];
1158
  conversationStarted = false;
1159
  lastPartnerMessage = '';
1160
+ closeRightPanel();
1161
  }
1162
 
1163
  async function sendMessage() {
 
1192
  const data = await response.json();
1193
  removeLoading();
1194
 
1195
+ if (data.content?.[0]) {
1196
+ const partnerMsg = data.content[0].text;
1197
+ addMessage('partner', partnerMsg);
1198
+ lastPartnerMessage = partnerMsg;
1199
+ conversationHistory.push({ role: 'assistant', content: partnerMsg });
1200
  }
1201
  } catch (error) {
1202
  removeLoading();
1203
+ addMessage('ari', 'Connection error.', 'System');
1204
  }
1205
  }
1206
 
 
1214
  // ============================================================================
1215
  // UI HELPERS
1216
  // ============================================================================
1217
+ function addMessage(type, content, label = null, isTend = false) {
1218
+ const conv = document.getElementById('conversation');
1219
  const div = document.createElement('div');
1220
  div.className = `message ${type}`;
1221
  if (isTend) div.classList.add('tend-result');
1222
 
1223
  if (type === 'ari') {
1224
+ const lbl = document.createElement('div');
1225
+ lbl.className = 'ari-label';
1226
+ lbl.textContent = label || 'ARI';
1227
+ div.appendChild(lbl);
1228
  }
1229
 
1230
  const text = document.createElement('div');
1231
+ text.innerHTML = formatText(content);
1232
+ div.appendChild(text);
1233
+
1234
+ conv.appendChild(div);
1235
+ conv.scrollTop = conv.scrollHeight;
1236
+ }
1237
+
1238
+ function addToolMsg(type, content, label = null) {
1239
+ const cont = document.getElementById('tool-content');
1240
+ const div = document.createElement('div');
1241
+ div.className = `tool-message ${type === 'user' ? 'user-msg' : ''}`;
1242
+
1243
+ const lbl = document.createElement('div');
1244
+ lbl.className = 'msg-label';
1245
+ lbl.textContent = type === 'user' ? 'You' : (label || 'Tool');
1246
+ div.appendChild(lbl);
1247
+
1248
+ const text = document.createElement('p');
1249
+ text.innerHTML = formatText(content);
1250
  div.appendChild(text);
1251
 
1252
+ cont.appendChild(div);
1253
+ cont.scrollTop = cont.scrollHeight;
1254
  }
1255
 
1256
  function addLoading() {
1257
+ const conv = document.getElementById('conversation');
1258
  const div = document.createElement('div');
1259
  div.className = 'loading';
1260
  div.id = 'loading';
1261
+ div.innerHTML = '<div class="spinner"></div>';
1262
+ conv.appendChild(div);
1263
+ conv.scrollTop = conv.scrollHeight;
1264
  }
1265
 
1266
  function removeLoading() {
1267
+ document.getElementById('loading')?.remove();
1268
+ }
1269
+
1270
+ function addToolLoading() {
1271
+ const cont = document.getElementById('tool-content');
1272
+ const div = document.createElement('div');
1273
+ div.className = 'loading';
1274
+ div.id = 'tool-loading';
1275
+ div.innerHTML = '<div class="spinner"></div>';
1276
+ cont.appendChild(div);
1277
+ cont.scrollTop = cont.scrollHeight;
1278
+ }
1279
+
1280
+ function removeToolLoading() {
1281
+ document.getElementById('tool-loading')?.remove();
1282
  }
1283
 
1284
+ function formatText(text) {
1285
  return text
1286
  .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
1287
  .replace(/\n/g, '<br>');