Nischal Subedi commited on
Commit
3c24aff
·
1 Parent(s): ff69f36

updated UI v6

Browse files
Files changed (1) hide show
  1. app.py +220 -465
app.py CHANGED
@@ -241,136 +241,16 @@ Answer:"""
241
  logging.error(f"Failed to load or process PDF '{pdf_path}': {str(e)}", exc_info=True)
242
  raise RuntimeError(f"Failed to process PDF '{pdf_path}': {e}") from e
243
 
244
- # --- NEW: Custom Gradio Theme Definition (Legal Aesthetic) ---
245
- class LegalAestheticTheme(themes.Base):
246
- def __init__(
247
- self,
248
- **kwargs,
249
- ):
250
- # Pass all accepted kwargs to the base class
251
- super().__init__(
252
- # Fonts are accepted directly by themes.Base
253
- font=[themes.GoogleFont("Inter"), "sans-serif"],
254
- font_mono=themes.GoogleFont("JetBrains Mono"),
255
-
256
- # --- Dark Mode Colors (Legal Noir) ---
257
- # Primary/Accent Colors (Goldenrod/Amber scale)
258
- primary_50_dark="#FFFDE7",
259
- primary_100_dark="#FFF9C4",
260
- primary_200_dark="#FFF59D",
261
- primary_300_dark="#FFF176",
262
- primary_400_dark="#FFEE58",
263
- primary_500_dark="#FFC107", # Main accent color
264
- primary_600_dark="#E0A800", # Hover accent color
265
- primary_700_dark="#B88A00",
266
- primary_800_dark="#8D6C00",
267
- primary_900_dark="#614B00",
268
- primary_950_dark="#403200",
269
-
270
- # Backgrounds
271
- background_page_dark="#0F0F1A",
272
- block_background_fill_dark="#1A1A2B",
273
- background_fill_primary_dark="#1E1E30",
274
- background_fill_secondary_dark="#2A2A40",
275
- background_fill_tertiary_dark="#3F3F5A",
276
-
277
- # Text Colors
278
- text_color_body_dark="#EAEAF0",
279
- text_color_subdued_dark="#A0A0B0",
280
- text_color_header_dark="#EAEAF0",
281
-
282
- # Borders
283
- border_color_primary_dark="#3F3F5A",
284
- border_color_secondary_dark="#505060",
285
-
286
- # Shadows (as hex colors for theme consistency, will be applied as rgba in CSS)
287
- # These are colors, not directly applied as 'shadow-lg' etc. by theme.Base
288
- # They will be used via CSS variables
289
- shadow_lg_dark="0F0F1A", # Represents darkest shadow color for rgba(..., 0.6)
290
- shadow_md_dark="1A1A2B", # Represents medium shadow color for rgba(..., 0.4)
291
- shadow_sm_dark="1E1E30", # Represents light shadow color for rgba(..., 0.35)
292
-
293
- # Links
294
- link_text_color_dark="#FFC107",
295
- link_text_color_hover_dark="#E0A800",
296
-
297
- # Buttons (theme handles primary colors, custom CSS for secondary)
298
- button_primary_background_dark="#FFC107",
299
- button_primary_background_hover_dark="#E0A800",
300
- button_primary_text_color_dark="#1A1A2B",
301
-
302
- # Error Colors
303
- color_error_50_dark="#4D001A",
304
- color_error_200_dark="#990026",
305
- color_error_500_dark="#CC0033",
306
- color_error_600_dark="#FFB3C2",
307
-
308
-
309
- # --- Light Mode Colors (Legal Lumen) ---
310
- primary_50_light="#E3F2FD",
311
- primary_100_light="#BBDEFB",
312
- primary_200_light="#90CAF9",
313
- primary_300_light="#64B5F6",
314
- primary_400_light="#42A5F5",
315
- primary_500_light="#2196F3",
316
- primary_600_light="#1E88E5",
317
- primary_700_light="#1976D2",
318
- primary_800_light="#1565C0",
319
- primary_900_light="#0D47A1",
320
- primary_950_light="#082F6A",
321
-
322
- background_page_light="#F0F2F5",
323
- block_background_fill_light="#E0E4EB",
324
- background_fill_primary_light="#FFFFFF",
325
- background_fill_secondary_light="#F8F9FA",
326
- background_fill_tertiary_light="#DDE2E8",
327
-
328
- text_color_body_light="#212529",
329
- text_color_subdued_light="#6C757D",
330
- text_color_header_light="#212529",
331
-
332
- border_color_primary_light="#DDE2E8",
333
- border_color_secondary_light="#CCD1D7",
334
-
335
- shadow_lg_light="F0F2F5",
336
- shadow_md_light="E0E4EB",
337
- shadow_sm_light="FFFFFF",
338
-
339
- link_text_color_light="#007bff",
340
- link_text_color_hover_light="#0056b3",
341
-
342
- button_primary_background_light="#007bff",
343
- button_primary_background_hover_light="#0056b3",
344
- button_primary_text_color_light="#FFFFFF",
345
-
346
- color_error_50_light="#F8D7DA",
347
- color_error_200_light="#F5C6CB",
348
- color_error_500_light="#DC3545",
349
- color_error_600_light="#721C24",
350
-
351
- # Radii and Spacing (matching custom CSS, and setting theme defaults)
352
- radius_xs="4px",
353
- radius_sm="8px",
354
- radius_md="12px",
355
- radius_lg="24px",
356
- spacing_md="2.5rem",
357
- spacing_lg="3.5rem",
358
- spacing_xl="4.5rem",
359
- **kwargs,
360
- )
361
- # Assign heading_font directly to self after super().__init__ as it's not a direct base constructor argument
362
- self.heading_font = [themes.GoogleFont("Playfair Display"), "serif"]
363
-
364
-
365
  def gradio_interface(self):
366
  def query_interface_wrapper(api_key: str, query: str, state: str) -> str:
367
  # Basic client-side validation for immediate feedback (redundant but good UX)
368
  if not api_key or not api_key.strip() or not api_key.startswith("sk-"):
369
  return "<div class='error-message'><span class='error-icon'>⚠️</span>Please provide a valid OpenAI API key (starting with 'sk-'). <a href='https://platform.openai.com/api-keys' target='_blank'>Get one free from OpenAI</a>.</div>"
370
  if not state or state == "Select a state..." or "Error" in state:
371
- return "<div class='error-message'>Error: Please select a valid state.</div>" # Simplified message
372
  if not query or not query.strip():
373
- return "<div class='error-message'>Error: Please enter your question.</div>" # Simplified message
374
 
375
  # Call the core processing logic
376
  result = self.process_query(query=query, state=state, openai_api_key=api_key)
@@ -381,8 +261,7 @@ Answer:"""
381
  return answer # Return the pre-formatted error message directly
382
  else:
383
  # Format the successful response with the new UI structure
384
- # Use a div for the icon and header text for better control over alignment and spacing
385
- formatted_response = f"<div class='response-header-container'><span class='response-icon'>📜</span><h4 class='response-title'>Response for {state}</h4></div><hr class='response-divider'>{answer}"
386
  return formatted_response
387
 
388
  try:
@@ -413,346 +292,215 @@ Answer:"""
413
  example_queries.append(["What basic rights do tenants have?", "California"])
414
 
415
 
416
- # --- Minimal Custom CSS for structural/behavioral overrides and specific aesthetics ---
417
- # This CSS is designed to complement our custom LegalAestheticTheme.
418
  custom_css = """
419
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@700;800;900&display=swap');
420
-
421
- /* Keyframe animation for initial header load */
422
  @keyframes fadeInSlideDown { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: translateY(0); } }
423
-
424
- /* General Gradio container layout and centering */
425
- body { margin: 0; padding: 0; } /* Reset default body margin */
426
- .gradio-container {
427
- font-family: var(--font-text) !important; /* Use theme's text font variable */
428
- min-height: 100vh;
429
- display: flex;
430
- justify-content: center; /* Center content horizontally */
431
- align-items: flex-start; /* Align content to top vertically */
432
- padding: var(--spacing-lg) 1rem; /* Overall padding top/bottom, fixed horizontal for small screens */
433
- background-color: var(--background-page); /* Use theme's page background */
434
- transition: background-color 0.4s ease-in-out, color 0.4s ease-in-out; /* Smooth theme transition */
435
- -webkit-font-smoothing: antialiased;
436
- -moz-osx-font-smoothing: grayscale;
437
- }
438
  .gradio-container > .flex.flex-col {
439
- max-width: 1120px; /* Constrain max width for desktop */
440
- width: 100%;
441
- margin: 0 auto; /* Center the main content column */
442
- gap: var(--spacing-md) !important; /* Consistent spacing between main sections */
443
- padding: 0 !important; /* Remove default Gradio column padding */
444
  }
445
-
446
- /* Header specific styling (outside main dashboard for distinct look) */
447
  .app-header-wrapper {
448
- background-color: var(--block-background-fill); /* Use theme's block background */
449
- padding: var(--spacing-xl) var(--spacing-lg); /* Spacious padding */
450
- text-align: center;
451
- border-radius: var(--radius-lg); /* Use theme radius */
452
- box-shadow: 0 1.2rem 3.5rem rgba(var(--shadow-lg),0.6); /* Use theme shadow color with rgba */
453
  position: relative;
454
  overflow: hidden;
455
  z-index: 10;
456
- border: 1px solid var(--border-color-primary); /* Use theme border */
 
 
 
 
 
 
457
  }
458
- .app-header { display: flex; flex-direction: column; align-items: center; }
459
  .app-header-logo {
460
  font-size: 5.5rem; margin-bottom: 0.8rem; line-height: 1;
461
- color: var(--primary-500); /* Use theme primary color */
462
- filter: drop-shadow(0 0 15px var(--primary-500));
463
  transform: translateY(-40px); opacity: 0;
464
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.3s;
465
  }
466
  .app-header-title {
467
- font-family: var(--font-heading) !important; /* Use theme's heading font variable */
468
  font-size: 4.2rem; font-weight: 900;
469
  margin: 0 0 0.8rem 0; letter-spacing: -0.07em;
470
- color: var(--text-color-header); /* Use theme text color for headings */
471
- text-shadow: 0 8px 16px rgba(0,0,0,0.5); /* Custom shadow for effect */
472
  transform: translateY(-40px); opacity: 0;
473
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.6s;
474
  }
475
  .app-header-tagline {
476
- font-family: var(--font-text) !important;
477
  font-size: 1.6rem; font-weight: 300;
478
- color: var(--text-color-subdued); /* Use theme subdued text color */
479
  opacity: 0.9; max-width: 900px;
480
  transform: translateY(-40px); opacity: 0;
481
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.9s;
482
  }
483
-
484
- /* Main dashboard console container */
485
  .main-dashboard-container {
486
- display: flex;
487
- flex-direction: column;
488
- gap: 2rem; /* Spacing between cards */
489
- background-color: var(--background-fill-primary); /* Use theme's main background */
490
- border-radius: var(--radius-lg);
491
- box-shadow: 0 0.8rem 2.5rem rgba(var(--shadow-lg),0.4); /* Use theme shadow color with rgba */
492
- padding: var(--spacing-md); /* Inner padding for the whole dashboard content */
493
- border: 1px solid var(--border-color-primary);
494
  }
495
-
496
- /* Individual Card Sections */
497
  .dashboard-card-section {
498
- background-color: var(--background-fill-secondary); /* A slightly darker/lighter background for cards */
499
- border-radius: var(--radius-md);
500
- padding: 2rem; /* Padding inside each card */
501
- box-shadow: 0 0.6rem 2rem rgba(var(--shadow-md),0.2); /* Subtle shadow for cards */
502
  border: 1px solid var(--border-color-primary);
 
 
 
503
  }
