Spaces:
Sleeping
Sleeping
| import base64, mimetypes | |
| from html import escape | |
| from pathlib import Path | |
| import streamlit as st | |
| # ========= PATHS (subfolder-safe) ========= | |
| BASE_DIR = Path(__file__).parent # -> Landing_Page/ | |
| ASSETS = BASE_DIR / "assets" # -> Landing_Page/assets/ | |
| # ========= CONTROLS (tweak these) ========= | |
| HERO_LOGO_SIZE = 180 # px (top company logo size) | |
| TOP_SPACER = 160 # px | |
| CARD_WIDTH = 300 # px | |
| CARD_THUMB_HEIGHT = 180 # px | |
| GRID_GAP = 100 # px | |
| BUTTON_TEXT = "Click to Run APP" | |
| OPEN_IN_NEW_TAB = False | |
| # NEW: Background image controls | |
| BACKGROUND_IMAGE = ASSETS / "bg.png" # <-- put your background image here | |
| BACKGROUND_OPACITY = 0.3 # 0.0 (invisible) ... 1.0 (fully opaque) | |
| # ========= BRAND / APPS ========= | |
| COMPANY_NAME = "Smart Thinking - Logging" | |
| TAGLINE = "We Deliver Smart AI-Based Solutions For O&G Industry" | |
| APP1 = { | |
| "title": "ST_Log_GR", | |
| "url": "https://smart-thinking-gr.hf.space/", | |
| "thumb": ASSETS / "app1.png", | |
| "blurb": "Real-Time GR Logging Prediction", | |
| "bg": "#F7FBFF", | |
| "border":"#713F12", | |
| } | |
| APP2 = { | |
| "title": "ST_Log_Sonic (Ts)", | |
| "url": "https://smart-thinking-sonic-ts.hf.space", | |
| "thumb": ASSETS / "app2.png", | |
| "blurb": "Real-Time Shear Slowness Logging Prediction", | |
| "bg": "#F6FFF7", | |
| "border":"#713F12", | |
| } | |
| APP3 = { | |
| "title": "ST_Log_Sonic (Tc)", | |
| "url": "https://smart-thinking-sonic-tc.hf.space", | |
| "thumb": ASSETS / "app3.png", | |
| "blurb": "Real-Time Compression Slowness Logging Prediction", | |
| "bg": "#F9F6FF", | |
| "border":"#713F12", | |
| } | |
| LOGO_PATH = ASSETS / "logo.png" | |
| # ========= PAGE META ========= | |
| page_icon = str(LOGO_PATH) if LOGO_PATH.exists() else "🧭" | |
| st.set_page_config(page_title=f"{COMPANY_NAME} — Apps", page_icon=page_icon, layout="wide") | |
| # ========= IMAGE HELPERS ========= | |
| def data_uri(path: Path) -> str | None: | |
| if 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) -> str: | |
| uri = data_uri(path) | |
| if uri: | |
| return f'<img class="{cls}" src="{uri}" alt="{escape(alt)}" />' | |
| return f'<div class="{cls}" style="display:flex;align-items:center;justify-content:center;color:#50545c;background:linear-gradient(180deg,#f6f7fb,#eceff4);">{escape(alt)}</div>' | |
| # ========= NEW: BACKGROUND INJECTION ========= | |
| def inject_background(image_path: Path, opacity: float = 0.12) -> None: | |
| """ | |
| Paint a page-wide background image with adjustable opacity. | |
| Uses a ::before layer attached to the Streamlit app container. | |
| """ | |
| uri = data_uri(image_path) | |
| if not uri: | |
| return | |
| # Tip: tweak filter() to taste (e.g., blur or desaturate for readability) | |
| st.markdown(f""" | |
| <style> | |
| :root {{ | |
| --bg-opacity: {max(0.0, min(opacity, 1.0))}; | |
| }} | |
| /* Full-page fixed background behind all content */ | |
| [data-testid="stAppViewContainer"]::before {{ | |
| content: ""; | |
| position: fixed; | |
| inset: 0; | |
| z-index: -1; | |
| background-image: url('{uri}'); | |
| background-size: cover; | |
| background-position: center; | |
| background-repeat: no-repeat; | |
| opacity: var(--bg-opacity); | |
| filter: saturate(0.9) brightness(1.05); /* add blur(2px) if you want */ | |
| }} | |
| /* Keep main container transparent to let the bg show through the gaps */ | |
| [data-testid="stAppViewContainer"] > .main {{ | |
| background: transparent !important; | |
| }} | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Apply background (safe no-op if image is missing) | |
| inject_background(BACKGROUND_IMAGE, BACKGROUND_OPACITY) | |
| # ========= CSS (layout & components) ========= | |
| st.markdown( | |
| f""" | |
| <style> | |
| :root {{ | |
| --top-spacer: {TOP_SPACER}px; | |
| --card-width: {CARD_WIDTH}px; | |
| --grid-gap: {GRID_GAP}px; | |
| --tagline-gap: 0px; | |
| --grid-top-space: 20px; | |
| }} | |
| html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }} | |
| [data-testid="stAppViewContainer"] > .main {{ padding-top: 0 !important; padding-bottom: 0 !important; }} | |
| .block-container {{ | |
| max-width: 1120px; | |
| min-height: 100vh; | |
| display: flex; flex-direction: column; | |
| justify-content: center; | |
| gap: 10px; | |
| padding: var(--top-spacer) 0 8px !important; | |
| }} | |
| /* HERO */ | |
| .hero {{ text-align:center; margin: 0 0 8px; }} | |
| .hero img {{ | |
| width: {HERO_LOGO_SIZE}px; max-width: 95vw; height: auto; | |
| display:block; margin: 0 auto 8px; | |
| filter: drop-shadow(0 2px 8px rgba(0,0,0,.12)); | |
| }} | |
| .hero h1 {{ font-size: 2.3rem; margin: .2rem 0 0; }} | |
| .hero p {{ | |
| color: #2b2f36; | |
| margin: var(--tagline-gap) auto 0; | |
| max-width: 720px; | |
| font-style: italic; | |
| text-shadow: 0 1px 2px rgba(255,255,255,0.35); /* slight lift over bg */ | |
| }} | |
| /* GRID */ | |
| .grid {{ | |
| display: flex; | |
| gap: var(--grid-gap); | |
| justify-content: center; | |
| align-items: stretch; | |
| flex-wrap: nowrap; | |
| margin-top: var(--grid-top-space); | |
| }} | |
| @media (max-width: calc(3 * var(--card-width) + 2 * var(--grid-gap) + 64px)) {{ | |
| .grid {{ flex-wrap: wrap; }} | |
| }} | |
| .card {{ | |
| width: var(--card-width); | |
| background: var(--card-bg, #fff); | |
| border: 1px solid var(--card-border, rgba(0,0,0,.06)); | |
| border-radius:16px; padding:14px; | |
| box-shadow:0 4px 18px rgba(0,0,0,.08); | |
| transition:transform .12s ease, box-shadow .12s ease; | |
| text-align:center; display:flex; flex-direction:column; gap:10px; | |
| align-items: center; | |
| backdrop-filter: none; /* set to blur(2px) if you want frosted cards */ | |
| }} | |
| .card:hover {{ transform: translateY(-2px); box-shadow: 0 10px 28px rgba(0,0,0,.12); }} | |
| .thumb {{ | |
| width: 100%; | |
| height: {CARD_THUMB_HEIGHT}px; | |
| border-radius:14px; | |
| border:1px solid rgba(0,0,0,.06); | |
| object-fit: contain; | |
| background:#fff; | |
| }} | |
| .card h3, .card p {{ text-align: center; width: 100%; }} | |
| .card p {{ color:#5a5f6a; min-height:30px; margin:0 8px 2px; }} | |
| .btn {{ | |
| display:inline-block; padding:10px 14px; border-radius:12px; | |
| border:1px solid #e5e7eb; text-decoration:none; | |
| background:#f3f4f6; color:#111827; font-weight:500; | |
| margin: 2px auto 0; | |
| align-self: center; | |
| }} | |
| .btn:hover {{ background:#e5e7eb; }} | |
| .footer {{ text-align:center; color:#374151; font-size:1.0em; margin-top: 6px; }} | |
| .footer hr {{ margin: 6px 0; }} | |
| </style> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # ========= HERO ========= | |
| st.markdown( | |
| '<div class="hero">' | |
| + (img_tag(LOGO_PATH, "logo", "") if LOGO_PATH.exists() else "") | |
| + f"<h1>{escape(COMPANY_NAME)}</h1>" | |
| + f"<p>{escape(TAGLINE)}</p>" | |
| + "</div>", | |
| unsafe_allow_html=True, | |
| ) | |
| # ========= CARDS ========= | |
| def app_card(app: dict) -> str: | |
| target = "_blank" if OPEN_IN_NEW_TAB else "_self" | |
| style = f"--card-bg:{app.get('bg','#fff')}; --card-border:{app.get('border','rgba(0,0,0,.06)')};" | |
| return ( | |
| f"<div class='card' style='{style}'>" | |
| + img_tag(app["thumb"], app["title"], "thumb") | |
| + f"<h3>{escape(app['title'])}</h3>" | |
| + f"<p>{escape(app['blurb'])}</p>" | |
| + f"<a class='btn' href='{escape(app['url'])}' target='{target}'>{escape(BUTTON_TEXT)}</a>" | |
| + "</div>" | |
| ) | |
| st.markdown("<div class='grid'>" + app_card(APP1) + app_card(APP2) + app_card(APP3) + "</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, | |
| ) |