UCS2014 commited on
Commit
2759f5f
·
verified ·
1 Parent(s): 445e667

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -97
app.py CHANGED
@@ -12,59 +12,61 @@ ASSETS = BASE_DIR / "assets"
12
  # ========= META =========
13
  st.set_page_config(page_title="ST_GeoMech SUITE", layout="wide")
14
 
 
 
 
 
15
  # ========= GLOBAL THEME (one place) =========
16
  THEME: Dict[str, Any] = {
17
  "strip": {
18
- "gap": 10, # px between pill and tagline
19
- "below_gap": 30, # px between strip row and hero
20
- "pill_pad_v": 8, # px
21
- "pill_pad_h": 14, # px
22
- "pill_font": 16, # px
23
- "tagline_font": 15, # px
24
- # Premium header palette — DARK GREEN with contrasting text
25
- "bg1": "#064E3B", # dark green
26
- "bg2": "#065F46", # deeper green
27
- "text": "#FFFFFF", # high-contrast pill text
28
- "tagline_color": "#000000", # soft-contrast tagline
29
  },
30
  "page": {
31
- "top_padding": 50, # px top padding for page
32
- "container_width": 1120, # max content width
33
- "bg_radial": True, # premium faint radial bg
34
  },
35
  "hero": {
36
- "width": 400, # px
37
- "margin_bottom": 30, # px space under hero
38
  "logo": ASSETS / "AI_Suite_GeoMech_logo.png",
39
  },
40
  "grid": {
41
- "gap": 50, # px between cards
42
- "card_width": 340, # default card width (px)
 
43
  },
44
  "card": {
45
- "radius": 22, # px
46
- "border_width": 2, # px
47
- "pad_v": 24, # px
48
- "pad_h": 20, # px
49
- "border": "#0B1220", # dark outline
50
  "border_hover": "#243447",
51
- # defaults (overridden per card below)
52
  "bg_top": "#FFFFFF",
53
  "bg_bot": "#FBFCFE",
54
  "title_color": "#0B1220",
55
  "blurb_color": "#566275",
56
  },
57
  "icon": {
58
- "diam": 118, # px outer circle
59
- "img": 106, # px image box (will be padded)
60
  "circle_bg": "#F1F5F9",
61
  "circle_border": "rgba(12,18,32,0.10)",
62
  },
63
  "button": {
64
- "pad_v": 12, # px
65
- "pad_h": 20, # px
66
- "radius": 14, # px
67
- # premium navy CTA
68
  "bg1": "#0B1220",
69
  "bg2": "#162338",
70
  "text": "#FFFFFF",
@@ -79,77 +81,49 @@ CARDS = [
79
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
80
  "url": "https://smart-thinking-ucs.hf.space/",
81
  "icon": ASSETS / "UCS_logo.png",
82
- "style": {
83
- "bg_top": "#EAF7F1",
84
- "bg_bot": "#F6FBF8",
85
- "border": "#0F3D3E",
86
- },
87
  },
88
  {
89
  "title": " ST_GeoMEch_UCS",
90
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
91
  "url": "https://smart-thinking-ucs.hf.space/",
92
  "icon": ASSETS / "UCS_logo.png",
93
- "style": {
94
- "bg_top": "#EAF7F1",
95
- "bg_bot": "#F6FBF8",
96
- "border": "#0F3D3E",
97
- },
98
  },
99
  {
100
  "title": " ST_GeoMEch_UCS",
101
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
102
  "url": "https://smart-thinking-ucs.hf.space/",
103
  "icon": ASSETS / "UCS_logo.png",
104
- "style": {
105
- "bg_top": "#EAF7F1",
106
- "bg_bot": "#F6FBF8",
107
- "border": "#0F3D3E",
108
- },
109
  },
110
  {
111
  "title": " ST_GeoMEch_UCS",
112
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
113
  "url": "https://smart-thinking-ucs.hf.space/",
114
  "icon": ASSETS / "UCS_logo.png",
115
- "style": {
116
- "bg_top": "#EAF7F1",
117
- "bg_bot": "#F6FBF8",
118
- "border": "#0F3D3E",
119
- },
120
  },
121
  {
122
  "title": " ST_GeoMEch_UCS",
123
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
124
  "url": "https://smart-thinking-ucs.hf.space/",
125
  "icon": ASSETS / "UCS_logo.png",
126
- "style": {
127
- "bg_top": "#EAF7F1",
128
- "bg_bot": "#F6FBF8",
129
- "border": "#0F3D3E",
130
- },
131
  },
132
  {
133
  "title": " ST_GeoMech_Ym",
134
  "blurb": "Real-time Static Young's Modulus Prediction.",
135
  "url": "https://smart-thinking-ym.hf.space",
136
  "icon": ASSETS / "Ym_logo.png",
137
- "style": {
138
- "bg_top": "#EAF7FD",
139
- "bg_bot": "#F5FBFF",
140
- "border": "#0E4A6E",
141
- },
142
  },
143
  {
144
  "title": " ST_GeoMech_SMW",
145
  "blurb": "Real-Time Safe Mud Window Prediction",
146
  "url": "https://smart-thinking-smw.hf.space",
147
  "icon": ASSETS / "SMW_logo.png",
148
- "style": {
149
- "bg_top": "#EEF0FF",
150
- "bg_bot": "#F7F8FF",
151
- "border": "#3E4EB8",
152
- },
153
  },
154
  ]
155
 
@@ -221,6 +195,9 @@ st.markdown(f"""
221
  --btn2: {button["bg2"]};
222
  --btnText: {button["text"]};
223
  --btnBorder: {button["border"]};
 
 
 
224
  }}
225
 
226
  html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
@@ -265,16 +242,19 @@ st.markdown(f"""
265
  filter: drop-shadow(0 6px 16px rgba(0,0,0,.10));
266
  }}
