Nischal Subedi commited on
Commit
da0edf8
·
1 Parent(s): 07e3330

updated UI v4

Browse files
Files changed (1) hide show
  1. app.py +175 -64
app.py CHANGED
@@ -5,7 +5,7 @@ from functools import lru_cache
5
  import re
6
 
7
  import gradio as gr
8
- # No longer importing gradio.themes as a separate object, as we'll use a string theme directly.
9
 
10
  try:
11
  # Assuming vector_db.py exists in the same directory or is installed
@@ -241,7 +241,112 @@ 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
- # --- GRADIO INTERFACE (Complete Overhaul for Legibility, Layout, and Hugging Face Compatibility) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)
@@ -262,7 +367,7 @@ Answer:"""
262
  else:
263
  # Format the successful response with the new UI structure
264
  # Use a div for the icon and header text for better control over alignment and spacing
265
- formatted_response = f"<div class='response-header-container'><span class='response-icon'>📜</span><h4 class='response-title'>Response for {state}</h4></div><hr class='divider'>{answer}"
266
  return formatted_response
267
 
268
  try:
@@ -294,7 +399,7 @@ Answer:"""
294
 
295
 
296
  # --- Minimal Custom CSS for structural/behavioral overrides and specific aesthetics ---
297
- # This CSS is designed to complement 'earneleh/paris' and fix layout issues on Hugging Face.
298
  custom_css = """
299
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@700;800;900&display=swap');
300
 
@@ -302,26 +407,31 @@ Answer:"""
302
  @keyframes fadeInSlideDown { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: translateY(0); } }
303
 
304
  /* General Gradio container layout and centering */
 
305
  .gradio-container {
306
  font-family: 'Inter', sans-serif !important; /* Ensure Inter is main font */
307
  min-height: 100vh;
308
  display: flex;
309
- justify-content: center;
310
- align-items: flex-start; /* Align content to top */
311
- padding: 2.5rem 1rem; /* Overall padding */
312
  background-color: var(--background-page); /* Use theme's page background */
 
 
 
313
  }
314
  .gradio-container > .flex.flex-col {
315
  max-width: 1120px; /* Constrain max width for desktop */
316
  width: 100%;
317
- margin: 0 auto;
318
- gap: 2.5rem !important; /* Consistent spacing between main sections */
 
319
  }
320
 
321
  /* Header specific styling (outside main dashboard for distinct look) */
322
  .app-header-wrapper {
323
  background-color: var(--block-background-fill); /* Use theme's block background */
324
- padding: 3.5rem 2.5rem; /* Spacious padding */
325
  text-align: center;
326
  border-radius: var(--radius-lg); /* Use theme radius */
327
  box-shadow: var(--shadow-lg); /* Use theme shadow */
@@ -364,7 +474,7 @@ Answer:"""
364
  background-color: var(--background-fill-primary); /* Use theme's main background */
365
  border-radius: var(--radius-lg);
366
  box-shadow: var(--shadow-lg);
367
- padding: 2.5rem; /* Inner padding for the whole dashboard content */
368
  border: 1px solid var(--border-color-primary);
369
  }
370
 
@@ -383,7 +493,7 @@ Answer:"""
383
  font-size: 2.2rem !important;
384
  font-weight: 800 !important;
385
  color: var(--text-color-body) !important; /* Use theme body text color */
386
- text-align: center !important;
387
  margin-bottom: 1.5rem !important;
388
  padding-bottom: 0.8rem !important;
389
  border-bottom: 1px solid var(--border-color-primary) !important;
@@ -391,6 +501,7 @@ Answer:"""
391
 
392
  /* General text styling within cards */
393
  .dashboard-card-section p {
 
394
  font-size: 1.1rem;
395
  line-height: 1.6;
396
  color: var(--text-color-body);
@@ -423,17 +534,20 @@ Answer:"""
423
  .gradio-input-info a:hover {
424
  color: var(--link-text-color-hover);
425
  }
426
- /* Style for the dropdown arrow */
427
  .gradio-dropdown select {
428
- 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%22%236C757D%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;
 
429
  background-position: right 1rem center !important;
430
  background-size: 1.2em !important;
431
  padding-right: 3rem !important;
432
  color: var(--text-color-body) !important; /* Ensure dropdown text is legible */
433
  }
434
- @media (prefers-color-scheme: dark) {
435
- .gradio-dropdown select {
436
- 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%22%23A0A0B0%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;
 
 
437
  }
438
  }
439
 
@@ -455,25 +569,17 @@ Answer:"""
455
  .gradio-button:active:not(:disabled) {
456
  transform: translateY(0);
457
  }
458
- .gr-button-primary {
459
- background-color: var(--primary-500) !important; /* Use theme primary color */
460
- color: var(--text-color-body) !important; /* Match text to body color for contrast */
461
- border-color: var(--primary-500) !important;
462
- }
463
- .gr-button-primary:hover:not(:disabled) {
464
- background-color: var(--primary-600) !important; /* Darker primary on hover */
465
- border-color: var(--primary-600) !important;
466
- }
467
  .gr-button-secondary {
468
  background-color: transparent !important;
469
  border: 1px solid var(--border-color-primary) !important;
470
- color: var(--text-color-body) !important;
471
  box-shadow: none !important;
472
  }
473
  .gr-button-secondary:hover:not(:disabled) {
474
  background-color: var(--primary-50) !important; /* Light tint of primary on hover */
475
- color: var(--primary-500) !important;
476
- border-color: var(--primary-500) !important;
477
  }
478
 
479
  /* Output Response Section */
@@ -495,12 +601,13 @@ Answer:"""
495
  margin: 0;
496
  padding: 0;
497
  }