504
-
505
- /* Section titles within cards */
506
- .card-section-title {
507
- font-family: var(--font-heading) !important; /* Use theme's heading font variable */
508
- font-size: 2.2rem !important;
509
- font-weight: 800 !important;
510
- color: var(--text-color-body) !important; /* Use theme body text color */
511
- text-align: center !important; /* Centered as requested */
512
- margin-bottom: 1.5rem !important;
513
- padding-bottom: 0.8rem !important;
514
- border-bottom: 1px solid var(--border-color-primary) !important;
515
- }
516
-
517
- /* General text styling within cards */
518
- .dashboard-card-section p {
519
- font-family: var(--font-text) !important;
520
- font-size: 1.1rem;
521
- line-height: 1.6;
522
- color: var(--text-color-body);
523
- margin-bottom: 1rem;
524
- }
525
- .dashboard-card-section strong {
526
- color: var(--text-color-body); /* Ensure strong text is legible */
527
- font-weight: 600;
528
- }
529
- .dashboard-card-section a {
530
- color: var(--link-text-color); /* Use theme link color */
531
- text-decoration: underline;
532
- }
533
- .dashboard-card-section a:hover {
534
- color: var(--link-text-color-hover); /* Use theme link hover color */
535
- text-decoration: none;
536
- }
537
-
538
- /* Input field styling (mostly handled by theme, but override specific padding/radius) */
539
- .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] {
540
- padding: 1.2rem 1.4rem !important; /* Adjust padding for consistency */
541
- border-radius: var(--radius-sm) !important; /* Use theme radius */
542
- font-size: 1.05rem !important;
543
- }
544
- .gradio-textbox textarea { min-height: 160px; }
545
- .gradio-input-info a {
546
- color: var(--link-text-color); /* Ensure info links are themed */
547
- text-decoration: underline;
548
- }
549
- .gradio-input-info a:hover {
550
- color: var(--link-text-color-hover);
551
- }
552
- /* Style for the dropdown arrow, ensuring it's visible in both themes */
553
- .gradio-dropdown select {
554
- background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22currentColor%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M5.293%207.293a1%201%200%20011.414%200L10%2010.586l3.293-3.293a1%201%200%20111.414%201.414l-4%204a1%201%200%2001-1.414%200l-4-4a1%201%200%20010-1.414z%22%20clip-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E') !important;
555
- background-repeat: no-repeat;
556
- background-position: right 1rem center !important;
557
- background-size: 1.2em !important;
558
- padding-right: 3rem !important;
559
- color: var(--text-color-body) !important; /* Ensure dropdown text is legible */
560
- }
561
- /* Current color for SVG icon will adapt with text_color_body, but explicitly set if needed */
562
- /* For light mode, the fill for SVG should be a darker color */
563
  @media (prefers-color-scheme: light) {
564
- .gradio-dropdown select {
565
- color: var(--text-color-body) !important; /* Ensure dropdown text is dark */
566
- }
567
  }
568
-
569
-
570
- /* Button styling overrides to match original design intent with theme */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
  .gradio-button {
572
- border-radius: var(--radius-sm) !important;
573
- font-weight: 600 !important;
574
- padding: 0.8rem 2rem !important; /* Make buttons slightly less tall */
575
- min-height: 48px; /* Ensure minimum height */
576
- box-shadow: 0 6px 20px rgba(var(--shadow-sm),0.35); /* Use theme shadow color with rgba */
577
- transition: all 0.2s ease-in-out;
578
- transform: translateY(0);
579
- }
580
- .gradio-button:hover:not(:disabled) {
581
- transform: translateY(-3px); /* Subtle lift on hover */
582
- box-shadow: 0 0.8rem 2.5rem rgba(var(--shadow-md),0.4);
583
- }
584
- .gradio-button:active:not(:disabled) {
585
- transform: translateY(0);
586
  }
587
- /* Primary button uses theme primary colors automatically */
588
- .gr-button-secondary {
589
- background-color: transparent !important;
590
- border: 1px solid var(--border-color-primary) !important;
591
- color: var(--text-color-body) !important; /* Use theme body text for secondary button */
592
  box-shadow: none !important;
593
  }