267
 
268
- /* ===== GRID ===== */
 
 
269
  .grid {{
270
  display: grid;
271
- grid-template-columns: repeat(3, var(--card-w));
272
  gap: var(--grid-gap);
273
- justify-content: center; align-items: stretch;
 
274
  margin-top: 10px;
275
  }}
276
  @media (max-width: 1120px) {{
277
- .grid {{ grid-template-columns: repeat(2, var(--card-w)); gap: calc(var(--grid-gap) - 12px); }}
278
  }}
279
  @media (max-width: 720px) {{
280
  .grid {{ grid-template-columns: 1fr; }}
@@ -283,7 +263,8 @@ st.markdown(f"""
283
  /* ===== CARD ===== */
284
  .card {{
285
  position: relative;
286
- width: var(--c-w, var(--card-w));
 
287
  border-radius: var(--c-radius, var(--card-r));
288
  padding: var(--c-pv, var(--card-pv)) var(--c-ph, var(--card-ph));
289
  background: linear-gradient(180deg, var(--c-bg-top, {card["bg_top"]}) 0%, var(--c-bg-bot, {card["bg_bot"]}) 100%);
@@ -294,6 +275,7 @@ st.markdown(f"""
294
  0 -1px 6px rgba(255,255,255,0.18) inset;
295
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease, filter .18s ease;
296
  text-align:center; display:flex; flex-direction:column; gap:16px; align-items: center;
 
297
  }}
298
  .card:hover {{
299
  transform: translateY(-6px) scale(1.01);
@@ -305,7 +287,7 @@ st.markdown(f"""
305
  filter: saturate(1.03);
306
  }}
307
 
308
- /* ===== ICON / LOGO — self-contained, not trimmed ===== */
309
  .icon-wrap {{
310
  width: var(--c-icon-d, {icon["diam"]}px); height: var(--c-icon-d, {icon["diam"]}px);
311
  border-radius: 9999px; display:grid; place-items:center;
@@ -314,24 +296,18 @@ st.markdown(f"""
314
  box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 10px 22px rgba(2,20,35,.07);
315
  }}
316
  .icon-wrap img {{
317
- /* Keep logos fully visible inside the circle */
318
  width: calc(var(--c-icon-img, {icon["img"]}px) - 12px);
319
  height: calc(var(--c-icon-img, {icon["img"]}px) - 12px);
320
- padding: 6px; /* breathing room */
321
- box-sizing: border-box;
322
- object-fit: contain; /* never crop */
323
- background: #FFFFFF; /* clean canvas for mixed logos */
324
- border: 2px solid var(--c-border, var(--cardStroke)); /* same as card border */
325
- border-radius: 14px; /* rounded square inside circle */
326
- display:block;
327
  }}
328
 
329
  .card h3 {{
330
  margin: 0; font-size: 25px; font-weight: 900;
331
- color: {card["title_color"]}; letter-spacing: .15px;
332
  }}
333
  .card p {{
334
- color: {card["blurb_color"]}; min-height: 40px; margin: 0 10px; font-size: 16px;
335
  }}
336
 
337
  .btn {{
@@ -371,14 +347,6 @@ st.markdown(
371
  )
372
 
373
  # ========= Hero =========
374
- def img_tag(path: Path, alt: str, cls: str = "", style: str = "") -> str:
375
- uri = data_uri(path)
376
- if not uri:
377
- return ""
378
- cls_attr = f' class="{cls}"' if cls else ""
379
- style_attr = f' style="{style}"' if style else ""
380
- return f'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />'
381
-
382
  hero_html = img_tag(THEME["hero"]["logo"], "ST_GeoMech SUITE")
383
  st.markdown(f"<div class='hero'>{hero_html}</div>", unsafe_allow_html=True)
384
 
@@ -397,20 +365,18 @@ def build_card_vars(style: Dict[str, Any]) -> str:
397
  if "blurb_color" in style: s.append(f"--c-blurb-color:{style['blurb_color']}")
398
  if "blurb_fs" in style: s.append(f"--c-blurb-fs:{int(style['blurb_fs'])}px")
399
  if "btn_bg1" in style: s.append(f"--c-btn-bg1:{style['btn_bg1']}")
400
- if "btn_bg2" in style: s.append(f"--c-btn-bg2:{style['btn_bg2']}")
401
- if "btn_text" in style: s.append(f"--c-btn-text:{style['btn_text']}")
402
- if "btn_border" in style: s.append(f"--c-btn-border:{style['btn_border']}")
403
  if "btn_fs" in style: s.append(f"--c-btn-fs:{int(style['btn_fs'])}px")
404
  if "btn_pad_v" in style: s.append(f"--c-btn-pv:{int(style['btn_pad_v'])}px")
405
  if "btn_pad_h" in style: s.append(f"--c-btn-ph:{int(style['btn_pad_h'])}px")
406
  if "icon_diam" in style: s.append(f"--c-icon-d:{int(style['icon_diam'])}px")
407
- if "icon_img" in style: s.append(f"--c-icon-img:{int(style['icon_img'])}px")
408
- if "icon_bg" in style: s.append(f"--c-icon-bg:{style['icon_bg']}")
409
- if "icon_border" in style: s.append(f"--c-icon-border:{style['icon_border']}")
410
  return "; ".join(s)
411
 
412
-
413
-
414
  def app_card(card_cfg: Dict[str, Any]) -> str:
415
  style = card_cfg.get("style", {})
416
  vars_inline = build_card_vars(style)
 
12
  # ========= META =========
13
  st.set_page_config(page_title="ST_GeoMech SUITE", layout="wide")
14
 
15
+ # ========= CARDS PER ROW (single control) =========
16
+ # Change this to any integer >= 1 (e.g., 3, 4, 5...)
17
+ CARDS_PER_ROW: int = 4
18
+
19
  # ========= GLOBAL THEME (one place) =========
20
  THEME: Dict[str, Any] = {
21
  "strip": {
22
+ "gap": 10,
23
+ "below_gap": 30,
24
+ "pill_pad_v": 8,
25
+ "pill_pad_h": 14,
26
+ "pill_font": 16,
27
+ "tagline_font": 15,
28
+ "bg1": "#064E3B",
29
+ "bg2": "#065F46",
30
+ "text": "#FFFFFF",
31
+ "tagline_color": "#000000",
 
32
  },
33
  "page": {
34
+ "top_padding": 50,
35
+ "container_width": 1120,
36
+ "bg_radial": True,
37
  },
38
  "hero": {
39
+ "width": 400,
40
+ "margin_bottom": 30,
41
  "logo": ASSETS / "AI_Suite_GeoMech_logo.png",
42
  },
43
  "grid": {
44
+ "gap": 50,
45
+ # Card width is now fluid; keep for max-width if you like
46
+ "card_width": 340,
47
  },
48
  "card": {
49
+ "radius": 22,
50
+ "border_width": 2,
51
+ "pad_v": 24,
52
+ "pad_h": 20,
53
+ "border": "#0B1220",
54
  "border_hover": "#243447",
 
55
  "bg_top": "#FFFFFF",
56
  "bg_bot": "#FBFCFE",
57
  "title_color": "#0B1220",
58
  "blurb_color": "#566275",
59
  },
60
  "icon": {
61
+ "diam": 118,
62
+ "img": 106,
63
  "circle_bg": "#F1F5F9",
64
  "circle_border": "rgba(12,18,32,0.10)",
65
  },
66
  "button": {
67
+ "pad_v": 12,
68
+ "pad_h": 20,
69
+ "radius": 14,
 
70
  "bg1": "#0B1220",
71
  "bg2": "#162338",
72
  "text": "#FFFFFF",
 
81
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
82
  "url": "https://smart-thinking-ucs.hf.space/",
83
  "icon": ASSETS / "UCS_logo.png",
84
+ "style": {"bg_top":"#EAF7F1","bg_bot":"#F6FBF8","border":"#0F3D3E"},
 
 
 
 
85
  },
86
  {
87
  "title": " ST_GeoMEch_UCS",
88
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
89
  "url": "https://smart-thinking-ucs.hf.space/",
90
  "icon": ASSETS / "UCS_logo.png",
91
+ "style": {"bg_top":"#EAF7F1","bg_bot":"#F6FBF8","border":"#0F3D3E"},
 
 
 
 
92
  },
93
  {
94
  "title": " ST_GeoMEch_UCS",
95
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
96
  "url": "https://smart-thinking-ucs.hf.space/",
97
  "icon": ASSETS / "UCS_logo.png",
98
+ "style": {"bg_top":"#EAF7F1","bg_bot":"#F6FBF8","border":"#0F3D3E"},
 
 
 
 
99
  },
100
  {
101
  "title": " ST_GeoMEch_UCS",
102
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
103
  "url": "https://smart-thinking-ucs.hf.space/",
104
  "icon": ASSETS / "UCS_logo.png",
105
+ "style": {"bg_top":"#EAF7F1","bg_bot":"#F6FBF8","border":"#0F3D3E"},
 
 
 
 
106
  },
107
  {
108
  "title": " ST_GeoMEch_UCS",
109
  "blurb": "Real-time Uniconfined Compressive Strength Prediction.",
110
  "url": "https://smart-thinking-ucs.hf.space/",
111
  "icon": ASSETS / "UCS_logo.png",
112
+ "style": {"bg_top":"#EAF7F1","bg_bot":"#F6FBF8","border":"#0F3D3E"},
 
 
 
 
113
  },
114
  {
115
  "title": " ST_GeoMech_Ym",
116
  "blurb": "Real-time Static Young's Modulus Prediction.",
117
  "url": "https://smart-thinking-ym.hf.space",
118
  "icon": ASSETS / "Ym_logo.png",
119
+ "style": {"bg_top":"#EAF7FD","bg_bot":"#F5FBFF","border":"#0E4A6E"},
 
 
 
 
120
  },
121
  {
122
  "title": " ST_GeoMech_SMW",
123
  "blurb": "Real-Time Safe Mud Window Prediction",
124
  "url": "https://smart-thinking-smw.hf.space",
125
  "icon": ASSETS / "SMW_logo.png",
126
+ "style": {"bg_top":"#EEF0FF","bg_bot":"#F7F8FF","border":"#3E4EB8"},
 
 
 
 
127
  },
128
  ]
129
 
 
195
  --btn2: {button["bg2"]};
196
  --btnText: {button["text"]};
197
  --btnBorder: {button["border"]};
198
+
199
+ /* NEW: cards per row control */
200
+ --cols: {CARDS_PER_ROW};
201
  }}
202
 
203
  html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
 
242
  filter: drop-shadow(0 6px 16px rgba(0,0,0,.10));
243
  }}
