| import base64 |
| import html |
| import os |
| from pathlib import Path |
| from urllib import request |
|
|
| import streamlit as st |
|
|
|
|
| def _icon_data_uri(filename: str) -> str: |
| icon_path = Path(__file__).resolve().parent.parent / "icons" / filename |
| if not icon_path.exists(): |
| return "" |
| try: |
| encoded = base64.b64encode(icon_path.read_bytes()).decode("ascii") |
| except Exception: |
| return "" |
| return f"data:image/png;base64,{encoded}" |
|
|
|
|
| def _config_value(name: str, default: str = "") -> str: |
| try: |
| if name in st.secrets: |
| return str(st.secrets[name]).strip() |
| except Exception: |
| pass |
| return str(os.getenv(name, default)).strip() |
|
|
|
|
| def _build_sidebar_icon_css() -> str: |
| fallback = { |
| 1: "🏠", |
| 2: "🔎", |
| 3: "📦", |
| 4: "🧬", |
| 5: "⚙️", |
| 6: "🧠", |
| 7: "✨", |
| 8: "💬", |
| } |
| icon_name = { |
| 1: "home1.png", |
| 2: "probe1.png", |
| 3: "batch1.png", |
| 4: "molecule1.png", |
| 5: "manual1.png", |
| 6: "ai1.png", |
| 7: "rnn1.png", |
| 8: "feedback.png", |
| } |
| rules = [ |
| '[data-testid="stSidebarNav"] ul li a { position: relative; padding-left: 3.25rem !important; }', |
| '[data-testid="stSidebarNav"] ul li a::before { content: ""; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 32px; height: 32px; background-size: contain; background-repeat: no-repeat; background-position: center; }', |
| ] |
| for idx in range(1, 9): |
| uri = _icon_data_uri(icon_name[idx]) |
| if uri: |
| rules.append( |
| '[data-testid="stSidebarNav"] ul li:nth-of-type(%d) a::before { content: ""; background-image: url("%s"); }' |
| % (idx, uri) |
| ) |
| else: |
| emoji = fallback[idx] |
| rules.append( |
| '[data-testid="stSidebarNav"] ul li:nth-of-type(%d) a::before { content: "%s"; background-image: none; width: auto; height: auto; font-size: 1.4rem; }' |
| % (idx, emoji) |
| ) |
| return "\n".join(rules) |
|
|
|
|
| def _log_visit_once_per_session() -> None: |
| if st.session_state.get("_visit_logged"): |
| return |
| webhook_url = _config_value("FEEDBACK_WEBHOOK_URL", "") |
| webhook_token = _config_value("FEEDBACK_WEBHOOK_TOKEN", "") |
| if not webhook_url: |
| return |
| endpoint = webhook_url |
| sep = "&" if "?" in webhook_url else "?" |
| endpoint = f"{webhook_url}{sep}event=visit" |
| if webhook_token: |
| endpoint = f"{endpoint}&token={webhook_token}" |
| try: |
| with request.urlopen(endpoint, timeout=3): |
| pass |
| except Exception: |
| pass |
| st.session_state["_visit_logged"] = True |
|
|
|
|
| def render_page_header(title: str, subtitle: str = "", badge: str = "") -> None: |
| title_html = html.escape(title) |
| subtitle_html = html.escape(subtitle) if subtitle else "" |
| badge_html = html.escape(badge) if badge else "" |
|
|
| st.markdown( |
| f""" |
| <section class="pp-page-header"> |
| {"<span class='pp-badge'>" + badge_html + "</span>" if badge_html else ""} |
| <h1 class="pp-page-title">{title_html}</h1> |
| {"<p class='pp-page-subtitle'>" + subtitle_html + "</p>" if subtitle_html else ""} |
| </section> |
| """, |
| unsafe_allow_html=True, |
| ) |
|
|
|
|
| def apply_global_style() -> None: |
| _log_visit_once_per_session() |
| icon_css = _build_sidebar_icon_css() |
| css = """ |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&display=swap'); |
| |
| :root { |
| --pp-text: #133a2a; |
| --pp-muted: #4e6f5d; |
| --pp-primary: #1f8f52; |
| --pp-primary-2: #2cab67; |
| --pp-border: rgba(255, 255, 255, 0.96); |
| --pp-surface: rgba(251, 247, 255, 0.90); |
| --pp-shadow: 0 10px 22px rgba(70, 66, 110, 0.12); |
| --pp-panel-shadow: |
| 0 14px 28px rgba(83, 73, 124, 0.16), |
| 0 1px 0 rgba(255, 255, 255, 0.58) inset; |
| } |
| |
| html, body, [class*="css"], [data-testid="stMarkdownContainer"] * { |
| font-family: "Manrope", "Avenir Next", "Segoe UI", sans-serif; |
| } |
| |
| [data-testid="stAppViewContainer"] { |
| min-height: 100vh; |
| overflow: visible !important; |
| background: |
| radial-gradient(1300px 700px at -10% 105%, rgba(188, 113, 202, 0.62), transparent 62%), |
| radial-gradient(1200px 700px at 108% 104%, rgba(130, 170, 235, 0.50), transparent 62%), |
| linear-gradient(120deg, #d5c4e3 0%, #cdd4e9 45%, #c4e1ea 100%) !important; |
| } |
| |
| .stApp, |
| [data-testid="stAppViewContainer"] > .main, |
| section[data-testid="stMain"] { |
| color: var(--pp-text); |
| background: transparent !important; |
| } |
| |
| [data-testid="stAppViewContainer"] > .main { |
| margin: 0 !important; |
| border: none !important; |
| border-radius: 0 !important; |
| box-shadow: none !important; |
| background: transparent !important; |
| overflow: visible !important; |
| } |
| |
| section[data-testid="stMain"] { |
| position: relative; |
| margin: 12px 14px 12px 18px; |
| min-height: calc(100vh - 24px) !important; |
| border-radius: 30px; |
| border: 1px solid rgba(255, 255, 255, 0.98); |
| border-right: 2px solid rgba(233, 223, 245, 0.98); |
| background: rgba(244, 239, 250, 0.96) !important; |
| box-shadow: |
| var(--pp-panel-shadow), |
| inset -1px 0 0 rgba(233, 223, 245, 0.95); |
| overflow: visible; |
| isolation: isolate; |
| } |
| |
| section[data-testid="stMain"] { |
| overflow-y: visible !important; |
| overflow-x: hidden !important; |
| } |
| |
| section[data-testid="stMain"]::before { |
| content: ""; |
| position: absolute; |
| inset: 0; |
| border-radius: inherit; |
| pointer-events: none; |
| box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.58); |
| z-index: 0; |
| } |
| |
| section[data-testid="stMain"]::after { |
| display: none; |
| } |
| |
| section[data-testid="stMain"] > div, |
| [data-testid="stMainBlockContainer"] { |
| position: relative; |
| z-index: 1; |
| background: transparent !important; |
| } |
| |
| [data-testid="stHeader"] { |
| background: transparent !important; |
| } |
| |
| .block-container { |
| max-width: 1220px; |
| padding-top: 1.3rem; |
| padding-bottom: 2.4rem; |
| } |
| |
| h1, h2, h3, h4, h5 { |
| letter-spacing: -0.02em; |
| } |
| |
| p, li, label, [data-testid="stCaptionContainer"] { |
| color: var(--pp-muted); |
| } |
| |
| a, a:visited { |
| color: #1f8f52 !important; |
| } |
| |
| section[data-testid="stSidebar"], |
| [data-testid="stSidebar"] { |
| background: transparent !important; |
| border-right: none !important; |
| --pp-sidebar-width: 300px; |
| height: calc(100vh - 24px) !important; |
| min-width: var(--pp-sidebar-width) !important; |
| width: var(--pp-sidebar-width) !important; |
| max-width: var(--pp-sidebar-width) !important; |
| flex: 0 0 var(--pp-sidebar-width) !important; |
| flex-basis: var(--pp-sidebar-width) !important; |
| } |
| |
| section[data-testid="stSidebar"][aria-expanded="true"] { |
| min-width: var(--pp-sidebar-width) !important; |
| width: var(--pp-sidebar-width) !important; |
| max-width: var(--pp-sidebar-width) !important; |
| flex: 0 0 var(--pp-sidebar-width) !important; |
| flex-basis: var(--pp-sidebar-width) !important; |
| } |
| |
| [data-testid="stSidebar"] > div:first-child { |
| margin: 12px 0 12px 12px; |
| height: calc(100vh - 24px); |
| border-radius: 30px; |
| border: 1px solid rgba(255, 255, 255, 0.98); |
| background: rgba(241, 226, 248, 0.96) !important; |
| box-shadow: var(--pp-panel-shadow); |
| backdrop-filter: blur(6px); |
| overflow-y: auto; |
| overflow-x: hidden; |
| } |
| |
| [data-testid="stSidebarUserContent"] { |
| padding-top: 0.25rem; |
| } |
| |
| [data-testid="stSidebarNav"] ul li, |
| [data-testid="stSidebarNav"] ul li a, |
| [data-testid="stSidebarNav"] ul li button { |
| margin: 0 !important; |
| padding: 0 !important; |
| } |
| |
| [data-testid="stSidebarNav"] ul li + li { |
| margin-top: 0.44rem !important; |
| } |
| |
| [data-testid="stSidebarNav"] ul li a, |
| [data-testid="stSidebarNav"] ul li button { |
| font-size: 1.02rem !important; |
| font-family: "Inter", "Manrope", "Avenir Next", "Segoe UI", sans-serif !important; |
| font-weight: 600 !important; |
| color: #1f6b4a !important; |
| border-radius: 12px !important; |
| display: flex !important; |
| align-items: center !important; |
| justify-content: flex-start !important; |
| height: 44px !important; |
| min-height: 44px !important; |
| max-height: 44px !important; |
| line-height: 1 !important; |
| padding: 0 0.78rem 0 3.1rem !important; |
| border: 1px solid rgba(255, 255, 255, 0.98); |
| background: rgba(255, 255, 255, 0.78) !important; |
| box-shadow: |
| 0 1px 0 rgba(255, 255, 255, 0.42) inset, |
| 0 2px 7px rgba(80, 86, 131, 0.07); |
| transition: all 140ms ease; |
| box-sizing: border-box !important; |
| overflow: hidden !important; |
| } |
| |
| [data-testid="stSidebarNav"] ul li a > div, |
| [data-testid="stSidebarNav"] ul li button > div { |
| margin: 0 !important; |
| padding: 0 !important; |
| display: flex !important; |
| align-items: center !important; |
| min-height: 0 !important; |
| line-height: 1 !important; |
| } |
| |
| [data-testid="stSidebarNav"] ul li a span, |
| [data-testid="stSidebarNav"] ul li button span { |
| font-size: 0.95rem !important; |
| font-family: "Inter", "Manrope", "Avenir Next", "Segoe UI", sans-serif !important; |
| font-weight: 600 !important; |
| color: #1f6b4a !important; |
| line-height: 1.05 !important; |
| white-space: nowrap !important; |
| margin: 0 !important; |
| padding: 0 !important; |
| } |
| |
| [data-testid="stSidebarNav"] ul li a:hover, |
| [data-testid="stSidebarNav"] ul li button:hover { |
| transform: translateY(-1px); |
| background: rgba(255, 255, 255, 0.90) !important; |
| box-shadow: |
| 0 1px 0 rgba(255, 255, 255, 0.44) inset, |
| 0 4px 10px rgba(85, 94, 142, 0.10); |
| } |
| |
| [data-testid="stSidebarNav"] ul li a[aria-current="page"], |
| [data-testid="stSidebarNav"] ul li button[aria-current="page"] { |
| border: 1px solid rgba(34, 163, 93, 0.34) !important; |
| background: linear-gradient(100deg, #21a35e, #34c67a) !important; |
| box-shadow: |
| 0 1px 0 rgba(255, 255, 255, 0.22) inset, |
| 0 8px 16px rgba(34, 163, 93, 0.28); |
| } |
| |
| [data-testid="stSidebarNav"] ul li a[aria-current="page"], |
| [data-testid="stSidebarNav"] ul li a[aria-current="page"] *, |
| [data-testid="stSidebarNav"] ul li button[aria-current="page"], |
| [data-testid="stSidebarNav"] ul li button[aria-current="page"] * { |
| color: #ffffff !important; |
| fill: #ffffff !important; |
| } |
| |
| |
| [data-testid="stSidebarNav"] ul li a[aria-current="page"]::before { |
| filter: brightness(0) invert(1); |
| opacity: 0.96; |
| } |
| |
| __ICON_CSS__ |
| |
| .stTextInput > div > div > input, |
| .stTextArea textarea, |
| .stSelectbox [data-baseweb="select"] > div, |
| .stMultiSelect [data-baseweb="select"] > div, |
| .stNumberInput input { |
| border-radius: 12px !important; |
| border: 1px solid #d7deeb !important; |
| background: rgba(255, 255, 255, 0.87) !important; |
| box-shadow: none !important; |
| color: #173b2b !important; |
| } |
| |
| .stTextInput > div > div > input:focus, |
| .stTextArea textarea:focus, |
| .stSelectbox [data-baseweb="select"] > div:focus-within, |
| .stMultiSelect [data-baseweb="select"] > div:focus-within, |
| .stNumberInput input:focus { |
| border-color: #21a35e !important; |
| box-shadow: 0 0 0 3px rgba(34, 163, 93, 0.18) !important; |
| } |
| |
| /* Force dropdown/expanded menus to green-white accents */ |
| [data-baseweb="popover"] [role="listbox"], |
| [data-baseweb="popover"] [data-baseweb="menu"], |
| div[role="listbox"] { |
| background: rgba(248, 252, 249, 0.98) !important; |
| border: 1px solid rgba(185, 214, 198, 0.95) !important; |
| border-radius: 12px !important; |
| box-shadow: 0 10px 24px rgba(44, 95, 67, 0.14) !important; |
| } |
| |
| [data-baseweb="popover"] [role="option"], |
| [data-baseweb="popover"] li, |
| [data-baseweb="popover"] [data-baseweb="menu"] > div, |
| div[role="listbox"] [role="option"] { |
| background: transparent !important; |
| color: #173b2b !important; |
| } |
| |
| [data-baseweb="popover"] [role="option"]:hover, |
| [data-baseweb="popover"] li:hover, |
| [data-baseweb="popover"] [data-highlighted="true"], |
| div[role="listbox"] [role="option"]:hover { |
| background: rgba(34, 163, 93, 0.10) !important; |
| } |
| |
| [data-baseweb="popover"] [role="option"][aria-selected="true"], |
| [data-baseweb="popover"] li[aria-selected="true"], |
| [data-baseweb="popover"] [aria-selected="true"], |
| div[role="listbox"] [role="option"][aria-selected="true"] { |
| background: rgba(34, 163, 93, 0.18) !important; |
| color: #173b2b !important; |
| } |
| |
| [data-baseweb="tag"] { |
| background: #2f9d62 !important; |
| border: 1px solid #288653 !important; |
| color: #ffffff !important; |
| } |
| |
| [data-baseweb="tag"] *, |
| [data-baseweb="tag"] svg { |
| color: #ffffff !important; |
| fill: #ffffff !important; |
| } |
| |
| /* Keep sliders/toggles green while page background stays blue */ |
| .stSlider [data-baseweb="slider"] > div > div > div:first-child, |
| [data-baseweb="slider"] > div > div > div:first-child { |
| background-color: #1f8f52 !important; |
| } |
| |
| .stSlider [data-baseweb="slider"] > div > div > div:last-child, |
| [data-baseweb="slider"] > div > div > div:last-child { |
| background-color: rgba(34, 163, 93, 0.30) !important; |
| } |
| |
| [data-baseweb="slider"] [style*="rgb(79, 70, 229)"], |
| [data-baseweb="slider"] [style*="rgb(91, 80, 255)"], |
| [data-baseweb="slider"] [style*="rgb(67, 56, 202)"], |
| [data-baseweb="slider"] [style*="rgb("] { |
| background-color: #1f8f52 !important; |
| border-color: #1f8f52 !important; |
| } |
| |
| [data-baseweb="slider"] [role="slider"] { |
| background-color: #1f8f52 !important; |
| border: 2px solid #ffffff !important; |
| box-shadow: 0 0 0 1px rgba(34, 163, 93, 0.35), 0 2px 6px rgba(34, 163, 93, 0.28) !important; |
| } |
| |
| [data-baseweb="checkbox"] [aria-checked="true"] { |
| color: #1f8f52 !important; |
| } |
| |
| [data-baseweb="checkbox"] [aria-checked="true"] > div { |
| background-color: #1f8f52 !important; |
| border-color: #1f8f52 !important; |
| } |
| |
| input[type="checkbox"], |
| input[type="radio"] { |
| accent-color: #1f8f52 !important; |
| } |
| |
| [data-baseweb="radio"] [aria-checked="true"] { |
| color: #1f8f52 !important; |
| } |
| |
| .stButton > button, |
| .stDownloadButton > button, |
| [data-testid="baseButton-secondary"] { |
| border-radius: 999px !important; |
| border: 1px solid #d7dff2 !important; |
| font-weight: 500 !important; |
| min-height: 2.65rem; |
| padding: 0.3rem 1.08rem !important; |
| background: rgba(255, 255, 255, 0.94) !important; |
| transition: all 140ms ease; |
| } |
| |
| .stButton > button[kind="primary"], |
| [data-testid="baseButton-primary"] { |
| background: linear-gradient(100deg, var(--pp-primary), var(--pp-primary-2)) !important; |
| color: #fff !important; |
| border: none !important; |
| box-shadow: 0 10px 22px rgba(31, 157, 85, 0.34); |
| } |
| |
| .stButton > button[kind="primary"] *, |
| [data-testid="baseButton-primary"] * { |
| color: #fff !important; |
| fill: #fff !important; |
| } |
| |
| [data-testid="stFormSubmitButton"] button, |
| [data-testid="stFormSubmitButton"] button * { |
| color: #fff !important; |
| fill: #fff !important; |
| } |
| |
| .stButton > button:hover, |
| .stDownloadButton > button:hover { |
| transform: translateY(-1px); |
| box-shadow: 0 10px 20px rgba(44, 95, 67, 0.2); |
| } |
| |
| div[data-testid="stVerticalBlockBorderWrapper"] { |
| border-radius: 18px !important; |
| border: 1px solid var(--pp-border) !important; |
| background: var(--pp-surface) !important; |
| box-shadow: var(--pp-shadow); |
| } |
| |
| div[data-testid="stMetric"] { |
| background: rgba(255, 255, 255, 0.72); |
| border-radius: 14px; |
| border: 1px solid rgba(255, 255, 255, 0.84); |
| padding: 0.45rem 0.7rem; |
| } |
| |
| div[data-testid="stMetric"] label { |
| color: #4d705d !important; |
| font-weight: 600 !important; |
| letter-spacing: 0.01em; |
| } |
| |
| div[data-testid="stMetricValue"] { |
| color: #1f8f52 !important; |
| font-weight: 800 !important; |
| } |
| |
| [data-testid="stDataFrame"], |
| [data-testid="stTable"] { |
| background: rgba(255, 255, 255, 0.78); |
| border-radius: 14px; |
| border: 1px solid rgba(255, 255, 255, 0.88); |
| overflow: hidden; |
| } |
| |
| [data-testid="stDataFrame"] [role="grid"], |
| [data-testid="stDataFrame"] [role="rowgroup"], |
| [data-testid="stDataFrame"] [role="row"], |
| [data-testid="stDataFrame"] [role="gridcell"], |
| [data-testid="stDataFrame"] [role="columnheader"] { |
| background-color: rgba(247, 252, 248, 0.90) !important; |
| border-color: rgba(188, 213, 196, 0.55) !important; |
| } |
| |
| [data-testid="stTable"] table, |
| [data-testid="stTable"] th, |
| [data-testid="stTable"] td { |
| background-color: rgba(248, 252, 249, 0.94) !important; |
| border-color: rgba(188, 213, 196, 0.62) !important; |
| } |
| |
| .pp-page-header { |
| margin: 0.1rem 0 1.0rem 0; |
| } |
| |
| .pp-page-title { |
| margin: 0.2rem 0 0.45rem 0; |
| font-size: clamp(1.95rem, 2.65vw, 3.0rem); |
| line-height: 1.1; |
| font-weight: 800; |
| color: #123726; |
| } |
| |
| .pp-page-subtitle { |
| margin: 0; |
| max-width: 880px; |
| color: #4a6e5b; |
| font-size: 1.02rem; |
| line-height: 1.62; |
| } |
| |
| .pp-badge { |
| display: inline-flex; |
| align-items: center; |
| gap: 0.38rem; |
| padding: 0.3rem 0.72rem; |
| border-radius: 999px; |
| border: 1px solid rgba(255, 255, 255, 0.92); |
| background: rgba(255, 255, 255, 0.72); |
| color: #3f6856; |
| font-size: 0.76rem; |
| font-weight: 700; |
| text-transform: uppercase; |
| letter-spacing: 0.04em; |
| } |
| |
| .pp-hero { |
| border-radius: 22px; |
| border: 1px solid rgba(255, 255, 255, 0.84); |
| background: |
| linear-gradient(95deg, rgba(255, 255, 255, 0.74), rgba(255, 255, 255, 0.60)), |
| radial-gradient(800px 300px at 100% 0%, rgba(34, 163, 93, 0.16), transparent 72%); |
| box-shadow: 0 16px 32px rgba(56, 70, 121, 0.11); |
| padding: 1.45rem 1.5rem; |
| margin: 0.3rem 0 1.2rem; |
| } |
| |
| .pp-hero-grid { |
| display: grid; |
| grid-template-columns: minmax(0, 4fr) minmax(130px, 1fr); |
| gap: 1.4rem; |
| align-items: center; |
| } |
| |
| .pp-hero-title { |
| margin: 0.58rem 0 0.5rem; |
| color: #123726; |
| font-size: clamp(1.55rem, 2.12vw, 2.35rem); |
| font-weight: 800; |
| letter-spacing: -0.014em; |
| line-height: 1.2; |
| } |
| |
| .pp-hero-copy { |
| margin: 0; |
| color: #4c6f5d; |
| max-width: 780px; |
| line-height: 1.66; |
| } |
| |
| .pp-hero-logo { |
| display: flex; |
| justify-content: center; |
| } |
| |
| .pp-hero-logo img { |
| width: 112px; |
| height: 112px; |
| border-radius: 18px; |
| border: 1px solid rgba(255, 255, 255, 0.88); |
| box-shadow: 0 12px 26px rgba(30, 49, 103, 0.2); |
| object-fit: contain; |
| background: rgba(255, 255, 255, 0.96); |
| padding: 8px; |
| } |
| |
| .pp-stat-card { |
| border-radius: 16px; |
| border: 1px solid rgba(255, 255, 255, 0.9); |
| background: rgba(255, 255, 255, 0.78); |
| padding: 0.85rem 0.92rem; |
| box-shadow: 0 8px 22px rgba(56, 78, 145, 0.12); |
| } |
| |
| .pp-stat-value { |
| margin: 0; |
| color: #2f9d62; |
| font-size: clamp(1.2rem, 1.8vw, 1.95rem); |
| font-weight: 800; |
| letter-spacing: -0.018em; |
| } |
| |
| .pp-stat-label { |
| margin: 0.2rem 0 0; |
| color: #678a76; |
| font-size: 0.8rem; |
| text-transform: uppercase; |
| letter-spacing: 0.045em; |
| font-weight: 700; |
| } |
| |
| .pp-kpi-strip { |
| border-radius: 28px; |
| border: 1px solid rgba(255, 255, 255, 0.9); |
| background: rgba(247, 251, 248, 0.86); |
| box-shadow: 0 12px 26px rgba(56, 78, 145, 0.08); |
| display: grid; |
| grid-template-columns: repeat(3, minmax(0, 1fr)); |
| gap: 0; |
| padding: 1.45rem 1.15rem 1.25rem; |
| margin: 0.62rem 0 0.9rem 0; |
| } |
| |
| .pp-kpi-item { |
| padding: 0.1rem 0.45rem 0.2rem; |
| text-align: center; |
| } |
| |
| .pp-kpi-strip .pp-kpi-value { |
| margin: 0; |
| color: #2f9d62 !important; |
| font-size: 3.5rem !important; |
| font-weight: 800 !important; |
| line-height: 0.98 !important; |
| letter-spacing: -0.03em !important; |
| } |
| |
| .pp-kpi-strip .pp-kpi-label { |
| margin: 0.75rem 0 0; |
| color: #6f8d7c !important; |
| font-size: 0.99rem !important; |
| font-weight: 700 !important; |
| letter-spacing: 0.12em !important; |
| text-transform: uppercase !important; |
| } |
| |
| .pp-step-card { |
| border-radius: 16px; |
| border: 1px solid rgba(255, 255, 255, 0.9); |
| background: rgba(255, 255, 255, 0.77); |
| padding: 0.8rem 0.92rem; |
| min-height: 120px; |
| } |
| |
| .pp-step-title { |
| margin: 0 0 0.26rem; |
| color: #1a4b36; |
| font-weight: 700; |
| font-size: 0.98rem; |
| } |
| |
| .pp-step-copy { |
| margin: 0; |
| color: #557562; |
| font-size: 0.89rem; |
| line-height: 1.45; |
| } |
| |
| .pp-module-card { |
| border-radius: 16px; |
| border: 1px solid rgba(255, 255, 255, 0.9); |
| background: rgba(255, 255, 255, 0.77); |
| box-shadow: 0 8px 22px rgba(56, 78, 145, 0.10); |
| padding: 1.02rem 1.05rem; |
| margin-bottom: 0.74rem; |
| min-height: 136px; |
| } |
| |
| .pp-module-title { |
| margin: 0 0 0.32rem; |
| color: #1a4b36; |
| font-size: 1.02rem; |
| font-weight: 800; |
| line-height: 1.32; |
| } |
| |
| .pp-module-copy { |
| margin: 0; |
| color: #557461; |
| font-size: 0.9rem; |
| line-height: 1.5; |
| } |
| |
| .pp-main-card { |
| border-radius: 16px; |
| border: 1px solid rgba(255, 255, 255, 0.9); |
| background: rgba(255, 255, 255, 0.77); |
| box-shadow: 0 8px 22px rgba(56, 78, 145, 0.10); |
| padding: 1.1rem 1.08rem; |
| margin: 3.25rem 0 0.9rem 0; |
| } |
| |
| .pp-main-grid { |
| display: grid; |
| grid-template-columns: minmax(0, 4fr) minmax(120px, 1fr); |
| gap: 1.1rem; |
| align-items: center; |
| } |
| |
| .pp-main-card .pp-main-title { |
| margin: 0; |
| color: #000000 !important; |
| font-size: clamp(2.8rem, 4.2vw, 3.5rem) !important; |
| font-weight: 800 !important; |
| line-height: 0.98 !important; |
| letter-spacing: -0.03em !important; |
| } |
| |
| .pp-main-copy { |
| margin: 0.5rem 0 0; |
| color: #557461; |
| font-size: 1.0rem; |
| line-height: 1.56; |
| max-width: 900px; |
| } |
| |
| .pp-main-logo { |
| display: flex; |
| justify-content: center; |
| } |
| |
| .pp-main-logo img { |
| width: 118px; |
| height: 118px; |
| border-radius: 16px; |
| border: 1px solid rgba(255, 255, 255, 0.62); |
| box-shadow: 0 10px 22px rgba(32, 92, 63, 0.20); |
| object-fit: contain; |
| background: linear-gradient(145deg, #2f8059, #1f9d55); |
| padding: 8px; |
| filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.25)); |
| } |
| |
| .pp-lab-card { |
| margin: 0.35rem 0 0.5rem 0; |
| border-radius: 20px; |
| border: 1px solid rgba(255, 255, 255, 0.92); |
| background: |
| linear-gradient(125deg, rgba(255, 255, 255, 0.82), rgba(242, 250, 245, 0.75)); |
| box-shadow: 0 12px 26px rgba(44, 95, 67, 0.12); |
| padding: 1.3rem 1.35rem 1.25rem; |
| } |
| |
| .pp-lab-kicker { |
| display: inline-flex; |
| align-items: center; |
| padding: 0.28rem 0.72rem; |
| border-radius: 999px; |
| background: rgba(255, 255, 255, 0.82); |
| border: 1px solid rgba(188, 213, 196, 0.95); |
| color: #4a6e5b; |
| text-transform: uppercase; |
| letter-spacing: 0.08em; |
| font-size: 0.74rem; |
| font-weight: 700; |
| } |
| |
| .pp-lab-title { |
| margin: 0.72rem 0 0.26rem; |
| font-size: clamp(1.52rem, 2.1vw, 2.05rem); |
| font-weight: 800; |
| letter-spacing: -0.02em; |
| color: #123726; |
| } |
| |
| .pp-lab-subtitle { |
| margin: 0; |
| color: #678a76; |
| font-size: 1.0rem; |
| font-weight: 600; |
| line-height: 1.45; |
| } |
| |
| .pp-lab-copy { |
| margin: 1rem 0 1.15rem; |
| color: #4c6f5d; |
| font-size: 1.02rem; |
| line-height: 1.68; |
| max-width: 1080px; |
| } |
| |
| .pp-lab-link { |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| text-decoration: none; |
| border-radius: 999px; |
| border: 1px solid rgba(34, 163, 93, 0.36); |
| background: linear-gradient(100deg, #21a35e, #34c67a); |
| color: #ffffff !important; |
| font-size: 0.95rem; |
| font-weight: 700; |
| letter-spacing: 0.01em; |
| padding: 0.56rem 1rem; |
| box-shadow: 0 8px 16px rgba(34, 163, 93, 0.25); |
| transition: transform 140ms ease, box-shadow 140ms ease, filter 140ms ease; |
| } |
| |
| .pp-lab-link:hover { |
| color: #ffffff !important; |
| transform: translateY(-1px); |
| box-shadow: 0 12px 20px rgba(34, 163, 93, 0.3); |
| filter: saturate(1.02); |
| } |
| |
| @media (max-width: 980px) { |
| [data-testid="stAppViewContainer"] { |
| height: auto; |
| overflow: visible !important; |
| } |
| |
| section[data-testid="stMain"] { |
| margin: 8px; |
| height: auto !important; |
| border-radius: 16px; |
| } |
| |
| [data-testid="stSidebar"] > div:first-child { |
| margin: 8px; |
| height: auto; |
| border-radius: 16px; |
| } |
| |
| .pp-main-grid { |
| grid-template-columns: 1fr; |
| gap: 0.75rem; |
| } |
| |
| .pp-kpi-strip { |
| grid-template-columns: 1fr; |
| gap: 0.42rem; |
| padding: 0.95rem 1rem 0.85rem; |
| } |
| |
| .pp-kpi-item { |
| padding: 0.18rem 0.45rem; |
| } |
| |
| .pp-kpi-strip .pp-kpi-value { |
| font-size: 3.05rem !important; |
| } |
| |
| .pp-kpi-strip .pp-kpi-label { |
| font-size: 0.7rem !important; |
| } |
| |
| .pp-main-logo { |
| justify-content: flex-start; |
| } |
| |
| .pp-main-card { |
| margin-top: 0.7rem; |
| } |
| |
| .pp-hero-grid { |
| grid-template-columns: 1fr; |
| gap: 0.85rem; |
| } |
| .pp-hero-logo { |
| justify-content: flex-start; |
| } |
| |
| .pp-lab-card { |
| padding: 1.0rem; |
| } |
| |
| .pp-lab-title { |
| margin-top: 0.58rem; |
| } |
| |
| .pp-lab-copy { |
| margin-top: 0.78rem; |
| font-size: 0.96rem; |
| } |
| } |
| </style> |
| """ |
| st.markdown(css.replace("__ICON_CSS__", icon_css), unsafe_allow_html=True) |
|
|