Nischal Subedi commited on
Commit
e1f5782
·
1 Parent(s): c9550de
Files changed (1) hide show
  1. app.py +375 -282
app.py CHANGED
@@ -291,298 +291,381 @@ Answer:"""
291
  else: # Fallback if states list is problematic
292
  example_queries.append(["What basic rights do tenants have?", "California"])
293
 
294
-
295
- # --- Minimal Custom CSS for structural/behavioral overrides not covered by the theme ---
296
- # This CSS focuses on animations, specific layout tweaks, and hiding unwanted Gradio default elements.
297
  custom_css = """
298
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@700;800;900&display=swap');
299
- /* Animation for header */
300
- @keyframes fadeInSlideDown { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: translateY(0); } }
301
- /* General layout adjustments to Gradio's base container */
302
- .gradio-container > .flex.flex-col {
303
- max-width: 1120px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  margin: 0 auto !important;
305
- padding: 0 !important; /* Remove default Gradio padding */
306
- gap: 0 !important; /* Remove default Gradio gap */
307
  }
308
- /* Header specific styling */
 
309
  .app-header-wrapper {
310
- padding: 4.5rem 3.5rem !important;
 
 
 
 
311
  text-align: center !important;
312
- border-bottom-left-radius: 24px;
313
- border-bottom-right-radius: 24px;
314
- position: relative;
315
- overflow: hidden;
316
- z-index: 10;
317
- margin-bottom: 1.5rem; /* Reduced margin */
318
- border: 3px solid var(--border-color-primary) !important; /* Thicker border */
319
- border-top: none;
320
- max-width: 1120px;
321
- margin-left: auto;
322
- margin-right: auto;
323
- width: 100%;
324
- /* Enhanced background */
325
- background-image: linear-gradient(135deg, var(--background-fill-primary) 0%, var(--background-fill-secondary) 100%);
326
- box-shadow: 0 12px 35px rgba(0,0,0,0.5); /* More pronounced shadow */
327
- }
328
- .app-header { display: flex; flex-direction: column; align-items: center; position: relative; z-index: 1; }
329
  .app-header-logo {
330
- font-size: 5.5rem; margin-bottom: 0.8rem; line-height: 1;
331
- filter: drop-shadow(0 0 15px var(--primary-500));
332
- transform: translateY(-40px); opacity: 0;
333
- animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.3s;
334
  }
 
335
  .app-header-title {
336
- font-family: 'Playfair Display', serif !important;
337
- font-size: 4.2rem; font-weight: 900;
338
- margin: 0 0 0.8rem 0; letter-spacing: -0.07em;
339
- text-shadow: 0 8px 16px rgba(0,0,0,0.5);
340
- transform: translateY(-40px); opacity: 0;
341
- animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.6s;
342
  }
 
343
  .app-header-tagline {
344
- font-family: 'Inter', sans-serif !important;
345
- font-size: 1.6rem; font-weight: 300;
346
- opacity: 0.9; max-width: 900px;
347
- transform: translateY(-40px); opacity: 0;
348
- animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.9s;
349
  }
350
- /* Main dashboard container */
 
351
  .main-dashboard-container {
352
- border-radius: 24px;
353
- border: 3px solid var(--border-color-primary) !important; /* Thicker border */
354
- padding: 3.5rem !important;
355
- margin: 0 auto 0.4rem auto; /* Further reduced margin-bottom */
356
- z-index: 1; position: relative;
357
- display: flex; flex-direction: column; gap: 1.5rem; /* Further reduced gap between boxes */
358
- max-width: 1120px;
359
- }
360
- /* Card sections within the dashboard */
361
  .dashboard-card-section {
362
- border-radius: 12px;
363
- border: 2px solid var(--border-color-primary); /* Keeping at 2px for inner cards, fits better */
364
- padding: 2.5rem;
365
- box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
366
- display: flex; flex-direction: column; gap: 1.5rem;
 
367
  }