244
 
245
+ /* ===== GRID =====
246
+ Uses --cols to set the number of cards per row on desktop.
247
+ Responsive fallbacks: 2 cols on tablets, 1 col on phones. */
248
  .grid {{
249
  display: grid;
250
+ grid-template-columns: repeat(var(--cols), 1fr);
251
  gap: var(--grid-gap);
252
+ justify-items: stretch;
253
+ align-items: stretch;
254
  margin-top: 10px;
255
  }}
256
  @media (max-width: 1120px) {{
257
+ .grid {{ grid-template-columns: repeat(2, 1fr); gap: calc(var(--grid-gap) - 12px); }}
258
  }}
259
  @media (max-width: 720px) {{
260
  .grid {{ grid-template-columns: 1fr; }}
 
263
  /* ===== CARD ===== */
264
  .card {{
265
  position: relative;
266
+ width: 100%; /* full width of its grid cell */
267
+ max-width: var(--card-w); /* optional max width for nice measure */
268
  border-radius: var(--c-radius, var(--card-r));
269
  padding: var(--c-pv, var(--card-pv)) var(--c-ph, var(--card-ph));
270
  background: linear-gradient(180deg, var(--c-bg-top, {card["bg_top"]}) 0%, var(--c-bg-bot, {card["bg_bot"]}) 100%);
 
275
  0 -1px 6px rgba(255,255,255,0.18) inset;
276
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease, filter .18s ease;
277
  text-align:center; display:flex; flex-direction:column; gap:16px; align-items: center;
278
+ margin: 0 auto; /* center within grid cell if narrower than cell */
279
  }}
280
  .card:hover {{
281
  transform: translateY(-6px) scale(1.01);
 
287
  filter: saturate(1.03);
288
  }}
289
 
290
+ /* ===== ICON / LOGO ===== */
291
  .icon-wrap {{
292
  width: var(--c-icon-d, {icon["diam"]}px); height: var(--c-icon-d, {icon["diam"]}px);
293
  border-radius: 9999px; display:grid; place-items:center;
 
296
  box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 10px 22px rgba(2,20,35,.07);
297
  }}
298
  .icon-wrap img {{
 
299
  width: calc(var(--c-icon-img, {icon["img"]}px) - 12px);
300
  height: calc(var(--c-icon-img, {icon["img"]}px) - 12px);
301
+ padding: 6px; box-sizing: border-box; object-fit: contain;
302
+ background: #FFFFFF; border: 2px solid var(--c-border, var(--cardStroke)); border-radius: 14px; display:block;
 
 
 
 
 
303
  }}
304
 
305
  .card h3 {{
306
  margin: 0; font-size: 25px; font-weight: 900;
307
+ color: var(--c-title-color, {card["title_color"]}); letter-spacing: .15px;
308
  }}
309
  .card p {{
310
+ color: var(--c-blurb-color, {card["blurb_color"]}); min-height: 40px; margin: 0 10px; font-size: 16px;
311
  }}
312
 
313
  .btn {{
 
347
  )
348
 
349
  # ========= Hero =========
 
 
 
 
 
 
 
 
350
  hero_html = img_tag(THEME["hero"]["logo"], "ST_GeoMech SUITE")
351
  st.markdown(f"<div class='hero'>{hero_html}</div>", unsafe_allow_html=True)
352
 
 
365
  if "blurb_color" in style: s.append(f"--c-blurb-color:{style['blurb_color']}")
366
  if "blurb_fs" in style: s.append(f"--c-blurb-fs:{int(style['blurb_fs'])}px")
367
  if "btn_bg1" in style: s.append(f"--c-btn-bg1:{style['btn_bg1']}")
368
+ if "btn_bg2" in style: s.append(f"--c-btn-bg2:{style['btn_bg2"]}")
369
+ if "btn_text" in style: s.append(f"--c-btn-text:{style['btn_text"]}")
370
+ if "btn_border" in style: s.append(f"--c-btn-border:{style['btn_border"]}")
371
  if "btn_fs" in style: s.append(f"--c-btn-fs:{int(style['btn_fs'])}px")
372
  if "btn_pad_v" in style: s.append(f"--c-btn-pv:{int(style['btn_pad_v'])}px")
373
  if "btn_pad_h" in style: s.append(f"--c-btn-ph:{int(style['btn_pad_h'])}px")
374
  if "icon_diam" in style: s.append(f"--c-icon-d:{int(style['icon_diam'])}px")
375
+ if "icon_img" in style: s.append(f"--c-icon-img:{int(style['icon_img"]) }px")
376
+ if "icon_bg" in style: s.append(f"--c-icon-bg:{style['icon_bg"]}")
377
+ if "icon_border" in style: s.append(f"--c-icon-border:{style['icon_border"]}")
378
  return "; ".join(s)
379
 
 
 
380
  def app_card(card_cfg: Dict[str, Any]) -> str:
381
  style = card_cfg.get("style", {})
382
  vars_inline = build_card_vars(style)