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

updated UI v2

Browse files
Files changed (1) hide show
  1. app.py +158 -297
app.py CHANGED
@@ -5,6 +5,8 @@ from functools import lru_cache
5
  import re
6
 
7
  import gradio as gr
 
 
8
  try:
9
  # Assuming vector_db.py exists in the same directory or is installed
10
  from vector_db import VectorDatabase
@@ -239,7 +241,7 @@ Answer:"""
239
  logging.error(f"Failed to load or process PDF '{pdf_path}': {str(e)}", exc_info=True)
240
  raise RuntimeError(f"Failed to process PDF '{pdf_path}': {e}") from e
241
 
242
- # --- GRADIO INTERFACE (Complete Overhaul for Cohesion, Legibility, and Advanced Look) ---
243
  def gradio_interface(self):
244
  def query_interface_wrapper(api_key: str, query: str, state: str) -> str:
245
  # Basic client-side validation for immediate feedback (redundant but good UX)
@@ -290,363 +292,227 @@ Answer:"""
290
  example_queries.append(["What basic rights do tenants have?", "California"])
291
 
292
 
293
- # --- Custom CSS for "Legal Noir" Theme (Dark Mode First) ---
 
294
  custom_css = """
295
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@700;800;900&display=swap');
296
- :root {
297
- /* Dark Theme Colors (Default: Legal Noir) */
298
- --app-bg-dark: #0F0F1A;
299
- --header-bg-dark: #1A1A2B;
300
- --main-card-bg-dark: #1E1E30;
301
- --card-section-bg-dark: #2A2A40;
302
- --text-primary-dark: #EAEAF0;
303
- --text-secondary-dark: #A0A0B0;
304
- --accent-main-dark: #FFC107; /* Goldenrod/Amber */
305
- --accent-hover-dark: #E0A800; /* Darker Goldenrod */
306
- --border-dark: #3F3F5A;
307
- --shadow-dark: 0 1.2rem 3.5rem rgba(0,0,0,0.6);
308
- --focus-ring-dark: rgba(255, 193, 7, 0.4);
309
- --error-bg-dark: #4D001A;
310
- --error-text-dark: #FFB3C2;
311
- --error-border-dark: #990026;
312
- /* Light Theme Colors (Alternative: Legal Lumen) */
313
- --app-bg-light: #F0F2F5;
314
- --header-bg-light: #E0E4EB;
315
- --main-card-bg-light: #FFFFFF;
316
- --card-section-bg-light: #F8F9FA;
317
- --text-primary-light: #212529;
318
- --text-secondary-light: #6C757D;
319
- --accent-main-light: #007bff; /* Standard Blue */
320
- --accent-hover-light: #0056b3; /* Darker Blue */
321
- --border-light: #DDE2E8;
322
- --shadow-light: 0 0.8rem 2.5rem rgba(0,0,0,0.15);
323
- --focus-ring-light: rgba(0, 123, 255, 0.3);
324
- --error-bg-light: #F8D7DA;
325
- --error-text-light: #721C24;
326
- --error-border-light: #F5C6CB;
327
- /* General Styling Variables */
328
- --font-family-main: 'Inter', sans-serif;
329
- --font-family-header: 'Playfair Display', serif;
330
- --radius-xs: 4px;
331
- --radius-sm: 8px;
332
- --radius-md: 12px;
333
- --radius-lg: 24px;
334
- --transition-speed: 0.4s ease-in-out;
335
- --padding-xl: 4.5rem;
336
- --padding-lg: 3.5rem;
337
- --padding-md: 2.5rem;
338
- --padding-sm: 1.8rem;
339
- }
340
- body, .gradio-container {
341
- font-family: var(--font-family-main) !important;
342
- background: var(--app-bg-dark) !important;
343
- color: var(--text-primary-dark) !important;
344
- margin: 0; padding: 0; min-height: 100vh; font-size: 16px;
345
- line-height: 1.75; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
346
- transition: background var(--transition-speed), color var(--transition-speed);
347
- }
348
- * { box-sizing: border-box; }
349
- @media (prefers-color-scheme: light) {
350
- body, .gradio-container { background: var(--app-bg-light) !important; color: var(--text-primary-light) !important; }
351
- }
352
  .gradio-container > .flex.flex-col {
353
- max-width: 1120px; margin: 0 auto !important; padding: 0 !important;
354
- gap: 0 !important; transition: padding var(--transition-speed);
 
 
355
  }
 
 
356
  .app-header-wrapper {
357
- background: var(--header-bg-dark); color: var(--text-primary-dark) !important;
358
- padding: var(--padding-xl) var(--padding-lg) !important; text-align: center !important;
359
- border-bottom-left-radius: var(--radius-lg); border-bottom-right-radius: var(--radius-lg);
360
- box-shadow: var(--shadow-dark); position: relative; overflow: hidden; z-index: 10;
361
- transition: background var(--transition-speed), box-shadow var(--transition-speed);
362
- margin-bottom: 2.5rem; border: 1px solid var(--border-dark); border-top: none;
363
- max-width: 1120px; margin-left: auto; margin-right: auto; width: 100%;
364
- }
365
- @media (prefers-color-scheme: light) {
366
- .app-header-wrapper { background: var(--header-bg-light); color: var(--text-primary-light) !important; box-shadow: var(--shadow-light); border: 1px solid var(--border-light); border-top: none; }
 
 
 
 
367
  }
368
  .app-header { display: flex; flex-direction: column; align-items: center; position: relative; z-index: 1; }
369
  .app-header-logo {
370
  font-size: 5.5rem; margin-bottom: 0.8rem; line-height: 1;
371
- filter: drop-shadow(0 0 15px var(--accent-main-dark));
372
  transform: translateY(-40px); opacity: 0;
373
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.3s;
374
- transition: filter var(--transition-speed);
375
  }
376
- @media (prefers-color-scheme: light) { .app-header-logo { filter: drop-shadow(0 0 10px var(--accent-main-light)); } }
377
  .app-header-title {
378
- font-family: var(--font-family-header) !important; font-size: 4.2rem; font-weight: 900;
 
379
  margin: 0 0 0.8rem 0; letter-spacing: -0.07em;
380
  text-shadow: 0 8px 16px rgba(0,0,0,0.5);
381
  transform: translateY(-40px); opacity: 0;
382
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.6s;
383
- transition: text-shadow var(--transition-speed), color var(--transition-speed);
384
  }
385
- @media (prefers-color-scheme: light) { .app-header-title { text-shadow: 0 6px 12px rgba(0,0,0,0.25); } }
386
  .app-header-tagline {
387
- font-family: var(--font-family-main) !important; font-size: 1.6rem; font-weight: 300;
 
388
  opacity: 0.9; max-width: 900px;
389
  transform: translateY(-40px); opacity: 0;
390
  animation: fadeInSlideDown 1.5s ease-out forwards; animation-delay: 0.9s;
391
- transition: opacity var(--transition-speed);
392
  }
393
- @keyframes fadeInSlideDown { from { opacity: 0; transform: translateY(-40px); } to { opacity: 1; transform: translateY(0); } }
 
394
  .main-dashboard-container {
395
- background: var(--main-card-bg-dark) !important; border-radius: var(--radius-lg);
396
- box-shadow: var(--shadow-dark); border: 1px solid var(--border-dark) !important;
397
- padding: var(--padding-lg) !important; margin: 0 auto 0.8rem auto;
 
398
  z-index: 1; position: relative;
399
- transition: background var(--transition-speed), border-color var(--transition-speed), box-shadow var(--transition-speed);
400
- display: flex; flex-direction: column; gap: 2.5rem; max-width: 1120px;
401
- }
402
- @media (prefers-color-scheme: light) {
403
- .main-dashboard-container { background: var(--main-card-bg-light) !important; box-shadow: var(--shadow-light); border: 1px solid var(--border-light) !important; }
404
  }
 
 
405
  .dashboard-card-section {
406
- background: var(--card-section-bg-dark) !important; border-radius: var(--radius-md);
407
- border: 1px solid var(--border-dark); padding: var(--padding-md);
 
408
  box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
409
- transition: background var(--transition-speed), border-color var(--transition-speed), box-shadow var(--transition-speed);
410
  display: flex; flex-direction: column; gap: 1.5rem;
411
  }
 
412
  @media (prefers-color-scheme: light) {
413
- .dashboard-card-section { background: var(--card-section-bg-light) !important; border: 1px solid var(--border-light); box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
414
- }
415
- .section-title {
416
- font-family: var(--font-family-header) !important; font-size: 2.8rem !important; font-weight: 800 !important;
417
- color: var(--text-primary-dark) !important; text-align: center !important;
418
- margin: 0 auto 1.8rem auto !important; padding-bottom: 0.8rem !important;
419
- border-bottom: 2px solid var(--border-dark) !important; width: 100%;
420
- transition: color var(--transition-speed), border-color var(--transition-speed);
421
  }
 
 
422
  .sub-section-title {
423
- font-family: var(--font-family-header) !important;
424
- font-size: 2.7rem !important; /* Slightly increased for more prominence */
425
- font-weight: 800 !important; /* Increased font weight for more visual emphasis */
426
- color: var(--text-primary-dark) !important;
427
  text-align: center !important;
428
  margin-top: 1.5rem !important;
429
  margin-bottom: 0.8rem !important;
430
- transition: color var(--transition-speed);
431
- display: block !important; /* Ensure it's a block element */
432
- width: 100% !important; /* Ensure it takes full width for centering */
433
- }
434
- @media (prefers-color-scheme: light) {
435
- .section-title, .sub-section-title { color: var(--text-primary-light) !important; border-bottom-color: var(--border-light) !important; }
436
  }
 
 
437
  .dashboard-card-section p, .output-content-wrapper p {
438
- font-size: 1.15rem; line-height: 1.8; color: var(--text-secondary-dark);
439
- margin-bottom: 1.2rem; transition: color var(--transition-speed);
440
  }
441
  .dashboard-card-section a, .output-content-wrapper a {
442
- color: var(--accent-main-dark); text-decoration: none; font-weight: 500;
443
- transition: color var(--transition-speed), text-decoration var(--transition-speed);
444
  }
445
- .dashboard-card-section a:hover, .output-content-wrapper a:hover { color: var(--accent-hover-dark); text-decoration: underline; }
446
- .dashboard-card-section strong, .output-content-wrapper strong { font-weight: 700; color: var(--text-primary-dark); transition: color var(--transition-speed); }
447
- @media (prefers-color-scheme: light) {
448
- .dashboard-card-section p, .output-content-wrapper p { color: var(--text-secondary-light); }
449
- .dashboard-card-section a, .output-content-wrapper a { color: var(--accent-main-light); }
450
- .dashboard-card-section a:hover, .output-content-wrapper a:hover { color: var(--accent-hover-light); }
451
- .dashboard-card-section strong, .output-content-wrapper strong { color: var(--text-primary-light); }
452
- }
453
- .section-divider { border: none; border-top: 1px solid var(--border-dark); margin: 2rem 0 !important; transition: border-color var(--transition-speed); }
454
- @media (prefers-color-scheme: light) { .section-divider { border-top: 1px solid var(--border-light); } }
455
  .input-field-group { margin-bottom: 1rem; }
456
  .input-row { display: flex; gap: 1.8rem; flex-wrap: wrap; margin-bottom: 1rem; }
457
  .input-field { flex: 1; }
458
- .gradio-input-label {
459
- font-size: 1.2rem !important; font-weight: 500 !important; color: var(--text-primary-dark) !important;
460
- margin-bottom: 0.8rem !important; display: block !important; transition: color var(--transition-speed);
461
- }
462
- .gradio-input-info { font-size: 1.0rem !important; color: var(--text-secondary-dark) !important; margin-top: 0.6rem; transition: color var(--transition-speed); }
463
- @media (prefers-color-scheme: light) {
464
- .gradio-input-label { color: var(--text-primary-light) !important; }
465
- .gradio-input-info { color: var(--text-secondary-light) !important; }
466
- }
467
- .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] {
468
- border: 2px solid var(--border-dark) !important; border-radius: var(--radius-md) !important;
469
- padding: 1.3rem 1.6rem !important; font-size: 1.15rem !important;
470
- background: var(--main-card-bg-dark) !important; color: var(--text-primary-dark) !important;
471
- width: 100% !important; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3);
472
- transition: border-color var(--transition-speed), box-shadow var(--transition-speed), background var(--transition-speed), color var(--transition-speed);
473
- }
474
- .gradio-textbox textarea { min-height: 180px; }
475
- .gradio-textbox textarea::placeholder, .gradio-textbox input[type=password]::placeholder { color: #808090 !important; }
476
- .gradio-textbox textarea:focus, .gradio-dropdown select:focus, .gradio-textbox input[type=password]:focus {
477
- border-color: var(--accent-main-dark) !important;
478
- box-shadow: 0 0 0 6px var(--focus-ring-dark) !important, inset 0 1px 3px rgba(0,0,0,0.4);
479
- outline: none !important;
480
- }
481
- @media (prefers-color-scheme: light) {
482
- .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] {
483
- border: 2px solid var(--border-light) !important; background: var(--main-card-bg-light) !important;
484
- color: var(--text-primary-light) !important; box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
485
- }
486
- .gradio-textbox textarea:focus, .gradio-dropdown select:focus, .gradio-textbox input[type=password]:focus {
487
- border-color: var(--accent-main-light) !important;
488
- box-shadow: 0 0 0 6px var(--focus-ring-light) !important, inset 0 1px 3px rgba(0,0,0,0.2);
489
- }
490
- }
491
- .gradio-dropdown select {
492
- appearance: none; -webkit-appearance: none; -moz-appearance: none;
493
- 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');
494
- background-repeat: no-repeat; background-position: right 1.8rem center; background-size: 1.4em;
495
- padding-right: 5rem !important;
496
- }
497
- @media (prefers-color-scheme: light) {
498
- .gradio-dropdown select {
499
- 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');
500
- }
501
- }
502
  .button-row { display: flex; gap: 2rem; margin-top: 2rem; flex-wrap: wrap; justify-content: flex-end; }
503
  .gradio-button {
504
- border-radius: var(--radius-md) !important; padding: 1.2rem 2.8rem !important;
505
- font-size: 1.15rem !important; font-weight: 600 !important;
506
- border: 1px solid transparent !important; box-shadow: 0 6px 20px rgba(0,0,0,0.35);
507
- transition: all var(--transition-speed) cubic-bezier(0.0, 0.0, 0.2, 1);
 
 
 
508
  }
509
  .gradio-button:hover:not(:disabled) { transform: translateY(-6px); box-shadow: 0 12px 28px rgba(0,0,0,0.45) !important; }
510
  .gradio-button:active:not(:disabled) { transform: translateY(-3px); }
511
  .gradio-button:disabled {
512
- background: #3A3A50 !important; color: #6A6A80 !important;
513
- box-shadow: none !important; border-color: #4A4A60 !important; cursor: not-allowed;
 
 
 
 
 
 
 
 
 
514
  }
515
- .gr-button-primary { background: var(--accent-main-dark) !important; color: var(--header-bg-dark) !important; border-color: var(--accent-main-dark) !important; }
516
- .gr-button-primary:hover:not(:disabled) { background: var(--accent-hover-dark) !important; border-color: var(--accent-hover-dark) !important; }
517
- .gr-button-secondary { background: transparent !important; color: var(--text-secondary-dark) !important; border: 2px solid var(--border-dark) !important; box-shadow: none !important; }
518
- .gr-button-secondary:hover:not(:disabled) { background: rgba(255, 193, 7, 0.15) !important; color: var(--accent-main-dark) !important; border-color: var(--accent-main-dark) !important; }
519
  @media (prefers-color-scheme: light) {
520
  .gradio-button { box-shadow: 0 6px 20px rgba(0,0,0,0.1); }
521
  .gradio-button:hover:not(:disabled) { box-shadow: 0 12px 28px rgba(0,0,0,0.2) !important; }
522
- .gradio-button:disabled { background: #E9ECEF !important; color: #ADB5BD !important; border-color: #DEE2E6 !important; }
523
- .gr-button-primary { background: var(--accent-main-light) !important; color: #FFFFFF !important; border-color: var(--accent-main-light) !important; }
524
- .gr-button-primary:hover:not(:disabled) { background: var(--accent-hover-light) !important; border-color: var(--accent-hover-light) !important; }
525
- .gr-button-secondary { background: transparent !important; color: var(--text-secondary-light) !important; border: 2px solid var(--border-light) !important; }
526
- .gr-button-secondary:hover:not(:disabled) { background: rgba(0, 123, 255, 0.1) !important; color: var(--accent-main-light) !important; border-color: var(--accent-main-light) !important; }
527
  }
 
 
528
  .output-card { padding: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; }
529
  .output-card .response-header {
530
- font-size: 1.8rem; font-weight: 700; color: var(--text-primary-dark);
531
  margin: 0 0 1rem 0; display: flex; align-items: center; gap: 1.2rem;
532
- transition: color var(--transition-speed);
533
  }
534
- .output-card .response-icon { font-size: 2rem; color: var(--accent-main-dark); transition: color var(--transition-speed); }
535
- .output-card .divider { border: none; border-top: 1px solid var(--border-dark); margin: 1.5rem 0 1.8rem 0; transition: border-color var(--transition-speed); }
536
- .output-card .output-content-wrapper { font-size: 1.15rem; line-height: 1.8; color: var(--text-primary-dark); transition: color var(--transition-speed); }
537
  .output-card .output-content-wrapper p { margin-bottom: 1rem; }
538
  .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; }
539
  .output-card .output-content-wrapper ol { list-style-type: decimal; }
540
  .output-card .output-content-wrapper li { margin-bottom: 0.8rem; }
541
- .output-card .output-content-wrapper strong { font-weight: 700; }
542
- .output-card .output-content-wrapper a { color: var(--accent-main-dark); text-decoration: underline; }
543
- .output-card .output-content-wrapper a:hover { color: var(--accent-hover-dark); }
544
- @media (prefers-color-scheme: light) {
545
- .output-card .response-header { color: var(--text-primary-light); }
546
- .output-card .response-icon { color: var(--accent-main-light); }
547
- .output-card .divider { border-top: 1px solid var(--border-light); }
548
- .output-card .output-content-wrapper { color: var(--text-primary-light); }
549
- .output-card .output-content-wrapper a { color: var(--accent-main-light); }
550
- .output-card .output-content-wrapper a:hover { color: var(--accent-hover-light); }
551
- }
552
  .output-card .error-message {
553
  padding: 1.5rem 2rem; margin-top: 1.5rem; font-size: 1.1rem;
554
- border-radius: var(--radius-md); background: var(--error-bg-dark);
555
- color: var(--error-text-dark); border: 2px solid var(--error-border-dark);
 
 
556
  display: flex; align-items: flex-start; gap: 1.5em;
557
- transition: background var(--transition-speed), color var(--transition-speed), border-color var(--transition-speed);
558
  }
559
  .output-card .error-message .error-icon { font-size: 1.8rem; line-height: 1; padding-top: 0.1em; }
560
  .output-card .error-details { font-size: 0.95rem; margin-top: 0.8rem; opacity: 0.9; word-break: break-word; }
561
- @media (prefers-color-scheme: light) {
562
- .output-card .error-message { background: var(--error-bg-light); color: var(--error-text-light); border: 2px solid var(--error-border-light); }
563
- }
564
  .output-card .placeholder {
565
- padding: 2.5rem 2rem; font-size: 1.2rem; border-radius: var(--radius-md);
566
- border: 3px dashed var(--border-dark); color: var(--text-secondary-dark);
567
  text-align: center; opacity: 0.8;
568
- transition: border-color var(--transition-speed), color var(--transition-speed);
569
- }
570
- @media (prefers-color-scheme: light) {
571
- .output-card .placeholder { border-color: var(--border-light); color: var(--text-secondary-light); }
572
  }
573
- /* Target the table within the examples section directly */
574
- .examples-section table.gr-samples-table { /* Updated selector based on common Gradio internal classes */
575
- border-radius: var(--radius-md) !important; border: 1px solid var(--border-dark) !important;
576
- overflow: hidden; background: var(--card-section-bg-dark) !important;
 
 
577
  box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
578
- transition: border-color var(--transition-speed), background var(--transition-speed), box-shadow var(--transition-speed);
579
  }
580
  @media (prefers-color-scheme: light) {
581
- .examples-section table.gr-samples-table { border: 1px solid var(--border-light) !important; background: var(--card-section-bg-light) !important; box-shadow: inset 0 0 8px rgba(0,0,0,0.05); }
582
  }
583
  .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; }
584
  .examples-section table.gr-samples-table th {
585
- background: var(--header-bg-dark) !important; color: var(--text-primary-dark) !important;
586
  font-weight: 600 !important; text-align: left;
587
- transition: background var(--transition-speed), color var(--transition-speed);
588
  }
589
  .examples-section table.gr-samples-table td {
590
- background: var(--card-section-bg-dark) !important; color: var(--text-primary-dark) !important;
591
- border-top: 1px solid var(--border-dark) !important; cursor: pointer;
592
- transition: background var(--transition-speed), color var(--transition-speed), border-color var(--transition-speed);
593
  }
594
- .examples-section table.gr-samples-table tr:hover td { background: rgba(255, 193, 7, 0.1) !important; }
595
  .examples-section table.gr-samples-table tr:first-child td { border-top: none !important; }
596
- @media (prefers-color-scheme: light) {
597
- .examples-section table.gr-samples-table { border: 1px solid var(--border-light) !important; background: var(--card-section-bg-light) !important; }
598
- .examples-section table.gr-samples-table th { background: var(--header-bg-light) !important; color: var(--text-primary-light) !important; }
599
- .examples-section table.gr-samples-table td { background: var(--card-section-bg-light) !important; color: var(--text-primary-light) !important; border-top: 1px solid var(--border-light) !important; }
600
- .examples-section table.gr-samples-table tr:hover td { background: rgba(0, 123, 255, 0.08) !important; }
601
- }
602
  .app-footer-wrapper {
603
- background: var(--header-bg-dark); border-top: 1px solid var(--border-dark) !important;
604
- margin-top: 0.5rem; padding-top: 2.5rem; padding-bottom: 2.5rem;
605
- border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-lg);
 
 
 
606
  box-shadow: inset 0 8px 15px rgba(0,0,0,0.2);
607
- transition: background var(--transition-speed), border-color var(--transition-speed), box-shadow var(--transition-speed);
608
- max-width: 1120px; margin-left: auto; margin-right: auto; width: 100%;
609
- /* MODIFIED: Align Markdown content to the left */
 
610
  display: flex !important;
611
  flex-direction: column !important;
612
- align-items: flex-start !important; /* Pushes the Markdown component to the left */
613
- }
614
- @media (prefers-color-scheme: light) {
615
- .app-footer-wrapper { background: var(--header-bg-light); border-top: 1px solid var(--border-light) !important; box-shadow: inset 0 6px 12px rgba(0,0,0,0.1); }
616
  }
617
  .app-footer {
618
- padding: 0 var(--padding-lg) !important;
619
  display: flex;
620
  flex-direction: column;
621
- align-items: stretch; /* Keep children (<p> tags) stretched to full width of this div */
622
- width: 100%; /* Ensure this div takes full width inside its markdown container */
623
  }
624
  .app-footer p {
625
  font-size: 1.05rem !important;
626
- color: var(--text-secondary-dark) !important;
627
- margin-bottom: 1rem;
628
- /* MODIFIED: Align text to the left */
629
  text-align: left !important;
630
- transition: color var(--transition-speed);
631
- width: 100%; /* Ensure <p> takes full width of stretched item */
632
- }
633
- .app-footer a { color: var(--accent-main-dark) !important; font-weight: 500; transition: color var(--transition-speed), text-decoration var(--transition-speed); }
634
- .app-footer a:hover { color: var(--accent-hover-dark) !important; text-decoration: underline; }
635
- @media (prefers-color-scheme: light) {
636
- .app-footer-wrapper { border-top-color: var(--border-light) !important; }
637
- .app-footer p { color: var(--text-secondary-light) !important; }
638
- .app-footer a { color: var(--accent-main-light) !important; }
639
- .app-footer a:hover { color: var(--accent-hover-light) !important; }
640
- }
641
- :focus-visible {
642
- outline: 5px solid var(--accent-main-dark) !important; outline-offset: 5px;
643
- box-shadow: 0 0 0 8px var(--focus-ring-dark) !important;
644
- border-radius: var(--radius-md) !important;
645
- }
646
- @media (prefers-color-scheme: light) {
647
- :focus-visible { outline-color: var(--accent-main-light) !important; box_shadow: 0 0 0 8px var(--focus-ring-light) !important; }
648
  }
649
- .gradio-button span:focus { outline: none !important; }
 
650
  /* Hide default Gradio example labels and related elements for cleaner presentation */
651
  .gr-examples .gr-label, .gr-examples button.gr-button-filter, .gr-examples .label-wrap,
652
  .gr-examples div[data-testid*="label-text"], .gr-examples span[data-testid*="label-text"],
@@ -662,51 +528,47 @@ Answer:"""
662
  font-size: 0 !important; line-height: 0 !important; position: absolute !important;