368
- /* Light mode specific inner shadow for card sections */
369
- @media (prefers-color-scheme: light) {
370
- .dashboard-card-section { box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
371
  }
372
- /* Section titles */
 
373
  .sub-section-title {
374
- font-family: 'Playfair Display', serif !important;
375
- font-size: 3.2rem !important; /* Increased font size */
376
- font-weight: 800 !important;
 
377
  text-align: center !important;
378
- margin-top: 1.5rem !important;
379
- margin-bottom: 0.8rem !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  display: block !important;
381
- width: 100% !important;
382
- }
383
- /* Text styling for custom markdown within cards */
384
- .dashboard-card-section p, .output-content-wrapper p {
385
- font-size: 1.15rem; line-height: 1.8;
386
- margin-bottom: 1.2rem;
387
- }
388
- .dashboard-card-section a, .output-content-wrapper a {
389
- text-decoration: none; font-weight: 500;
390
- }
391
- .dashboard-card-section a:hover, .output-content-wrapper a:hover { text-decoration: underline; }
392
- .dashboard-card-section strong, .output-content-wrapper strong { font-weight: 700; }
393
- /* Input layout */
394
- .input-field-group { margin-bottom: 1rem; }
395
- .input-row { display: flex; gap: 1.8rem; flex-wrap: wrap; margin-bottom: 1rem; }
396
- .input-field { flex: 1; }
397
- /* Button layout */
398
- .button-row { display: flex; gap: 2rem; margin-top: 2rem; flex-wrap: wrap; justify-content: flex-end; }
 
 
 
 
 
 
 
 
 
 
 
399
  .gradio-button {
400
- border-radius: 12px !important;
401
- padding: 1.2rem 2.8rem !important;
402
- font-size: 1.15rem !important;
403
- font-weight: 600 !important;
404
- border: 1px solid transparent !important;
405
- box-shadow: 0 6px 20px rgba(0,0,0,0.35);
406
- transition: all 0.4s cubic-bezier(0.0, 0.0, 0.2, 1);
 
 
 
 
 
 
407
  }
408
- .gradio-button:hover:not(:disabled) { transform: translateY(-6px); box-shadow: 0 12px 28px rgba(0,0,0,0.45) !important; }
409
- .gradio-button:active:not(:disabled) { transform: translateY(-3px); }
410
- .gradio-button:disabled {
411
- box-shadow: none !important;
412
- cursor: not-allowed;
413
  }
414
- .gr-button-secondary { /* Override for secondary button specific look */
 
415
  background: transparent !important;
416
- border: 2px solid var(--neutral-700) !important; /* Made border darker for contrast */
417
- box-shadow: none !important;
418
- }
419
- .gr-button-secondary:hover:not(:disabled) {
420
- background: var(--button-secondary-background-hover) !important;
421
- border-color: var(--primary-500) !important;
422
- }
423
- @media (prefers-color-scheme: light) {
424
- .gradio-button { box-shadow: 0 6px 20px rgba(0,0,0,0.1); }
425
- .gradio-button:hover:not(:disabled) { box-shadow: 0 12px 28px rgba(0,0,0,0.2) !important; }
426
- }
427
- /* Output card and placeholders */
428
- .output-card { padding: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; }
429
- .output-card .response-header {
430
- font-size: 1.8rem; font-weight: 700;
431
- margin: 0 0 1rem 0; display: flex; align-items: center; gap: 1.2rem;
432
- }
433
- .output-card .response-icon { font-size: 2rem; color: var(--primary-500); }
434
- .output-card .divider { border: none; border-top: 1px solid var(--border-color-primary); margin: 1.5rem 0 1.8rem 0; }
435
- .output-card .output-content-wrapper { font-size: 1.15rem; line-height: 1.8; }
436
- .output-card .output-content-wrapper p { margin-bottom: 1rem; }
437
- .output-card .output-content-wrapper ul, .output-card .output-content-wrapper ol { margin-left: 2.2rem; margin-bottom: 1.2rem; padding-left: 0; list-style-type: disc; }
438
- .output-card .output-content-wrapper ol { list-style-type: decimal; }
439
- .output-card .output-content-wrapper li { margin-bottom: 0.8rem; }
440
- .output-card .error-message {
441
- padding: 1.5rem 2rem; margin-top: 1.5rem; font-size: 1.1rem;
442
- border-radius: 12px;
443
- background: var(--error-background-fill) !important;
444
- color: var(--error-text-color) !important;
445
- border: 2px solid var(--error-border-color) !important;
446
- display: flex; align-items: flex-start; gap: 1.5em;
447
- }
448
- .output-card .error-message .error-icon { font-size: 1.8rem; line-height: 1; padding-top: 0.1em; }
449
- .output-card .error-details { font-size: 0.95rem; margin-top: 0.8rem; opacity: 0.9; word-break: break-word; }
450
- .output-card .placeholder {
451
- padding: 2.5rem 2rem; font-size: 1.2rem; border-radius: 12px;
452
- border: 3px dashed var(--border-color-primary);
453
- text-align: center; opacity: 0.8;
454
  }
455
- /* Examples table styling */
456
- .examples-section table.gr-samples-table {
457
- border-radius: 12px !important;
458
- border: 2px solid var(--border-color-primary) !important; /* Keeps border at 2px for consistency with other cards */
459
- overflow: hidden;
460
- box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
461
  }
462
- @media (prefers-color-scheme: light) {
463
- .examples-section table.gr-samples-table { box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  }
465
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 1rem 1.2rem !important; font-size: 1.05rem !important; border: none !important; }
466
- .examples-section table.gr-samples-table th {
467
- background: var(--block-background-fill) !important;
468
- font-weight: 600 !important; text-align: left;
469
  }
470
- .examples-section table.gr-samples-table td {
471
- border-top: 1px solid var(--border-color-primary) !important;
472
- cursor: pointer;
 
 
473
  }
474
- .examples-section table.gr-samples-table tr:hover td { background: var(--hover-color) !important; }
475
- .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
476
- /* Footer styling */
477
- .app-footer-wrapper {
478
- border-top: 3px solid var(--border-color-primary) !important; /* Thicker border */
479
- margin-top: 0.5rem;
480
- padding-top: 2.5rem;
481
- padding-bottom: 2.5rem;
482
- border-top-left-radius: 24px;
483
- border-top-right-radius: 24px;
484
- box-shadow: inset 0 8px 15px rgba(0,0,0,0.2);
485
- max-width: 1120px;
486
- margin-left: auto;
487
- margin-right: auto;
488
- width: 100%;
489
  display: flex !important;
490
- flex-direction: column !important;
491
  align-items: flex-start !important;
 
 
492
  }
493
- .app-footer {
494
- padding: 0 3.5rem !important;
495
- display: flex;
496
- flex-direction: column;
497
- align-items: stretch;
498
- width: 100%;
499
  }
500
- .app-footer p {
501
- font-size: 1.05rem !important;
502
- text-align: left !important; /* Explicitly left-aligned */
503
- margin-bottom: 1rem;
 
 
 
 
 
 
504
  }
505
- .app-footer strong {
506
- color: var(--text-color-body) !important; /* Ensure bold text is clearly visible */
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  .app-footer a {
509
- font-weight: 500;
510
- color: var(--primary-500) !important; /* Ensure links are themed and clear */
511
- text-decoration: underline; /* Added underline for clarity */
512
  }
 
513
  .app-footer a:hover {
514
- text-decoration: none; /* Remove underline on hover */
515
- }
516
- /* Hide default Gradio example labels and related elements for cleaner presentation */
517
- .gr-examples .gr-label, .gr-examples button.gr-button-filter, .gr-examples .label-wrap,
518
- .gr-examples div[data-testid*="label-text"], .gr-examples span[data-testid*="label-text"],
519
- .gr-examples div[class*="label"], .gr-examples .gr-example-label,
520
- .gr-examples .gr-box.gr-component.gradio-example > div:first-child:has(> span[data-testid]),
521
- .gr-examples .gr-box.gr-component.gradio-example > div:first-child > span,
522
- .gr-examples .gr-accordion-header, .gr-examples .gr-accordion-title, .gr-examples .gr-accordion-toggle-icon,
523
- .gr-examples .gr-accordion-header button, .gr-examples .gr-button.gr-button-filter,
524
- .gr-examples .gr-button.gr-button-primary.gr-button-filter,
525
- .gr-examples .gr-examples-header, .gr-examples .gr-examples-header > * {
526
- display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important;
527
- overflow: hidden !important; margin: 0 !important; padding: 0 !important; border: 0 !important;
528
- font-size: 0 !important; line-height: 0 !important; position: absolute !important;
529
- pointer-events: none !important;
530
- }
531
- /* Responsive Adjustments */
532
- @media (max-width: 1024px) {
533
- .gradio-container > .flex.flex-col { max-width: 960px; padding: 0 1.5rem !important; }
534
- .app-header-title { font-size: 3.8rem; } .app-header-tagline { font-size: 1.5rem; }
535
- .app-header-wrapper { padding: 2.5rem 3.5rem !important; margin-bottom: 1.2rem; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; } /* Adjusted margin */
536
- .main-dashboard-container { padding: 2.5rem !important; margin-bottom: 0.3rem; border-radius: 12px; gap: 1.2rem; } /* Adjusted gap */
537
- .dashboard-card-section { padding: 1.8rem; border-radius: 8px; }
538
- .sub-section-title { font-size: 2.8rem !important; margin-bottom: 0.7rem !important; } /* Adjusted font size */
539
- .input-row { gap: 1.5rem; } .input-field { min-width: 280px; }
540
- .gradio-textbox textarea { min-height: 160px; } .output-card .response-header { font-size: 1.7rem; }
541
- .examples-section { padding-top: 1.2rem; }
542
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.9rem 1.1rem !important; }
543
- .app-footer-wrapper { margin-top: 0.6rem; border-top-left-radius: 12px; border-top-right-radius: 12px; }
544
- .app-footer { padding: 0 1.5rem !important; }
545
  }
 
 
 
 
 
 
 
 
 
546
  @media (max-width: 768px) {
547
- .gradio-container > .flex.flex-col { padding: 0 1rem !important; }
548
- .app-header-wrapper { padding: 1.8rem 2.5rem !important; margin-bottom: 1rem; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; } /* Adjusted margin */
549
- .app-header-logo { font-size: 4.5rem; margin-bottom: 0.6rem; } .app-header-title { font-size: 3.2rem; letter-spacing: -0.06em; }
550
- .app-header-tagline { font-size: 1.3rem; }
551
- .main-dashboard-container { padding: 1.8rem !important; margin-bottom: 0.2rem; border-radius: 12px; gap: 1rem; } /* Adjusted gap */
552
- .dashboard-card-section { padding: 1.5rem; border-radius: 8px; }
553
- .sub-section-title { font-size: 2.3rem !important; margin-top: 1rem !important; margin-bottom: 0.6rem !important; } /* Adjusted font size */
554
- .input-row { flex-direction: column; gap: 1rem; } .input-field { min-width: 100%; }
555
- .gradio-textbox textarea { min-height: 140px; } .button-row { justify-content: stretch; gap: 1rem; }
556
- .gradio-button { width: 100%; padding: 1.1rem 2rem !important; font-size: 1.1rem !important; }
557
- .output-card .response-header { font-size: 1.5rem; } .output-card .response-icon { font-size: 1.7rem; }
558
- .output-card .placeholder { padding: 2.5rem 1.5rem; font-size: 1.1rem; }
559
- .examples-section { padding-top: 1.2rem; }
560
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.9rem 1.1rem !important; font-size: 1.0rem !important; }
561
- .app-footer-wrapper { margin-top: 0.6rem; border-top-left-radius: 12px; border-top-right-radius: 12px; padding-top: 2rem; padding-bottom: 2rem; }
562
- .app-footer { padding: 0 1rem !important; }
563
- }
564
- @media (max-width: 480px) {
565
- .gradio-container > .flex.flex-col { padding: 0 0.8rem !important; }
566
- .app-header-wrapper { padding: 1.2rem 1rem !important; margin-bottom: 0.8rem; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; } /* Adjusted margin */
567
- .app-header-logo { font-size: 3.8rem; margin-bottom: 0.5rem; } .app-header-title { font-size: 2.8rem; }
568
- .app-header-tagline { font-size: 1.1rem; }
569
- .main-dashboard-container { padding: 1.2rem !important; margin-bottom: 0.2rem; border-radius: 8px; gap: 0.8rem; } /* Adjusted gap */
570
- .dashboard-card-section { padding: 1rem; border-radius: 4px; }
571
- .sub-section-title { font-size: 2.0rem !important; margin-top: 0.8rem !important; margin-bottom: 0.5rem !important; } /* Adjusted font size */
572
- .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] { font-size: 1.05rem !important; padding: 1rem 1.2rem !important; }
573
- .gradio-textbox textarea { min-height: 120px; }
574
- .gradio-button { padding: 1rem 1.5rem !important; font-size: 1.0rem !important; }
575
- .output-card .response-header { font-size: 1.4rem; } .output-card .response-icon { font-size: 1.5rem; }
576
- .output-card .placeholder { padding: 2rem 1rem; font-size: 1.05rem; }
577
- .examples-section { padding-top: 0.8rem; }
578
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.6rem 0.8rem !important; font-size: 0.95rem !important; }
579
- .app-footer-wrapper { margin-top: 0.4rem; border-top-left-radius: 8px; border-top-right-radius: 8px; padding-top: 1.5rem; padding-bottom: 1.5rem; }
580
- .app-footer { padding: 0 0.8rem !important; }
581
  }