594
  .gr-button-secondary:hover:not(:disabled) {
595
- background-color: var(--primary-50) !important; /* Light tint of primary on hover */
596
- color: var(--primary-500) !important; /* Primary color for text on hover */
597
- border-color: var(--primary-500) !important; /* Primary color for border on hover */
598
- }
599
-
600
- /* Output Response Section */
601
- .response-header-container {
602
- display: flex;
603
- align-items: center;
604
- gap: 1rem;
605
- margin-bottom: 1rem;
606
- }
607
- .response-icon {
608
- font-size: 2.2rem;
609
- color: var(--primary-500); /* Use theme primary color */
610
- }
611
- .response-title {
612
- font-family: var(--font-heading) !important;
613
- font-size: 1.8rem !important;
614
- font-weight: 700 !important;
615
- color: var(--text-color-body) !important; /* Ensure heading is legible */
616
- margin: 0;
617
- padding: 0;
618
- }
619
- .output-content-wrapper .response-divider { /* Specific class for this divider */
620
- border: none;
621
- border-top: 1px solid var(--border-color-secondary); /* A lighter border for divider */
622
- margin: 1rem 0 1.5rem 0;
623
- }
624
- .output-content-wrapper {
625
- font-family: var(--font-text) !important;
626
- line-height: 1.7;
627
- font-size: 1.05rem;
628
- color: var(--text-color-body);
629
- }
630
- .output-content-wrapper ul, .output-content-wrapper ol {
631
- margin-left: 2rem;
632
- margin-bottom: 1rem;
633
- padding-left: 0;
634
- }
635
- .output-content-wrapper li {
636
- margin-bottom: 0.5rem;
637
- }
638
- .output-card .placeholder {
639
- padding: 2rem;
640
- font-size: 1.1rem;
641
- border-radius: var(--radius-md);
642
- border: 2px dashed var(--border-color-secondary);
643
- color: var(--text-color-subdued);
644
- text-align: center;
645
- opacity: 0.7;
646
- min-height: 150px; /* Ensure some height for placeholder */
647
- display: flex;
648
- align-items: center;
649
- justify-content: center;
650
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
  .output-card .error-message {
652
- padding: 1.5rem;
653
- font-size: 1.05rem;
654
- border-radius: var(--radius-sm);
655
- background-color: var(--color-error-50); /* Use theme error color with low opacity */
656
- color: var(--color-error-600); /* Strong error text color */
657
- border: 1px solid var(--color-error-200); /* Theme error border */
658
- display: flex;
659
- align-items: flex-start;
660
- gap: 1rem;
661
- }
662
- .output-card .error-message .error-icon {
663
- font-size: 1.8rem;
664
- line-height: 1;
665
- color: var(--color-error-500); /* Use theme error color */
666
- }
667
- .output-card .error-details {
668
- font-size: 0.9rem;
669
- margin-top: 0.5rem;
670
- opacity: 0.8;
671
- word-break: break-word;
672
  }
673
-
674
- /* Examples Table (from gr.Examples) */
675
  .examples-section table.gr-samples-table {
676
- border-radius: var(--radius-sm) !important;
677
  border: 1px solid var(--border-color-primary) !important;
678
  overflow: hidden;
679
- background-color: var(--background-fill-secondary); /* Match card background */
680
  }
681
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td {
682
- padding: 0.8rem 1rem !important;
683
- font-size: 0.95rem !important;
684
- border: none !important;
685
- color: var(--text-color-body) !important;
686
  }
 
687
  .examples-section table.gr-samples-table th {
688
- background-color: var(--background-fill-tertiary) !important; /* Slightly different background for header */
689
- font-weight: 600 !important;
690
- text-align: left;
691
- color: var(--text-color-header) !important;
692
  }
693
  .examples-section table.gr-samples-table td {
694
- border-top: 1px solid var(--border-color-secondary) !important; /* Lighter border for rows */
695
  cursor: pointer;
696
  }
697
- .examples-section table.gr-samples-table tr:hover td {
698
- background-color: var(--primary-50) !important; /* Light tint on hover */
699
- }
700
  .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
701
-
702
- /* Footer styling (at the very bottom of the page) */
703
  .app-footer-wrapper {
704
- background-color: var(--block-background-fill); /* Use theme's block background */
705
  border-top: 1px solid var(--border-color-primary) !important;
706
- padding: 1.5rem 2.5rem; /* Consistent padding */
707
- border-radius: var(--radius-lg);
708
- box-shadow: 0 1.2rem 3.5rem rgba(var(--shadow-lg),0.6); /* Use theme shadow color with rgba */
709
- width: 100%;
 
 
710
  max-width: 1120px;
711
- margin: var(--spacing-md) auto 0 auto; /* Spacing from main dashboard */
712
- display: flex;
713
- flex-direction: column;
714
- align-items: flex-start; /* Left aligns the content */
 
 
715
  }
716
  .app-footer {
717
- width: 100%; /* Ensure content takes full width */
 
 
 
 
718
  }
719
  .app-footer p {
720
- font-family: var(--font-text) !important;
721
- font-size: 0.95rem !important;
722
- color: var(--text-color-subdued) !important;
723
- margin-bottom: 0.5rem;
724
  text-align: left !important;
 
725
  }
726
- .app-footer a {
727
- color: var(--link-text-color) !important;
728
- font-weight: 500;
729
- text-decoration: underline;
730
- }
731
- .app-footer a:hover {
732
- color: var(--link-text-color-hover) !important;
733
- text-decoration: none;
734
- }
735
-
736
- /* Hide unwanted Gradio default elements for a clean look */
737
- .gr-messages-row, .gr-share-btn, .gr-api-btn, .gr-view-api, /* Common boilerplate elements */
738
- .gradio-container > footer, /* Default Gradio footer text that appears below blocks */
739
- .gradio-app .version-info, /* Gradio version info */
740
- .gradio-app .dark\:text-gray-400, /* Target other default footer text classes */
741
- .gradio-app .absolute.bottom-0.right-0.px-2.py-1.text-gray-400.text-xs /* another common footer class */ {
742
- display: none !important;
743
- visibility: hidden !important;
744
- width: 0 !important;
745
- height: 0 !important;
746
- overflow: hidden !important;
747
- margin: 0 !important;
748
- padding: 0 !important;
749
- border: 0 !important;
750
- font-size: 0 !important;
751
- line-height: 0 !important;
752
- position: absolute !important;
753
- pointer-events: none !important;
754
- }
755
- /* More specific example label/button hiding */
756
  .gr-examples .gr-label, .gr-examples button.gr-button-filter, .gr-examples .label-wrap,
757
  .gr-examples div[data-testid*="label-text"], .gr-examples span[data-testid*="label-text"],
758
  .gr-examples div[class*="label"], .gr-examples .gr-example-label,
@@ -767,54 +515,61 @@ Answer:"""
767
  font-size: 0 !important; line-height: 0 !important; position: absolute !important;
768
  pointer-events: none !important;
769
  }
770
- /* Further clean up any potential residual margin/padding Gradio adds to its components */
771
- .gradio-container .gr-box { margin: 0 !important; padding: 0 !important; }
772
- .gradio-container .gr-form { gap: 0 !important; } /* Remove gaps in forms where not desired */
773
-
774
-
775
- /* Responsive Adjustments (using hardcoded values for precision, but mapping to theme variables where applicable for color/font) */
776
  @media (max-width: 1024px) {
777
- .gradio-container { padding: 2rem 0.8rem; }
778
- .gradio-container > .flex.flex-col { gap: 2rem !important; }
779
- .app-header-wrapper { padding: 2.5rem 1.8rem; margin-bottom: 2rem; border-radius: 12px; }
780
- .app-header-title { font-size: 3.5rem; } .app-header-tagline { font-size: 1.4rem; }
781
- .main-dashboard-container { padding: 2rem; gap: 1.8rem; border-radius: 12px; }
782
- .dashboard-card-section { padding: 1.5rem; border-radius: 8px; }
783
- .card-section-title { font-size: 1.8rem !important; margin-bottom: 1.2rem !important; }
784
- .input-row { flex-direction: column; gap: 1rem; }
785
- .gradio-textbox textarea { min-height: 140px; }
786
- .button-row { justify-content: center; gap: 1rem; flex-direction: column; }
787
- .gradio-button { width: 100%; max-width: 300px; margin: 0 auto; } /* Center buttons on smaller screens */
788
- .response-header-container { gap: 0.8rem; } .response-icon { font-size: 2rem; } .response-title { font-size: 1.6rem !important; }
789
- .output-card .placeholder { padding: 1.5rem; font-size: 1rem; min-height: 120px;}
790
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.7rem 0.9rem !important; font-size: 0.9rem !important; }
791
- .app-footer-wrapper { padding: 1.2rem 1.8rem; margin-top: 1.5rem; border-radius: 12px; }
792
- .app-footer p { font-size: 0.85rem !important; }
793
  }
794
  @media (max-width: 768px) {
795
- .gradio-container { padding: 1.5rem 0.5rem; }
796
- .gradio-container > .flex.flex-col { gap: 1.8rem !important; }
797
- .app-header-wrapper { padding: 2rem 1rem; margin-bottom: 1.5rem; border-radius: 12px; }
798
- .app-header-logo { font-size: 4.5rem; } .app-header-title { font-size: 2.8rem; } .app-header-tagline { font-size: 1.2rem; }
799
- .main-dashboard-container { padding: 1.5rem; gap: 1.5rem; border-radius: 12px; }
800
- .dashboard-card-section { padding: 1.2rem; border-radius: 8px; }
801
- .card-section-title { font-size: 1.6rem !important; margin-bottom: 1rem !important; }
802
- .gradio-textbox textarea { min-height: 120px; }
803
- .output-content-wrapper { font-size: 1rem; }
804
- .output-card .error-message { padding: 1rem; }
805
- .app-footer-wrapper { padding: 1rem 1rem; margin-top: 1rem; border-radius: 12px; }
 
 
 
 
 
806
  }
807
  @media (max-width: 480px) {
808
- .app-header-logo { font-size: 3.5rem; } .app-header-title { font-size: 2.2rem; } .app-header-tagline { font-size: 1rem; }
809
- .gradio-textbox textarea { min-height: 100px; }
810
- .gradio-button { padding: 0.6rem 1.5rem !important; min-height: 40px; font-size: 0.95rem !important; }
811
- .response-icon { font-size: 1.8rem; } .response-title { font-size: 1.4rem !important; }
812
- .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.5rem 0.7rem !important; font-size: 0.85rem !important; }
 
 
 
 
 
 
 
 
 
 
 
813
  }
814
  """
815
 
816
- with gr.Blocks(theme=self.LegalAestheticTheme(), css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
817
- # --- Header Section (distinct, top-level block) ---
818
  with gr.Group(elem_classes="app-header-wrapper"):
819
  gr.Markdown(
820
  """
@@ -826,59 +581,60 @@ Answer:"""
826
  """
827
  )
828
 
829
- # --- Main Dashboard Console Container (centralized, card-based) ---
830
  with gr.Column(elem_classes="main-dashboard-container"):
831
 
832
- # --- Section 1: Welcome & Disclaimer Card ---
833
  with gr.Group(elem_classes="dashboard-card-section"):
834
- gr.Markdown("<h3 class='card-section-title'>Welcome & Disclaimer</h3>")
835
  gr.Markdown(
836
  """
837
  <p>Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.</p>
838
  <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>
839
- """,
840
- elem_classes="dashboard-card-content" # Apply general text styling
841
  )
842
 
843
  # --- Section 2: OpenAI API Key Input Card ---
844
  with gr.Group(elem_classes="dashboard-card-section"):
845
- gr.Markdown("<h3 class='card-section-title'>OpenAI API Key</h3>") # Centered by CSS
846
  api_key_input = gr.Textbox(
847
- label="OpenAI API Key", # Keep label for theme compatibility and accessibility
848
  type="password", placeholder="Enter your API key (e.g., sk-...)",
849
  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,
850
  elem_classes=["input-field-group"]
851
  )
852
 
853
- # --- Section 3: Ask Your Question Card ---
854
  with gr.Group(elem_classes="dashboard-card-section"):
855
- gr.Markdown("<h3 class='card-section-title'>Ask Your Question</h3>") # Centered by CSS
856
- with gr.Row():
857
- with gr.Column(scale=3):
858
  query_input = gr.Textbox(
859
  label="Question", placeholder="E.g., What are the rules for security deposit returns in my state?",
860
- lines=5, max_lines=10
 
861
  )
862
- with gr.Column(scale=1, min_width=200):
863
  state_input = gr.Dropdown(
864
  label="Select State", choices=dropdown_choices, value=initial_value,
865
- allow_custom_value=False
 
866
  )
867
  with gr.Row(elem_classes="button-row"):
868
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
869
  submit_button = gr.Button("Submit Query", variant="primary", elem_classes=["gr-button-primary"])
870
 
871
- # --- Section 4: Legal Assistant's Response Card ---
872
  with gr.Group(elem_classes="dashboard-card-section"):
873
- gr.Markdown("<h3 class='card-section-title'>Legal Assistant's Response</h3>") # Centered by CSS
874
  output = gr.Markdown(
875
  value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
876
  elem_classes="output-content-wrapper output-card"
877
  )
878
 
879
- # --- Section 5: Example Questions Card ---
880
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
881
- gr.Markdown("<h3 class='card-section-title'>Example Questions to Ask</h3>") # Centered by CSS
882
  if example_queries:
883
  gr.Examples(
884
  examples=example_queries, inputs=[query_input, state_input],
@@ -888,7 +644,7 @@ Answer:"""
888
  else:
889
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
890
 
891
- # --- Footer Section (distinct, bottom-level block) ---
892
  with gr.Group(elem_classes="app-footer-wrapper"):
893
  gr.Markdown(
894
  """
@@ -898,8 +654,7 @@ Answer:"""
898
  Connect on <a href="https://www.linkedin.com/in/nischal1/" target='_blank'>LinkedIn</a>
899
  or explore insights at <a href="https://datascientistinsights.substack.com/" target='_blank'>Substack</a>.</p>
900
  </div>
901
- """,
902
- elem_classes="footer-content" # Apply general text styling to footer markdown
903
  )
904
 
905
  # --- Event Listeners (Logic remains identical) ---
@@ -915,7 +670,7 @@ Answer:"""
915
  ),
916
  inputs=[], outputs=[api_key_input, query_input, state_input, output]
917
  )
918
- logging.info("Gradio interface created with custom LegalAestheticTheme and refined CSS for optimal layout, legibility, and Hugging Face compatibility.")
919
  return demo
920
 
921
  # --- Main Execution Block (remains untouched from original logic) ---
 
241
  logging.error(f"Failed to load or process PDF '{pdf_path}': {str(e)}", exc_info=True)
242
  raise RuntimeError(f"Failed to process PDF '{pdf_path}': {e}") from e
243
 
244
+ # --- GRADIO INTERFACE (Refactored to use earneleh/paris theme) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  def gradio_interface(self):
246
  def query_interface_wrapper(api_key: str, query: str, state: str) -> str:
247
  # Basic client-side validation for immediate feedback (redundant but good UX)
248
  if not api_key or not api_key.strip() or not api_key.startswith("sk-"):
249
  return "<div class='error-message'><span class='error-icon'>⚠️</span>Please provide a valid OpenAI API key (starting with 'sk-'). <a href='https://platform.openai.com/api-keys' target='_blank'>Get one free from OpenAI</a>.</div>"
250
  if not state or state == "Select a state..." or "Error" in state:
251
+ return "<div class='error-message'><span class='error-icon'>⚠️</span>Please select a valid state from the dropdown.</div>"
252
  if not query or not query.strip():
253
+ return "<div class='error-message'><span class='error-icon'>⚠️</span>Please enter your question in the text box.</div>"
254
 
255
  # Call the core processing logic
256
  result = self.process_query(query=query, state=state, openai_api_key=api_key)
 
261
  return answer # Return the pre-formatted error message directly
262
  else:
263
  # Format the successful response with the new UI structure
264
+ formatted_response = f"<div class='response-header'><span class='response-icon'>📜</span>Response for {state}</div><hr class='divider'>{answer}"
 
265
  return formatted_response
266
 
267
  try:
 
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; /* Use hardcoded padding to override theme potentially */
311
+ text-align: center !important;
312
+ border-bottom-left-radius: 24px; /* Use hardcoded radius */
313
+ border-bottom-right-radius: 24px;
 
314
  position: relative;
315
  overflow: hidden;
316
  z-index: 10;
317
+ margin-bottom: 2.5rem; /* Use hardcoded margin */
318
+ border: 1px solid var(--border-color-primary) !important;
319
+ border-top: none;
320
+ max-width: 1120px;
321
+ margin-left: auto;
322
+ margin-right: auto;
323
+ width: 100%;
324
  }
325
+ .app-header { display: flex; flex-direction: column; align-items: center; position: relative; z-index: 1; }
326
  .app-header-logo {
327
  font-size: 5.5rem; margin-bottom: 0.8rem; line-height: 1;
328
+ filter: drop-shadow(0 0 15px var(--primary-500)); /* Using primary color from theme */
 
329
  transform: translateY(-40px); opacity: 0;
330
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.3s;
331
  }
332
  .app-header-title {
333
+ font-family: 'Playfair Display', serif !important; /* Explicit font family */
334
  font-size: 4.2rem; font-weight: 900;
335
  margin: 0 0 0.8rem 0; letter-spacing: -0.07em;
336
+ text-shadow: 0 8px 16px rgba(0,0,0,0.5);
 
337
  transform: translateY(-40px); opacity: 0;
338
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.6s;
339
  }
340
  .app-header-tagline {
341
+ font-family: 'Inter', sans-serif !important; /* Explicit font family */
342
  font-size: 1.6rem; font-weight: 300;
 
343
  opacity: 0.9; max-width: 900px;
344
  transform: translateY(-40px); opacity: 0;
345
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.9s;
346
  }
347
+ /* Main dashboard container */
 
348
  .main-dashboard-container {
349
+ border-radius: 24px; /* Use hardcoded radius */
350
+ border: 1px solid var(--border-color-primary) !important;
351
+ padding: 3.5rem !important; /* Use hardcoded padding */
352
+ margin: 0 auto 0.8rem auto;
353
+ z-index: 1; position: relative;
354
+ display: flex; flex-direction: column; gap: 2.5rem; /* Use hardcoded gap */
355
+ max-width: 1120px;
 
356
  }
357
+ /* Card sections within the dashboard */
 
358
  .dashboard-card-section {
359
+ border-radius: 12px; /* Use hardcoded radius */
 
 
 
360
  border: 1px solid var(--border-color-primary);
361
+ padding: 2.5rem; /* Use hardcoded padding */
362
+ box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
363
+ display: flex; flex-direction: column; gap: 1.5rem;
364
  }
365
+ /* Light mode specific inner shadow for card sections */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  @media (prefers-color-scheme: light) {
367
+ .dashboard-card-section { box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
 
 
368
  }
369
+ /* Section titles */
370
+ .sub-section-title {
371
+ font-family: 'Playfair Display', serif !important;
372
+ font-size: 2.7rem !important;
373
+ font-weight: 800 !important;
374
+ text-align: center !important;
375
+ margin-top: 1.5rem !important;
376
+ margin-bottom: 0.8rem !important;
377
+ display: block !important;
378
+ width: 100% !important;
379
+ }
380
+ /* Text styling for custom markdown within cards */
381
+ .dashboard-card-section p, .output-content-wrapper p {
382
+ font-size: 1.15rem; line-height: 1.8;
383
+ margin-bottom: 1.2rem;
384
+ }
385
+ .dashboard-card-section a, .output-content-wrapper a {
386
+ text-decoration: none; font-weight: 500;
387
+ }
388
+ .dashboard-card-section a:hover, .output-content-wrapper a:hover { text-decoration: underline; }
389
+ .dashboard-card-section strong, .output-content-wrapper strong { font-weight: 700; }
390
+ /* Input layout */
391
+ .input-field-group { margin-bottom: 1rem; }
392
+ .input-row { display: flex; gap: 1.8rem; flex-wrap: wrap; margin-bottom: 1rem; }
393
+ .input-field { flex: 1; }
394
+ /* Button layout */
395
+ .button-row { display: flex; gap: 2rem; margin-top: 2rem; flex-wrap: wrap; justify-content: flex-end; }
396
  .gradio-button {
397
+ border-radius: 12px !important; /* Hardcode radius */
398
+ padding: 1.2rem 2.8rem !important; /* Hardcode padding */
399
+ font-size: 1.15rem !important; /* Hardcode font size */
400
+ font-weight: 600 !important; /* Hardcode font weight */
401
+ border: 1px solid transparent !important;
402
+ box-shadow: 0 6px 20px rgba(0,0,0,0.35); /* Custom shadow */
403
+ transition: all 0.4s cubic-bezier(0.0, 0.0, 0.2, 1);
404
+ }
405
+ .gradio-button:hover:not(:disabled) { transform: translateY(-6px); box-shadow: 0 12px 28px rgba(0,0,0,0.45) !important; }
406
+ .gradio-button:active:not(:disabled) { transform: translateY(-3px); }
407
+ .gradio-button:disabled {
408
+ box-shadow: none !important;
409
+ cursor: not-allowed;
 
410
  }
411
+ .gr-button-secondary { /* Override for secondary button specific look */
412
+ background: transparent !important;
413
+ border: 2px solid var(--border-color-primary) !important;
 
 
414
  box-shadow: none !important;
415
  }
416
  .gr-button-secondary:hover:not(:disabled) {
417
+ background: var(--button-secondary-background-hover) !important; /* Use theme variable */
418
+ border-color: var(--primary-500) !important; /* Use theme primary color */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  }
420
+ @media (prefers-color-scheme: light) {
421
+ .gradio-button { box-shadow: 0 6px 20px rgba(0,0,0,0.1); }
422
+ .gradio-button:hover:not(:disabled) { box-shadow: 0 12px 28px rgba(0,0,0,0.2) !important; }
423
+ }
424
+ /* Output card and placeholders */
425
+ .output-card { padding: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; }
426
+ .output-card .response-header {
427
+ font-size: 1.8rem; font-weight: 700;
428
+ margin: 0 0 1rem 0; display: flex; align-items: center; gap: 1.2rem;
429
+ }
430
+ .output-card .response-icon { font-size: 2rem; color: var(--primary-500); } /* Use theme primary color */
431
+ .output-card .divider { border: none; border-top: 1px solid var(--border-color-primary); margin: 1.5rem 0 1.8rem 0; }
432
+ .output-card .output-content-wrapper { font-size: 1.15rem; line-height: 1.8; }
433
+ .output-card .output-content-wrapper p { margin-bottom: 1rem; }
434
+ .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; }
435
+ .output-card .output-content-wrapper ol { list-style-type: decimal; }
436
+ .output-card .output-content-wrapper li { margin-bottom: 0.8rem; }
437
  .output-card .error-message {
438
+ padding: 1.5rem 2rem; margin-top: 1.5rem; font-size: 1.1rem;
439
+ border-radius: 12px; /* Hardcoded radius */
440
+ background: var(--error-background-fill) !important; /* Use theme error background */
441
+ color: var(--error-text-color) !important; /* Use theme error text */
442
+ border: 2px solid var(--error-border-color) !important; /* Use theme error border */
443
+ display: flex; align-items: flex-start; gap: 1.5em;
444
+ }
445
+ .output-card .error-message .error-icon { font-size: 1.8rem; line-height: 1; padding-top: 0.1em; }
446
+ .output-card .error-details { font-size: 0.95rem; margin-top: 0.8rem; opacity: 0.9; word-break: break-word; }
447
+ .output-card .placeholder {
448
+ padding: 2.5rem 2rem; font-size: 1.2rem; border-radius: 12px; /* Hardcoded radius */
449
+ border: 3px dashed var(--border-color-primary);
450
+ text-align: center; opacity: 0.8;
 
 
 
 
 
 
 
451
  }
452
+ /* Examples table styling */
 
453
  .examples-section table.gr-samples-table {
454
+ border-radius: 12px !important; /* Hardcoded radius */
455
  border: 1px solid var(--border-color-primary) !important;
456
  overflow: hidden;
457
+ box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
458
  }
459
+ @media (prefers-color-scheme: light) {
460
+ .examples-section table.gr-samples-table { box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
 
 
 
461
  }
462
+ .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; }
463
  .examples-section table.gr-samples-table th {
464
+ background: var(--block-background-fill) !important; /* Using theme background fill for table headers */
465
+ font-weight: 600 !important; text-align: left;
 
 
466
  }
467
  .examples-section table.gr-samples-table td {
468
+ border-top: 1px solid var(--border-color-primary) !important;
469
  cursor: pointer;
470
  }
471
+ .examples-section table.gr-samples-table tr:hover td { background: var(--hover-color) !important; } /* Use theme hover color */
 
 
472
  .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
473
+ /* Footer styling */
 
474
  .app-footer-wrapper {
 
475
  border-top: 1px solid var(--border-color-primary) !important;
476
+ margin-top: 0.5rem; /* Hardcoded margin */
477
+ padding-top: 2.5rem; /* Hardcoded padding */
478
+ padding-bottom: 2.5rem; /* Hardcoded padding */
479
+ border-top-left-radius: 24px; /* Hardcoded radius */
480
+ border-top-right-radius: 24px;
481
+ box-shadow: inset 0 8px 15px rgba(0,0,0,0.2);
482
  max-width: 1120px;
483
+ margin-left: auto;
484
+ margin-right: auto;
485
+ width: 100%;
486
+ display: flex !important;
487
+ flex-direction: column !important;
488
+ align-items: flex-start !important; /* Left aligns the content */
489
  }
490
  .app-footer {
491
+ padding: 0 3.5rem !important; /* Hardcoded padding */
492
+ display: flex;
493
+ flex-direction: column;
494
+ align-items: stretch;
495
+ width: 100%;
496
  }
497
  .app-footer p {
498
+ font-size: 1.05rem !important;
 
 
 
499
  text-align: left !important;
500
+ margin-bottom: 1rem;
501
  }
502
+ .app-footer a { font-weight: 500; }
503
+ /* Hide default Gradio example labels and related elements for cleaner presentation */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  .gr-examples .gr-label, .gr-examples button.gr-button-filter, .gr-examples .label-wrap,
505
  .gr-examples div[data-testid*="label-text"], .gr-examples span[data-testid*="label-text"],
506
  .gr-examples div[class*="label"], .gr-examples .gr-example-label,
 
515
  font-size: 0 !important; line-height: 0 !important; position: absolute !important;
516
  pointer-events: none !important;
517
  }
