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

updated UI v3

Browse files
Files changed (1) hide show
  1. app.py +314 -209
app.py CHANGED
@@ -5,7 +5,7 @@ from functools import lru_cache
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,7 +241,7 @@ 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 (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)
@@ -261,7 +261,8 @@ Answer:"""
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,228 +293,341 @@ Answer:"""
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
 
300
- /* Animation for header */
301
  @keyframes fadeInSlideDown { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: translateY(0); } }
302
 
303
- /* General layout adjustments to Gradio's base container */
 
 
 
 
 
 
 
 
 
304
  .gradio-container > .flex.flex-col {
305
- max-width: 1120px;
306
- margin: 0 auto !important;
307
- padding: 0 !important; /* Remove default Gradio padding */
308
- gap: 0 !important; /* Remove default Gradio gap */
309
  }
310
 
311
- /* Header specific styling */
312
  .app-header-wrapper {
313
- padding: 4.5rem 3.5rem !important; /* Use hardcoded padding to override theme potentially */
314
- text-align: center !important;
315
- border-bottom-left-radius: 24px; /* Use hardcoded radius */
316
- border-bottom-right-radius: 24px;
 
317
  position: relative;
318
  overflow: hidden;
319
  z-index: 10;
320
- margin-bottom: 2.5rem; /* Use hardcoded margin */
321
- border: 1px solid var(--border-color-primary) !important;
322
- border-top: none;
323
- max-width: 1120px;
324
- margin-left: auto;
325
- margin-right: auto;
326
- width: 100%;
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)); /* Using primary color from theme */
 
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; /* Explicit font family */
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; /* Explicit font family */
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
 
351
- /* Main dashboard container */
352
  .main-dashboard-container {
353
- border-radius: 24px; /* Use hardcoded radius */
354
- border: 1px solid var(--border-color-primary) !important;
355
- padding: 3.5rem !important; /* Use hardcoded padding */
356
- margin: 0 auto 0.8rem auto;
357
- z-index: 1; position: relative;
358
- display: flex; flex-direction: column; gap: 2.5rem; /* Use hardcoded gap */
359
- max-width: 1120px;
 
360
  }
361
 