582
  """
583
 
584
- with gr.Blocks(theme="earneleh/paris", css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
585
- # --- Header Section ---
586
  with gr.Group(elem_classes="app-header-wrapper"):
587
  gr.Markdown(
588
  """
@@ -594,42 +677,49 @@ Answer:"""
594
  """
595
  )
596
 
597
- # --- Main Dashboard Console Container ---
598
  with gr.Column(elem_classes="main-dashboard-container"):
599
 
600
- # --- Section 1: Introduction and Disclaimer Card ---
601
  with gr.Group(elem_classes="dashboard-card-section"):
602
  gr.Markdown("<h3 class='sub-section-title'>Welcome & Disclaimer</h3>")
603
  gr.Markdown(
604
  """
605
- <p>Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.</p>
606
- <p><strong>Disclaimer:</strong> This tool is for informational purposes only and does not constitute legal advice. For specific legal guidance, always consult a licensed attorney in your jurisdiction.</p>
 
607
  """
608
  )
609
 
610
- # --- Section 2: OpenAI API Key Input Card ---
611
  with gr.Group(elem_classes="dashboard-card-section"):
612
  gr.Markdown("<h3 class='sub-section-title'>OpenAI API Key</h3>")
613
  api_key_input = gr.Textbox(
614
- label="OpenAI API Key", # Keep label for theme compatibility
615
- type="password", placeholder="Enter your API key (e.g., sk-...)",
616
- info="Required to process your query. Securely used per request, not stored. <a href='https://platform.openai.com/api-keys' target='_blank'>Get one free from OpenAI</a>.", lines=1,
 
 
617
  elem_classes=["input-field-group"]
618
  )