518
+ /* Responsive Adjustments (remaining hardcoded overrides) */
 
 
 
 
 
519
  @media (max-width: 1024px) {
520
+ .gradio-container > .flex.flex-col { max-width: 960px; padding: 0 1.5rem !important; }
521
+ .app-header-title { font-size: 3.8rem; } .app-header-tagline { font-size: 1.5rem; }
522
+ .app-header-wrapper { padding: 2.5rem 3.5rem !important; margin-bottom: 2rem; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; }
523
+ .main-dashboard-container { padding: 2.5rem !important; margin-bottom: 0.6rem; border-radius: 12px; gap: 2rem; }
524
+ .dashboard-card-section { padding: 1.8rem; border-radius: 8px; }
525
+ .sub-section-title { font-size: 2.0rem !important; margin-bottom: 0.7rem !important; }
526
+ .input-row { gap: 1.5rem; } .input-field { min-width: 280px; }
527
+ .gradio-textbox textarea { min-height: 160px; } .output-card .response-header { font-size: 1.7rem; }
528
+ .examples-section { padding-top: 1.2rem; }
529
+ .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.9rem 1.1rem !important; }
530
+ .app-footer-wrapper { margin-top: 0.6rem; border-top-left-radius: 12px; border-top-right-radius: 12px; }
531
+ .app-footer { padding: 0 1.5rem !important; }
 
 
 
 
532
  }