663
  pointer-events: none !important;
664
  }
665
- /* Responsive Adjustments (meticulously refined) */
666
  @media (max-width: 1024px) {
667
  .gradio-container > .flex.flex-col { max-width: 960px; padding: 0 1.5rem !important; }
668
  .app-header-title { font-size: 3.8rem; } .app-header-tagline { font-size: 1.5rem; }
669
- .app-header-wrapper { padding: var(--padding-md) var(--padding-lg) !important; margin-bottom: 2rem; border-bottom-left-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
670
- .main-dashboard-container { padding: var(--padding-md) !important; margin-bottom: 0.6rem; border-radius: var(--radius-md); gap: 2rem; }
671
- .dashboard-card-section { padding: var(--padding-sm); border-radius: var(--radius-sm); }
672
- .section-title { font-size: 2.2rem !important; margin-bottom: 1.5rem !important; }
673
- .sub-section-title { font-size: 2.0rem !important; margin-bottom: 0.7rem !important; } /* Adjusted for responsiveness */
674
- .section-divider { margin: 1.8rem 0; } .input-row { gap: 1.5rem; } .input-field { min-width: 280px; }
675
  .gradio-textbox textarea { min-height: 160px; } .output-card .response-header { font-size: 1.7rem; }
676
  .examples-section { padding-top: 1.2rem; }
677
  .examples-section table.gr-samples-table th, .examples-section table.gr-samples-table td { padding: 0.9rem 1.1rem !important; }
678
- .app-footer-wrapper { margin-top: 0.6rem; border-top-left-radius: var(--radius-md); border-top-right-radius: var(--radius-md); }
679
  .app-footer { padding: 0 1.5rem !important; }
680
  }
681
  @media (max-width: 768px) {
682
  .gradio-container > .flex.flex-col { padding: 0 1rem !important; }
683
- .app-header-wrapper { padding: var(--padding-sm) var(--padding-md) !important; margin-bottom: 1.8rem; border-bottom-left-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); }
684
  .app-header-logo { font-size: 4.5rem; margin-bottom: 0.6rem; } .app-header-title { font-size: 3.2rem; letter-spacing: -0.06em; }
685
  .app-header-tagline { font-size: 1.3rem; }
686
- .main-dashboard-container { padding: var(--padding-sm) !important; margin-bottom: 0.5rem; border-radius: var(--radius-md); gap: 1.8rem; }
687
- .dashboard-card-section { padding: 1.5rem; border-radius: var(--radius-sm); }
688
- .section-title { font-size: 2rem !important; margin-bottom: 1.2rem !important; }
689
- .sub-section-title { font-size: 1.8rem !important; margin-top: 1rem !important; margin-bottom: 0.6rem !important; } /* Adjusted for responsiveness */
690
- .section-divider { margin: 1.5rem 0; } .input-row { flex-direction: column; gap: 1rem; } .input-field { min-width: 100%; }
691
  .gradio-textbox textarea { min-height: 140px; } .button-row { justify-content: stretch; gap: 1rem; }
692
  .gradio-button { width: 100%; padding: 1.1rem 2rem !important; font-size: 1.1rem !important; }
693
  .output-card .response-header { font-size: 1.5rem; } .output-card .response-icon { font-size: 1.7rem; }
694
  .output-card .placeholder { padding: 2.5rem 1.5rem; font-size: 1.1rem; }
695
  .examples-section { padding-top: 1.2rem; }
696
  .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; }