619
 
620
- # --- Section 3: Query Input and State Selection Card ---
621
  with gr.Group(elem_classes="dashboard-card-section"):
622
  gr.Markdown("<h3 class='sub-section-title'>Ask Your Question</h3>")
623
  with gr.Row(elem_classes="input-row"):
624
  with gr.Column(elem_classes="input-field", scale=3):
625
  query_input = gr.Textbox(
626
- label="Question", placeholder="E.g., What are the rules for security deposit returns in my state?",
627
- lines=5, max_lines=10,
 
 
628
  elem_classes=["input-field-group"]
629
  )
630
  with gr.Column(elem_classes="input-field", scale=1):
631
  state_input = gr.Dropdown(
632
- label="Select State", choices=dropdown_choices, value=initial_value,
 
 
633
  allow_custom_value=False,
634
  elem_classes=["input-field-group"]
635
  )
@@ -637,53 +727,56 @@ Answer:"""
637
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
638
  submit_button = gr.Button("Submit Query", variant="primary", elem_classes=["gr-button-primary"])
639
 
640
- # --- Section 4: Output Display Card ---
641
  with gr.Group(elem_classes="dashboard-card-section"):
642
  gr.Markdown("<h3 class='sub-section-title'>Legal Assistant's Response</h3>")
643
  output = gr.Markdown(
644
- value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
645
- elem_classes="output-content-wrapper output-card"
646
  )
647
 
648
- # --- Section 5: Example Questions Section ---
649
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
650
- gr.Markdown("<h3 class='sub-section-title'>Example Questions to Ask</h3>")
651
  if example_queries:
652
  gr.Examples(
653
- examples=example_queries, inputs=[query_input, state_input],
 
654
  examples_per_page=5,
655
- label="" # Hide default Examples label
656
  )
657
  else:
658
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
659
 
660
- # --- Footer Section ---
661
  with gr.Group(elem_classes="app-footer-wrapper"):
662
  gr.Markdown(
663
  """