498
- .output-content-wrapper .divider {
499
  border: none;
500
  border-top: 1px solid var(--border-color-secondary); /* A lighter border for divider */
501
  margin: 1rem 0 1.5rem 0;
502
  }
503
  .output-content-wrapper {
 
504
  line-height: 1.7;
505
  font-size: 1.05rem;
506
  color: var(--text-color-body);
@@ -577,24 +684,25 @@ Answer:"""
577
  }
578
  .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
579
 
580
- /* Footer styling (at the very bottom of the page, outside main dashboard) */
581
  .app-footer-wrapper {
582
  background-color: var(--block-background-fill); /* Use theme's block background */
583
  border-top: 1px solid var(--border-color-primary) !important;
584
- padding: 1.5rem 2.5rem; /* Reduced padding */
585
  border-radius: var(--radius-lg);
586
  box-shadow: var(--shadow-lg);
587
  width: 100%;
588
  max-width: 1120px;
589
- margin: 2.5rem auto 0 auto; /* Spacing from main dashboard */
590
  display: flex;
591
  flex-direction: column;
592
  align-items: flex-start; /* Left aligns the content */
593
  }
594
  .app-footer {
595
- width: 100%;
596
  }
597
  .app-footer p {
 
598
  font-size: 0.95rem !important;
599
  color: var(--text-color-subdued) !important;
600
  margin-bottom: 0.5rem;
@@ -610,10 +718,11 @@ Answer:"""
610
  text-decoration: none;
611
  }
612
 
613
- /* Hide unwanted Gradio default elements */
614
  .gr-messages-row, .gr-share-btn, .gr-api-btn, .gr-view-api, /* Common boilerplate elements */
615
- .gradio-container > footer, /* Default Gradio footer text */
616
- #component-4 /* example element */ {
 
617
  display: none !important;
618
  visibility: hidden !important;
619
  width: 0 !important;
@@ -627,7 +736,7 @@ Answer:"""
627
  position: absolute !important;
628
  pointer-events: none !important;
629
  }
630
- /* More specific example label hiding */
631
  .gr-examples .gr-label, .gr-examples button.gr-button-filter, .gr-examples .label-wrap,
632
  .gr-examples div[data-testid*="label-text"], .gr-examples span[data-testid*="label-text"],
633
  .gr-examples div[class*="label"], .gr-examples .gr-example-label,
@@ -647,14 +756,14 @@ Answer:"""
647
  .gradio-container .gr-form { gap: 0 !important; } /* Remove gaps in forms where not desired */
648
 
649
 