362
- /* Card sections within the dashboard */
363
  .dashboard-card-section {
364
- border-radius: 12px; /* Use hardcoded radius */
 
 
 
365
  border: 1px solid var(--border-color-primary);
366
- padding: 2.5rem; /* Use hardcoded padding */
367
- box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
368
- display: flex; flex-direction: column; gap: 1.5rem;
369
- }
370
- /* Light mode specific inner shadow for card sections */
371
- @media (prefers-color-scheme: light) {
372
- .dashboard-card-section { box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
373
  }
374
 
375
- /* Section titles */
376
- .sub-section-title {
377
  font-family: 'Playfair Display', serif !important;
378
- font-size: 2.7rem !important;
379
  font-weight: 800 !important;
 
380
  text-align: center !important;
381
- margin-top: 1.5rem !important;
382
- margin-bottom: 0.8rem !important;
383
- display: block !important;
384
- width: 100% !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  }
386
 
387
- /* Text styling for custom markdown within cards */
388
- .dashboard-card-section p, .output-content-wrapper p {
389
- font-size: 1.15rem; line-height: 1.8;
390
- margin-bottom: 1.2rem;
 
 
 
 
 
 
391
  }
392
- .dashboard-card-section a, .output-content-wrapper a {
393
- text-decoration: none; font-weight: 500;
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  }
395
- .dashboard-card-section a:hover, .output-content-wrapper a:hover { text-decoration: underline; }
396
- .dashboard-card-section strong, .output-content-wrapper strong { font-weight: 700; }
397
 
398
- /* Input layout */
399
- .input-field-group { margin-bottom: 1rem; }
400
- .input-row { display: flex; gap: 1.8rem; flex-wrap: wrap; margin-bottom: 1rem; }
401
- .input-field { flex: 1; }
402
 
403
- /* Button layout */
404
- .button-row { display: flex; gap: 2rem; margin-top: 2rem; flex-wrap: wrap; justify-content: flex-end; }
405
  .gradio-button {
406
- border-radius: 12px !important; /* Hardcode radius */
407
- padding: 1.2rem 2.8rem !important; /* Hardcode padding */
408
- font-size: 1.15rem !important; /* Hardcode font size */
409
- font-weight: 600 !important; /* Hardcode font weight */
410
- border: 1px solid transparent !important;
411
- box-shadow: 0 6px 20px rgba(0,0,0,0.35); /* Custom shadow */
412
- transition: all 0.4s cubic-bezier(0.0, 0.0, 0.2, 1);
413
- }
414
- .gradio-button:hover:not(:disabled) { transform: translateY(-6px); box-shadow: 0 12px 28px rgba(0,0,0,0.45) !important; }
415
- .gradio-button:active:not(:disabled) { transform: translateY(-3px); }
416
- .gradio-button:disabled {
417
- box-shadow: none !important;
418
- cursor: not-allowed;
419
  }
420
- .gr-button-secondary { /* Override for secondary button specific look */
421
- background: transparent !important;
422
- border: 2px solid var(--border-color-primary) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  box-shadow: none !important;
424
  }
425
  .gr-button-secondary:hover:not(:disabled) {
426
- background: var(--button-secondary-background-hover) !important; /* Use theme variable */
427
- border-color: var(--primary-500) !important; /* Use theme primary color */
428
- }
429
- @media (prefers-color-scheme: light) {
430
- .gradio-button { box-shadow: 0 6px 20px rgba(0,0,0,0.1); }
431
- .gradio-button:hover:not(:disabled) { box-shadow: 0 12px 28px rgba(0,0,0,0.2) !important; }
432
- }
433
-
434
- /* Output card and placeholders */
435
- .output-card { padding: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; }
436
- .output-card .response-header {
437
- font-size: 1.8rem; font-weight: 700;
438
- margin: 0 0 1rem 0; display: flex; align-items: center; gap: 1.2rem;
439
- }
440
- .output-card .response-icon { font-size: 2rem; color: var(--primary-500); } /* Use theme primary color */
441
- .output-card .divider { border: none; border-top: 1px solid var(--border-color-primary); margin: 1.5rem 0 1.8rem 0; }
442
- .output-card .output-content-wrapper { font-size: 1.15rem; line-height: 1.8; }
443
- .output-card .output-content-wrapper p { margin-bottom: 1rem; }
444
- .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; }
445
- .output-card .output-content-wrapper ol { list-style-type: decimal; }
446
- .output-card .output-content-wrapper li { margin-bottom: 0.8rem; }
447
- .output-card .error-message {
448
- padding: 1.5rem 2rem; margin-top: 1.5rem; font-size: 1.1rem;
449
- border-radius: 12px; /* Hardcoded radius */
450
- background: var(--error-background-fill) !important; /* Use theme error background */
451
- color: var(--error-text-color) !important; /* Use theme error text */
452
- border: 2px solid var(--error-border-color) !important; /* Use theme error border */
453
- display: flex; align-items: flex-start; gap: 1.5em;
454
- }
455
- .output-card .error-message .error-icon { font-size: 1.8rem; line-height: 1; padding-top: 0.1em; }
456
- .output-card .error-details { font-size: 0.95rem; margin-top: 0.8rem; opacity: 0.9; word-break: break-word; }
 
 
 
 
 
 
 
 
 
 
 
457
  .output-card .placeholder {
458
- padding: 2.5rem 2rem; font-size: 1.2rem; border-radius: 12px; /* Hardcoded radius */
459
- border: 3px dashed var(--border-color-primary);
460
- text-align: center; opacity: 0.8;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  }
462
 
463
- /* Examples table styling */
464
  .examples-section table.gr-samples-table {
465
- border-radius: 12px !important; /* Hardcoded radius */
466
  border: 1px solid var(--border-color-primary) !important;
467
  overflow: hidden;
468
- box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
469
  }
470
- @media (prefers-color-scheme: light) {
471
- .examples-section table.gr-samples-table { box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
 
 
 
472
  }
473
- .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; }
474
  .examples-section table.gr-samples-table th {
475
- background: var(--block-background-fill) !important; /* Using theme background fill for table headers */
476
- font-weight: 600 !important; text-align: left;
 
 
477
  }
478
  .examples-section table.gr-samples-table td {
479
- border-top: 1px solid var(--border-color-primary) !important;
480
  cursor: pointer;
481
  }
