evaldoc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.env.staging.example +19 -0
  2. package/.firebaserc +6 -0
  3. package/.github/workflows/auto-merge-to-main.yml +28 -0
  4. package/.github/workflows/ci.yml +27 -0
  5. package/.github/workflows/npxhub-publish.yml +170 -0
  6. package/.prettierignore +4 -0
  7. package/.prettierrc.json +7 -0
  8. package/README.md +142 -0
  9. package/SRS.md +448 -0
  10. package/apphosting.staging.yaml +48 -0
  11. package/apphosting.yaml +50 -0
  12. package/bin/evaldoc.cjs +105 -0
  13. package/cors.json +8 -0
  14. package/docs/ARCHITECTURE.md +769 -0
  15. package/docs/ARCHITECTURE_SUMMARY.md +323 -0
  16. package/docs/DEPLOYMENT.md +86 -0
  17. package/docs/DEPLOYMENT_STEPS.md +373 -0
  18. package/docs/DEPLOY_FIRESTORE_RULES.md +239 -0
  19. package/docs/DEPLOY_STORAGE_RULES.md +82 -0
  20. package/docs/DOCTOR_TUTORIAL.md +467 -0
  21. package/docs/FIREBASE_STORAGE_CORS_FIX.md +131 -0
  22. package/docs/FIXES_APPLIED.md +128 -0
  23. package/docs/IMPLEMENTATION.md +498 -0
  24. package/docs/IMPLEMENTATION_COMPLETE.md +317 -0
  25. package/docs/LLM-SETUP.md +216 -0
  26. package/docs/MANUAL_TEST_GUIDE.md +327 -0
  27. package/docs/PATIENT_GUIDE.md +451 -0
  28. package/docs/PLATFORM_OVERVIEW.md +194 -0
  29. package/docs/QUICKSTART.md +252 -0
  30. package/docs/QUICK_REFERENCE.md +222 -0
  31. package/docs/READY_TO_TEST.md +383 -0
  32. package/docs/RUN.md +237 -0
  33. package/docs/START_TESTING.md +130 -0
  34. package/docs/STRIPE_CONNECT_SETUP.md +454 -0
  35. package/docs/SYSTEM_READY.md +345 -0
  36. package/docs/THREADS_IMPLEMENTATION_COMPLETE.md +431 -0
  37. package/docs/THREAD_IMPLEMENTATION_GUIDE.md +345 -0
  38. package/docs/firebasefiles.md +21 -0
  39. package/docs/homepage.md +1208 -0
  40. package/docs/payments.md +159 -0
  41. package/eslint.config.js +76 -0
  42. package/firebase.json +27 -0
  43. package/firestore.indexes.json +88 -0
  44. package/firestore.rules +125 -0
  45. package/functions/package-lock.json +2884 -0
  46. package/functions/package.json +20 -0
  47. package/functions/src/analytics.js +101 -0
  48. package/functions/src/audit.js +28 -0
  49. package/functions/src/auth.js +19 -0
  50. package/functions/src/drafts.js +128 -0
  51. package/functions/src/feedback.js +164 -0
  52. package/functions/src/firebase-init.js +29 -0
  53. package/functions/src/i18n.js +134 -0
  54. package/functions/src/limits.js +27 -0
  55. package/functions/src/llm.js +271 -0
  56. package/functions/src/ner.js +162 -0
  57. package/functions/src/notifications.js +273 -0
  58. package/functions/src/orgs.js +127 -0
  59. package/functions/src/patient-api.js +545 -0
  60. package/functions/src/payment.js +830 -0
  61. package/functions/src/safety.js +188 -0
  62. package/functions/src/stripe-config.js +33 -0
  63. package/functions/src/topics.js +94 -0
  64. package/functions/src/triage.js +191 -0
  65. package/functions/src/utils.js +28 -0
  66. package/functions/src/validate.js +177 -0
  67. package/functions/test-firebase.js +36 -0
  68. package/jest.config.js +18 -0
  69. package/package.json +52 -0
  70. package/public/404.html +93 -0
  71. package/public/app/analytics.html +336 -0
  72. package/public/app/analytics.js +177 -0
  73. package/public/app/app.css +1400 -0
  74. package/public/app/app.js +1754 -0
  75. package/public/app/config.js +14 -0
  76. package/public/app/inbox-threads.js +843 -0
  77. package/public/app/inbox.html +598 -0
  78. package/public/app/inbox.js +397 -0
  79. package/public/app/index.html +612 -0
  80. package/public/assets/logo.jpg +0 -0
  81. package/public/chat.html +137 -0
  82. package/public/css/chat.css +870 -0
  83. package/public/css/landing-enhance.css +298 -0
  84. package/public/css/styles.css +1198 -0
  85. package/public/favicon.svg +5 -0
  86. package/public/find-doctor.html +220 -0
  87. package/public/index.html +399 -0
  88. package/public/js/chat-threads.js +513 -0
  89. package/public/js/chat.js +832 -0
  90. package/public/js/search.js +41 -0
  91. package/public/privacy.html +101 -0
  92. package/public/robots.txt +6 -0
  93. package/public/site.webmanifest +12 -0
  94. package/public/terms.html +110 -0
  95. package/scripts/create-test-provider.js +55 -0
  96. package/scripts/deploy-staging.sh +51 -0
  97. package/scripts/fix-provider.js +39 -0
  98. package/scripts/quick-test.sh +37 -0
  99. package/scripts/test-chat.sh +54 -0
  100. package/scripts/test-multi-turn.js +137 -0
  101. package/scripts/test-ner-simple.js +28 -0
  102. package/scripts/test-ner.js +60 -0
  103. package/scripts/test-stripe-connect.js +304 -0
  104. package/scripts/test-webhook-secret.sh +24 -0
  105. package/server.js +3864 -0
  106. package/storage.rules +22 -0
  107. package/test-api.json +1 -0
  108. package/tests/emulator/README.md +38 -0
  109. package/tests/emulator/counters.emulator.test.js +81 -0
  110. package/tests/integration/api-account-selfserve.test.js +312 -0
  111. package/tests/integration/api-admin-digest.test.js +202 -0
  112. package/tests/integration/api-chat-satisfaction.test.js +259 -0
  113. package/tests/integration/api-doctor-drafts.test.js +236 -0
  114. package/tests/integration/api-doctor-inbox.test.js +401 -0
  115. package/tests/integration/api-org.test.js +328 -0
  116. package/tests/integration/api-payment.test.js +664 -0
  117. package/tests/integration/api-provider-settings.test.js +475 -0
  118. package/tests/integration/api-qr.test.js +121 -0
  119. package/tests/integration/api-security.test.js +241 -0
  120. package/tests/integration/api-stripe-search.test.js +303 -0
  121. package/tests/integration/api-webhook-deletion.test.js +231 -0
  122. package/tests/mocks/firebase-admin.js +220 -0
  123. package/tests/unit/analytics.test.js +151 -0
  124. package/tests/unit/audit-bugs.test.js +203 -0
  125. package/tests/unit/chat-ui-logic.test.js +283 -0
  126. package/tests/unit/drafts.test.js +104 -0
  127. package/tests/unit/feedback.test.js +148 -0
  128. package/tests/unit/i18n.test.js +229 -0
  129. package/tests/unit/llm-retry.test.js +122 -0
  130. package/tests/unit/notifications.test.js +155 -0
  131. package/tests/unit/orgs.test.js +328 -0
  132. package/tests/unit/payment.test.js +889 -0
  133. package/tests/unit/safety-edge-cases.test.js +509 -0
  134. package/tests/unit/safety-srs-compliance.test.js +252 -0
  135. package/tests/unit/safety.test.js +291 -0
  136. package/tests/unit/server-helpers.test.js +774 -0
  137. package/tests/unit/topics-limits.test.js +158 -0
  138. package/tests/unit/triage.test.js +152 -0
  139. package/tests/unit/validate.test.js +408 -0