650
- /* Responsive Adjustments */
651
  @media (max-width: 1024px) {
652
  .gradio-container { padding: 2rem 0.8rem; }
653
  .gradio-container > .flex.flex-col { gap: 2rem !important; }
654
- .app-header-wrapper { padding: 2.5rem 1.8rem; margin-bottom: 2rem; }
655
  .app-header-title { font-size: 3.5rem; } .app-header-tagline { font-size: 1.4rem; }
656
- .main-dashboard-container { padding: 2rem; gap: 1.8rem; }
657
- .dashboard-card-section { padding: 1.5rem; }
658
  .card-section-title { font-size: 1.8rem !important; margin-bottom: 1.2rem !important; }
659
  .input-row { flex-direction: column; gap: 1rem; }
660
  .gradio-textbox textarea { min-height: 140px; }
@@ -663,21 +772,21 @@ Answer:"""
663
  .response-header-container { gap: 0.8rem; } .response-icon { font-size: 2rem; } .response-title { font-size: 1.6rem !important; }
664
  .output-card .placeholder { padding: 1.5rem; font-size: 1rem; min-height: 120px;}
665
  .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; }
666
- .app-footer-wrapper { padding: 1.2rem 1.8rem; margin-top: 1.5rem; }
667
  .app-footer p { font-size: 0.85rem !important; }
668
  }
669
  @media (max-width: 768px) {
670
  .gradio-container { padding: 1.5rem 0.5rem; }
671
  .gradio-container > .flex.flex-col { gap: 1.8rem !important; }
672
- .app-header-wrapper { padding: 2rem 1rem; margin-bottom: 1.5rem; }
673
  .app-header-logo { font-size: 4.5rem; } .app-header-title { font-size: 2.8rem; } .app-header-tagline { font-size: 1.2rem; }
674
- .main-dashboard-container { padding: 1.5rem; gap: 1.5rem; }
675
- .dashboard-card-section { padding: 1.2rem; }
676
  .card-section-title { font-size: 1.6rem !important; margin-bottom: 1rem !important; }
677
  .gradio-textbox textarea { min-height: 120px; }
678
  .output-content-wrapper { font-size: 1rem; }
679
  .output-card .error-message { padding: 1rem; }
680
- .app-footer-wrapper { padding: 1rem 1rem; margin-top: 1rem; }
681
  }
682
  @media (max-width: 480px) {
683
  .app-header-logo { font-size: 3.5rem; } .app-header-title { font-size: 2.2rem; } .app-header-tagline { font-size: 1rem; }
@@ -688,8 +797,8 @@ Answer:"""
688
  }
689
  """
690
 
691
- with gr.Blocks(theme="earneleh/paris", css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
692
- # --- Header Section (outside main dashboard for distinct look) ---
693
  with gr.Group(elem_classes="app-header-wrapper"):
694
  gr.Markdown(
695
  """
@@ -701,22 +810,23 @@ Answer:"""
701
  """
702
  )
703
 
704
- # --- Main Dashboard Console Container (Centralized and well-structured) ---
705
  with gr.Column(elem_classes="main-dashboard-container"):
706
 
707
- # --- Section 1: Introduction and Disclaimer Card ---
708
  with gr.Group(elem_classes="dashboard-card-section"):
709
  gr.Markdown("<h3 class='card-section-title'>Welcome & Disclaimer</h3>")
710
  gr.Markdown(
711
  """
712
  <p>Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.</p>
713
  <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>
714
- """
 
715
  )
716
 
717
  # --- Section 2: OpenAI API Key Input Card ---
718
  with gr.Group(elem_classes="dashboard-card-section"):
