| """ |
| styles.py β Global WCO colour palette, CSS injection, and shared UI helpers |
| """ |
|
|
| import streamlit as st |
|
|
| |
| WCO_NAVY = "#003087" |
| WCO_BLUE = "#0066CC" |
| WCO_GOLD = "#C8A951" |
| WCO_GREEN = "#00843D" |
| WCO_RED = "#C8102E" |
| WCO_YELLOW = "#F5A800" |
| WCO_GREY_BG = "#0B1220" |
| WCO_CARD_BG = "#0F1C35" |
| WCO_BORDER = "#1E3A6E" |
| WCO_TEXT = "#D0DCF0" |
| WCO_MUTED = "#6B85AA" |
|
|
| CHANNEL_COLORS = { |
| "RED": WCO_RED, |
| "YELLOW": WCO_YELLOW, |
| "GREEN": WCO_GREEN, |
| } |
|
|
| RISK_COLORS = { |
| "Drugs & Narcotics": "#C8102E", |
| "Environmental/Plastic Waste": "#00843D", |
| "Revenue Leakage": "#F5A800", |
| "IPR Enforcement": "#9B59B6", |
| "Wildlife Smuggling": "#E67E22", |
| } |
|
|
|
|
| def inject_global_css(): |
| st.markdown(f""" |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&family=IBM+Plex+Sans:wght@300;400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap'); |
| |
| /* ββ Root reset ββ */ |
| html, body, [data-testid="stAppViewContainer"] {{ |
| background-color: {WCO_GREY_BG} !important; |
| color: {WCO_TEXT} !important; |
| font-family: 'IBM Plex Sans', sans-serif; |
| }} |
| [data-testid="stSidebar"] {{ |
| background-color: #070E1C !important; |
| border-right: 1px solid {WCO_BORDER}; |
| }} |
| [data-testid="stSidebar"] * {{ color: {WCO_TEXT} !important; }} |
| |
| /* ββ Header bar ββ */ |
| .wco-header {{ |
| background: linear-gradient(135deg, {WCO_NAVY} 0%, #001550 100%); |
| border-bottom: 3px solid {WCO_GOLD}; |
| padding: 18px 28px; |
| border-radius: 12px; |
| margin-bottom: 20px; |
| display: flex; |
| align-items: center; |
| gap: 16px; |
| }} |
| .wco-header h1 {{ |
| font-family: 'Playfair Display', serif; |
| color: {WCO_GOLD}; |
| font-size: 26px; |
| margin: 0; |
| line-height: 1.2; |
| }} |
| .wco-header p {{ |
| color: #8BAAD4; |
| font-size: 12px; |
| margin: 4px 0 0; |
| font-family: 'IBM Plex Mono', monospace; |
| letter-spacing: 0.06em; |
| }} |
| |
| /* ββ Cards ββ */ |
| .wco-card {{ |
| background: {WCO_CARD_BG}; |
| border: 1px solid {WCO_BORDER}; |
| border-radius: 12px; |
| padding: 22px 24px; |
| margin-bottom: 16px; |
| }} |
| .wco-card-gold {{ |
| background: {WCO_CARD_BG}; |
| border: 1px solid {WCO_GOLD}; |
| border-radius: 12px; |
| padding: 22px 24px; |
| margin-bottom: 16px; |
| }} |
| .wco-card h3 {{ |
| font-family: 'Playfair Display', serif; |
| color: {WCO_GOLD}; |
| font-size: 16px; |
| margin: 0 0 14px; |
| padding-bottom: 10px; |
| border-bottom: 1px solid {WCO_BORDER}; |
| }} |
| |
| /* ββ Section title ββ */ |
| .section-title {{ |
| font-family: 'Playfair Display', serif; |
| color: {WCO_GOLD}; |
| font-size: 20px; |
| font-weight: 700; |
| margin: 8px 0 16px; |
| letter-spacing: 0.01em; |
| }} |
| |
| /* ββ KPI tiles ββ */ |
| .kpi-row {{ display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; }} |
| .kpi-tile {{ |
| flex: 1; |
| min-width: 140px; |
| background: {WCO_CARD_BG}; |
| border: 1px solid {WCO_BORDER}; |
| border-radius: 10px; |
| padding: 16px 18px; |
| text-align: center; |
| }} |
| .kpi-tile .val {{ |
| font-family: 'Playfair Display', serif; |
| font-size: 32px; |
| font-weight: 700; |
| line-height: 1; |
| }} |
| .kpi-tile .lbl {{ |
| font-size: 11px; |
| color: {WCO_MUTED}; |
| margin-top: 6px; |
| text-transform: uppercase; |
| letter-spacing: 0.08em; |
| }} |
| |
| /* ββ Channel badges ββ */ |
| .badge-red {{ background:#3A0010; color:#FF4466; border:1px solid #C8102E; border-radius:6px; padding:3px 10px; font-size:12px; font-weight:600; }} |
| .badge-yellow {{ background:#2A1900; color:#FFD700; border:1px solid #F5A800; border-radius:6px; padding:3px 10px; font-size:12px; font-weight:600; }} |
| .badge-green {{ background:#001A09; color:#44CC88; border:1px solid #00843D; border-radius:6px; padding:3px 10px; font-size:12px; font-weight:600; }} |
| .badge-gold {{ background:#1C1400; color:{WCO_GOLD}; border:1px solid {WCO_GOLD}; border-radius:6px; padding:3px 10px; font-size:12px; font-weight:600; }} |
| |
| /* ββ Tables ββ */ |
| .wco-table {{ width:100%; border-collapse:collapse; font-size:13px; }} |
| .wco-table th {{ |
| background:{WCO_NAVY}; |
| color:{WCO_GOLD}; |
| font-family:'Playfair Display',serif; |
| padding:10px 14px; |
| text-align:left; |
| border-bottom:2px solid {WCO_GOLD}; |
| font-size:12px; |
| letter-spacing:0.05em; |
| }} |
| .wco-table td {{ |
| padding:9px 14px; |
| border-bottom:1px solid {WCO_BORDER}; |
| color:{WCO_TEXT}; |
| vertical-align:top; |
| }} |
| .wco-table tr:hover td {{ background:rgba(0,48,135,0.25); }} |
| |
| /* ββ Progress bars ββ */ |
| .prog-bar-wrap {{ background:#111D30; border-radius:6px; height:10px; margin:4px 0; overflow:hidden; }} |
| .prog-bar-fill {{ height:100%; border-radius:6px; transition:width 0.6s ease; }} |
| |
| /* ββ Alerts ββ */ |
| .alert-gold {{ |
| background:rgba(200,169,81,0.1); |
| border-left:4px solid {WCO_GOLD}; |
| border-radius:0 8px 8px 0; |
| padding:12px 16px; |
| color:#D4B96A; |
| font-size:13px; |
| margin:10px 0; |
| }} |
| .alert-blue {{ |
| background:rgba(0,102,204,0.1); |
| border-left:4px solid {WCO_BLUE}; |
| border-radius:0 8px 8px 0; |
| padding:12px 16px; |
| color:#66AAFF; |
| font-size:13px; |
| margin:10px 0; |
| }} |
| |
| /* ββ Streamlit widget tweaks ββ */ |
| .stSlider > div > div > div {{ background:{WCO_BORDER} !important; }} |
| .stSlider [data-baseweb="slider"] [role="slider"] {{ background:{WCO_GOLD} !important; border-color:{WCO_GOLD} !important; }} |
| .stButton > button {{ |
| background:linear-gradient(135deg,{WCO_NAVY},{WCO_BLUE}); |
| color:{WCO_GOLD}; |
| border:1px solid {WCO_GOLD}; |
| border-radius:8px; |
| font-family:'IBM Plex Sans',sans-serif; |
| font-weight:600; |
| letter-spacing:0.04em; |
| padding:8px 24px; |
| transition:all 0.2s; |
| }} |
| .stButton > button:hover {{ |
| background:linear-gradient(135deg,{WCO_BLUE},{WCO_NAVY}); |
| border-color:#FFD700; |
| color:#FFD700; |
| transform:translateY(-1px); |
| box-shadow:0 4px 16px rgba(200,169,81,0.25); |
| }} |
| div[data-testid="stMetricValue"] {{ |
| font-family:'Playfair Display',serif !important; |
| color:{WCO_GOLD} !important; |
| }} |
| [data-testid="stMetricLabel"] {{ color:{WCO_MUTED} !important; font-size:11px !important; text-transform:uppercase !important; letter-spacing:0.07em !important; }} |
| [data-testid="stExpander"] {{ |
| background:{WCO_CARD_BG} !important; |
| border:1px solid {WCO_BORDER} !important; |
| border-radius:10px !important; |
| }} |
| div[data-testid="stDataFrame"] {{ border-radius:10px; overflow:hidden; }} |
| .stTabs [data-baseweb="tab-list"] {{ background:{WCO_CARD_BG}; border-radius:10px 10px 0 0; }} |
| .stTabs [data-baseweb="tab"] {{ color:{WCO_MUTED}; font-family:'IBM Plex Sans',sans-serif; }} |
| .stTabs [aria-selected="true"] {{ color:{WCO_GOLD} !important; border-bottom:2px solid {WCO_GOLD} !important; }} |
| hr {{ border-color:{WCO_BORDER} !important; }} |
| </style> |
| """, unsafe_allow_html=True) |
|
|
|
|
| def page_header(icon: str, title: str, subtitle: str): |
| st.markdown(f""" |
| <div class="wco-header"> |
| <div style="font-size:40px">{icon}</div> |
| <div> |
| <h1>{title}</h1> |
| <p>{subtitle}</p> |
| </div> |
| <div style="margin-left:auto;text-align:right;"> |
| <div style="color:#C8A951;font-family:'IBM Plex Mono',monospace;font-size:11px;">π WCO ACCREDITED</div> |
| <div style="color:#6B85AA;font-size:10px;margin-top:2px;">No-CelH Self-Learning RMS v2.0</div> |
| </div> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
|
|
| def metric_row(metrics: list): |
| """metrics = [(value, label, color), ...]""" |
| cols_html = "" |
| for val, lbl, col in metrics: |
| cols_html += f""" |
| <div class="kpi-tile"> |
| <div class="val" style="color:{col}">{val}</div> |
| <div class="lbl">{lbl}</div> |
| </div>""" |
| st.markdown(f'<div class="kpi-row">{cols_html}</div>', unsafe_allow_html=True) |
|
|