482
- .examples-section table.gr-samples-table tr:hover td { background: var(--hover-color) !important; } /* Use theme hover color */
 
 
483
  .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
484
 
485
- /* Footer styling */
486
  .app-footer-wrapper {
 
487
  border-top: 1px solid var(--border-color-primary) !important;
488
- margin-top: 0.5rem; /* Hardcoded margin */
489
- padding-top: 2.5rem; /* Hardcoded padding */
490
- padding-bottom: 2.5rem; /* Hardcoded padding */
491
- border-top-left-radius: 24px; /* Hardcoded radius */
492
- border-top-right-radius: 24px;
493
- box-shadow: inset 0 8px 15px rgba(0,0,0,0.2);
494
- max-width: 1120px;
495
- margin-left: auto;
496
- margin-right: auto;
497
  width: 100%;
498
- display: flex !important;
499
- flex-direction: column !important;
500
- align-items: flex-start !important; /* Left aligns the content */
501
- }
502
- .app-footer {
503
- padding: 0 3.5rem !important; /* Hardcoded padding */
504
  display: flex;
505
  flex-direction: column;
506
- align-items: stretch;
 
 
507
  width: 100%;
508
  }
509
  .app-footer p {
510
- font-size: 1.05rem !important;
 
 
511
  text-align: left !important;
512
- margin-bottom: 1rem;
513
  }
514
- .app-footer a { font-weight: 500; }
 
 
 
 
 
 
 
 
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,
@@ -528,61 +642,54 @@ Answer:"""
528
  font-size: 0 !important; line-height: 0 !important; position: absolute !important;
529
  pointer-events: none !important;
530
  }
531
- /* Responsive Adjustments (remaining hardcoded overrides) */
 
 
 
 
 
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: 2rem; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; }
536
- .main-dashboard-container { padding: 2.5rem !important; margin-bottom: 0.6rem; border-radius: 12px; gap: 2rem; }
537
- .dashboard-card-section { padding: 1.8rem; border-radius: 8px; }
538
- .sub-section-title { font-size: 2.0rem !important; margin-bottom: 0.7rem !important; }
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: 1.8rem; border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; }
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.5rem; border-radius: 12px; gap: 1.8rem; }
552
- .dashboard-card-section { padding: 1.5rem; border-radius: 8px; }
553
- .sub-section-title { font-size: 1.8rem !important; margin-top: 1rem !important; margin-bottom: 0.6rem !important; }
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: 1.5rem; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; }
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.4rem; border-radius: 8px; gap: 1.5rem; }
570
- .dashboard-card-section { padding: 1rem; border-radius: 4px; }
571
- .sub-section-title { font-size: 1.5rem !important; margin-top: 0.8rem !important; margin-bottom: 0.5rem !important; }
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: 1rem !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,12 +701,12 @@ 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>
@@ -609,9 +716,9 @@ Answer:"""
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"]
@@ -619,19 +726,17 @@ Answer:"""
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
  )
636
  with gr.Row(elem_classes="button-row"):
637
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
@@ -639,7 +744,7 @@ Answer:"""
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"
@@ -647,7 +752,7 @@ Answer:"""
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],
@@ -657,7 +762,7 @@ Answer:"""
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
  """
@@ -683,7 +788,7 @@ Answer:"""
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) ---
 
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
  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)
 
261
  return answer # Return the pre-formatted error message directly
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:
 
293
  example_queries.append(["What basic rights do tenants have?", "California"])
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
 
301
+ /* Keyframe animation for initial header load */
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 */
328
  position: relative;
329
  overflow: hidden;
330
  z-index: 10;
331
+ border: 1px solid var(--border-color-primary); /* Use theme border */
 
 
 
 
 
 
332
  }
333
+ .app-header { display: flex; flex-direction: column; align-items: center; }
334
  .app-header-logo {
335
  font-size: 5.5rem; margin-bottom: 0.8rem; line-height: 1;
336
+ color: var(--primary-500); /* Use theme primary color */
337
+ filter: drop-shadow(0 0 15px var(--primary-500));
338
  transform: translateY(-40px); opacity: 0;
339
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.3s;
340
  }