719
- gr.Markdown("<h3 class='card-section-title'>OpenAI API Key</h3>")
720
  api_key_input = gr.Textbox(
721
  label="OpenAI API Key", # Keep label for theme compatibility and accessibility
722
  type="password", placeholder="Enter your API key (e.g., sk-...)",
@@ -724,16 +834,16 @@ Answer:"""
724
  elem_classes=["input-field-group"]
725
  )
726
 
727
- # --- Section 3: Query Input and State Selection Card ---
728
  with gr.Group(elem_classes="dashboard-card-section"):
729
- gr.Markdown("<h3 class='card-section-title'>Ask Your Question</h3>")
730
- with gr.Row(): # Removed custom elem_classes as row itself should be simple
731
  with gr.Column(scale=3):
732
  query_input = gr.Textbox(
733
  label="Question", placeholder="E.g., What are the rules for security deposit returns in my state?",
734
  lines=5, max_lines=10
735
  )
736
- with gr.Column(scale=1, min_width=200): # Give state dropdown a min-width
737
  state_input = gr.Dropdown(
738
  label="Select State", choices=dropdown_choices, value=initial_value,
739
  allow_custom_value=False
@@ -742,17 +852,17 @@ Answer:"""
742
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
743
  submit_button = gr.Button("Submit Query", variant="primary", elem_classes=["gr-button-primary"])
744
 
745
- # --- Section 4: Output Display Card ---
746
  with gr.Group(elem_classes="dashboard-card-section"):
747
- gr.Markdown("<h3 class='card-section-title'>Legal Assistant's Response</h3>")
748
  output = gr.Markdown(
749
  value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
750
  elem_classes="output-content-wrapper output-card"
751
  )
752
 
753
- # --- Section 5: Example Questions Section ---
754
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
755
- gr.Markdown("<h3 class='card-section-title'>Example Questions to Ask</h3>")
756
  if example_queries:
757
  gr.Examples(
758
  examples=example_queries, inputs=[query_input, state_input],
@@ -762,7 +872,7 @@ Answer:"""
762
  else:
763
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
764
 
765
- # --- Footer Section (at the very bottom of the page) ---
766
  with gr.Group(elem_classes="app-footer-wrapper"):
767
  gr.Markdown(
768
  """
@@ -772,7 +882,8 @@ Answer:"""
772
  Connect on <a href="https://www.linkedin.com/in/nischal1/" target='_blank'>LinkedIn</a>
773
  or explore insights at <a href="https://datascientistinsights.substack.com/" target='_blank'>Substack</a>.</p>
774
  </div>
775
- """
 
776
  )
777
 
778
  # --- Event Listeners (Logic remains identical) ---
@@ -788,7 +899,7 @@ Answer:"""
788
  ),
789
  inputs=[], outputs=[api_key_input, query_input, state_input, output]
790
  )
791
- logging.info("Gradio interface created with earneleh/paris theme and refined custom CSS for optimal layout and legibility.")
792
  return demo
793
 
794
  # --- Main Execution Block (remains untouched from original logic) ---
 
5
  import re
6
 
7
  import gradio as gr
8
+ import gradio.themes as themes # Import gradio.themes
9
 
10
  try:
11
  # Assuming vector_db.py exists in the same directory or is installed
 
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
+ super().__init__(
251
+ # Custom fonts
252
+ font=[themes.GoogleFont("Inter"), "sans-serif"],
253
+ font_mono=themes.GoogleFont("JetBrains Mono"),
254
+ heading_font=[themes.GoogleFont("Playfair Display"), "serif"],
255
+
256
+ # --- Dark Mode Colors (Legal Noir) ---
257
+ # Backgrounds
258
+ background_page_dark="#0F0F1A", # Overall app background
259
+ block_background_fill_dark="#1A1A2B", # Header and main footer background
260
+ background_fill_primary_dark="#1E1E30", # Main dashboard container background (slightly lighter than page)
261
+ background_fill_secondary_dark="#2A2A40", # Inner card sections background (lighter than primary)
262
+ background_fill_tertiary_dark="#3F3F5A", # For things like table headers, slightly more prominent
263
+
264
+ # Text Colors
265
+ text_color_body_dark="#EAEAF0", # Main text
266
+ text_color_subdued_dark="#A0A0B0", # Secondary text, placeholders
267
+ text_color_header_dark="#EAEAF0", # Heading text (matches body for consistency)
268
+
269
+ # Accent/Primary Colors (Goldenrod/Amber)
270
+ primary_hue_dark=themes.Color("#FFC107", "#E0A800"), # Main accent color
271
+ primary_50_dark="#403200", # Lightest primary tint (for subtle hovers)
272
+
273
+ # Borders
274
+ border_color_primary_dark="#3F3F5A", # Main borders for blocks/cards
275
+ border_color_secondary_dark="#505060", # Lighter borders for internal elements (e.g., table rows)
276
+
277
+ # Shadows
278
+ shadow_lg_dark="0 1.2rem 3.5rem rgba(0,0,0,0.6)", # Large shadow for main containers
279
+ shadow_md_dark="0 0.8rem 2.5rem rgba(0,0,0,0.4)", # Medium shadow for cards
280
+ shadow_sm_dark="0 6px 20px rgba(0,0,0,0.35)", # Small shadow for buttons
281
+
282
+ # Links
283
+ link_text_color_dark="#FFC107",
284
+ link_text_color_hover_dark="#E0A800",
285
+
286
+ # Buttons (custom variant backgrounds will be handled by CSS, but theme sets default)
287
+ button_primary_background_dark="#FFC107",
288
+ button_primary_background_hover_dark="#E0A800",
289
+ button_primary_text_color_dark="#1A1A2B", # Dark text on primary button
290
+ button_secondary_background_dark="transparent", # Custom in CSS
291
+ button_secondary_background_hover_dark="#403200", # Custom in CSS
292
+ button_secondary_text_color_dark="#A0A0B0", # Custom in CSS
293
+ button_secondary_border_color_dark="#3F3F5A", # Custom in CSS
294
+
295
+ # Error Colors
296
+ color_error_50_dark="#4D001A", # Lightest error tint (for background)
297
+ color_error_200_dark="#990026", # Medium error (for border)
298
+ color_error_500_dark="#CC0033", # Main error (for icon)
299
+ color_error_600_dark="#FFB3C2", # Darker error (for text)
300
+
301
+
302
+ # --- Light Mode Colors (Legal Lumen) ---
303
+ background_page_light="#F0F2F5",
304
+ block_background_fill_light="#E0E4EB",
305
+ background_fill_primary_light="#FFFFFF",
306
+ background_fill_secondary_light="#F8F9FA",
307
+ background_fill_tertiary_light="#DDE2E8",
308
+
309
+ text_color_body_light="#212529",
310
+ text_color_subdued_light="#6C757D",
311
+ text_color_header_light="#212529",
312
+
313
+ primary_hue_light=themes.Color("blue", "#007bff", "#0056b3"),
314
+ primary_50_light="#E0F0FF",
315
+
316
+ border_color_primary_light="#DDE2E8",
317
+ border_color_secondary_light="#CCD1D7",
318
+
319
+ shadow_lg_light="0 0.8rem 2.5rem rgba(0,0,0,0.15)",
320
+ shadow_md_light="0 0.6rem 2rem rgba(0,0,0,0.1)",
321
+ shadow_sm_light="0 3px 10px rgba(0,0,0,0.08)",
322
+
323
+ link_text_color_light="#007bff",
324
+ link_text_color_hover_light="#0056b3",
325
+
326
+ button_primary_background_light="#007bff",
327
+ button_primary_background_hover_light="#0056b3",
328
+ button_primary_text_color_light="#FFFFFF",
329
+ button_secondary_background_light="transparent",
330
+ button_secondary_background_hover_light="#E0F0FF",
331
+ button_secondary_text_color_light="#6C757D",
332
+ button_secondary_border_color_light="#DDE2E8",
333
+
334
+ color_error_50_light="#F8D7DA",
335
+ color_error_200_light="#F5C6CB",
336
+ color_error_500_light="#DC3545",
337
+ color_error_600_light="#721C24",
338
+
339
+ # Radii and Spacing (matching custom CSS)
340
+ radius_xs="4px",
341
+ radius_sm="8px",
342
+ radius_md="12px",
343
+ radius_lg="24px",
344
+ spacing_md="2.5rem", # Matches gaps between major sections
345
+ spacing_lg="3.5rem", # Matches padding for header/main dashboard
346
+ spacing_xl="4.5rem", # Matches header padding top/bottom
347
+ **kwargs,
348
+ )
349
+
350
  def gradio_interface(self):
351
  def query_interface_wrapper(api_key: str, query: str, state: str) -> str:
352
  # Basic client-side validation for immediate feedback (redundant but good UX)
 
367
  else:
368
  # Format the successful response with the new UI structure
369
  # Use a div for the icon and header text for better control over alignment and spacing
370
+ 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}"
371
  return formatted_response
372
 
373
  try:
 
399
 
400
 
401
  # --- Minimal Custom CSS for structural/behavioral overrides and specific aesthetics ---
402
+ # This CSS is designed to complement our custom LegalAestheticTheme.
403
  custom_css = """
404
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@700;800;900&display=swap');
405
 
 
407
  @keyframes fadeInSlideDown { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: translateY(0); } }