@@ -0,0 +1,870 @@
1
+ :root {
2
+ --bg-dark: #0e1a14;
3
+ --bg-mid: #1b3a2b;
4
+ --bg-light: #f1efe7;
5
+ --accent: #f4b860;
6
+ --accent-2: #4fb3a4;
7
+ --text: #0b0b0b;
8
+ --card: rgba(255, 255, 255, 0.9);
9
+ --shadow: 0 22px 60px rgba(14, 26, 20, 0.25);
10
+ }
11
+
12
+ * {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ .hidden {
17
+ display: none;
18
+ }
19
+
20
+ /* Screen-reader only utility */
21
+ .sr-only {
22
+ position: absolute;
23
+ width: 1px;
24
+ height: 1px;
25
+ padding: 0;
26
+ margin: -1px;
27
+ overflow: hidden;
28
+ clip: rect(0, 0, 0, 0);
29
+ white-space: nowrap;
30
+ border: 0;
31
+ }
32
+
33
+ /* Contact Banner */
34
+ .contact-banner {
35
+ background: linear-gradient(135deg, rgba(79, 179, 164, 0.1), rgba(79, 179, 164, 0.05));
36
+ border: 1px solid rgba(79, 179, 164, 0.3);
37
+ border-radius: 12px;
38
+ padding: 1rem;
39
+ margin-bottom: 1rem;
40
+ animation: slideDown 0.3s ease;
41
+ }
42
+
43
+ @keyframes slideDown {
44
+ from {
45
+ opacity: 0;
46
+ transform: translateY(-10px);
47
+ }
48
+ to {
49
+ opacity: 1;
50
+ transform: translateY(0);
51
+ }
52
+ }
53
+
54
+ .contact-banner-content {
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: 1rem;
58
+ }
59
+
60
+ .contact-banner-text strong {
61
+ display: block;
62
+ font-size: 1rem;
63
+ margin-bottom: 0.25rem;
64
+ color: var(--text);
65
+ }
66
+
67
+ .contact-banner-text p {
68
+ margin: 0;
69
+ font-size: 0.875rem;
70
+ color: rgba(11, 11, 11, 0.7);
71
+ }
72
+
73
+ .contact-banner-form {
74
+ display: flex;
75
+ gap: 0.5rem;
76
+ flex-wrap: wrap;
77
+ align-items: center;
78
+ }
79
+
80
+ .contact-banner-form input {
81
+ flex: 1;
82
+ min-width: 150px;
83
+ padding: 0.625rem 0.875rem;
84
+ border: 2px solid rgba(0, 0, 0, 0.1);
85
+ border-radius: 8px;
86
+ font-family: inherit;
87
+ font-size: 0.9rem;
88
+ background: white;
89
+ transition: all 0.2s;
90
+ }
91
+
92
+ .contact-banner-form input:focus {
93
+ outline: none;
94
+ border-color: var(--accent-2);
95
+ box-shadow: 0 0 0 3px rgba(79, 179, 164, 0.1);
96
+ }
97
+
98
+ .contact-banner-form button {
99
+ padding: 0.625rem 1.25rem;
100
+ border: none;
101
+ border-radius: 8px;
102
+ font-family: inherit;
103
+ font-size: 0.9rem;
104
+ font-weight: 600;
105
+ cursor: pointer;
106
+ transition: all 0.2s;
107
+ background: linear-gradient(135deg, var(--accent-2), #3a8a7e);
108
+ color: white;
109
+ }
110
+
111
+ .contact-banner-form button:hover {
112
+ transform: translateY(-1px);
113
+ box-shadow: 0 4px 12px rgba(79, 179, 164, 0.3);
114
+ }
115
+
116
+ .contact-banner-form button.dismiss-btn {
117
+ background: rgba(0, 0, 0, 0.05);
118
+ color: var(--text);
119
+ padding: 0.625rem 0.875rem;
120
+ font-size: 1.1rem;
121
+ line-height: 1;
122
+ }
123
+
124
+ .contact-banner-form button.dismiss-btn:hover {
125
+ background: rgba(0, 0, 0, 0.1);
126
+ box-shadow: none;
127
+ transform: none;
128
+ }
129
+
130
+ /* Modal styles */
131
+ .modal {
132
+ position: fixed;
133
+ inset: 0;
134
+ background: rgba(0, 0, 0, 0.75);
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ z-index: 9999;
139
+ backdrop-filter: blur(4px);
140
+ pointer-events: auto;
141
+ }
142
+
143
+ .modal-content {
144
+ background: var(--card);
145
+ padding: 2.5rem;
146
+ border-radius: 16px;
147
+ width: min(480px, 90vw);
148
+ box-shadow: 0 30px 80px rgba(0, 0, 0, 0.3);
149
+ }
150
+
151
+ .modal-content h2 {
152
+ margin: 0 0 0.75rem 0;
153
+ font-size: 1.75rem;
154
+ color: var(--text);
155
+ }
156
+
157
+ .modal-content form {
158
+ display: flex;
159
+ flex-direction: column;
160
+ gap: 1.25rem;
161
+ }
162
+
163
+ .modal-content label {
164
+ display: flex;
165
+ flex-direction: column;
166
+ gap: 0.5rem;
167
+ font-weight: 500;
168
+ font-size: 0.95rem;
169
+ color: var(--text);
170
+ }
171
+
172
+ .modal-content input {
173
+ padding: 0.875rem 1rem;
174
+ border: 2px solid rgba(0, 0, 0, 0.1);
175
+ border-radius: 8px;
176
+ font-family: inherit;
177
+ font-size: 1rem;
178
+ background: white;
179
+ transition: all 0.2s;
180
+ }
181
+
182
+ .modal-content input:focus {
183
+ outline: none;
184
+ border-color: var(--accent-2);
185
+ box-shadow: 0 0 0 3px rgba(79, 179, 164, 0.1);
186
+ }
187
+
188
+ .modal-content button {
189
+ padding: 0.875rem 1.5rem;
190
+ border: none;
191
+ border-radius: 8px;
192
+ font-family: inherit;
193
+ font-size: 1rem;
194
+ font-weight: 600;
195
+ cursor: pointer;
196
+ transition: all 0.2s;
197
+ background: linear-gradient(135deg, var(--bg-dark), var(--bg-mid));
198
+ color: white;
199
+ }
200
+
201
+ .modal-content button:hover {
202
+ transform: translateY(-1px);
203
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
204
+ }
205
+
206
+ .modal-content button.secondary {
207
+ background: rgba(0, 0, 0, 0.05);
208
+ color: var(--text);
209
+ }
210
+
211
+ .modal-content button.secondary:hover {
212
+ background: rgba(0, 0, 0, 0.1);
213
+ }
214
+
215
+ body {
216
+ margin: 0;
217
+ font-family: "Space Grotesk", sans-serif;
218
+ color: var(--text);
219
+ background: var(--bg-light);
220
+ min-height: 100vh;
221
+ }
222
+
223
+ .background {
224
+ position: fixed;
225
+ inset: 0;
226
+ background: radial-gradient(circle at 10% 20%, #ffffff 0%, transparent 50%),
227
+ radial-gradient(circle at 85% 10%, rgba(244, 184, 96, 0.35) 0%, transparent 55%),
228
+ radial-gradient(circle at 70% 80%, rgba(79, 179, 164, 0.25) 0%, transparent 60%),
229
+ linear-gradient(120deg, #fefbf4 0%, #e4f0e8 100%);
230
+ z-index: -1;
231
+ }
232
+
233
+ .page {
234
+ width: min(960px, 92vw);
235
+ margin: 48px auto 80px;
236
+ display: grid;
237
+ gap: 28px;
238
+ }
239
+
240
+ .header {
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: space-between;
244
+ gap: 24px;
245
+ }
246
+
247
+ .eyebrow {
248
+ margin: 0 0 6px;
249
+ text-transform: uppercase;
250
+ letter-spacing: 0.25em;
251
+ font-size: 12px;
252
+ color: rgba(0, 0, 0, 0.58);
253
+ }
254
+
255
+ .title {
256
+ font-size: clamp(2.2rem, 4vw, 3.4rem);
257
+ margin: 0 0 8px;
258
+ }
259
+
260
+ .subtitle {
261
+ margin: 0;
262
+ font-size: 1rem;
263
+ color: rgba(0, 0, 0, 0.62);
264
+ }
265
+
266
+ .brand {
267
+ background: linear-gradient(135deg, var(--bg-dark), var(--bg-mid));
268
+ color: white;
269
+ padding: 18px 24px;
270
+ border-radius: 18px;
271
+ box-shadow: var(--shadow);
272
+ min-width: 140px;
273
+ text-align: center;
274
+ }
275
+
276
+ .logo {
277
+ font-size: 1.6rem;
278
+ font-weight: 700;
279
+ letter-spacing: 0.1em;
280
+ }
281
+
282
+ .logo-img {
283
+ height: 60px;
284
+ width: auto;
285
+ object-fit: contain;
286
+ border-radius: 8px;
287
+ }
288
+
289
+ .info {
290
+ background: var(--card);
291
+ padding: 24px;
292
+ border-radius: 18px;
293
+ box-shadow: var(--shadow);
294
+ display: grid;
295
+ gap: 16px;
296
+ }
297
+
298
+ .welcome {
299
+ margin: 0;
300
+ font-size: 1.05rem;
301
+ }
302
+
303
+ .badges {
304
+ display: flex;
305
+ flex-wrap: wrap;
306
+ gap: 10px;
307
+ }
308
+
309
+ .badge {
310
+ background: rgba(79, 179, 164, 0.15);
311
+ padding: 6px 12px;
312
+ border-radius: 999px;
313
+ font-size: 0.85rem;
314
+ }
315
+
316
+ .chat {
317
+ background: var(--card);
318
+ padding: 24px;
319
+ border-radius: 18px;
320
+ box-shadow: var(--shadow);
321
+ display: grid;
322
+ gap: 16px;
323
+ }
324
+
325
+ .chat-header {
326
+ display: flex;
327
+ align-items: center;
328
+ justify-content: space-between;
329
+ gap: 16px;
330
+ }
331
+
332
+ select {
333
+ border: 1px solid rgba(0, 0, 0, 0.15);
334
+ border-radius: 10px;
335
+ padding: 8px 12px;
336
+ font-size: 0.95rem;
337
+ background: white;
338
+ }
339
+
340
+ .auto-detected-topic {
341
+ font-size: 0.8rem;
342
+ color: #6c757d;
343
+ background: #f0f4f8;
344
+ padding: 3px 8px;
345
+ border-radius: 12px;
346
+ white-space: nowrap;
347
+ text-transform: capitalize;
348
+ }
349
+
350
+ .messages {
351
+ min-height: 280px;
352
+ max-height: 380px;
353
+ overflow-y: auto;
354
+ display: grid;
355
+ gap: 12px;
356
+ padding-right: 6px;
357
+ }
358
+
359
+ .message {
360
+ padding: 12px 14px;
361
+ border-radius: 12px;
362
+ line-height: 1.4;
363
+ font-size: 0.95rem;
364
+ white-space: pre-wrap;
365
+ word-break: break-word;
366
+ overflow-wrap: anywhere;
367
+ max-width: 82%;
368
+ min-width: 0;
369
+ }
370
+
371
+ .message.user {
372
+ background: rgba(244, 184, 96, 0.2);
373
+ justify-self: end;
374
+ }
375
+
376
+ .message.assistant {
377
+ background: rgba(15, 25, 20, 0.08);
378
+ justify-self: start;
379
+ }
380
+
381
+ .message.assistant ul {
382
+ margin: 4px 0;
383
+ padding-left: 18px;
384
+ }
385
+
386
+ .message.assistant li {
387
+ margin-bottom: 2px;
388
+ }
389
+
390
+ .message.assistant strong {
391
+ font-weight: 600;
392
+ }
393
+
394
+ .message.doctor {
395
+ background: rgba(41, 123, 86, 0.15);
396
+ justify-self: start;
397
+ border-left: 3px solid #297B56;
398
+ }
399
+
400
+ .composer {
401
+ display: flex;
402
+ gap: 12px;
403
+ }
404
+
405
+ .rating {
406
+ display: flex;
407
+ align-items: center;
408
+ justify-content: space-between;
409
+ gap: 12px;
410
+ padding: 10px 12px;
411
+ border-radius: 12px;
412
+ background: rgba(79, 179, 164, 0.12);
413
+ }
414
+
415
+ .rating-buttons {
416
+ display: flex;
417
+ gap: 8px;
418
+ }
419
+
420
+ .rating button {
421
+ padding: 6px 10px;
422
+ font-size: 0.85rem;
423
+ background: white;
424
+ color: var(--text);
425
+ border: 1px solid rgba(0, 0, 0, 0.2);
426
+ }
427
+
428
+ input[type="text"],
429
+ .composer textarea {
430
+ flex: 1;
431
+ padding: 12px 14px;
432
+ border-radius: 12px;
433
+ border: 1px solid rgba(0, 0, 0, 0.15);
434
+ font-size: 1rem;
435
+ }
436
+
437
+ .composer textarea {
438
+ resize: none;
439
+ line-height: 1.4;
440
+ max-height: 140px;
441
+ overflow-y: auto;
442
+ font-family: inherit;
443
+ }
444
+
445
+ button {
446
+ padding: 12px 18px;
447
+ border: none;
448
+ border-radius: 12px;
449
+ background: var(--bg-dark);
450
+ color: white;
451
+ font-weight: 600;
452
+ cursor: pointer;
453
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
454
+ }
455
+
456
+ button:hover {
457
+ transform: translateY(-1px);
458
+ box-shadow: 0 12px 24px rgba(15, 25, 20, 0.2);
459
+ }
460
+
461
+ .disclaimer {
462
+ font-size: 0.85rem;
463
+ color: rgba(0, 0, 0, 0.62);
464
+ margin: 0;
465
+ transition: background-color 0.3s ease;
466
+ }
467
+
468
+ .disclaimer-highlight {
469
+ background-color: rgba(255, 200, 50, 0.15);
470
+ border-radius: 4px;
471
+ padding: 2px 4px;
472
+ }
473
+
474
+ /* Payment Modal */
475
+ .payment-modal {
476
+ position: fixed;
477
+ z-index: 1000;
478
+ left: 0;
479
+ top: 0;
480
+ width: 100%;
481
+ height: 100%;
482
+ background-color: rgba(0, 0, 0, 0.6);
483
+ display: flex;
484
+ align-items: center;
485
+ justify-content: center;
486
+ backdrop-filter: blur(4px);
487
+ }
488
+
489
+ .payment-modal-content {
490
+ background: white;
491
+ border-radius: 16px;
492
+ padding: 2rem;
493
+ max-width: 500px;
494
+ width: 90%;
495
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
496
+ }
497
+
498
+ .payment-modal-content h2 {
499
+ margin: 0 0 0.5rem 0;
500
+ color: var(--bg-dark);
501
+ }
502
+
503
+ .payment-modal-content p {
504
+ margin: 0 0 1.5rem 0;
505
+ color: #666;
506
+ }
507
+
508
+ #card-element {
509
+ border: 1px solid #e0e0e0;
510
+ border-radius: 8px;
511
+ padding: 12px;
512
+ margin-bottom: 1rem;
513
+ background: white;
514
+ }
515
+
516
+ #card-errors {
517
+ color: #dc3545;
518
+ margin-bottom: 1rem;
519
+ font-size: 0.875rem;
520
+ }
521
+
522
+ .payment-submit-btn, .payment-cancel-btn {
523
+ padding: 0.75rem 1.5rem;
524
+ border: none;
525
+ border-radius: 8px;
526
+ font-weight: 600;
527
+ cursor: pointer;
528
+ transition: all 0.2s;
529
+ margin-right: 0.5rem;
530
+ }
531
+
532
+ .payment-submit-btn {
533
+ background: linear-gradient(135deg, #4fb3a4, #3a8a7e);
534
+ color: white;
535
+ }
536
+
537
+ .payment-submit-btn:hover {
538
+ transform: translateY(-2px);
539
+ box-shadow: 0 4px 12px rgba(79, 179, 164, 0.3);
540
+ }
541
+
542
+ .payment-submit-btn:disabled {
543
+ opacity: 0.6;
544
+ cursor: not-allowed;
545
+ }
546
+
547
+ .payment-cancel-btn {
548
+ background: #f0f0f0;
549
+ color: #333;
550
+ }
551
+
552
+ .payment-cancel-btn:hover {
553
+ background: #e0e0e0;
554
+ }
555
+
556
+ /* Pricing banner */
557
+ .pricing-banner {
558
+ display: flex;
559
+ align-items: center;
560
+ gap: 8px;
561
+ background: linear-gradient(135deg, rgba(244, 184, 96, 0.12), rgba(79, 179, 164, 0.10));
562
+ border: 1px solid rgba(244, 184, 96, 0.3);
563
+ border-radius: 12px;
564
+ padding: 12px 16px;
565
+ font-size: 0.92rem;
566
+ color: var(--text);
567
+ }
568
+
569
+ .pricing-icon {
570
+ font-size: 1.2rem;
571
+ color: var(--accent);
572
+ }
573
+
574
+ /* Message timestamps */
575
+ .message-time {
576
+ display: block;
577
+ font-size: 0.7rem;
578
+ color: rgba(0, 0, 0, 0.55);
579
+ margin-top: 4px;
580
+ text-align: right;
581
+ }
582
+
583
+ /* Typing indicator */
584
+ .typing-indicator {
585
+ display: flex;
586
+ gap: 4px;
587
+ align-items: center;
588
+ padding: 14px 18px;
589
+ }
590
+
591
+ .typing-indicator .dot {
592
+ width: 8px;
593
+ height: 8px;
594
+ background: rgba(0, 0, 0, 0.3);
595
+ border-radius: 50%;
596
+ animation: typingBounce 1.4s infinite ease-in-out both;
597
+ }
598
+
599
+ .typing-indicator .dot:nth-child(1) { animation-delay: 0s; }
600
+ .typing-indicator .dot:nth-child(2) { animation-delay: 0.2s; }
601
+ .typing-indicator .dot:nth-child(3) { animation-delay: 0.4s; }
602
+
603
+ @keyframes typingBounce {
604
+ 0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
605
+ 40% { transform: scale(1); opacity: 1; }
606
+ }
607
+
608
+ /* Retry button */
609
+ .retry-btn {
610
+ display: inline;
611
+ padding: 4px 12px;
612
+ font-size: 0.85rem;
613
+ background: var(--accent-2);
614
+ color: white;
615
+ border: none;
616
+ border-radius: 6px;
617
+ cursor: pointer;
618
+ margin-left: 8px;
619
+ }
620
+
621
+ .retry-btn:hover {
622
+ transform: none;
623
+ box-shadow: none;
624
+ opacity: 0.9;
625
+ }
626
+
627
+ .error-message {
628
+ background: rgba(220, 53, 69, 0.08);
629
+ border-left: 3px solid #dc3545;
630
+ }
631
+
632
+ .emergency-banner {
633
+ background: #fff5f5;
634
+ color: #8a1c1c;
635
+ border: 1px solid #f3c0c0;
636
+ border-left: 4px solid #dc3545;
637
+ padding: 0.6rem 1rem;
638
+ border-radius: 8px;
639
+ font-size: 0.85rem;
640
+ text-align: center;
641
+ margin-bottom: 0.5rem;
642
+ }
643
+ .emergency-banner a {
644
+ color: #b91c1c;
645
+ font-weight: 700;
646
+ text-decoration: underline;
647
+ }
648
+
649
+ .provider-details {
650
+ display: flex;
651
+ gap: 1rem;
652
+ align-items: center;
653
+ margin-bottom: 0.5rem;
654
+ font-size: 0.8rem;
655
+ }
656
+ .provider-status {
657
+ font-weight: 600;
658
+ }
659
+ .status-online {
660
+ color: #16a766;
661
+ }
662
+ .status-offline {
663
+ color: #999;
664
+ }
665
+ .provider-hours {
666
+ color: #666;
667
+ }
668
+
669
+ /* Provider contact info on chat page */
670
+ .provider-contact-info {
671
+ display: flex;
672
+ flex-wrap: wrap;
673
+ gap: 0.75rem;
674
+ font-size: 0.85rem;
675
+ color: var(--text);
676
+ padding: 0.5rem 0 0;
677
+ }
678
+ .provider-contact-info a {
679
+ color: var(--accent-2);
680
+ text-decoration: none;
681
+ font-weight: 500;
682
+ }
683
+ .provider-contact-info a:hover {
684
+ text-decoration: underline;
685
+ }
686
+
687
+ /* Privacy notice */
688
+ .privacy-notice {
689
+ padding-top: 0.25rem;
690
+ }
691
+ .privacy-notice small {
692
+ font-size: 0.75rem;
693
+ color: rgba(0, 0, 0, 0.58);
694
+ line-height: 1.4;
695
+ }
696
+
697
+ /* Focus styles for accessibility */
698
+ button:focus-visible,
699
+ input:focus-visible,
700
+ select:focus-visible {
701
+ outline: 2px solid var(--accent-2);
702
+ outline-offset: 2px;
703
+ }
704
+ .rating button:focus-visible {
705
+ outline: 2px solid var(--accent);
706
+ outline-offset: 1px;
707
+ }
708
+
709
+ /* Skip to content link for keyboard users */
710
+ .skip-link {
711
+ position: absolute;
712
+ top: -40px;
713
+ left: 0;
714
+ background: var(--bg-dark);
715
+ color: white;
716
+ padding: 8px 16px;
717
+ z-index: 100;
718
+ font-size: 0.9rem;
719
+ border-radius: 0 0 8px 0;
720
+ transition: top 0.2s;
721
+ }
722
+ .skip-link:focus {
723
+ top: 0;
724
+ }
725
+
726
+ @media (max-width: 700px) {
727
+ .header {
728
+ flex-direction: column;
729
+ align-items: flex-start;
730
+ }
731
+
732
+ .brand {
733
+ width: 100%;
734
+ }
735
+
736
+ .composer {
737
+ flex-direction: column;
738
+ }
739
+
740
+ button {
741
+ width: 100%;
742
+ }
743
+
744
+ .payment-modal-content {
745
+ padding: 1.5rem;
746
+ }
747
+ }
748
+
749
+ @media (prefers-color-scheme: dark) {
750
+ :root {
751
+ --bg-light: #1a1a1a;
752
+ --text: #e5e5e5;
753
+ --card: rgba(40, 40, 40, 0.95);
754
+ }
755
+
756
+ body {
757
+ background-color: #121212;
758
+ color: #e5e5e5;
759
+ }
760
+
761
+ .message.user {
762
+ background: rgba(244, 184, 96, 0.15);
763
+ color: #f0f0f0;
764
+ }
765
+
766
+ .message.assistant {
767
+ background: rgba(255, 255, 255, 0.08);
768
+ color: #e0e0e0;
769
+ }
770
+
771
+ select, input[type="text"] {
772
+ background: #2a2a2a;
773
+ color: #e5e5e5;
774
+ border-color: rgba(255, 255, 255, 0.15);
775
+ }
776
+
777
+ .disclaimer {
778
+ color: rgba(255, 255, 255, 0.7);
779
+ }
780
+
781
+ .privacy-notice {
782
+ background: rgba(255, 255, 255, 0.05);
783
+ color: rgba(255, 255, 255, 0.7);
784
+ }
785
+
786
+ .message-time {
787
+ color: rgba(255, 255, 255, 0.55);
788
+ }
789
+
790
+ .eyebrow {
791
+ color: rgba(255, 255, 255, 0.65);
792
+ }
793
+
794
+ .subtitle {
795
+ color: rgba(255, 255, 255, 0.7);
796
+ }
797
+
798
+ .provider-contact-info a {
799
+ color: var(--accent-2);
800
+ }
801
+ }
802
+
803
+ /* ── Production UX polish ───────────────────────────────────────────── */
804
+
805
+ /* Conversation starters shown under an empty chat log */
806
+ .suggestions {
807
+ display: flex;
808
+ flex-wrap: wrap;
809
+ gap: 8px;
810
+ margin: 0 0 4px;
811
+ }
812
+ .suggestion-chip {
813
+ padding: 8px 14px;
814
+ min-height: 40px;
815
+ font-size: 0.85rem;
816
+ background: rgba(79, 179, 164, 0.12);
817
+ color: var(--text);
818
+ border: 1px solid rgba(79, 179, 164, 0.4);
819
+ border-radius: 999px;
820
+ cursor: pointer;
821
+ }
822
+ .suggestion-chip:hover { background: rgba(79, 179, 164, 0.22); }
823
+
824
+ /* Empty-state greeting bubble reuses .message.assistant */
825
+ .messages:empty::before {
826
+ content: "";
827
+ }
828
+
829
+ /* Disabled buttons read as non-interactive */
830
+ button:disabled {
831
+ opacity: 0.5;
832
+ cursor: not-allowed;
833
+ transform: none !important;
834
+ box-shadow: none !important;
835
+ }
836
+
837
+ /* Rating: end anchors + larger tap targets */
838
+ .rating-scale-label {
839
+ font-size: 0.7rem;
840
+ color: #6b7280;
841
+ white-space: nowrap;
842
+ }
843
+ .rating button {
844
+ min-width: 40px;
845
+ min-height: 40px;
846
+ }
847
+ .rating-thanks {
848
+ font-size: 0.9rem;
849
+ color: #297B56;
850
+ font-weight: 600;
851
+ }
852
+
853
+ /* Char counter legibility */
854
+ #charCounter { color: #6b7280 !important; }
855
+
856
+ /* Notched-phone safe areas + keep composer clear of the home indicator */
857
+ .page {
858
+ padding-bottom: max(48px, env(safe-area-inset-bottom));
859
+ }
860
+
861
+ /* Respect reduced-motion preference */
862
+ @media (prefers-reduced-motion: reduce) {
863
+ *, *::before, *::after {
864
+ animation-duration: 0.001ms !important;
865
+ animation-iteration-count: 1 !important;
866
+ transition-duration: 0.001ms !important;
867
+ scroll-behavior: auto !important;
868
+ }
869
+ .typing-indicator .dot { opacity: 0.6; }
870
+ }