341
  .app-header-title {
342
+ font-family: 'Playfair Display', serif !important; /* Override with specific heading font */
343
  font-size: 4.2rem; font-weight: 900;
344
  margin: 0 0 0.8rem 0; letter-spacing: -0.07em;
345
+ color: var(--text-color-header); /* Use theme text color for headings */
346
+ text-shadow: 0 8px 16px rgba(0,0,0,0.5); /* Custom shadow for effect */
347
  transform: translateY(-40px); opacity: 0;
348
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.6s;
349
  }
350
  .app-header-tagline {
351
+ font-family: 'Inter', sans-serif !important;
352
  font-size: 1.6rem; font-weight: 300;
353
+ color: var(--text-color-subdued); /* Use theme subdued text color */
354
  opacity: 0.9; max-width: 900px;
355
  transform: translateY(-40px); opacity: 0;
356
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.9s;
357
  }
358
 
359
+ /* Main dashboard console container */
360
  .main-dashboard-container {
361
+ display: flex;
362
+ flex-direction: column;
363
+ gap: 2rem; /* Spacing between cards */
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
 
371
+ /* Individual Card Sections */
372
  .dashboard-card-section {
373
+ background-color: var(--background-fill-secondary); /* A slightly darker/lighter background for cards */
374
+ border-radius: var(--radius-md);
375
+ padding: 2rem; /* Padding inside each card */
376
+ box-shadow: var(--shadow-md); /* Subtle shadow for cards */
377
  border: 1px solid var(--border-color-primary);
 
 
 
 
 
 
 
378
  }
379
 
380
+ /* Section titles within cards */
381
+ .card-section-title {
382
  font-family: 'Playfair Display', serif !important;
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;
390
+ }
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);
397
+ margin-bottom: 1rem;
398
+ }
399
+ .dashboard-card-section strong {
400
+ color: var(--text-color-body); /* Ensure strong text is legible */
401
+ font-weight: 600;
402
+ }
403
+ .dashboard-card-section a {
404
+ color: var(--link-text-color); /* Use theme link color */
405
+ text-decoration: underline;
406
+ }
407
+ .dashboard-card-section a:hover {
408
+ color: var(--link-text-color-hover); /* Use theme link hover color */
409
+ text-decoration: none;
410
  }
411
 
412
+ /* Input field styling (mostly handled by theme, but override specific padding/radius) */
413
+ .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] {
414
+ padding: 1.2rem 1.4rem !important; /* Adjust padding for consistency */
415
+ border-radius: var(--radius-sm) !important; /* Use theme radius */
416
+ font-size: 1.05rem !important;
417
+ }
418
+ .gradio-textbox textarea { min-height: 160px; }
419
+ .gradio-input-info a {
420
+ color: var(--link-text-color); /* Ensure info links are themed */
421
+ text-decoration: underline;
422
  }
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
 
 
 
 
 
440
 
441
+ /* Button styling overrides to match original design intent with theme */
 
442
  .gradio-button {
443
+ border-radius: var(--radius-sm) !important;
444
+ font-weight: 600 !important;
445
+ padding: 0.8rem 2rem !important; /* Make buttons slightly less tall */
446
+ min-height: 48px; /* Ensure minimum height */
447
+ box-shadow: var(--shadow-sm); /* Use theme shadow */
448
+ transition: all 0.2s ease-in-out;
449
+ transform: translateY(0);
450
+ }
451
+ .gradio-button:hover:not(:disabled) {
452
+ transform: translateY(-3px); /* Subtle lift on hover */
453
+ box-shadow: var(--shadow-md);
 
 
454
  }
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 */
480
+ .response-header-container {
481
+ display: flex;
482
+ align-items: center;
483
+ gap: 1rem;
484
+ margin-bottom: 1rem;
485
+ }
486
+ .response-icon {
487
+ font-size: 2.2rem;
488
+ color: var(--primary-500); /* Use theme primary color */
489
+ }
490
+ .response-title {
491
+ font-family: 'Playfair Display', serif !important;
492
+ font-size: 1.8rem !important;
493
+ font-weight: 700 !important;
494
+ color: var(--text-color-body) !important; /* Ensure heading is legible */
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);
507
+ }
508
+ .output-content-wrapper ul, .output-content-wrapper ol {
509
+ margin-left: 2rem;
510
+ margin-bottom: 1rem;
511
+ padding-left: 0;
512
+ }
513
+ .output-content-wrapper li {
514
+ margin-bottom: 0.5rem;
515
+ }
516
  .output-card .placeholder {
517
+ padding: 2rem;
518
+ font-size: 1.1rem;
519
+ border-radius: var(--radius-md);
520
+ border: 2px dashed var(--border-color-secondary);
521
+ color: var(--text-color-subdued);
522
+ text-align: center;
523
+ opacity: 0.7;
524
+ min-height: 150px; /* Ensure some height for placeholder */
525
+ display: flex;
526
+ align-items: center;
527
+ justify-content: center;
528
+ }
529
+ .output-card .error-message {
530
+ padding: 1.5rem;
531
+ font-size: 1.05rem;
532
+ border-radius: var(--radius-sm);
533
+ background-color: var(--color-error-50); /* Use theme error color with low opacity */
534
+ color: var(--color-error-600); /* Strong error text color */
535
+ border: 1px solid var(--color-error-200); /* Theme error border */
536
+ display: flex;
537
+ align-items: flex-start;
538
+ gap: 1rem;
539
+ }
540
+ .output-card .error-message .error-icon {
541
+ font-size: 1.8rem;
542
+ line-height: 1;
543
+ color: var(--color-error-500); /* Use theme error color */
544
+ }
545
+ .output-card .error-details {
546
+ font-size: 0.9rem;
547
+ margin-top: 0.5rem;
548
+ opacity: 0.8;
549
+ word-break: break-word;
550
  }