408
 
409
  /* General Gradio container layout and centering */
410
+ body { margin: 0; padding: 0; } /* Reset default body margin */
411
  .gradio-container {
412
  font-family: 'Inter', sans-serif !important; /* Ensure Inter is main font */
413
  min-height: 100vh;
414
  display: flex;
415
+ justify-content: center; /* Center content horizontally */
416
+ align-items: flex-start; /* Align content to top vertically */
417
+ padding: var(--spacing-lg) 1rem; /* Overall padding top/bottom, fixed horizontal for small screens */
418
  background-color: var(--background-page); /* Use theme's page background */
419
+ transition: background-color 0.4s ease-in-out, color 0.4s ease-in-out; /* Smooth theme transition */
420
+ -webkit-font-smoothing: antialiased;
421
+ -moz-osx-font-smoothing: grayscale;
422
  }
423
  .gradio-container > .flex.flex-col {
424
  max-width: 1120px; /* Constrain max width for desktop */
425
  width: 100%;
426
+ margin: 0 auto; /* Center the main content column */
427
+ gap: var(--spacing-md) !important; /* Consistent spacing between main sections */
428
+ padding: 0 !important; /* Remove default Gradio column padding */
429
  }
430
 
431
  /* Header specific styling (outside main dashboard for distinct look) */
