Spaces:
Sleeping
Sleeping
| # -*- coding: utf-8 -*- | |
| import base64, mimetypes | |
| from html import escape | |
| from pathlib import Path | |
| import streamlit as st | |
| # ========= PATHS ========= | |
| BASE_DIR = Path(__file__).parent | |
| ASSETS = BASE_DIR / "assets" | |
| # ========= META ========= | |
| st.set_page_config(page_title="ST_LOG SUITE — Apps", page_icon="🧭", layout="wide") | |
| # ========= BRAND / DATA ========= | |
| SUITE_NAME = "ST_LOG SUITE" | |
| ACCENT = "#0E7490" # teal start (kept subtly in chips) | |
| ACCENT_END = "#14B8A6" # teal end | |
| # Logos (put these PNGs in assets/) | |
| HERO_LOGO = ASSETS / "AI_Suite_Log_logo.png" | |
| ICON_GR = ASSETS / "GR_logo.png" | |
| ICON_TS = ASSETS / "Ts_logo.png" | |
| ICON_TC = ASSETS / "Tc_logo.png" | |
| APPS = [ | |
| { | |
| "title": "ST_Log_GR", | |
| "url": "https://smart-thinking-gr.hf.space/", | |
| "blurb": "Real-time gamma-ray log prediction.", | |
| "icon": ICON_GR, | |
| }, | |
| { | |
| "title": "ST_Log_Sonic (Ts)", | |
| "url": "https://smart-thinking-sonic-ts.hf.space", | |
| "blurb": "Predict shear slowness (DtS) in real time.", | |
| "icon": ICON_TS, | |
| }, | |
| { | |
| "title": "ST_Log_Sonic (Tc)", | |
| "url": "https://smart-thinking-sonic-tc.hf.space", | |
| "blurb": "Predict compressional slowness (DtC) in real time.", | |
| "icon": ICON_TC, | |
| }, | |
| ] | |
| # ========= HELPERS ========= | |
| def data_uri(path: Path) -> str | None: | |
| if not path or not path.exists(): | |
| return None | |
| mime, _ = mimetypes.guess_type(path.name) | |
| if not mime: | |
| mime = "image/png" | |
| b64 = base64.b64encode(path.read_bytes()).decode("utf-8") | |
| return f"data:{mime};base64,{b64}" | |
| def img_tag(path: Path, alt: str, cls: str = "", style: str = "") -> str: | |
| uri = data_uri(path) | |
| if not uri: | |
| return "" | |
| cls_attr = f' class="{cls}"' if cls else "" | |
| style_attr = f' style="{style}"' if style else "" | |
| return f'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />' | |
| # ========= BACKGROUND (clean, calm gradient) ========= | |
| st.markdown(f""" | |
| <style> | |
| :root {{ | |
| --brand: {ACCENT}; | |
| --brand2: {ACCENT_END}; | |
| --btnDark1: #0B1220; /* dark slate for button */ | |
| --btnDark2: #334155; /* slate */ | |
| }} | |
| html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }} | |
| [data-testid="stAppViewContainer"] > .main {{ | |
| padding-top: 0 !important; padding-bottom: 0 !important; | |
| }} | |
| .block-container {{ | |
| max-width: 1100px; | |
| min-height: 100vh; | |
| display: flex; flex-direction: column; | |
| gap: 16px; | |
| padding: 72px 0 28px !important; | |
| background: | |
| radial-gradient(980px 460px at 50% -140px, | |
| rgba(20,184,166,0.08) 0%, | |
| rgba(14,116,144,0.05) 26%, | |
| rgba(255,255,255,0.98) 60%, | |
| rgba(255,255,255,1) 100%); | |
| }} | |
| /* HERO (logo only — title/tagline removed) */ | |
| .hero {{ text-align:center; margin: 0; }} | |
| .hero img {{ | |
| width: 560px; max-width: 92vw; height: auto; | |
| display:block; margin: 0 auto 6px; | |
| filter: drop-shadow(0 6px 16px rgba(0,0,0,.10)); | |
| }} | |
| /* SUITE BANNER (tiny, elegant) */ | |
| .suite-banner {{ | |
| display:flex; gap:10px; justify-content:center; align-items:center; | |
| margin: 4px 0 10px; | |
| }} | |
| .suite-pill {{ | |
| background: linear-gradient(90deg, var(--brand) 0%, var(--brand2) 100%); | |
| color:#fff; padding:6px 12px; border-radius:999px; | |
| font-weight:700; letter-spacing:.2px; | |
| box-shadow: 0 6px 14px rgba(14,116,144,.20); | |
| }} | |
| /* GRID (3 → 2 → 1) */ | |
| .grid {{ | |
| display: grid; | |
| grid-template-columns: repeat(3, 320px); | |
| gap: 36px; | |
| justify-content: center; | |
| align-items: stretch; | |
| margin-top: 8px; | |
| }} | |
| @media (max-width: 1100px) {{ | |
| .grid {{ grid-template-columns: repeat(2, 320px); gap: 28px; }} | |
| }} | |
| @media (max-width: 700px) {{ | |
| .grid {{ grid-template-columns: 1fr; }} | |
| }} | |
| /* CARD — premium look */ | |
| .card {{ | |
| position: relative; | |
| width: 320px; | |
| background: linear-gradient(180deg, #ffffff 0%, #FBFCFE 100%); | |
| border: 1px solid rgba(12,18,32,.08); | |
| border-radius:20px; padding:22px 18px 18px; | |
| box-shadow: 0 10px 34px rgba(2,20,35,.08), 0 1px 0 rgba(255,255,255,0.9) inset; | |
| transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease; | |
| text-align:center; display:flex; flex-direction:column; gap:14px; | |
| align-items: center; | |
| }} | |
| .card:hover {{ | |
| transform: translateY(-4px); | |
| box-shadow: 0 18px 52px rgba(2,20,35,.16); | |
| border-color: rgba(14,116,144,0.18); | |
| }} | |
| /* Corner suite chip */ | |
| .suite-chip {{ | |
| position: absolute; top: 12px; right: 12px; | |
| background: linear-gradient(90deg, var(--brand) 0%, var(--brand2) 100%); | |
| color:#fff; padding:6px 10px; border-radius:999px; | |
| font-weight:700; font-size:.82rem; letter-spacing:.2px; | |
| box-shadow: 0 6px 14px rgba(14,116,144,.22); | |
| }} | |
| /* Larger round icon */ | |
| .icon-wrap {{ | |
| width: 104px; height: 104px; border-radius: 9999px; | |
| background: #F0F5F9; | |
| display:grid; place-items:center; | |
| border: 1px solid rgba(12,18,32,0.10); | |
| box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 8px 20px rgba(2,20,35,.06); | |
| }} | |
| .icon-wrap img {{ | |
| width: 92px; height: 92px; border-radius:9999px; display:block; | |
| }} | |
| /* Enlarged app title */ | |
| .card h3 {{ | |
| margin: 2px 0 0; font-size: 1.26rem; font-weight: 900; color:#0b1220; | |
| letter-spacing: .1px; | |
| }} | |
| .card p {{ | |
| color:#566275; min-height: 40px; margin: 0 10px; font-size: .98rem; | |
| }} | |
| /* PREMIUM BUTTON (dark slate, not green) */ | |
| .btn {{ | |
| display:inline-block; padding:12px 18px; border-radius:14px; | |
| border: 1px solid rgba(15,23,42,.35); text-decoration:none; | |
| background: linear-gradient(180deg, var(--btnDark1) 0%, var(--btnDark2) 100%); | |
| color: white; font-weight: 700; letter-spacing:.25px; | |
| margin: 6px auto 0; | |
| box-shadow: 0 10px 26px rgba(15,23,42,.25), inset 0 1px 0 rgba(255,255,255,.08); | |
| }} | |
| .btn:hover {{ | |
| filter: brightness(1.02) saturate(1.02); | |
| transform: translateY(-1px); | |
| box-shadow: 0 14px 34px rgba(15,23,42,.30); | |
| }} | |
| .footer {{ text-align:center; color:#3f4a5a; font-size:0.96em; margin-top: 24px; }} | |
| .footer hr {{ margin: 12px 0; border-color: rgba(0,0,0,.08); }} | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # ========= HERO (logo only) ========= | |
| hero_html = img_tag(HERO_LOGO, "ST_LOG SUITE") | |
| st.markdown(f"<div class='hero'>{hero_html}</div>", unsafe_allow_html=True) | |
| # ========= SUITE BANNER ========= | |
| st.markdown(f"<div class='suite-banner'><span class='suite-pill'>{escape(SUITE_NAME)}</span></div>", unsafe_allow_html=True) | |
| # ========= CARDS ========= | |
| def app_card(app: dict) -> str: | |
| icon_html = img_tag(app.get("icon"), "icon", | |
| style="width:92px;height:92px;border-radius:9999px;") if app.get("icon") and app["icon"].exists() else "" | |
| target = "_self" # change to "_blank" if you prefer new tab | |
| return ( | |
| "<div class='card'>" | |
| + f"<div class='suite-chip'>{escape(SUITE_NAME)}</div>" | |
| + f"<div class='icon-wrap'>{icon_html}</div>" | |
| + f"<h3>{escape(app['title'])}</h3>" | |
| + f"<p>{escape(app['blurb'])}</p>" | |
| + f"<a class='btn' href='{escape(app['url'])}' target='{target}' rel='noopener'>Run App</a>" | |
| + "</div>" | |
| ) | |
| cards_html = "".join(app_card(a) for a in APPS) | |
| st.markdown(f"<div class='grid'>{cards_html}</div>", unsafe_allow_html=True) | |
| # ========= FOOTER ========= | |
| st.markdown( | |
| """ | |
| <hr> | |
| <div class='footer'> | |
| © 2025 Smart Thinking AI-Solutions Team. All rights reserved.<br> | |
| Website: <a href="https://smartthinking.com.sa" target="_blank" rel="noopener noreferrer">smartthinking.com.sa</a> | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |