File size: 12,573 Bytes
5b186ea
9c6863e
216b468
8ec6473
3a3b3ae
 
8ec6473
5b186ea
f9d59bf
 
216b468
8b91916
2dc2d54
5b186ea
9c6863e
 
 
8caa672
 
 
7ebdbd1
9c6863e
 
7ebdbd1
 
 
9c6863e
 
7ebdbd1
 
9c6863e
 
8caa672
9c6863e
8caa672
 
 
 
 
 
 
 
9c6863e
 
8caa672
 
 
 
9c6863e
 
8caa672
9c6863e
 
 
 
8caa672
 
 
9c6863e
 
 
7ebdbd1
9c6863e
a2a6274
380507d
a2a6274
9c6863e
 
8caa672
a2a6274
 
380507d
5b186ea
9c6863e
 
8caa672
a2a6274
 
380507d
a2a6274
9c6863e
 
8caa672
a2a6274
eb15cd9
 
 
 
 
8caa672
eb15cd9
a2a6274
c7e583c
9c6863e
aabdd76
 
 
fdd7e80
8caa672
fdd7e80
 
 
5b186ea
fdd7e80
8caa672
5b186ea
 
 
8a8dcb3
7ebdbd1
9c6863e
 
 
 
 
 
 
 
 
 
 
 
f639641
 
a2a6274
9c6863e
 
 
 
 
 
 
 
 
 
 
 
8caa672
9c6863e
8caa672
 
 
 
 
9c6863e
 
 
 
 
8caa672
 
 
 
 
9c6863e
 
 
8caa672
 
9c6863e
 
 
 
 
a2a6274
 
 
aabdd76
a2a6274
 
9c6863e
a2a6274
 
d5b988e
dd16bfb
9c6863e
f639641
fdd7e80
9c6863e
16bde8f
 
 
aabdd76
16bde8f
a2a6274
8caa672
 
dd16bfb
 
 
 
34ceced
280e045
 
 
8caa672
dd16bfb
 
d5b988e
 
9c6863e
dd16bfb
d5b988e
dd16bfb
 
d5b988e
a2a6274
41d63dd
8caa672
a2a6274
 
8caa672
 
 
 
 
d5b988e
a2a6274
8caa672
 
 
41d63dd
acb4956
a2a6274
5b186ea
8caa672
 
 
 
 
 
 
 
280e045
9c6863e
a2a6274
 
dd16bfb
9c6863e
8caa672
 
 
dd16bfb
a2a6274
41d63dd
8caa672
5b186ea
8caa672
9c6863e
8caa672
 
280e045
5b186ea
 
8caa672
 
 
 
a2a6274
41d63dd
8caa672
 
ea51ae8
a2a6274
acb4956
 
 
 
 
8b91916
34ceced
8caa672
a2a6274
d5b988e
acb4956
d5b988e
8caa672
c7e583c
280e045
a2a6274
 
 
f9d59bf
9c6863e
280e045
 
d5b988e
9c6863e
 
280e045
 
 
 
8a8dcb3
9c6863e
acb4956
d5b988e
 
8caa672
9c6863e
 
8caa672
acb4956
8caa672
 
 
 
 
 
 
 
9c6863e
 
 
 
 
 
34ceced
674b03f
9c6863e
5b186ea
9c6863e
 
 
674b03f
 
 
9c6863e
674b03f
9c6863e
ea51ae8
 
 
 
 
 
 
 
 
e2645b8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# -*- 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'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />'