432
  .app-header-wrapper {
433
  background-color: var(--block-background-fill); /* Use theme's block background */
434
+ padding: var(--spacing-xl) var(--spacing-lg); /* Spacious padding */
435
  text-align: center;
436
  border-radius: var(--radius-lg); /* Use theme radius */
437
  box-shadow: var(--shadow-lg); /* Use theme shadow */
 
474
  background-color: var(--background-fill-primary); /* Use theme's main background */
475
  border-radius: var(--radius-lg);
476
  box-shadow: var(--shadow-lg);
477
+ padding: var(--spacing-md); /* Inner padding for the whole dashboard content */
478
  border: 1px solid var(--border-color-primary);
479
  }
480
 
 
493
  font-size: 2.2rem !important;
494
  font-weight: 800 !important;
495
  color: var(--text-color-body) !important; /* Use theme body text color */
496
+ text-align: center !important; /* Centered as requested */
497
  margin-bottom: 1.5rem !important;
498
  padding-bottom: 0.8rem !important;
499
  border-bottom: 1px solid var(--border-color-primary) !important;
 
501
 
502
  /* General text styling within cards */
503
  .dashboard-card-section p {
504
+ font-family: 'Inter', sans-serif !important;
505
  font-size: 1.1rem;
506
  line-height: 1.6;
507
  color: var(--text-color-body);
 
534
  .gradio-input-info a:hover {
535
  color: var(--link-text-color-hover);
536
  }
537
+ /* Style for the dropdown arrow, ensuring it's visible in both themes */
538
  .gradio-dropdown select {
539
+ 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;
540
+ background-repeat: no-repeat;
541
  background-position: right 1rem center !important;
542
  background-size: 1.2em !important;
543
  padding-right: 3rem !important;
544
  color: var(--text-color-body) !important; /* Ensure dropdown text is legible */
545
  }
546
+ /* Current color for SVG icon will adapt with text_color_body, but explicitly set if needed */
547
+ /* For light mode, the fill for SVG should be a darker color */
548
+ @media (prefers-color-scheme: light) {
549
+ .gradio-dropdown select {
550
+ color: var(--text-color-body) !important; /* Ensure dropdown text is dark */
551
  }
552
  }
553
 
 
569
  .gradio-button:active:not(:disabled) {
570
  transform: translateY(0);
571
  }
572
+ /* Primary button uses theme primary colors automatically */
 
 
 
 
 
 
 
 
573
  .gr-button-secondary {
574
  background-color: transparent !important;
575
  border: 1px solid var(--border-color-primary) !important;
576
+ color: var(--text-color-body) !important; /* Use theme body text for secondary button */
577
  box-shadow: none !important;
578
  }
579
  .gr-button-secondary:hover:not(:disabled) {
580
  background-color: var(--primary-50) !important; /* Light tint of primary on hover */
581
+ color: var(--primary-500) !important; /* Primary color for text on hover */
582
+ border-color: var(--primary-500) !important; /* Primary color for border on hover */
583
  }
584
 
585
  /* Output Response Section */
 
601
  margin: 0;
602
  padding: 0;
603
  }
604
+ .output-content-wrapper .response-divider { /* Specific class for this divider */
605
  border: none;
606
  border-top: 1px solid var(--border-color-secondary); /* A lighter border for divider */
607
  margin: 1rem 0 1.5rem 0;
608
  }
609
  .output-content-wrapper {
610
+ font-family: 'Inter', sans-serif !important;
611
  line-height: 1.7;
612
  font-size: 1.05rem;
613
  color: var(--text-color-body);
 
684
  }
685
  .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
686
 