533
  @media (max-width: 768px) {
534
+ .gradio-container > .flex.flex-col { padding: 0 1rem !important; }
535
+ .app-header-wrapper { padding: 1.8rem 2.5rem !important; margin-bottom: 1.8rem; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; }
536
+ .app-header-logo { font-size: 4.5rem; margin-bottom: 0.6rem; } .app-header-title { font-size: 3.2rem; letter-spacing: -0.06em; }
537
+ .app-header-tagline { font-size: 1.3rem; }
538
+ .main-dashboard-container { padding: 1.8rem !important; margin-bottom: 0.5rem; border-radius: 12px; gap: 1.8rem; }
539
+ .dashboard-card-section { padding: 1.5rem; border-radius: 8px; }
540
+ .sub-section-title { font-size: 1.8rem !important; margin-top: 1rem !important; margin-bottom: 0.6rem !important; }
541
+ .input-row { flex-direction: column; gap: 1rem; } .input-field { min-width: 100%; }
542
+ .gradio-textbox textarea { min-height: 140px; } .button-row { justify-content: stretch; gap: 1rem; }
543
+ .gradio-button { width: 100%; padding: 1.1rem 2rem !important; font-size: 1.1rem !important; }
544
+ .output-card .response-header { font-size: 1.5rem; } .output-card .response-icon { font-size: 1.7rem; }
545
+ .output-card .placeholder { padding: 2.5rem 1.5rem; font-size: 1.1rem; }
546
+ .examples-section { padding-top: 1.2rem; }
547
+ .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; }
548
+ .app-footer-wrapper { margin-top: 0.6rem; border-top-left-radius: 12px; border-top-right-radius: 12px; padding-top: 2rem; padding-bottom: 2rem; }
549
+ .app-footer { padding: 0 1rem !important; }
550
  }
