# -*- coding: utf-8 -*- from typing import Optional, Dict, Any 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", layout="wide") # ========= GLOBAL THEME (one place) ========= THEME: Dict[str, Any] = { "strip": { "gap": 10, "below_gap": 30, "pill_pad_v": 8, "pill_pad_h": 14, "pill_font": 16, "tagline_font": 15, "bg1": "#D4AF37", "bg2": "#B88917", "text": "#000052", "tagline_color": "#000000", }, "page": { "top_padding": 50, "container_width": 1120, "bg_radial": True, }, "hero": { "width": 400, "margin_bottom": 30, "logo": ASSETS / "AI_Suite_Log_logo.png", }, # ---------- GRID CONTROLS (you asked for this) ---------- "grid": { "card_width": 340, # fixed card width to keep sizes consistent "gap_x": 40, # horizontal gap between cards (px) "gap_y": 40, # vertical gap between rows (px) "cols_desktop": 4, # cards per row on desktop "cols_tablet": 2, # cards per row for medium widths "cols_mobile": 1, # cards per row on phones # Optional: force left/center alignment of the whole grid row "justify": "center", # 'start' | 'center' | 'end' }, "card": { "radius": 22, "border_width": 2, "pad_v": 24, "pad_h": 20, "border": "#0B1220", "border_hover": "#243447", "bg_top": "#FFFFFF", "bg_bot": "#FBFCFE", "title_color": "#0B1220", "blurb_color": "#566275", }, "icon": { "diam": 118, "img": 106, "circle_bg": "#F1F5F9", "circle_border": "rgba(12,18,32,0.10)", }, "button": { "pad_v": 12, "pad_h": 20, "radius": 14, "bg1": "#0B1220", "bg2": "#162338", "text": "#FFFFFF", "border": "rgba(11,18,32,.55)", }, } # ========= CARDS ========= CARDS = [ { "title": " ST_Log_GR", "blurb": "Real-time gamma-ray log prediction.", "url": "https://smart-thinking-gr.hf.space/", "icon": ASSETS / "GR_logo.png", "style": {"bg_top": "#EAF7F1", "bg_bot": "#F6FBF8", "border": "#0F3D3E"}, }, { "title": " ST_Log_Sonic (Ts)", "blurb": "Predict shear slowness (DtS) in real time.", "url": "https://smart-thinking-sonic-ts.hf.space", "icon": ASSETS / "Ts_logo.png", "style": {"bg_top": "#EAF7FD", "bg_bot": "#F5FBFF", "border": "#0E4A6E"}, }, { "title": " ST_Log_Sonic (Tc)", "blurb": "Predict compressional slowness (DtC) in real time.", "url": "https://smart-thinking-sonic-tc.hf.space", "icon": ASSETS / "Tc_logo.png", "style": {"bg_top": "#EEF0FF", "bg_bot": "#F7F8FF", "border": "#3E4EB8"}, }, { "title": " ST_Log_RHOB", "blurb": "Predict formation bulk density in real time.", "url": "https://smart-thinking-rhob.hf.space", "icon": ASSETS / "RHOB_logo.png", "style": {"bg_top": "#EAF7FD", "bg_bot": "#F5FBFF", "border": "#0E4A6E"}, }, ] # ========= Helpers ========= def data_uri(path: Path) -> Optional[str]: 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'' # ========= CSS ========= strip = THEME["strip"]; page = THEME["page"]; hero = THEME["hero"] grid = THEME["grid"]; card = THEME["card"]; icon = THEME["icon"]; button = THEME["button"] bg_radial_css = """ background: radial-gradient(980px 460px at 50% -140px, rgba(2,12,30,0.06) 0%, rgba(2,12,30,0.04) 28%, rgba(255,255,255,0.98) 60%, rgba(255,255,255,1) 100%); """ if page["bg_radial"] else "background: #fff;" st.markdown(f""" """, unsafe_allow_html=True) # ========= Strip (top-left) ========= st.markdown( f"""
{"ST_LOG SUITE"} {"Generating AI-Based Well Logging Profiles While Drilling"}
""", unsafe_allow_html=True, ) # ========= Hero ========= hero_html = img_tag(THEME["hero"]["logo"], "ST_LOG SUITE") st.markdown(f"
{hero_html}
", unsafe_allow_html=True) # ========= Cards ========= def build_card_vars(style: Dict[str, Any]) -> str: s = [] if "width" in style: s.append(f"--card-w:{int(style['width'])}px") if "radius" in style: s.append(f"--c-radius:{int(style['radius'])}px") if "pad_v" in style: s.append(f"--card-pv:{int(style['pad_v'])}px") if "pad_h" in style: s.append(f"--card-ph:{int(style['pad_h'])}px") if "bg_top" in style: s.append(f"--cardBgTop:{style['bg_top']}") if "bg_bot" in style: s.append(f"--cardBgBot:{style['bg_bot']}") if "border" in style: s.append(f"--cardStroke:{style['border']}") if "border_width" in style: s.append(f"--card-bw:{int(style['border_width'])}px") if "title_color" in style: s.append(f"--titleColor:{style['title_color']}") if "blurb_color" in style: s.append(f"--blurbColor:{style['blurb_color']}") return "; ".join(s) def app_card(card_cfg: Dict[str, Any]) -> str: style = card_cfg.get("style", {}) vars_inline = build_card_vars(style) icon_html = img_tag(card_cfg.get("icon"), "icon") if card_cfg.get("icon") and card_cfg["icon"].exists() else "" target = "_self" return ( f"
" + f"
{icon_html}
" + f"

{escape(card_cfg['title'])}

" + f"

{escape(card_cfg['blurb'])}

" + f"Run App" + "
" ) st.markdown("
" + "".join(app_card(c) for c in CARDS) + "
", unsafe_allow_html=True) # ========= Footer ========= st.markdown( """
""", unsafe_allow_html=True, )