687
+ /* Footer styling (at the very bottom of the page) */
688
  .app-footer-wrapper {
689
  background-color: var(--block-background-fill); /* Use theme's block background */
690
  border-top: 1px solid var(--border-color-primary) !important;
691
+ padding: 1.5rem 2.5rem; /* Consistent padding */
692
  border-radius: var(--radius-lg);
693
  box-shadow: var(--shadow-lg);
694
  width: 100%;
695
  max-width: 1120px;
696
+ margin: var(--spacing-md) auto 0 auto; /* Spacing from main dashboard */
697
  display: flex;
698
  flex-direction: column;
699
  align-items: flex-start; /* Left aligns the content */
700
  }
701
  .app-footer {
702
+ width: 100%; /* Ensure content takes full width */
703
  }
704
  .app-footer p {
705
+ font-family: 'Inter', sans-serif !important;
706
  font-size: 0.95rem !important;
707
  color: var(--text-color-subdued) !important;
708
  margin-bottom: 0.5rem;
 
718
  text-decoration: none;
719
  }
720
 
721
+ /* Hide unwanted Gradio default elements for a clean look */
722
  .gr-messages-row, .gr-share-btn, .gr-api-btn, .gr-view-api, /* Common boilerplate elements */
723
+ .gradio-container > footer, /* Default Gradio footer text that appears below blocks */
724
+ .gradio-app .version-info, /* Gradio version info */
725
+ .gradio-app .dark\:text-gray-400 /* Target other default footer text */ {
726
  display: none !important;
727
  visibility: hidden !important;
728
  width: 0 !important;
 
736
  position: absolute !important;
737
  pointer-events: none !important;
738
  }
739
+ /* More specific example label/button hiding */
740
  .gr-examples .gr-label, .gr-examples button.gr-button-filter, .gr-examples .label-wrap,
741
  .gr-examples div[data-testid*="label-text"], .gr-examples span[data-testid*="label-text"],
742
  .gr-examples div[class*="label"], .gr-examples .gr-example-label,
 
756
  .gradio-container .gr-form { gap: 0 !important; } /* Remove gaps in forms where not desired */
757
 
758
 
759
+ /* Responsive Adjustments (using hardcoded values for precision, but mapping to theme variables where applicable for color/font) */
760
  @media (max-width: 1024px) {
761
  .gradio-container { padding: 2rem 0.8rem; }
762
  .gradio-container > .flex.flex-col { gap: 2rem !important; }
763
+ .app-header-wrapper { padding: 2.5rem 1.8rem; margin-bottom: 2rem; border-radius: 12px; }
764
  .app-header-title { font-size: 3.5rem; } .app-header-tagline { font-size: 1.4rem; }
765
+ .main-dashboard-container { padding: 2rem; gap: 1.8rem; border-radius: 12px; }
766
+ .dashboard-card-section { padding: 1.5rem; border-radius: 8px; }
767
  .card-section-title { font-size: 1.8rem !important; margin-bottom: 1.2rem !important; }
768
  .input-row { flex-direction: column; gap: 1rem; }
769
  .gradio-textbox textarea { min-height: 140px; }
 
772
  .response-header-container { gap: 0.8rem; } .response-icon { font-size: 2rem; } .response-title { font-size: 1.6rem !important; }
773
  .output-card .placeholder { padding: 1.5rem; font-size: 1rem; min-height: 120px;}
774
  .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; }
775
+ .app-footer-wrapper { padding: 1.2rem 1.8rem; margin-top: 1.5rem; border-radius: 12px; }
776
  .app-footer p { font-size: 0.85rem !important; }
777
  }
778
  @media (max-width: 768px) {
779
  .gradio-container { padding: 1.5rem 0.5rem; }
780
  .gradio-container > .flex.flex-col { gap: 1.8rem !important; }
781
+ .app-header-wrapper { padding: 2rem 1rem; margin-bottom: 1.5rem; border-radius: 12px; }
782
  .app-header-logo { font-size: 4.5rem; } .app-header-title { font-size: 2.8rem; } .app-header-tagline { font-size: 1.2rem; }
783
+ .main-dashboard-container { padding: 1.5rem; gap: 1.5rem; border-radius: 12px; }
784
+ .dashboard-card-section { padding: 1.2rem; border-radius: 8px; }
785
  .card-section-title { font-size: 1.6rem !important; margin-bottom: 1rem !important; }
786
  .gradio-textbox textarea { min-height: 120px; }
787
  .output-content-wrapper { font-size: 1rem; }
788
  .output-card .error-message { padding: 1rem; }
789
+ .app-footer-wrapper { padding: 1rem 1rem; margin-top: 1rem; border-radius: 12px; }
790
  }