664
- <div class="app-footer">
665
- <p>This tool is for informational purposes only and does not constitute legal advice. For legal guidance, always consult with a licensed attorney in your jurisdiction.</p>
666
- <p>Developed by <strong>Nischal Subedi</strong>.
667
- Connect on <a href="https://www.linkedin.com/in/nischal1/" target='_blank'>LinkedIn</a>
668
- or explore insights at <a href="https://datascientistinsights.substack.com/" target='_blank'>Substack</a>.</p>
669
- </div>
670
  """
671
  )
672
 
673
- # --- Event Listeners (Logic remains identical) ---
674
  submit_button.click(
675
- fn=query_interface_wrapper, inputs=[api_key_input, query_input, state_input], outputs=output, api_name="submit_query"
 
 
 
676
  )
 
677
  clear_button.click(
678
  fn=lambda: (
679
  "",
680
  "",
681
  initial_value,
682
- "<div class='placeholder output-card'>Inputs cleared. Ready for your next question.</div>"
683
  ),
684
- inputs=[], outputs=[api_key_input, query_input, state_input, output]
 
685
  )
686
- logging.info("Gradio interface created with earneleh/paris theme and refined custom CSS.")
687
  return demo
688
 
689
  # --- Main Execution Block (remains untouched from original logic, just fixed the exit) ---
 
291
  else: # Fallback if states list is problematic
292
  example_queries.append(["What basic rights do tenants have?", "California"])
293
 
294
+ # Improved Custom CSS for better UI design and HuggingFace compatibility
 
 
295
  custom_css = """
296
+ /* Import legible fonts */
297
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@600;700;800&display=swap');
298
+
299
+ /* Root variables for consistent theming */
300
+ :root {
301
+ --primary-color: #2563eb;
302
+ --primary-hover: #1d4ed8;
303
+ --background-primary: #ffffff;
304
+ --background-secondary: #f8fafc;
305
+ --text-primary: #1e293b;
306
+ --text-secondary: #64748b;
307
+ --border-color: #e2e8f0;
308
+ --border-focus: #2563eb;
309
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
310
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
311
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
312
+ --error-bg: #fef2f2;
313
+ --error-border: #fecaca;
314
+ --error-text: #dc2626;
315
+ }
316
+
317
+ /* Dark mode variables */
318
+ @media (prefers-color-scheme: dark) {
319
+ :root {
320
+ --background-primary: #0f172a;
321
+ --background-secondary: #1e293b;
322
+ --text-primary: #f1f5f9;
323
+ --text-secondary: #94a3b8;
324
+ --border-color: #334155;
325
+ --error-bg: #1e1b1b;
326
+ --error-border: #451a1a;
327
+ --error-text: #f87171;
328
+ }
329
+ }
330
+
331
+ /* Base container improvements */
332
+ .gradio-container {
333
+ max-width: 1000px !important;
334
  margin: 0 auto !important;
335
+ padding: 1rem !important;
336
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
337
  }
338
+
339
+ /* Header styling */
340
  .app-header-wrapper {
341
+ background: linear-gradient(135deg, var(--background-primary) 0%, var(--background-secondary) 100%) !important;
342
+ border: 2px solid var(--border-color) !important;
343
+ border-radius: 16px !important;
344
+ padding: 2rem !important;
345
+ margin-bottom: 1.5rem !important;
346
  text-align: center !important;
347
+ box-shadow: var(--shadow-md) !important;
348
+ }
349
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  .app-header-logo {
351
+ font-size: 3rem !important;
352
+ margin-bottom: 0.5rem !important;
353
+ display: block !important;
 
354
  }