551
  @media (max-width: 480px) {
552
+ .gradio-container > .flex.flex-col { padding: 0 0.8rem !important; }
553
+ .app-header-wrapper { padding: 1.2rem 1rem !important; margin-bottom: 1.5rem; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; }
554
+ .app-header-logo { font-size: 3.8rem; margin-bottom: 0.5rem; } .app-header-title { font-size: 2.8rem; }
555
+ .app-header-tagline { font-size: 1.1rem; }
556
+ .main-dashboard-container { padding: 1.2rem !important; margin-bottom: 0.4rem; border-radius: 8px; gap: 1.5rem; }
557
+ .dashboard-card-section { padding: 1rem; border-radius: 4px; }
558
+ .sub-section-title { font-size: 1.5rem !important; margin-top: 0.8rem !important; margin-bottom: 0.5rem !important; }
559
+ .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] { font-size: 1.05rem !important; padding: 1rem 1.2rem !important; }
560
+ .gradio-textbox textarea { min-height: 120px; }
561
+ .gradio-button { padding: 1rem 1.5rem !important; font-size: 1rem !important; }
562
+ .output-card .response-header { font-size: 1.4rem; } .output-card .response-icon { font-size: 1.5rem; }
563
+ .output-card .placeholder { padding: 2rem 1rem; font-size: 1.05rem; }
564
+ .examples-section { padding-top: 0.8rem; }
565
+ .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; }
566
+ .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; }
567
+ .app-footer { padding: 0 0.8rem !important; }
568
  }