791
  @media (max-width: 480px) {
792
  .app-header-logo { font-size: 3.5rem; } .app-header-title { font-size: 2.2rem; } .app-header-tagline { font-size: 1rem; }
 
797
  }
798
  """
799
 
800
+ with gr.Blocks(theme=self.LegalAestheticTheme(), css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
801
+ # --- Header Section (distinct, top-level block) ---
802
  with gr.Group(elem_classes="app-header-wrapper"):
803
  gr.Markdown(
804
  """
 
810
  """
811
  )
812
 
813
+ # --- Main Dashboard Console Container (centralized, card-based) ---
814
  with gr.Column(elem_classes="main-dashboard-container"):
815
 
816
+ # --- Section 1: Welcome & Disclaimer Card ---
817
  with gr.Group(elem_classes="dashboard-card-section"):
818
  gr.Markdown("<h3 class='card-section-title'>Welcome & Disclaimer</h3>")
819
  gr.Markdown(
820
  """
821
  <p>Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.</p>
822
  <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>
823
+ """,
824
+ elem_classes="dashboard-card-content" # Apply general text styling
825
  )
826
 
827
  # --- Section 2: OpenAI API Key Input Card ---
828
  with gr.Group(elem_classes="dashboard-card-section"):
829
+ gr.Markdown("<h3 class='card-section-title'>OpenAI API Key</h3>") # Centered by CSS
830
  api_key_input = gr.Textbox(
831
  label="OpenAI API Key", # Keep label for theme compatibility and accessibility
832
  type="password", placeholder="Enter your API key (e.g., sk-...)",
 
834
  elem_classes=["input-field-group"]
835
  )
836
 
837
+ # --- Section 3: Ask Your Question Card ---
838
  with gr.Group(elem_classes="dashboard-card-section"):
839
+ gr.Markdown("<h3 class='card-section-title'>Ask Your Question</h3>") # Centered by CSS
840
+ with gr.Row():
841
  with gr.Column(scale=3):
842
  query_input = gr.Textbox(
843
  label="Question", placeholder="E.g., What are the rules for security deposit returns in my state?",
844
  lines=5, max_lines=10
845
  )
846
+ with gr.Column(scale=1, min_width=200):
847
  state_input = gr.Dropdown(
848
  label="Select State", choices=dropdown_choices, value=initial_value,
849
  allow_custom_value=False
 
852
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
853
  submit_button = gr.Button("Submit Query", variant="primary", elem_classes=["gr-button-primary"])
854
 
855
+ # --- Section 4: Legal Assistant's Response Card ---
856
  with gr.Group(elem_classes="dashboard-card-section"):
857
+ gr.Markdown("<h3 class='card-section-title'>Legal Assistant's Response</h3>") # Centered by CSS
858
  output = gr.Markdown(
859
  value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
860
  elem_classes="output-content-wrapper output-card"
861
  )
862
 
863
+ # --- Section 5: Example Questions Card ---
864
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
865
+ gr.Markdown("<h3 class='card-section-title'>Example Questions to Ask</h3>") # Centered by CSS
866
  if example_queries:
867
  gr.Examples(
868
  examples=example_queries, inputs=[query_input, state_input],
 
872
  else:
873
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
874
 
875
+ # --- Footer Section (distinct, bottom-level block) ---
876
  with gr.Group(elem_classes="app-footer-wrapper"):
877
  gr.Markdown(
878
  """
 
882
  Connect on <a href="https://www.linkedin.com/in/nischal1/" target='_blank'>LinkedIn</a>
883
  or explore insights at <a href="https://datascientistinsights.substack.com/" target='_blank'>Substack</a>.</p>
884
  </div>
885
+ """,
886
+ elem_classes="footer-content" # Apply general text styling to footer markdown
887
  )
888
 
889
  # --- Event Listeners (Logic remains identical) ---
 
899
  ),
900
  inputs=[], outputs=[api_key_input, query_input, state_input, output]
901
  )
902
+ logging.info("Gradio interface created with custom LegalAestheticTheme and refined CSS for optimal layout, legibility, and Hugging Face compatibility.")
903
  return demo
904
 
905
  # --- Main Execution Block (remains untouched from original logic) ---