355
+
356
  .app-header-title {
357
+ font-family: 'Poppins', sans-serif !important;
358
+ font-size: 2.5rem !important;
359
+ font-weight: 700 !important;
360
+ color: var(--text-primary) !important;
361
+ margin: 0 0 0.5rem 0 !important;
362
+ line-height: 1.2 !important;
363
  }
364
+
365
  .app-header-tagline {
366
+ font-size: 1.1rem !important;
367
+ color: var(--text-secondary) !important;
368
+ font-weight: 400 !important;
369
+ margin: 0 !important;
 
370
  }
371
+
372
+ /* Main container with compact spacing */
373
  .main-dashboard-container {
374
+ display: flex !important;
375
+ flex-direction: column !important;
376
+ gap: 1rem !important; /* Reduced gap for more compact look */
377
+ }
378
+
379
+ /* Card sections with better boundaries */
 
 
 
380
  .dashboard-card-section {
381
+ background: var(--background-primary) !important;
382
+ border: 2px solid var(--border-color) !important;
383
+ border-radius: 12px !important;
384
+ padding: 1.5rem !important; /* Reduced padding for compactness */
385
+ box-shadow: var(--shadow-sm) !important;
386
+ transition: all 0.2s ease !important;
387
  }
388
+
389
+ .dashboard-card-section:hover {
390
+ box-shadow: var(--shadow-md) !important;
391
  }
392
+
393
+ /* Centered section titles with better typography */
394
  .sub-section-title {
395
+ font-family: 'Poppins', sans-serif !important;
396
+ font-size: 1.5rem !important;
397
+ font-weight: 600 !important;
398
+ color: var(--text-primary) !important;
399
  text-align: center !important;
400
+ margin: 0 0 1rem 0 !important;
401
+ padding-bottom: 0.5rem !important;
402
+ border-bottom: 2px solid var(--border-color) !important;
403
+ display: block !important;
404
+ }
405
+
406
+ /* Improved input styling with clear boundaries */
407
+ .gradio-textbox, .gradio-dropdown {
408
+ margin-bottom: 0.75rem !important; /* Reduced margin for compactness */
409
+ }
410
+
411
+ .gradio-textbox textarea,
412
+ .gradio-textbox input,
413
+ .gradio-dropdown select {
414
+ background: var(--background-primary) !important;
415
+ border: 2px solid var(--border-color) !important;
416
+ border-radius: 8px !important;
417
+ padding: 0.75rem !important;
418
+ font-size: 0.95rem !important;
419
+ font-family: 'Inter', sans-serif !important;
420
+ color: var(--text-primary) !important;
421
+ transition: all 0.2s ease !important;
422
+ box-shadow: var(--shadow-sm) !important;
423
+ }
424
+
425
+ .gradio-textbox textarea:focus,
426
+ .gradio-textbox input:focus,
427
+ .gradio-dropdown select:focus {
428
+ outline: none !important;
429
+ border-color: var(--border-focus) !important;
430
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1) !important;
431
+ }
432
+
433
+ /* Placeholder text styling */
434
+ .gradio-textbox textarea::placeholder,
435
+ .gradio-textbox input::placeholder {
436
+ color: var(--text-secondary) !important;
437
+ opacity: 0.7 !important;
438
+ }
439
+
440
+ /* Label styling for better readability */
441
+ .gradio-textbox label,
442
+ .gradio-dropdown label {
443
+ font-weight: 500 !important;
444
+ color: var(--text-primary) !important;
445
+ font-size: 0.9rem !important;
446
+ margin-bottom: 0.5rem !important;
447
  display: block !important;
448
+ }
449
+
450
+ /* Info text styling */
451
+ .gradio-textbox .gr-form,
452
+ .gradio-dropdown .gr-form {
453
+ font-size: 0.85rem !important;
454
+ color: var(--text-secondary) !important;
455
+ margin-top: 0.25rem !important;
456
+ }
457
+
458
+ /* Input row layout improvements */
459
+ .input-row {
460
+ display: flex !important;
461
+ gap: 1rem !important;
462
+ margin-bottom: 0.5rem !important; /* Reduced margin */
463
+ }
464
+
465
+ .input-field {
466
+ flex: 1 !important;
467
+ }
468
+
469
+ /* Button styling improvements */
470
+ .button-row {
471
+ display: flex !important;
472
+ gap: 1rem !important;
473
+ justify-content: flex-end !important;
474
+ margin-top: 1rem !important;
475
+ }
476
+
477
  .gradio-button {
478
+ padding: 0.75rem 1.5rem !important;
479
+ border-radius: 8px !important;
480
+ font-weight: 500 !important;
481
+ font-size: 0.9rem !important;
482
+ transition: all 0.2s ease !important;
483
+ cursor: pointer !important;
484
+ border: 2px solid transparent !important;
485
+ }
486
+
487
+ .gr-button-primary {
488
+ background: var(--primary-color) !important;
489
+ color: white !important;
490
+ box-shadow: var(--shadow-sm) !important;
491
  }