551
 
552
+ /* Examples Table (from gr.Examples) */
553
  .examples-section table.gr-samples-table {
554
+ border-radius: var(--radius-sm) !important;
555
  border: 1px solid var(--border-color-primary) !important;
556
  overflow: hidden;
557
+ background-color: var(--background-fill-secondary); /* Match card background */
558
  }
559
+ .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td {
560
+ padding: 0.8rem 1rem !important;
561
+ font-size: 0.95rem !important;
562
+ border: none !important;
563
+ color: var(--text-color-body) !important;
564
  }
 
565
  .examples-section table.gr-samples-table th {
566
+ background-color: var(--background-fill-tertiary) !important; /* Slightly different background for header */
567
+ font-weight: 600 !important;
568
+ text-align: left;
569
+ color: var(--text-color-header) !important;
570
  }
571
  .examples-section table.gr-samples-table td {
572
+ border-top: 1px solid var(--border-color-secondary) !important; /* Lighter border for rows */
573
  cursor: pointer;
574
  }
575
+ .examples-section table.gr-samples-table tr:hover td {
576
+ background-color: var(--primary-50) !important; /* Light tint on hover */
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;
601
  text-align: left !important;
 
602
  }
603
+ .app-footer a {
604
+ color: var(--link-text-color) !important;
605
+ font-weight: 500;
606
+ text-decoration: underline;
607
+ }
608
+ .app-footer a:hover {
609
+ color: var(--link-text-color-hover) !important;
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;
620
+ height: 0 !important;
621
+ overflow: hidden !important;
622
+ margin: 0 !important;
623
+ padding: 0 !important;
624
+ border: 0 !important;
625
+ font-size: 0 !important;
626
+ line-height: 0 !important;
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,
 
642
  font-size: 0 !important; line-height: 0 !important; position: absolute !important;
643
  pointer-events: none !important;
644
  }
645
+ /* Further clean up any potential residual margin/padding Gradio adds to its components */
646
+ .gradio-container .gr-box { margin: 0 !important; padding: 0 !important; }
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; }
661
+ .button-row { justify-content: center; gap: 1rem; flex-direction: column; }
662
+ .gradio-button { width: 100%; max-width: 300px; margin: 0 auto; } /* Center buttons on smaller screens */
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; }
684
+ .gradio-textbox textarea { min-height: 100px; }
685
+ .gradio-button { padding: 0.6rem 1.5rem !important; min-height: 40px; font-size: 0.95rem !important; }
686
+ .response-icon { font-size: 1.8rem; } .response-title { font-size: 1.4rem !important; }
687
+ .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; }
 
 
 
 
 
 
 
 
 
 
 
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
  """
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>
 
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-...)",
723
  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,
724
  elem_classes=["input-field-group"]
 
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
 
740
  )
741
  with gr.Row(elem_classes="button-row"):
742
  clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
 
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"
 
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
  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
  """
 
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) ---