Locolab/Lower-Limb-Similarity-Analysis
1
1"""2Unified styling module for both Streamlit UI and matplotlib plots.3Contains all styling definitions to ensure consistency across the application.4 5Note: When used outside of Streamlit environment (e.g., in Jupyter notebooks), 6you may see warnings about missing ScriptRunContext or Session state. These 7warnings are harmless and can be safely ignored - the core plotting functions 8(get_plot_style, set_plot_style, PLOT_COLORS) work correctly regardless.9"""10 11import warnings12import logging13import matplotlib.pyplot as plt14import seaborn as sns15import matplotlib.font_manager as fm16 17# Suppress Streamlit warnings when running outside streamlit environment18warnings.filterwarnings('ignore', category=UserWarning, module='streamlit')19warnings.filterwarnings('ignore', message='.*ScriptRunContext.*')20warnings.filterwarnings('ignore', message='.*Session state.*')21warnings.filterwarnings('ignore', message='.*missing ScriptRunContext.*')22warnings.filterwarnings('ignore', message='.*does not function when running.*')23warnings.filterwarnings('ignore', module='streamlit.runtime.*')24warnings.filterwarnings('ignore', module='streamlit.runtime.scriptrunner_utils.*')25warnings.filterwarnings('ignore', module='streamlit.runtime.state.*')26 27# Suppress Streamlit loggers that generate warnings outside streamlit environment28logging.getLogger('streamlit.runtime.scriptrunner_utils.script_run_context').setLevel(logging.ERROR)29logging.getLogger('streamlit.runtime.state.session_state_proxy').setLevel(logging.ERROR)30logging.getLogger('streamlit').setLevel(logging.ERROR)31 32try:33 # Set logging level before importing to suppress initial warnings34 for logger_name in ['streamlit', 'streamlit.runtime', 'streamlit.runtime.scriptrunner_utils', 35 'streamlit.runtime.state', 'streamlit.runtime.scriptrunner_utils.script_run_context',36 'streamlit.runtime.state.session_state_proxy']:37 logging.getLogger(logger_name).setLevel(logging.ERROR)38 39 import streamlit as st40 _STREAMLIT_AVAILABLE = True41except ImportError:42 _STREAMLIT_AVAILABLE = False43 # Create a mock streamlit module for non-streamlit environments44 class MockStreamlit:45 class session_state:46 dark_theme = False47 st = MockStreamlit()48 49def _suppress_streamlit_warnings(func):50 """Decorator to suppress streamlit warnings in functions."""51 def wrapper(*args, **kwargs):52 with warnings.catch_warnings():53 warnings.simplefilter('ignore')54 return func(*args, **kwargs)55 return wrapper56 57# ==========================58# Shared Color Themes59# ==========================60 61# Light theme colors - consistent across UI and plots62LIGHT_COLORS = {63 'background': '#F7FAFC',64 'figure_background': '#FFFFFF',65 'sidebar_bg_start': '#F0F4F8',66 'sidebar_bg_end': '#E4EBF3',67 'border_light': '#E2E8F0',68 'border_medium': '#CBD5E1',69 'text_primary': '#1F2933',70 'text_secondary': '#334E68',71 'text_tertiary': '#52606D',72 'text_light': '#829AB1',73 'card_background': '#FFFFFF',74 'code_background': '#EEF2FF',75 'code_text': '#1E3A8A',76 'button_bg_start': '#2563EB',77 'button_bg_end': '#1D4ED8',78 'button_hover_start': '#1D4ED8',79 'button_hover_end': '#1E40AF',80 'alert_error_bg': '#FDE8E8',81 'alert_error_border': '#F76B6B',82 'alert_error_text': '#B91C1C',83 'alert_info_bg': '#E0F2FE',84 'alert_info_border': '#3B82F6',85 'alert_info_text': '#1E3A8A',86 'warning_bg': '#FEF3C7',87 'warning_border': '#F59E0B',88 'success_bg': '#DCFCE7',89 'success_border': '#16A34A',90 'generate_button_bg': '#047857',91 'generate_button_hover': '#0F9D58',92 'panel_background': '#FFFFFF',93 'panel_border': '#E2E8F0',94 'panel_shadow': '0 8px 24px rgba(15, 23, 42, 0.08)',95 # Plot-specific colors96 'axes_background': '#FFFFFF',97 'grid_color': '#E2E8F0',98 'spine_color': '#CBD5E1',99}100 101# Paper theme colors - pure white backgrounds for publication102PAPER_COLORS = {103 'background': '#FFFFFF',104 'figure_background': '#FFFFFF',105 'sidebar_bg_start': '#F5F7FA',106 'sidebar_bg_end': '#E4EBF3',107 'border_light': '#E2E8F0',108 'border_medium': '#CBD5E1',109 'text_primary': '#1F2933',110 'text_secondary': '#334E68',111 'text_tertiary': '#52606D',112 'text_light': '#829AB1',113 'card_background': '#FFFFFF',114 'code_background': '#EEF2FF',115 'code_text': '#1E3A8A',116 'button_bg_start': '#2563EB',117 'button_bg_end': '#1D4ED8',118 'button_hover_start': '#1D4ED8',119 'button_hover_end': '#1E40AF',120 'alert_error_bg': '#FDE8E8',121 'alert_error_border': '#F76B6B',122 'alert_error_text': '#B91C1C',123 'alert_info_bg': '#E0F2FE',124 'alert_info_border': '#3B82F6',125 'alert_info_text': '#1E3A8A',126 'warning_bg': '#FEF3C7',127 'warning_border': '#F59E0B',128 'success_bg': '#DCFCE7',129 'success_border': '#16A34A',130 'generate_button_bg': '#047857',131 'generate_button_hover': '#0F9D58',132 'panel_background': '#FFFFFF',133 'panel_border': '#E2E8F0',134 'panel_shadow': '0 8px 24px rgba(15, 23, 42, 0.08)',135 # Plot-specific colors - pure white for papers136 'axes_background': '#FFFFFF',137 'grid_color': '#E2E8F0',138 'spine_color': '#CBD5E1',139}140 141# Dark theme colors - consistent across UI and plots142DARK_COLORS = {143 'background': '#0F172A',144 'figure_background': '#1E293B',145 'sidebar_bg_start': '#111C2E',146 'sidebar_bg_end': '#1B2537',147 'border_light': '#27364C',148 'border_medium': '#334155',149 'text_primary': '#F8FAFC',150 'text_secondary': '#CBD5F5',151 'text_tertiary': '#94A3B8',152 'text_light': '#64748B',153 'card_background': '#1F2937',154 'code_background': '#1E3A5F',155 'code_text': '#C7D2FE',156 'button_bg_start': '#3B82F6',157 'button_bg_end': '#2563EB',158 'button_hover_start': '#2563EB',159 'button_hover_end': '#1D4ED8',160 'alert_error_bg': '#451A1A',161 'alert_error_border': '#F87171',162 'alert_error_text': '#FCA5A5',163 'alert_info_bg': '#1E293B',164 'alert_info_border': '#60A5FA',165 'alert_info_text': '#BFDBFE',166 'warning_bg': '#3D2D12',167 'warning_border': '#FBBF24',168 'success_bg': '#163225',169 'success_border': '#34D399',170 'generate_button_bg': '#10B981',171 'generate_button_hover': '#34D399',172 'panel_background': '#1F2937',173 'panel_border': '#334155',174 'panel_shadow': '0 18px 36px rgba(2, 6, 23, 0.55)',175 # Plot-specific colors176 'axes_background': '#0F172A',177 'grid_color': '#27364C',178 'spine_color': '#334155',179}180 181@_suppress_streamlit_warnings182def get_current_colors():183 """Return the active color scheme, defaulting to the light palette."""184 try:185 dark_mode = getattr(st.session_state, 'dark_theme', False)186 except Exception:187 dark_mode = False188 189 return DARK_COLORS if dark_mode else LIGHT_COLORS190 191# ==========================192# Plot Styling193# ==========================194 195# Font configuration196DEFAULT_FONT_FAMILY = 'sans-serif'197 198try:199 fm.fontManager.addfont('/usr/share/fonts/truetype/msttcorefonts/Arial.ttf')200 PLOT_STYLE_FONT_FAMILY = 'Arial'201 print("Successfully loaded Arial font.")202except FileNotFoundError:203 print("Arial.ttf not found. Using default system font.")204 PLOT_STYLE_FONT_FAMILY = DEFAULT_FONT_FAMILY205except Exception as e:206 print(f"An error occurred while trying to load Arial font: {e}. Using default system font.")207 PLOT_STYLE_FONT_FAMILY = DEFAULT_FONT_FAMILY208 209# Color constants for plots210PLOT_COLORS = {211 'input_similarity': sns.color_palette('rocket', as_cmap=True),212 'output_difference': sns.cubehelix_palette(start=.2, rot=-.3, dark=0, light=0.85, 213 reverse=True, as_cmap=True),214 'conflict': sns.cubehelix_palette(start=2, rot=0, dark=0, light=0.85, 215 reverse=True, as_cmap=True),216 'output_biomechanical': sns.cubehelix_palette(start=2.8, rot=0.4, dark=0, light=0.85, 217 reverse=True, as_cmap=True)218}219 220# Additional palettes221purple_helix = sns.cubehelix_palette(start=.2, rot=-.4, dark=0, light=0.85, 222 reverse=True, as_cmap=True)223my_purple_helix = sns.cubehelix_palette(start=.2, rot=-.1, dark=0, light=0.85, 224 reverse=True, as_cmap=True)225 226def get_plot_style(style='default'):227 """Get plot style with specified color theme.228 229 Args:230 style: 'default' for cream theme, 'paper' for pure white backgrounds, 'dark' for dark theme231 """232 if style == 'paper':233 theme_colors = PAPER_COLORS234 elif style == 'dark':235 theme_colors = DARK_COLORS236 else: # default237 theme_colors = get_current_colors()238 239 return {240 'font_family': PLOT_STYLE_FONT_FAMILY,241 'font_size': 18,242 'title_size': 20,243 'label_size': 18,244 'tick_size': 15,245 'tick_length': 5,246 'tick_width': 0.5,247 'tick_pad': 5,248 'label_pad_x': -15,249 'label_pad_y': -35,250 'figure_dpi': 300,251 'aspect_ratio': 'equal',252 'subplot_wspace': 0.05,253 'subplot_hspace': 0.1,254 # Theme-specific styling255 'figure_facecolor': theme_colors['figure_background'],256 'axes_facecolor': theme_colors['axes_background'],257 'text_color': theme_colors['text_primary'],258 'grid_color': theme_colors['grid_color'],259 'spine_color': theme_colors['spine_color'],260 }261 262def set_plot_style(style='default'):263 """Set consistent plot styling across all figures.264 265 Args:266 style: 'default' for cream theme, 'paper' for pure white backgrounds, 'dark' for dark theme267 """268 plot_style = get_plot_style(style=style)269 270 plt.rcParams['font.family'] = plot_style['font_family']271 plt.rcParams['font.size'] = plot_style['font_size']272 plt.rcParams['axes.labelsize'] = plot_style['label_size']273 plt.rcParams['axes.titlesize'] = plot_style['title_size']274 plt.rcParams['xtick.labelsize'] = plot_style['tick_size']275 plt.rcParams['ytick.labelsize'] = plot_style['tick_size']276 plt.rcParams['xtick.major.pad'] = plot_style['tick_pad']277 plt.rcParams['ytick.major.pad'] = plot_style['tick_pad']278 plt.rcParams['figure.dpi'] = plot_style['figure_dpi']279 plt.rcParams['figure.subplot.wspace'] = plot_style['subplot_wspace']280 plt.rcParams['figure.subplot.hspace'] = plot_style['subplot_hspace']281 282 # Apply theme styling283 plt.rcParams['figure.facecolor'] = plot_style['figure_facecolor']284 plt.rcParams['axes.facecolor'] = plot_style['axes_facecolor']285 plt.rcParams['text.color'] = plot_style['text_color']286 plt.rcParams['axes.labelcolor'] = plot_style['text_color']287 plt.rcParams['xtick.color'] = plot_style['text_color']288 plt.rcParams['ytick.color'] = plot_style['text_color']289 plt.rcParams['axes.edgecolor'] = plot_style['spine_color']290 plt.rcParams['grid.color'] = plot_style['grid_color']291 plt.rcParams['grid.alpha'] = 0.7292 293def apply_theme_to_figure(fig, ax=None):294 """Apply current theme to an existing figure and axes"""295 theme_colors = get_current_colors()296 297 if fig:298 fig.patch.set_facecolor(theme_colors['figure_background'])299 300 if ax is not None:301 # Handle single axes or iterables of axes robustly302 if hasattr(ax, 'flatten'):303 axes_list = ax.flatten()304 elif isinstance(ax, (list, tuple)):305 axes_list = ax306 elif hasattr(ax, '__iter__'):307 axes_list = list(ax)308 else:309 axes_list = [ax]310 311 for axis in axes_list:312 if axis is not None:313 axis.set_facecolor(theme_colors['axes_background'])314 315 # Update text colors316 axis.title.set_color(theme_colors['text_primary'])317 axis.xaxis.label.set_color(theme_colors['text_primary'])318 axis.yaxis.label.set_color(theme_colors['text_primary'])319 320 # Update tick colors321 axis.tick_params(colors=theme_colors['text_primary'])322 323 # Update spine colors324 for spine in axis.spines.values():325 spine.set_color(theme_colors['spine_color'])326 327 # Update grid328 axis.grid(True, color=theme_colors['grid_color'], alpha=0.7)329 330 return fig, ax331 332def set_paper_plot_style():333 """Convenience function to set pure white backgrounds for paper publication."""334 set_plot_style(style='paper')335 336# Legacy function name for backward compatibility337def apply_cream_theme_to_figure(fig, ax=None):338 """Apply current theme to an existing figure and axes (legacy function name)"""339 return apply_theme_to_figure(fig, ax)340 341# ==========================342# Streamlit UI Styling343# ==========================344 345def get_base_css():346 """Returns the base CSS styling used across all pages."""347 return f"""348 <style>349 /* Main background styling */350 .stApp {{351 background: {get_current_colors()['background']};352 color: {get_current_colors()['text_primary']} !important;353 }}354 355 header[role="banner"] {{356 display: none !important;357 }}358 359 360 header[data-testid="stHeader"] {{361 display: none !important;362 }}363 364 div[data-testid="stDecoration"] {{365 display: none !important;366 }}367 368 div[data-testid="stToolbar"] {{369 display: none !important;370 }}371 372 .main .block-container {{373 padding-top: 6rem;374 padding-bottom: 2rem;375 background-color: {get_current_colors()['background']};376 border-radius: 15px;377 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);378 margin-top: 1rem;379 color: {get_current_colors()['text_primary']} !important;380 }}381 382 /* Explicit text color styling for all elements */383 .stApp, .stApp * {{384 color: {get_current_colors()['text_primary']} !important;385 }}386 387 code {{388 background: {get_current_colors()['code_background']};389 color: {get_current_colors()['code_text']} !important;390 padding: 0.15rem 0.45rem;391 border-radius: 6px;392 font-weight: 600;393 }}394 395 pre code {{396 display: block;397 padding: 0.75rem 1rem;398 border-radius: 10px;399 }}400 401 /* Button styling */402 .stButton>button {{403 width: 100%;404 margin-top: 1rem;405 margin-bottom: 1rem;406 background: linear-gradient(45deg, {get_current_colors()['button_bg_start']}, {get_current_colors()['button_bg_end']});407 color: white !important;408 border: none;409 border-radius: 10px;410 padding: 0.75rem 1rem;411 font-weight: 600;412 transition: all 0.3s ease;413 box-shadow: 0 2px 10px rgba(107, 107, 107, 0.2);414 }}415 416 .stButton>button:hover {{417 background: linear-gradient(45deg, {get_current_colors()['button_hover_start']}, {get_current_colors()['button_hover_end']});418 transform: translateY(-2px);419 box-shadow: 0 4px 15px rgba(107, 107, 107, 0.3);420 color: white !important;421 }}422 423 /* Sidebar styling */424 section[data-testid="stSidebar"] {{425 background: linear-gradient(180deg, {get_current_colors()['sidebar_bg_start']} 0%, {get_current_colors()['figure_background']} 85%);426 border-right: 1px solid {get_current_colors()['border_medium']};427 color: {get_current_colors()['text_primary']} !important;428 margin-top: 5.5rem;429 height: calc(100vh - 5.5rem);430 }}431 432 section[data-testid="stSidebar"] > div {{433 background: transparent;434 padding: 1.75rem 1.2rem 2.3rem 1.2rem;435 color: {get_current_colors()['text_primary']} !important;436 }}437 /* Headers styling */438 h1 {{439 padding-bottom: 1rem;440 border-bottom: 3px solid {get_current_colors()['border_medium']};441 color: {get_current_colors()['text_primary']} !important;442 text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.05);443 }}444 445 h2 {{446 margin-top: 2rem;447 padding-bottom: 0.5rem;448 color: {get_current_colors()['text_secondary']} !important;449 font-weight: 600;450 }}451 452 h3 {{453 margin-top: 1.5rem;454 color: {get_current_colors()['text_tertiary']} !important;455 font-weight: 600;456 }}457 458 /* Alert box styling */459 .auth-alert {{460 background: {get_current_colors()['alert_error_bg']};461 color: {get_current_colors()['alert_error_text']} !important;462 padding: 15px;463 border-radius: 10px;464 margin: 15px 0;465 border: 1px solid {get_current_colors()['alert_error_border']};466 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);467 }}468 469 .auth-info {{470 background: {get_current_colors()['alert_info_bg']};471 color: {get_current_colors()['alert_info_text']} !important;472 padding: 15px;473 border-radius: 10px;474 margin: 15px 0;475 border: 1px solid {get_current_colors()['alert_info_border']};476 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);477 }}478 479 /* Hide default multipage navigation */480 section[data-testid="stSidebar"] nav[data-testid="stSidebarNav"] {{481 display: none !important;482 }}483 484 section[data-testid="stSidebar"] div[data-testid="stSidebarNav"] {{485 display: none !important;486 }}487 488 section[data-testid="stSidebarNav"] {{489 display: none !important;490 }}491 492 nav[data-testid="stSidebarNav"] {{493 display: none !important;494 }}495 496 /* Top navigation */497 .top-nav-outer {{498 position: fixed;499 top: 0;500 left: 0;501 width: 100%;502 z-index: 1000;503 padding: 0.85rem 1.5rem;504 background: linear-gradient(180deg, {get_current_colors()['background']} 0%, {get_current_colors()['figure_background']} 100%);505 border-bottom: 1px solid {get_current_colors()['border_medium']};506 box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1);507 }}508 509 .top-nav-inner {{510 max-width: 1100px;511 margin: 0 auto;512 display: flex;513 gap: 0.75rem;514 justify-content: center;515 flex-wrap: wrap;516 }}517 518 .top-nav-inner .nav-link {{519 display: inline-flex;520 align-items: center;521 justify-content: center;522 min-width: 150px;523 padding: 0.55rem 1rem;524 font-weight: 600;525 border-radius: 12px;526 text-decoration: none;527 background: linear-gradient(180deg, {get_current_colors()['figure_background']} 0%, {get_current_colors()['background']} 100%) !important;528 border: 2px solid {get_current_colors()['border_medium']} !important;529 color: {get_current_colors()['text_secondary']} !important;530 box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08);531 transition: all 0.2s ease;532 }}533 534 .top-nav-inner .nav-link:hover {{535 background: linear-gradient(180deg, {get_current_colors()['background']} 0%, {get_current_colors()['figure_background']} 100%) !important;536 color: {get_current_colors()['text_primary']} !important;537 border-color: {get_current_colors()['text_secondary']} !important;538 transform: translateY(-2px);539 }}540 541 .nav-active {{542 display: inline-flex;543 align-items: center;544 justify-content: center;545 min-width: 160px;546 padding: 0.65rem 1.1rem;547 border-radius: 14px;548 border: 2px solid {get_current_colors()['text_secondary']};549 background: linear-gradient(180deg, {get_current_colors()['figure_background']} 0%, {get_current_colors()['background']} 100%);550 font-weight: 700;551 box-shadow: 0 10px 20px rgba(0, 0, 0, 0.12);552 color: {get_current_colors()['text_primary']} !important;553 text-align: center;554 }}555 556 </style>557 """558 559def get_home_page_css():560 """Returns additional CSS specific to the home page."""561 return f"""562 <style>563 /* Hide sidebar affordance on the landing page where it is unused */564 [data-testid="stSidebar"] {{565 display: none !important;566 }}567 568 /* Navigation button styling */569 .nav-button {{570 border: 2px solid {get_current_colors()['border_medium']};571 border-radius: 12px;572 padding: 20px;573 text-align: center;574 margin-bottom: 15px;575 background: {get_current_colors()['background']};576 transition: all 0.3s ease;577 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);578 color: {get_current_colors()['text_primary']} !important;579 }}580 581 .nav-button:hover {{582 background: {get_current_colors()['figure_background']};583 transform: translateY(-3px);584 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);585 border-color: {get_current_colors()['text_secondary']};586 color: {get_current_colors()['text_primary']} !important;587 }}588 589 .nav-button h3 {{590 color: {get_current_colors()['text_secondary']} !important;591 margin-bottom: 0.5rem;592 }}593 594 /* Description boxes */595 .description-box {{596 text-align: center;597 padding: 15px;598 border-radius: 10px;599 margin-top: -10px;600 background: {get_current_colors()['card_background']};601 border: 1px solid {get_current_colors()['border_medium']};602 box-shadow: none;603 color: {get_current_colors()['text_primary']} !important;604 }}605 606 /* Hero banner */607 .hero {{608 background: linear-gradient(135deg, {get_current_colors()['figure_background']}, {get_current_colors()['background']});609 border: 2px solid {get_current_colors()['border_medium']};610 border-radius: 18px;611 padding: 2.5rem 3rem;612 margin-bottom: 2.5rem;613 box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);614 color: {get_current_colors()['text_primary']} !important;615 }}616 617 .hero-overline {{618 text-transform: uppercase;619 letter-spacing: 0.28rem;620 font-size: 0.75rem;621 font-weight: 700;622 color: {get_current_colors()['text_tertiary']} !important;623 display: inline-block;624 margin-bottom: 0.8rem;625 }}626 627 .hero-subtext {{628 font-size: 1.1rem;629 line-height: 1.7rem;630 color: {get_current_colors()['text_secondary']} !important;631 margin-top: 1rem;632 max-width: 720px;633 }}634 635 /* Grid layouts for value props and steps */636 .value-grid {{637 display: grid;638 grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));639 gap: 1.25rem;640 margin-top: 1.5rem;641 }}642 643 .value-card {{644 background: {get_current_colors()['card_background']};645 border: 1px solid {get_current_colors()['border_medium']};646 border-radius: 12px;647 padding: 1.25rem;648 box-shadow: none;649 color: {get_current_colors()['text_primary']} !important;650 }}651 652 .value-card h3 {{653 margin-bottom: 0.6rem;654 color: {get_current_colors()['text_secondary']} !important;655 font-size: 1.1rem;656 }}657 658 .checklist {{659 list-style: none;660 padding: 0;661 margin: 1rem 0 0 0;662 }}663 664 .checklist li {{665 display: flex;666 align-items: flex-start;667 gap: 0.6rem;668 margin-bottom: 0.75rem;669 color: {get_current_colors()['text_secondary']} !important;670 }}671 672 .checklist span {{673 font-weight: 600;674 color: {get_current_colors()['text_primary']} !important;675 }}676 677 .step-grid {{678 display: grid;679 grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));680 gap: 1.5rem;681 margin-top: 1.5rem;682 }}683 684 .step-card {{685 background: {get_current_colors()['card_background']};686 border: 1px solid {get_current_colors()['border_medium']};687 border-radius: 12px;688 padding: 1.5rem;689 box-shadow: none;690 position: relative;691 color: {get_current_colors()['text_primary']} !important;692 }}693 694 .step-number {{695 display: inline-flex;696 align-items: center;697 justify-content: center;698 width: 32px;699 height: 32px;700 border-radius: 50%;701 background: {get_current_colors()['text_secondary']};702 color: {get_current_colors()['background']} !important;703 font-weight: 700;704 margin-bottom: 1rem;705 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);706 }}707 708 .deliverables-grid {{709 display: grid;710 grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));711 gap: 1.25rem;712 margin-top: 1.5rem;713 }}714 715 .deliverable-card {{716 background: {get_current_colors()['card_background']};717 border: 1px solid {get_current_colors()['border_medium']};718 border-radius: 12px;719 padding: 1.25rem;720 box-shadow: none;721 color: {get_current_colors()['text_primary']} !important;722 }}723 724 .auth-help {{725 margin-top: 1.5rem;726 color: {get_current_colors()['text_secondary']} !important;727 text-align: center;728 font-size: 0.95rem;729 }}730 731 .auth-help a {{732 color: {get_current_colors()['text_secondary']} !important;733 font-weight: 600;734 }}735 </style>736 """737 738def get_documentation_page_css():739 """Returns additional CSS specific to the documentation page."""740 return f"""741 <style>742 /* Reduce sidebar padding so controls sit right below the nav */743 [data-testid="stSidebar"] > div {{744 padding: 1.05rem 1.1rem 1.8rem 1.1rem !important;745 }}746 747 .gaussian-explanation-container {{748 padding: 20px;749 background: {get_current_colors()['background']};750 border-radius: 12px;751 margin-bottom: 20px;752 border: 2px solid {get_current_colors()['border_medium']};753 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.03);754 color: {get_current_colors()['text_primary']} !important;755 }}756 757 .metric-card {{758 background: {get_current_colors()['background']};759 padding: 15px;760 border-radius: 10px;761 border: 2px solid {get_current_colors()['border_medium']};762 margin: 10px 0;763 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);764 color: {get_current_colors()['text_primary']} !important;765 }}766 767 .glossary-term {{768 background: {get_current_colors()['background']};769 padding: 15px;770 border-radius: 10px;771 border-left: 4px solid {get_current_colors()['text_secondary']};772 margin: 10px 0;773 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);774 color: {get_current_colors()['text_primary']} !important;775 }}776 </style>777 """778 779def get_tool_page_css():780 """Returns additional CSS specific to the analysis tool page."""781 return f"""782 <style>783 /* Reduce sidebar padding so controls sit right below the nav */784 [data-testid="stSidebar"] > div {{785 padding: 1.05rem 1.1rem 1.8rem 1.1rem !important;786 }}787 788 /* Expander styling */789 .streamlit-expanderHeader {{790 background-color: {get_current_colors()['figure_background']};791 border: 2px solid {get_current_colors()['border_medium']};792 border-radius: 8px;793 color: {get_current_colors()['text_primary']} !important;794 font-weight: 600;795 box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);796 }}797 798 /* Generate button special styling */799 div[data-testid="stButton"] button {{800 background: linear-gradient(45deg, {get_current_colors()['generate_button_bg']}, {get_current_colors()['generate_button_hover']}) !important;801 color: white !important;802 border: none !important;803 font-weight: 700 !important;804 font-size: 1.1rem !important;805 padding: 1rem !important;806 box-shadow: 0 3px 15px rgba(34, 139, 34, 0.3) !important;807 }}808 809 div[data-testid="stButton"] button:hover {{810 background: linear-gradient(45deg, {get_current_colors()['generate_button_hover']}, #7CFC00) !important;811 transform: translateY(-3px) !important;812 box-shadow: 0 5px 20px rgba(34, 139, 34, 0.4) !important;813 color: white !important;814 }}815 816 .quick-start {{817 display: grid;818 grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));819 gap: 1rem;820 margin-bottom: 1.5rem;821 }}822 823 .quick-step {{824 background: linear-gradient(135deg, {get_current_colors()['card_background']}, {get_current_colors()['figure_background']});825 border: 1px solid {get_current_colors()['border_medium']};826 border-radius: 14px;827 padding: 1rem 1.25rem;828 box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08);829 color: {get_current_colors()['text_primary']} !important;830 }}831 832 .quick-step .step-index {{833 display: inline-block;834 margin-bottom: 0.45rem;835 font-size: 0.85rem;836 font-weight: 700;837 letter-spacing: 0.08em;838 text-transform: uppercase;839 background: {get_current_colors()['text_secondary']};840 color: {get_current_colors()['background']} !important;841 padding: 0.2rem 0.75rem;842 border-radius: 999px;843 box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);844 }}845 846 .sidebar-step-title {{847 font-size: 0.85rem;848 letter-spacing: 0.1em;849 text-transform: uppercase;850 font-weight: 700;851 margin: 1.1rem 0 0.5rem 0;852 color: {get_current_colors()['text_secondary']} !important;853 }}854 855 .tooltip-container {{856 display: flex;857 flex-direction: column;858 gap: 0.75rem;859 background: {get_current_colors()['figure_background']};860 border: 1px solid {get_current_colors()['border_medium']};861 border-radius: 16px;862 padding: 1rem;863 box-shadow: 0 12px 28px rgba(0, 0, 0, 0.08);864 margin-bottom: 1rem;865 }}866 867 .tooltip-content {{868 background: {get_current_colors()['background']};869 border-radius: 12px;870 padding: 0.75rem 1rem;871 box-shadow: inset 0 0 0 1px {get_current_colors()['border_light']};872 display: flex;873 flex-direction: column;874 gap: 0.45rem;875 }}876 877 .tooltip-content .color-legend {{878 display: flex;879 align-items: center;880 gap: 0.5rem;881 color: {get_current_colors()['text_secondary']} !important;882 }}883 884 .tooltip-content .color-box {{885 width: 14px;886 height: 14px;887 border-radius: 4px;888 border: 1px solid {get_current_colors()['border_light']};889 }}890 </style>891 """892 893def apply_base_styling():894 """Apply the base styling to the current Streamlit page."""895 if not _STREAMLIT_AVAILABLE:896 return897 st.markdown(get_base_css(), unsafe_allow_html=True)898 899def apply_home_page_styling():900 """Apply styling specific to the home page."""901 if not _STREAMLIT_AVAILABLE:902 return903 st.markdown(get_base_css(), unsafe_allow_html=True)904 st.markdown(get_home_page_css(), unsafe_allow_html=True)905 906def apply_documentation_page_styling():907 """Apply styling specific to the documentation page."""908 if not _STREAMLIT_AVAILABLE:909 return910 st.markdown(get_base_css(), unsafe_allow_html=True)911 st.markdown(get_documentation_page_css(), unsafe_allow_html=True)912 913def apply_tool_page_styling():914 """Apply styling specific to the analysis tool page."""915 if not _STREAMLIT_AVAILABLE:916 return917 st.markdown(get_base_css(), unsafe_allow_html=True)918 st.markdown(get_tool_page_css(), unsafe_allow_html=True)919 