# ========= 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"""
<style>
  :root {{
    --top-pad: {page["top_padding"]}px;

    --strip-gap: {strip["gap"]}px;
    --strip-row-mb: {strip["below_gap"]}px;
    --strip-pill-pv: {strip["pill_pad_v"]}px;
    --strip-pill-ph: {strip["pill_pad_h"]}px;
    --strip-pill-fs: {strip["pill_font"]}px;
    --tagline-fs: {strip["tagline_font"]}px;

    --hero-w: {hero["width"]}px;
    --hero-mb: {hero["margin_bottom"]}px;

    /* GRID controls you can tweak */
    --card-w: {grid["card_width"]}px;
    --grid-gap-x: {grid["gap_x"]}px;
    --grid-gap-y: {grid["gap_y"]}px;
    --grid-cols: {grid["cols_desktop"]};
    --grid-justify: {grid["justify"]};

    --card-r: {card["radius"]}px;
    --card-bw: {card["border_width"]}px;
    --card-pv: {card["pad_v"]}px;
    --card-ph: {card["pad_h"]}px;

    --cardBgTop: {card["bg_top"]};
    --cardBgBot: {card["bg_bot"]};
    --cardStroke: {card["border"]};
    --cardStrokeHover: {card["border_hover"]};

    --icon-d: {icon["diam"]}px;
    --icon-img: {icon["img"]}px;

    --titleColor: {card["title_color"]};
    --blurbColor: {card["blurb_color"]};

    --btn1: {button["bg1"]};
    --btn2: {button["bg2"]};
    --btnText: {button["text"]};
    --btnBorder: {button["border"]};
  }}

  html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
  [data-testid="stAppViewContainer"] > .main {{ padding-top: 0 !important; padding-bottom: 0 !important; }}

  .block-container {{
    max-width: {page["container_width"]}px;
    min-height: 100vh;
    display: flex; flex-direction: column;
    gap: 14px;
    padding: var(--top-pad) 0 28px !important;
    {bg_radial_css}
  }}

  /* ===== TOP-LEFT STRIP ===== */
  .suite-row {{
    display:flex; align-items:center; gap: var(--strip-gap);
    justify-content:flex-start; flex-wrap: wrap;
    margin: 0 0 var(--strip-row-mb) 0;
  }}
  .suite-pill {{
    background: linear-gradient(90deg, {strip["bg1"]} 0%, {strip["bg2"]} 100%);
    color: {strip["text"]};
    padding: var(--strip-pill-pv) var(--strip-pill-ph);
    border-radius: 999px;
    font-weight: 800; letter-spacing: .25px;
    font-size: var(--strip-pill-fs);
    box-shadow: 0 6px 14px rgba(2,12,30,.18);
    white-space: nowrap;
  }}
  .suite-tagline {{
    color: {strip["tagline_color"]}; font-weight: 600; opacity: .95;
    font-size: var(--tagline-fs);
    white-space: nowrap;
  }}

  /* ===== HERO ===== */
  .hero {{ text-align:center; margin: 0 0 var(--hero-mb); }}
  .hero img {{
    width: var(--hero-w); max-width: 92vw; height: auto;
    display:block; margin: 0 auto var(--hero-mb);
    filter: drop-shadow(0 6px 16px rgba(0,0,0,.10));
  }}

  /* ===== GRID (cards per row are controlled via --grid-cols) ===== */
  .grid {{
    display: grid;
    grid-template-columns: repeat(var(--grid-cols), var(--card-w));
    column-gap: var(--grid-gap-x);
    row-gap: var(--grid-gap-y);
    justify-content: var(--grid-justify);
    align-items: stretch;
    margin-top: 10px;
  }}
  /* Override # of columns on smaller screens */
  @media (max-width: 1120px) {{ .grid {{ --grid-cols: {grid["cols_tablet"]}; }} }}
  @media (max-width: 720px)  {{ .grid {{ --grid-cols: {grid["cols_mobile"]}; }} }}

  /* ===== CARD ===== */
  .card {{
    position: relative;
    width: var(--card-w);
    border-radius: {card["radius"]}px;
    padding: var(--card-pv) var(--card-ph);
    background: linear-gradient(180deg, var(--cardBgTop) 0%, var(--cardBgBot) 100%);
    border: var(--card-bw) solid var(--cardStroke);
    box-shadow: 0 14px 32px rgba(2,20,35,.12),
                0 1px 0 rgba(255,255,255,0.70) inset,
                0 -1px 6px rgba(255,255,255,0.18) inset;
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease, filter .18s ease;
    text-align:center; display:flex; flex-direction:column; gap:16px; align-items: center;
  }}
  .card:hover {{
    transform: translateY(-6px) scale(1.01);
    border-color: var(--cardStrokeHover);
    box-shadow: 0 22px 56px rgba(2,20,35,.18),
                0 1px 0 rgba(255,255,255,0.82) inset,
                0 -1px 10px rgba(255,255,255,0.28) inset;
    filter: saturate(1.03);
  }}

  /* Icon (kept same styling) */
  .icon-wrap {{
    width: var(--icon-d); height: var(--icon-d);
    border-radius: 9999px; display:grid; place-items:center;
    background: {icon["circle_bg"]};
    border: 1px solid {icon["circle_border"]};
    box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 10px 22px rgba(2,20,35,.07);
  }}
  .icon-wrap img {{
    width: calc(var(--icon-img) - 12px);
    height: calc(var(--icon-img) - 12px);
    padding: 6px; box-sizing: border-box; object-fit: contain;
    background: #FFFFFF; border: 2px solid var(--cardStroke); border-radius: 14px; display:block;
  }}

  .card h3 {{ margin: 0; font-size: 25px; font-weight: 900; color: var(--titleColor); letter-spacing: .15px; }}
  .card p  {{ color: var(--blurbColor); min-height: 40px; margin: 0 10px; font-size: 16px; }}

  .btn {{
    display:inline-block; padding: {button["pad_v"]}px {button["pad_h"]}px;
    border-radius: {button["radius"]}px;
    border: 1px solid {button["border"]};
    background: linear-gradient(180deg, {button["bg1"]} 0%, {button["bg2"]} 100%);
    font-weight: 800; letter-spacing:.3px; font-size: 16px;
    margin: 6px auto 0;
    box-shadow: 0 12px 28px rgba(11,18,32,.28), inset 0 1px 0 rgba(255,255,255,.10);
    color: {button["text"]}; text-decoration: none;
  }}
  a.btn, a.btn:link, a.btn:visited, a.btn:hover, a.btn:active, a.btn:focus {{
    color: {button["text"]} !important; text-decoration: none !important;
  }}
  .btn:hover {{ filter: brightness(1.03) saturate(1.04); transform: translateY(-1px); }}

  .footer {{ text-align:center; color:#3f4a5a; font-size:0.96em; margin-top: 26px; }}
  .footer hr {{ margin: 12px 0; border-color: rgba(0,0,0,.08); }}
</style>
""", unsafe_allow_html=True)

# ========= Strip (top-left) =========
st.markdown(
    f"""
    <div class="suite-row">
      <span class="suite-pill">{"ST_LOG SUITE"}</span>
      <span class="suite-tagline">{"Generating AI-Based Well Logging Profiles While Drilling"}</span>
    </div>
    """,
    unsafe_allow_html=True,
)

# ========= Hero =========
hero_html = img_tag(THEME["hero"]["logo"], "ST_LOG SUITE")
st.markdown(f"<div class='hero'>{hero_html}</div>", 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"<div class='card' style='{vars_inline}'>"
        + f"<div class='icon-wrap'>{icon_html}</div>"
        + f"<h3>{escape(card_cfg['title'])}</h3>"
        + f"<p>{escape(card_cfg['blurb'])}</p>"
        + f"<a class='btn' href='{escape(card_cfg['url'])}' target='{target}' rel='noopener'>Run App</a>"
        + "</div>"
    )

st.markdown("<div class='grid'>" + "".join(app_card(c) for c in CARDS) + "</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,
)