569
  """
570
 
571
+ with gr.Blocks(theme="earneleh/paris", css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
572
+ # --- Header Section ---
573
  with gr.Group(elem_classes="app-header-wrapper"):
574
  gr.Markdown(
575
  """
 
581
  """
582
  )
583
 
584
+ # --- Main Dashboard Console Container ---
585
  with gr.Column(elem_classes="main-dashboard-container"):
586
 
587
+ # --- Section 1: Introduction and Disclaimer Card ---
588
  with gr.Group(elem_classes="dashboard-card-section"):
589
+ gr.Markdown("<h3 class='sub-section-title'>Welcome & Disclaimer</h3>")
590
  gr.Markdown(
591
  """
592
  <p>Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.</p>
593
  <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>
594
+ """
 
595
  )
596
 
597
  # --- Section 2: OpenAI API Key Input Card ---
598
  with gr.Group(elem_classes="dashboard-card-section"):
599
+ gr.Markdown("<h3 class='sub-section-title'>OpenAI API Key</h3>")
600
  api_key_input = gr.Textbox(
601
+ label="OpenAI API Key", # Keep label for theme compatibility
602
  type="password", placeholder="Enter your API key (e.g., sk-...)",
603
  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,
604
  elem_classes=["input-field-group"]
605
  )
606
 
607
+ # --- Section 3: Query Input and State Selection Card ---
608
  with gr.Group(elem_classes="dashboard-card-section"):
609
+ gr.Markdown("<h3 class='sub-section-title'>Ask Your Question</h3>")
610
+ with gr.Row(elem_classes="input-row"):
611
+ with gr.Column(elem_classes="input-field", scale=3):
612
  query_input = gr.Textbox(
613
  label="Question", placeholder="E.g., What are the rules for security deposit returns in my state?",
614
+ lines=5, max_lines=10,
615
+ elem_classes=["input-field-group"]
616
  )
617
+ with gr.Column(elem_classes="input-field", scale=1):
618
  state_input = gr.Dropdown(
619
  label="Select State", choices=dropdown_choices, value=initial_value,
620
+ allow_custom_value=False,
621
+ elem_classes=["input-field-group"]
622
  )
623
  with gr.Row(elem_classes="button-row"):
624
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
625
  submit_button = gr.Button("Submit Query", variant="primary", elem_classes=["gr-button-primary"])
626
 
627
+ # --- Section 4: Output Display Card ---
628
  with gr.Group(elem_classes="dashboard-card-section"):
629
+ gr.Markdown("<h3 class='sub-section-title'>Legal Assistant's Response</h3>")
630
  output = gr.Markdown(
631
  value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
632
  elem_classes="output-content-wrapper output-card"
633
  )
634
 
635
+ # --- Section 5: Example Questions Section ---
636
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
637
+ gr.Markdown("<h3 class='sub-section-title'>Example Questions to Ask</h3>")
638
  if example_queries:
639
  gr.Examples(
640
  examples=example_queries, inputs=[query_input, state_input],
 
644
  else:
645
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
646
 
647
+ # --- Footer Section ---
648
  with gr.Group(elem_classes="app-footer-wrapper"):
649
  gr.Markdown(
650
  """
 
654
  Connect on <a href="https://www.linkedin.com/in/nischal1/" target='_blank'>LinkedIn</a>
655
  or explore insights at <a href="https://datascientistinsights.substack.com/" target='_blank'>Substack</a>.</p>
656
  </div>
657
+ """
 
658
  )
659
 
660
  # --- Event Listeners (Logic remains identical) ---
 
670
  ),
671
  inputs=[], outputs=[api_key_input, query_input, state_input, output]
672
  )
673
+ logging.info("Gradio interface created with earneleh/paris theme and refined custom CSS.")
674
  return demo
675
 
676
  # --- Main Execution Block (remains untouched from original logic) ---