""" UI styling — kept separate from app.py so you can change the LOOK without touching any app logic. Edit THEME and CSS below; nothing here affects how the tool works, only how it looks. HOW TO ITERATE (see changes live, no redeploy, no tokens): 1. Open a terminal in this project folder. 2. Run: .venv\\Scripts\\gradio app.py (Windows) .venv/bin/gradio app.py (mac/Linux) 3. Open http://localhost:7860 in your browser. 4. Edit THIS file, save -> it auto-reloads -> refresh the browser. (Use `gradio app.py`, not `python app.py`, so it hot-reloads on save.) """ import gradio as gr # Keep dark-mode tokens identical to light so OS "dark preference" cannot # flip the UI back to charcoal (this app is designed as a light surface). _CANVAS = "#F0F1F3" _SURFACE = "#F7F8FA" _PANEL = "#FFFFFF" _BORDER = "#D5DAE2" # Darker text for readable contrast on light gray / white _TEXT = "#1A2332" _MUTED = "#3D4A5C" # Darker blue-gray so WHITE label/button text meets contrast (~AA) _BTN = "#4A6580" _BTN_HOVER = "#3D5570" _BTN_SECONDARY = "#D5DEE8" _BTN_SECONDARY_HOVER = "#C5D0DC" # Professional / sleek: light gray canvas, EB Garamond, cool blue-gray accents. THEME = gr.themes.Soft( primary_hue="slate", secondary_hue="slate", neutral_hue="slate", spacing_size="lg", text_size="lg", radius_size="sm", font=[ gr.themes.GoogleFont("EB Garamond", weights=(400, 500, 600, 700)), "Georgia", "Times New Roman", "serif", ], font_mono=[ gr.themes.GoogleFont("IBM Plex Mono", weights=(400, 500)), "ui-monospace", "Consolas", "monospace", ], ).set( # Canvas — light gray (light + dark tokens matched) body_background_fill=_CANVAS, body_background_fill_dark=_CANVAS, background_fill_primary=_SURFACE, background_fill_primary_dark=_SURFACE, background_fill_secondary=_CANVAS, background_fill_secondary_dark=_CANVAS, block_background_fill=_PANEL, block_background_fill_dark=_PANEL, block_border_color=_BORDER, block_border_color_dark=_BORDER, border_color_primary=_BORDER, border_color_primary_dark=_BORDER, # Primary buttons — darker blue-gray + white text (readable) button_primary_background_fill=_BTN, button_primary_background_fill_hover=_BTN_HOVER, button_primary_background_fill_dark=_BTN, button_primary_background_fill_hover_dark=_BTN_HOVER, button_primary_text_color="#FFFFFF", button_primary_text_color_dark="#FFFFFF", button_primary_text_color_hover="#FFFFFF", button_primary_text_color_hover_dark="#FFFFFF", button_primary_border_color=_BTN, button_primary_border_color_dark=_BTN, button_primary_border_color_hover=_BTN_HOVER, button_primary_border_color_hover_dark=_BTN_HOVER, # Secondary / default buttons button_secondary_background_fill=_BTN_SECONDARY, button_secondary_background_fill_hover=_BTN_SECONDARY_HOVER, button_secondary_background_fill_dark=_BTN_SECONDARY, button_secondary_background_fill_hover_dark=_BTN_SECONDARY_HOVER, button_secondary_text_color=_TEXT, button_secondary_text_color_dark=_TEXT, button_secondary_border_color=_BTN_SECONDARY, button_secondary_border_color_dark=_BTN_SECONDARY, button_secondary_border_color_hover=_BTN_SECONDARY_HOVER, button_secondary_border_color_hover_dark=_BTN_SECONDARY_HOVER, # Text body_text_color=_TEXT, body_text_color_dark=_TEXT, body_text_color_subdued=_MUTED, body_text_color_subdued_dark=_MUTED, block_info_text_color=_MUTED, block_info_text_color_dark=_MUTED, # Labels match primary button: dark blue-gray + white block_label_background_fill=_BTN, block_label_background_fill_dark=_BTN, block_label_border_color=_BTN, block_label_border_color_dark=_BTN, block_label_text_color="#FFFFFF", block_label_text_color_dark="#FFFFFF", block_title_text_color=_TEXT, block_title_text_color_dark=_TEXT, # Checkbox / radio rows — dark text on light fill; white on selected dark chip checkbox_label_text_color=_TEXT, checkbox_label_text_color_dark=_TEXT, checkbox_label_text_color_selected="#FFFFFF", checkbox_label_text_color_selected_dark="#FFFFFF", checkbox_label_background_fill=_PANEL, checkbox_label_background_fill_dark=_PANEL, checkbox_label_background_fill_hover=_SURFACE, checkbox_label_background_fill_hover_dark=_SURFACE, checkbox_label_background_fill_selected=_BTN, checkbox_label_background_fill_selected_dark=_BTN, checkbox_label_border_color=_BORDER, checkbox_label_border_color_dark=_BORDER, checkbox_label_border_color_selected=_BTN, checkbox_label_border_color_selected_dark=_BTN, checkbox_background_color_selected=_BTN, checkbox_background_color_selected_dark=_BTN, # Inputs input_background_fill=_PANEL, input_background_fill_dark=_PANEL, input_border_color=_BORDER, input_border_color_dark=_BORDER, input_border_color_focus=_BTN, input_border_color_focus_dark=_BTN, input_placeholder_color=_MUTED, input_placeholder_color_dark=_MUTED, # Dataframe / table cells — light, never dark navy table_even_background_fill=_PANEL, table_even_background_fill_dark=_PANEL, table_odd_background_fill=_SURFACE, table_odd_background_fill_dark=_SURFACE, table_border_color=_BORDER, table_border_color_dark=_BORDER, table_text_color=_TEXT, table_text_color_dark=_TEXT, table_row_focus=_BTN_SECONDARY, table_row_focus_dark=_BTN_SECONDARY, ) # Prefer light UI; strip a sticky dark preference if present. JS = """ () => { try { document.documentElement.classList.remove("dark"); document.body?.classList?.remove("dark"); localStorage.setItem("theme", "light"); localStorage.setItem("colorScheme", "light"); } catch (e) {} } """ CSS = f""" gradio-app, .gradio-container, .gradio-container .main, .gradio-container .wrap, .gradio-container .fillable, .gradio-container .contain {{ background-color: {_CANVAS} !important; background: {_CANVAS} !important; color: {_TEXT} !important; }} .gradio-container {{ max-width: 1280px !important; margin: 0 auto !important; padding: 1.75rem 1.5rem 3rem !important; font-family: "EB Garamond", Georgia, "Times New Roman", serif !important; }} /* Centered title frame */ .hero-frame {{ max-width: 52rem !important; margin: 0.5rem auto 2rem !important; padding: 2rem 2.5rem 1.85rem !important; border: 1px solid {_BORDER} !important; background: {_PANEL} !important; text-align: center !important; box-shadow: none !important; }} .hero-frame .prose, .hero-frame span.md.prose, .hero-frame .markdown {{ text-align: center !important; max-width: 40rem; margin-left: auto !important; margin-right: auto !important; }} .hero-frame h1 {{ text-align: center !important; margin-left: auto !important; margin-right: auto !important; }} .hero-frame p {{ text-align: center !important; color: {_MUTED} !important; font-size: 1.08rem !important; line-height: 1.55 !important; margin: 0.85rem auto 0 !important; }} /* Crisp, flat primary actions — no heavy shadow */ button.primary, .primary {{ box-shadow: none !important; letter-spacing: 0.02em; min-height: 2.75rem !important; padding: 0.65rem 1.35rem !important; font-size: 1.05rem !important; background: {_BTN} !important; color: #FFFFFF !important; border-color: {_BTN} !important; }} button.primary:hover, .primary:hover {{ background: {_BTN_HOVER} !important; border-color: {_BTN_HOVER} !important; color: #FFFFFF !important; }} /* Tabs — dark text on light canvas */ .gradio-container button.selected {{ color: {_TEXT} !important; font-weight: 600 !important; opacity: 1 !important; border-bottom-color: {_BTN} !important; }} .tab-nav button, .tab-nav button * {{ font-family: "EB Garamond", Georgia, serif !important; font-size: 1.15rem !important; padding: 0.85rem 1.1rem !important; color: {_TEXT} !important; opacity: 1 !important; }} .tab-nav button.selected, .tab-nav button.selected * {{ color: {_TEXT} !important; border-bottom-color: {_BTN} !important; font-weight: 600 !important; }} /* Field labels — force white on blue chips (Soft theme sometimes uses body text color) */ .block .container > span, .block .label-wrap, .block .label-wrap span, .block label.float, .block label.float span, .block .has-info, .block span.has-info {{ background: {_BTN} !important; color: #FFFFFF !important; border-color: {_BTN} !important; }} /* Empty dataframe title is div.label > p (not a