697
- .app-footer-wrapper { margin-top: 0.6rem; border-top-left-radius: var(--radius-md); border-top-right-radius: var(--radius-md); padding-top: 2rem; padding-bottom: 2rem; }
698
  .app-footer { padding: 0 1rem !important; }
699
  }
700
  @media (max-width: 480px) {
701
  .gradio-container > .flex.flex-col { padding: 0 0.8rem !important; }
702
- .app-header-wrapper { padding: 1.2rem 1rem !important; margin-bottom: 1.5rem; border-bottom-left-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }
703
  .app-header-logo { font-size: 3.8rem; margin-bottom: 0.5rem; } .app-header-title { font-size: 2.8rem; }
704
  .app-header-tagline { font-size: 1.1rem; }
705
- .main-dashboard-container { padding: 1.2rem !important; margin-bottom: 0.4rem; border-radius: var(--radius-sm); gap: 1.5rem; }
706
- .dashboard-card-section { padding: 1rem; border-radius: var(--radius-xs); }
707
- .section-title { font-size: 1.8rem !important; margin-bottom: 1rem !important; }
708
- .sub-section-title { font-size: 1.5rem !important; margin-top: 0.8rem !important; margin-bottom: 0.5rem !important; } /* Adjusted for responsiveness */
709
- .section-divider { margin: 1rem 0; }
710
  .gradio-textbox textarea, .gradio-dropdown select, .gradio-textbox input[type=password] { font-size: 1.05rem !important; padding: 1rem 1.2rem !important; }
711
  .gradio-textbox textarea { min-height: 120px; }
712
  .gradio-button { padding: 1rem 1.5rem !important; font-size: 1rem !important; }
@@ -714,12 +576,12 @@ Answer:"""
714
  .output-card .placeholder { padding: 2rem 1rem; font-size: 1.05rem; }
715
  .examples-section { padding-top: 0.8rem; }
716
  .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; }
717
- .app-footer-wrapper { margin-top: 0.4rem; border-top-left-radius: var(--radius-sm); border-top-right-radius: var(--radius-sm); padding-top: 1.5rem; padding-bottom: 1.5rem; }
718
  .app-footer { padding: 0 0.8rem !important; }
719
  }
720
  """
721
 
722
- with gr.Blocks(theme=None, css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
723
  # --- Header Section ---
724
  with gr.Group(elem_classes="app-header-wrapper"):
725
  gr.Markdown(
@@ -732,12 +594,12 @@ Answer:"""
732
  """
733
  )
734
 
735
- # --- NEW: Main Dashboard Console Container (This holds everything central) ---
736
  with gr.Column(elem_classes="main-dashboard-container"):
737
 
738
  # --- Section 1: Introduction and Disclaimer Card ---
739
  with gr.Group(elem_classes="dashboard-card-section"):
740
- gr.Markdown("<h3 class='sub-section-title'>Welcome & Disclaimer</h3>") # Centered by CSS
741
  gr.Markdown(
742
  """
743
  <p>Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.</p>
@@ -747,9 +609,9 @@ Answer:"""
747
 
748
  # --- Section 2: OpenAI API Key Input Card ---
749
  with gr.Group(elem_classes="dashboard-card-section"):
750
- gr.Markdown("<h3 class='sub-section-title'>OpenAI API Key</h3>") # Centered by CSS
751
  api_key_input = gr.Textbox(
752
- label="", # Label hidden by CSS, custom label below
753
  type="password", placeholder="Enter your API key (e.g., sk-...)",
754
  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,
755
  elem_classes=["input-field-group"]
@@ -757,7 +619,7 @@ Answer:"""
757
 
758
  # --- Section 3: Query Input and State Selection Card ---
759
  with gr.Group(elem_classes="dashboard-card-section"):
760
- gr.Markdown("<h3 class='sub-section-title'>Ask Your Question</h3>") # Centered by CSS
761
  with gr.Row(elem_classes="input-row"):
762
  with gr.Column(elem_classes="input-field", scale=3):
763
  query_input = gr.Textbox(
@@ -777,21 +639,20 @@ Answer:"""
777
 
778
  # --- Section 4: Output Display Card ---
779
  with gr.Group(elem_classes="dashboard-card-section"):
780
- gr.Markdown("<h3 class='sub-section-title'>Legal Assistant's Response</h3>") # Centered by CSS
781
  output = gr.Markdown(
782
  value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
783
  elem_classes="output-content-wrapper output-card"
784
  )
785
 
786
- # --- Section 5: Example Questions Section (NO ACCORDION, direct display) ---
787
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
788
- gr.Markdown("<h3 class='sub-section-title'>Example Questions to Ask</h3>") # Centered by CSS
789
  if example_queries:
790
  gr.Examples(
791
  examples=example_queries, inputs=[query_input, state_input],
792
  examples_per_page=5,
793
- label="" # Label hidden by CSS
794
- # Removed: elem_classes=["gr-examples-table"]
795
  )
796
  else:
797
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
@@ -822,7 +683,7 @@ Answer:"""
822
  ),
823
  inputs=[], outputs=[api_key_input, query_input, state_input, output]
824
  )
825
- logging.info("Completely new, cohesive, dynamic, and legible Gradio interface created with Legal Console theme and UI fixes.")
826
  return demo
827
 
828
  # --- Main Execution Block (remains untouched from original logic) ---
@@ -865,7 +726,7 @@ if __name__ == "__main__":
865
 
866
  logging.info(f"Launching Gradio app on http://0.0.0.0:{SERVER_PORT}")
867
  print(f"\n--- Gradio App Running ---\nAccess at: http://localhost:{SERVER_PORT} or your public Spaces URL\n--------------------------\n")
868
- app_interface.launch(server_name="0.0.0.0", server_port=SERVER_PORT, share=True) # share=False is typical for Spaces
869
 
870
  except ModuleNotFoundError as e:
871
  if "vector_db" in str(e):
 
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
12
  from vector_db import VectorDatabase
 
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)
 
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"],
 
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; }
 
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(
 
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
 
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
 
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(
 
639
 
640
  # --- Section 4: Output Display Card ---
641
  with gr.Group(elem_classes="dashboard-card-section"):
642
+ gr.Markdown("<h3 class='sub-section-title'>Legal Assistant's Response</h3>")
643
  output = gr.Markdown(
644
  value="<div class='placeholder output-card'>The answer will appear here after submitting your query.</div>",
645
  elem_classes="output-content-wrapper output-card"
646
  )
647
 
648
+ # --- Section 5: Example Questions Section ---
649
  with gr.Group(elem_classes="dashboard-card-section examples-section"):
650
+ gr.Markdown("<h3 class='sub-section-title'>Example Questions to Ask</h3>")
651
  if example_queries:
652
  gr.Examples(
653
  examples=example_queries, inputs=[query_input, state_input],
654
  examples_per_page=5,
655
+ label="" # Hide default Examples label
 
656
  )
657
  else:
658
  gr.Markdown("<div class='placeholder'>Sample questions could not be loaded.</div>")
 
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) ---
 
726
 
727
  logging.info(f"Launching Gradio app on http://0.0.0.0:{SERVER_PORT}")
728
  print(f"\n--- Gradio App Running ---\nAccess at: http://localhost:{SERVER_PORT} or your public Spaces URL\n--------------------------\n")
729
+ app_interface.launch(server_name="0.0.0.0", server_port=SERVER_PORT, share=False) # share=False is typical for Spaces
730
 
731
  except ModuleNotFoundError as e:
732
  if "vector_db" in str(e):