"""Plotly template "fantabeto_dark" — single source of truth for all chart styling. Registers once at module import. Every chart uses this template. """ import plotly.graph_objects as go import plotly.io as pio # ─── Semantic palette ─────────────────────────────────────────────── PITCH_GREEN = "#00D084" GOLD = "#FFC94D" RED = "#FF4D5E" SKY = "#38BDF8" VIOLET = "#A78BFA" BG = "#0B0F17" CARD_BG = "#111827" BORDER = "#1F2937" TEXT = "#E5E7EB" TEXT_SECONDARY = "#9CA3AF" GRIDLINE = "rgba(31,41,55,0.8)" WHITE = "#FFFFFF" # Role colors ROLE_COLORS = {"P": "#e74c3c", "D": "#3498db", "C": "#2ecc71", "A": "#f39c12"} ROLE_ICONS = {"P": "🧤", "D": "🛡", "C": "⚙", "A": "⚡"} # Sequential palette for heatmaps HEATMAP_COLORS = [ [0.0, "#0B0F17"], [0.2, "#1F2937"], [0.4, "#38BDF8"], [0.6, "#00D084"], [0.8, "#FFC94D"], [1.0, "#FF4D5E"], ] DISCRETE_10 = [ SKY, PITCH_GREEN, GOLD, VIOLET, RED, "#FB923C", "#34D399", "#60A5FA", "#C084FC", "#F87171", ] FANTABETO_TEMPLATE = "fantabeto_dark" def register_template(): """Register the custom Plotly template. Call once at app startup.""" if FANTABETO_TEMPLATE in pio.templates: return t = go.layout.Template() t.layout.update( # Canvas paper_bgcolor=BG, plot_bgcolor=BG, font=dict(color=TEXT, family="Inter, sans-serif", size=12), title=dict(font=dict(family="Space Grotesk, sans-serif", size=18, color=WHITE)), # Axes xaxis=dict( gridcolor=GRIDLINE, zerolinecolor=GRIDLINE, linecolor=BORDER, tickcolor=BORDER, title_font=dict(color=TEXT_SECONDARY, size=11), ), yaxis=dict( gridcolor=GRIDLINE, zerolinecolor=GRIDLINE, linecolor=BORDER, tickcolor=BORDER, title_font=dict(color=TEXT_SECONDARY, size=11), ), # Legend legend=dict( bgcolor="rgba(17,24,39,0.85)", bordercolor=BORDER, font=dict(color=TEXT_SECONDARY, size=11), ), # Margins margin=dict(l=50, r=30, t=60, b=50), # Hover hoverlabel=dict( bgcolor=CARD_BG, bordercolor=BORDER, font=dict(color=TEXT, family="Inter, sans-serif"), ), # Annotations default annotationdefaults=dict( font=dict(color=TEXT, size=11, family="Inter, sans-serif"), ), # Colorway colorway=DISCRETE_10, ) # Bar trace defaults t.data.bar = [ go.Bar(marker=dict(line=dict(width=0)), textposition="none"), ] # Scatter trace defaults t.data.scatter = [ go.Scatter( marker=dict(line=dict(width=0)), line=dict(width=2), ), ] # Heatmap defaults t.data.heatmap = [ go.Heatmap( colorscale=HEATMAP_COLORS, colorbar=dict( bgcolor=CARD_BG, bordercolor=BORDER, tickfont=dict(color=TEXT_SECONDARY), ), ), ] pio.templates[FANTABETO_TEMPLATE] = t def insight_caption(text: str) -> str: """Generate markdown insight caption for charts.""" return f'

{text}

' def stub_render(): # for test import return True # Auto-register on import register_template()