492
+
493
+ .gr-button-primary:hover {
494
+ background: var(--primary-hover) !important;
495
+ box-shadow: var(--shadow-md) !important;
496
+ transform: translateY(-1px) !important;
497
  }
498
+
499
+ .gr-button-secondary {
500
  background: transparent !important;
501
+ color: var(--text-primary) !important;
502
+ border-color: var(--border-color) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  }
504
+
505
+ .gr-button-secondary:hover {
506
+ background: var(--background-secondary) !important;
507
+ border-color: var(--primary-color) !important;
 
 
508
  }
509
+
510
+ /* Output styling with clear boundaries */
511
+ .output-content-wrapper {
512
+ background: var(--background-primary) !important;
513
+ border: 2px solid var(--border-color) !important;
514
+ border-radius: 8px !important;
515
+ padding: 1rem !important;
516
+ min-height: 100px !important;
517
+ font-size: 0.95rem !important;
518
+ line-height: 1.6 !important;
519
+ color: var(--text-primary) !important;
520
+ }
521
+
522
+ .response-header {
523
+ font-size: 1.2rem !important;
524
+ font-weight: 600 !important;
525
+ color: var(--text-primary) !important;
526
+ margin-bottom: 0.75rem !important;
527
+ display: flex !important;
528
+ align-items: center !important;
529
+ gap: 0.5rem !important;
530
  }
531
+
532
+ .response-icon {
533
+ font-size: 1.3rem !important;
534
+ color: var(--primary-color) !important;
535
  }
536
+
537
+ .divider {
538
+ border: none !important;
539
+ border-top: 1px solid var(--border-color) !important;
540
+ margin: 0.75rem 0 !important;
541
  }
542
+
543
+ /* Error message styling */
544
+ .error-message {
545
+ background: var(--error-bg) !important;
546
+ border: 2px solid var(--error-border) !important;
547
+ color: var(--error-text) !important;
548
+ padding: 1rem !important;
549
+ border-radius: 8px !important;
 
 
 
 
 
 
 
550
  display: flex !important;
 
551
  align-items: flex-start !important;
552
+ gap: 0.75rem !important;
553
+ font-size: 0.9rem !important;
554
  }
555
+
556
+ .error-icon {
557
+ font-size: 1.2rem !important;
558
+ line-height: 1 !important;
559
+ margin-top: 0.1rem !important;
 
560
  }
561
+
562
+ /* Placeholder styling */
563
+ .placeholder {
564
+ background: var(--background-secondary) !important;
565
+ border: 2px dashed var(--border-color) !important;
566
+ border-radius: 8px !important;
567
+ padding: 2rem 1rem !important;
568
+ text-align: center !important;
569
+ color: var(--text-secondary) !important;
570
+ font-style: italic !important;
571
  }
572
+
573
+ /* Examples table styling */
574
+ .examples-section .gr-samples-table {
575
+ border: 2px solid var(--border-color) !important;
576
+ border-radius: 8px !important;
577
+ overflow: hidden !important;
578
+ margin-top: 1rem !important;
579
+ }
580
+
581
+ .examples-section .gr-samples-table th,
582
+ .examples-section .gr-samples-table td {
583
+ padding: 0.75rem !important;
584
+ border: none !important;
585
+ font-size: 0.9rem !important;
586
  }
587
+
588
+ .examples-section .gr-samples-table th {
589
+ background: var(--background-secondary) !important;
590
+ font-weight: 600 !important;
591
+ color: var(--text-primary) !important;
592
+ }
593
+
594
+ .examples-section .gr-samples-table td {
595
+ background: var(--background-primary) !important;
596
+ color: var(--text-primary) !important;
597
+ border-top: 1px solid var(--border-color) !important;
598
+ cursor: pointer !important;
599
+ }
600
+
601
+ .examples-section .gr-samples-table tr:hover td {
602
+ background: var(--background-secondary) !important;
603
+ }
604
+
605
+ /* Footer styling */
606
+ .app-footer-wrapper {
607
+ background: var(--background-secondary) !important;
608
+ border: 2px solid var(--border-color) !important;
609
+ border-radius: 12px !important;
610
+ padding: 1.5rem !important;
611
+ margin-top: 1.5rem !important;
612
+ text-align: center !important;
613
+ }
614
+
615
+ .app-footer p {
616
+ margin: 0.5rem 0 !important;
617
+ font-size: 0.9rem !important;
618
+ color: var(--text-secondary) !important;
619
+ line-height: 1.5 !important;
620
+ }
621
+
622
  .app-footer a {
623
+ color: var(--primary-color) !important;
624
+ text-decoration: none !important;
625
+ font-weight: 500 !important;
626
  }
627
+
628
  .app-footer a:hover {
629
+ text-decoration: underline !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
630
  }
