CoolFace
Apppublic

Locolab/Lower-Limb-Similarity-Analysis

sourceHugging Faceupdated 1y agoView on Hugging Face
1likes
plot_styling.py919 linesDownload Raw Back to root
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