631
+
632
+ /* Hide Gradio default elements */
633
+ .gr-examples .gr-label,
634
+ .gr-examples .label-wrap,
635
+ .gr-examples .gr-accordion-header {
636
+ display: none !important;
637
+ }
638
+
639
+ /* Responsive design */
640
  @media (max-width: 768px) {
641
+ .gradio-container {
642
+ padding: 0.5rem !important;
643
+ }
644
+
645
+ .app-header-title {
646
+ font-size: 2rem !important;
647
+ }
648
+
649
+ .app-header-tagline {
650
+ font-size: 1rem !important;
651
+ }
652
+
653
+ .input-row {
654
+ flex-direction: column !important;
655
+ }
656
+
657
+ .button-row {
658
+ flex-direction: column !important;
659
+ }
660
+
661
+ .gradio-button {
662
+ width: 100% !important;
663
+ }
 
 
 
 
 
 
 
 
 
 
 
664
  }
665
  """
666
 
667
+ with gr.Blocks(theme="soft", css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
668
+ # Header Section
669
  with gr.Group(elem_classes="app-header-wrapper"):
670
  gr.Markdown(
671
  """
 
677
  """
678
  )
679
 
680
+ # Main Dashboard Container
681
  with gr.Column(elem_classes="main-dashboard-container"):
682
 
683
+ # Introduction and Disclaimer Card
684
  with gr.Group(elem_classes="dashboard-card-section"):
685
  gr.Markdown("<h3 class='sub-section-title'>Welcome & Disclaimer</h3>")
686
  gr.Markdown(
687
  """
688
+ Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.
689
+
690
+ **Disclaimer:** This tool is for informational purposes only and does not constitute legal advice. For specific legal guidance, always consult a licensed attorney in your jurisdiction.
691
  """
692
  )
693
 
694
+ # OpenAI API Key Input Card
695
  with gr.Group(elem_classes="dashboard-card-section"):
696
  gr.Markdown("<h3 class='sub-section-title'>OpenAI API Key</h3>")
697
  api_key_input = gr.Textbox(
698
+ label="API Key",
699
+ type="password",
700
+ placeholder="Enter your API key (e.g., sk-...)",
701
+ info="Required to process your query. Get one free from OpenAI.",
702
+ lines=1,
703
  elem_classes=["input-field-group"]
704
  )
705
 
706
+ # Query Input and State Selection Card
707
  with gr.Group(elem_classes="dashboard-card-section"):
708
  gr.Markdown("<h3 class='sub-section-title'>Ask Your Question</h3>")
709
  with gr.Row(elem_classes="input-row"):
710
  with gr.Column(elem_classes="input-field", scale=3):
711
  query_input = gr.Textbox(
712
+ label="Your Question",
713
+ placeholder="E.g., What are the rules for security deposit returns in my state?",
714
+ lines=4,
715
+ max_lines=8,
716
  elem_classes=["input-field-group"]
717
  )
718
  with gr.Column(elem_classes="input-field", scale=1):
719
  state_input = gr.Dropdown(
720
+ label="Select State",
721
+ choices=dropdown_choices,
722
+ value=initial_value,
723
  allow_custom_value=False,
724
  elem_classes=["input-field-group"]
725
  )
 
727
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
728
  submit_button = gr.Button("Submit Query", variant="primary", elem_classes=["gr-button-primary"])
729
 
730
+ # Output Display Card
731
  with gr.Group(elem_classes="dashboard-card-section"):
732
  gr.Markdown("<h3 class='sub-section-title'>Legal Assistant's Response</h3>")
733
  output = gr.Markdown(
734
+ value="<div class='placeholder'>The answer will appear here after submitting your query.</div>",
735
+ elem_classes="output-content-wrapper"
736
  )
737
 
738
+ # Example Questions Section
739
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
740
+ gr.Markdown("<h3 class='sub-section-title'>Example Questions</h3>")
741
  if example_queries:
742
  gr.Examples(
743
+ examples=example_queries,
744
+ inputs=[query_input, state_input],
745
  examples_per_page=5,
746
+ label=""
747
  )
748
  else:
749
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
750
 
751
+ # Footer Section
752
  with gr.Group(elem_classes="app-footer-wrapper"):
753
  gr.Markdown(
754
  """
755
+ This tool is for informational purposes only and does not constitute legal advice. For legal guidance, always consult with a licensed attorney in your jurisdiction.
756
+
757
+ Developed by **Nischal Subedi**. Connect on [LinkedIn](https://www.linkedin.com/in/nischal1/) or explore insights at [Substack](https://datascientistinsights.substack.com/).
 
 
 
758
  """
759
  )
760
 
761
+ # Event Listeners
762
  submit_button.click(
763
+ fn=query_interface_wrapper,
764
+ inputs=[api_key_input, query_input, state_input],
765
+ outputs=output,
766
+ api_name="submit_query"
767
  )
768
+
769
  clear_button.click(
770
  fn=lambda: (
771
  "",
772
  "",
773
  initial_value,
774
+ "<div class='placeholder'>Inputs cleared. Ready for your next question.</div>"
775
  ),
776
+ inputs=[],
777
+ outputs=[api_key_input, query_input, state_input, output]
778
  )
779
+
780
  return demo
781
 
782
  # --- Main Execution Block (remains untouched from original logic, just fixed the exit) ---