Update app.py
Browse files
app.py
CHANGED
|
@@ -15,63 +15,66 @@ st.set_page_config(page_title="Smart Thinking — AI Suite", layout="wide")
|
|
| 15 |
# ========= GLOBAL THEME (all knobs live here) =========
|
| 16 |
THEME: Dict[str, Any] = {
|
| 17 |
"page": {
|
| 18 |
-
"container_width": 1120,
|
| 19 |
-
"bg_radial": True,
|
| 20 |
-
"section_gap":
|
| 21 |
-
"footer_gap": 36,
|
| 22 |
-
"top_padding": 40,
|
| 23 |
},
|
| 24 |
"hero": {
|
| 25 |
-
|
| 26 |
-
"
|
| 27 |
-
"width": 340, # rendered pixel width of hero logo
|
| 28 |
"tagline": "We Deliver AI-Based Solutions For O&G Industry",
|
| 29 |
-
"tagline_size": 22,
|
| 30 |
-
"tagline_weight": 500,
|
| 31 |
-
"tagline_italic": True,
|
| 32 |
"tagline_color": "#0B1220",
|
| 33 |
-
"logo_bottom":
|
| 34 |
-
"block_bottom": 22,
|
| 35 |
},
|
| 36 |
"grid": {
|
| 37 |
-
"columns": 2,
|
| 38 |
-
"gap": 40,
|
| 39 |
-
"card_width":
|
| 40 |
},
|
| 41 |
"card": {
|
| 42 |
-
"radius": 22,
|
| 43 |
-
"border_width": 2,
|
| 44 |
-
"border": "#0B1220",
|
| 45 |
"border_hover": "#243447",
|
| 46 |
-
"bg_top": "#FFFFFF",
|
| 47 |
-
"bg_bot": "#FBFCFE",
|
| 48 |
-
"pad_v": 24,
|
| 49 |
-
"pad_h": 22,
|
| 50 |
-
"gap":
|
| 51 |
|
| 52 |
-
# Title & paragraph
|
| 53 |
"title_color": "#0B1220",
|
| 54 |
"title_size": 26,
|
| 55 |
"title_weight": 900,
|
| 56 |
-
"
|
|
|
|
| 57 |
"blurb_color": "#566275",
|
| 58 |
"blurb_size": 16,
|
| 59 |
-
"blurb_mt": 0,
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
"
|
| 64 |
-
"
|
|
|
|
|
|
|
| 65 |
},
|
| 66 |
"button": {
|
| 67 |
"pad_v": 12, "pad_h": 20, "radius": 14,
|
| 68 |
"bg1": "#0B1220", "bg2": "#162338",
|
| 69 |
"text": "#FFFFFF", "border": "rgba(11,18,32,.55)",
|
| 70 |
"font_size": 16, "font_weight": 800,
|
|
|
|
| 71 |
},
|
| 72 |
}
|
| 73 |
|
| 74 |
-
# ========= CARDS
|
| 75 |
CARDS = [
|
| 76 |
{
|
| 77 |
"title": "Geomechanics",
|
|
@@ -112,7 +115,7 @@ def img_tag(path: Path, alt: str, cls: str = "", style: str = "") -> str:
|
|
| 112 |
style_attr = f' style="{style}"' if style else ""
|
| 113 |
return f'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />'
|
| 114 |
|
| 115 |
-
# ========= CSS
|
| 116 |
page = THEME["page"]; hero = THEME["hero"]
|
| 117 |
grid = THEME["grid"]; card = THEME["card"]; button = THEME["button"]
|
| 118 |
|
|
@@ -158,13 +161,16 @@ st.markdown(f"""
|
|
| 158 |
--card-title-color: {card["title_color"]};
|
| 159 |
--card-title-size: {card["title_size"]}px;
|
| 160 |
--card-title-weight: {card["title_weight"]};
|
|
|
|
| 161 |
--card-title-mb: {card["title_mb"]}px;
|
| 162 |
|
| 163 |
--card-blurb-color: {card["blurb_color"]};
|
| 164 |
--card-blurb-size: {card["blurb_size"]}px;
|
| 165 |
--card-blurb-mt: {card["blurb_mt"]}px;
|
|
|
|
| 166 |
|
| 167 |
--logo-max-w: {card["logo_max_w"]}px;
|
|
|
|
| 168 |
--logo-top: {card["logo_top"]}px;
|
| 169 |
--logo-bottom: {card["logo_bottom"]}px;
|
| 170 |
|
|
@@ -177,6 +183,7 @@ st.markdown(f"""
|
|
| 177 |
--btnPV: {button["pad_v"]}px;
|
| 178 |
--btnPH: {button["pad_h"]}px;
|
| 179 |
--btnRadius: {button["radius"]}px;
|
|
|
|
| 180 |
}}
|
| 181 |
|
| 182 |
html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
|
|
@@ -191,7 +198,7 @@ st.markdown(f"""
|
|
| 191 |
{bg_radial_css}
|
| 192 |
}}
|
| 193 |
|
| 194 |
-
/* ===== HERO
|
| 195 |
.hero {{ text-align:center; }}
|
| 196 |
.hero img {{
|
| 197 |
width: var(--hero-w); max-width: 92vw; height: auto;
|
|
@@ -207,7 +214,7 @@ st.markdown(f"""
|
|
| 207 |
margin-bottom: var(--hero-block-mb);
|
| 208 |
}}
|
| 209 |
|
| 210 |
-
/* ===== GRID
|
| 211 |
.grid {{
|
| 212 |
display: grid;
|
| 213 |
grid-template-columns: repeat(var(--grid-cols), var(--card-w));
|
|
@@ -239,19 +246,24 @@ st.markdown(f"""
|
|
| 239 |
filter: saturate(1.03);
|
| 240 |
}}
|
| 241 |
|
| 242 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
.card .logo-img {{
|
| 244 |
-
margin-top: var(--logo-top);
|
| 245 |
-
margin-bottom: var(--logo-bottom);
|
| 246 |
max-width: var(--logo-max-w);
|
| 247 |
-
|
| 248 |
-
height: auto;
|
| 249 |
object-fit: contain;
|
| 250 |
display:block;
|
| 251 |
}}
|
| 252 |
|
| 253 |
.card h3 {{
|
| 254 |
-
margin:
|
| 255 |
font-size: var(--card-title-size);
|
| 256 |
font-weight: var(--card-title-weight);
|
| 257 |
color: var(--c-title-color, var(--card-title-color));
|
|
@@ -262,13 +274,14 @@ st.markdown(f"""
|
|
| 262 |
.card p {{
|
| 263 |
color: var(--c-blurb-color, var(--card-blurb-color));
|
| 264 |
font-size: var(--card-blurb-size);
|
| 265 |
-
margin: var(--card-blurb-mt)
|
| 266 |
text-align:center;
|
| 267 |
width: 100%;
|
| 268 |
}}
|
| 269 |
|
| 270 |
.btn {{
|
| 271 |
display:inline-block;
|
|
|
|
| 272 |
padding: var(--btnPV) var(--btnPH);
|
| 273 |
border-radius: var(--btnRadius);
|
| 274 |
border: 1px solid var(--btnBorder);
|
|
@@ -290,7 +303,7 @@ st.markdown(f"""
|
|
| 290 |
</style>
|
| 291 |
""", unsafe_allow_html=True)
|
| 292 |
|
| 293 |
-
# ========= HERO
|
| 294 |
hero_html = img_tag(hero["logo"], "Company Logo")
|
| 295 |
st.markdown(
|
| 296 |
f"""
|
|
@@ -310,7 +323,7 @@ def build_card_vars(style: Dict[str, Any]) -> str:
|
|
| 310 |
if "pad_v" in style: s.append(f"--c-pv:{int(style['pad_v'])}px")
|
| 311 |
if "pad_h" in style: s.append(f"--c-ph:{int(style['pad_h'])}px")
|
| 312 |
if "bg_top" in style: s.append(f"--c-bg-top:{style['bg_top']}")
|
| 313 |
-
if "bg_bot" in style: s.append(f"--c-bg-bot:{style['bg_bot
|
| 314 |
if "border" in style: s.append(f"--c-border:{style['border']}")
|
| 315 |
if "border_width" in style: s.append(f"--c-bw:{int(style['border_width'])}px")
|
| 316 |
if "title_color" in style: s.append(f"--c-title-color:{style['title_color']}")
|
|
@@ -326,7 +339,7 @@ def app_card(card_cfg: Dict[str, Any]) -> str:
|
|
| 326 |
target = "_self"
|
| 327 |
return (
|
| 328 |
f"<div class='card' style='{vars_inline}'>"
|
| 329 |
-
+ f"{icon_html}"
|
| 330 |
+ f"<h3>{escape(card_cfg['title'])}</h3>"
|
| 331 |
+ f"<p>{escape(card_cfg['blurb'])}</p>"
|
| 332 |
+ f"<a class='btn' href='{escape(card_cfg.get('url', '#'))}' target='{target}' rel='noopener'>Explore</a>"
|
|
|
|
| 15 |
# ========= GLOBAL THEME (all knobs live here) =========
|
| 16 |
THEME: Dict[str, Any] = {
|
| 17 |
"page": {
|
| 18 |
+
"container_width": 1120,
|
| 19 |
+
"bg_radial": True,
|
| 20 |
+
"section_gap": 44,
|
| 21 |
+
"footer_gap": 36,
|
| 22 |
+
"top_padding": 40,
|
| 23 |
},
|
| 24 |
"hero": {
|
| 25 |
+
"logo": ASSETS / "logo.png",
|
| 26 |
+
"width": 340,
|
|
|
|
| 27 |
"tagline": "We Deliver AI-Based Solutions For O&G Industry",
|
| 28 |
+
"tagline_size": 22,
|
| 29 |
+
"tagline_weight": 500, # lighter + italic looks premium
|
| 30 |
+
"tagline_italic": True,
|
| 31 |
"tagline_color": "#0B1220",
|
| 32 |
+
"logo_bottom": 14,
|
| 33 |
+
"block_bottom": 22,
|
| 34 |
},
|
| 35 |
"grid": {
|
| 36 |
+
"columns": 2,
|
| 37 |
+
"gap": 40,
|
| 38 |
+
"card_width": 460,
|
| 39 |
},
|
| 40 |
"card": {
|
| 41 |
+
"radius": 22,
|
| 42 |
+
"border_width": 2,
|
| 43 |
+
"border": "#0B1220",
|
| 44 |
"border_hover": "#243447",
|
| 45 |
+
"bg_top": "#FFFFFF",
|
| 46 |
+
"bg_bot": "#FBFCFE",
|
| 47 |
+
"pad_v": 24,
|
| 48 |
+
"pad_h": 22,
|
| 49 |
+
"gap": 12, # base gap between stacked elements
|
| 50 |
|
| 51 |
+
# Title & paragraph (FULL CONTROL)
|
| 52 |
"title_color": "#0B1220",
|
| 53 |
"title_size": 26,
|
| 54 |
"title_weight": 900,
|
| 55 |
+
"title_mt": 0, # <- space above title
|
| 56 |
+
"title_mb": 2, # <- space below title
|
| 57 |
"blurb_color": "#566275",
|
| 58 |
"blurb_size": 16,
|
| 59 |
+
"blurb_mt": 0, # <- space above blurb
|
| 60 |
+
"blurb_mb": 8, # <- space below blurb
|
| 61 |
+
|
| 62 |
+
# Card logo controls (no frame/border)
|
| 63 |
+
"logo_max_w": 250,
|
| 64 |
+
"logo_area_h": 160, # <- FIXED area so Space 1 == Space 2
|
| 65 |
+
"logo_top": 6,
|
| 66 |
+
"logo_bottom": 6,
|
| 67 |
},
|
| 68 |
"button": {
|
| 69 |
"pad_v": 12, "pad_h": 20, "radius": 14,
|
| 70 |
"bg1": "#0B1220", "bg2": "#162338",
|
| 71 |
"text": "#FFFFFF", "border": "rgba(11,18,32,.55)",
|
| 72 |
"font_size": 16, "font_weight": 800,
|
| 73 |
+
"btn_mt": 8, # <- space above the button
|
| 74 |
},
|
| 75 |
}
|
| 76 |
|
| 77 |
+
# ========= CARDS =========
|
| 78 |
CARDS = [
|
| 79 |
{
|
| 80 |
"title": "Geomechanics",
|
|
|
|
| 115 |
style_attr = f' style="{style}"' if style else ""
|
| 116 |
return f'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />'
|
| 117 |
|
| 118 |
+
# ========= CSS =========
|
| 119 |
page = THEME["page"]; hero = THEME["hero"]
|
| 120 |
grid = THEME["grid"]; card = THEME["card"]; button = THEME["button"]
|
| 121 |
|
|
|
|
| 161 |
--card-title-color: {card["title_color"]};
|
| 162 |
--card-title-size: {card["title_size"]}px;
|
| 163 |
--card-title-weight: {card["title_weight"]};
|
| 164 |
+
--card-title-mt: {card["title_mt"]}px;
|
| 165 |
--card-title-mb: {card["title_mb"]}px;
|
| 166 |
|
| 167 |
--card-blurb-color: {card["blurb_color"]};
|
| 168 |
--card-blurb-size: {card["blurb_size"]}px;
|
| 169 |
--card-blurb-mt: {card["blurb_mt"]}px;
|
| 170 |
+
--card-blurb-bm: {card["blurb_mb"]}px;
|
| 171 |
|
| 172 |
--logo-max-w: {card["logo_max_w"]}px;
|
| 173 |
+
--logo-area-h: {card["logo_area_h"]}px; /* fixed logo area height */
|
| 174 |
--logo-top: {card["logo_top"]}px;
|
| 175 |
--logo-bottom: {card["logo_bottom"]}px;
|
| 176 |
|
|
|
|
| 183 |
--btnPV: {button["pad_v"]}px;
|
| 184 |
--btnPH: {button["pad_h"]}px;
|
| 185 |
--btnRadius: {button["radius"]}px;
|
| 186 |
+
--btnMT: {button["btn_mt"]}px;
|
| 187 |
}}
|
| 188 |
|
| 189 |
html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
|
|
|
|
| 198 |
{bg_radial_css}
|
| 199 |
}}
|
| 200 |
|
| 201 |
+
/* ===== HERO ===== */
|
| 202 |
.hero {{ text-align:center; }}
|
| 203 |
.hero img {{
|
| 204 |
width: var(--hero-w); max-width: 92vw; height: auto;
|
|
|
|
| 214 |
margin-bottom: var(--hero-block-mb);
|
| 215 |
}}
|
| 216 |
|
| 217 |
+
/* ===== GRID ===== */
|
| 218 |
.grid {{
|
| 219 |
display: grid;
|
| 220 |
grid-template-columns: repeat(var(--grid-cols), var(--card-w));
|
|
|
|
| 246 |
filter: saturate(1.03);
|
| 247 |
}}
|
| 248 |
|
| 249 |
+
/* FIXED LOGO AREA → equal top spacing on all cards */
|
| 250 |
+
.card .logo-wrap {{
|
| 251 |
+
width: 100%;
|
| 252 |
+
height: var(--logo-area-h);
|
| 253 |
+
padding-top: var(--logo-top);
|
| 254 |
+
padding-bottom: var(--logo-bottom);
|
| 255 |
+
display:flex; align-items:center; justify-content:center;
|
| 256 |
+
}}
|
| 257 |
.card .logo-img {{
|
|
|
|
|
|
|
| 258 |
max-width: var(--logo-max-w);
|
| 259 |
+
max-height: 100%;
|
| 260 |
+
width: auto; height: auto;
|
| 261 |
object-fit: contain;
|
| 262 |
display:block;
|
| 263 |
}}
|
| 264 |
|
| 265 |
.card h3 {{
|
| 266 |
+
margin: var(--card-title-mt) auto var(--card-title-mb) auto; /* centered + controllable */
|
| 267 |
font-size: var(--card-title-size);
|
| 268 |
font-weight: var(--card-title-weight);
|
| 269 |
color: var(--c-title-color, var(--card-title-color));
|
|
|
|
| 274 |
.card p {{
|
| 275 |
color: var(--c-blurb-color, var(--card-blurb-color));
|
| 276 |
font-size: var(--card-blurb-size);
|
| 277 |
+
margin: var(--card-blurb-mt) auto var(--card-blurb-bm) auto; /* centered + controllable */
|
| 278 |
text-align:center;
|
| 279 |
width: 100%;
|
| 280 |
}}
|
| 281 |
|
| 282 |
.btn {{
|
| 283 |
display:inline-block;
|
| 284 |
+
margin-top: var(--btnMT);
|
| 285 |
padding: var(--btnPV) var(--btnPH);
|
| 286 |
border-radius: var(--btnRadius);
|
| 287 |
border: 1px solid var(--btnBorder);
|
|
|
|
| 303 |
</style>
|
| 304 |
""", unsafe_allow_html=True)
|
| 305 |
|
| 306 |
+
# ========= HERO =========
|
| 307 |
hero_html = img_tag(hero["logo"], "Company Logo")
|
| 308 |
st.markdown(
|
| 309 |
f"""
|
|
|
|
| 323 |
if "pad_v" in style: s.append(f"--c-pv:{int(style['pad_v'])}px")
|
| 324 |
if "pad_h" in style: s.append(f"--c-ph:{int(style['pad_h'])}px")
|
| 325 |
if "bg_top" in style: s.append(f"--c-bg-top:{style['bg_top']}")
|
| 326 |
+
if "bg_bot" in style: s.append(f"--c-bg-bot:{style['bg_bot"]}")
|
| 327 |
if "border" in style: s.append(f"--c-border:{style['border']}")
|
| 328 |
if "border_width" in style: s.append(f"--c-bw:{int(style['border_width'])}px")
|
| 329 |
if "title_color" in style: s.append(f"--c-title-color:{style['title_color']}")
|
|
|
|
| 339 |
target = "_self"
|
| 340 |
return (
|
| 341 |
f"<div class='card' style='{vars_inline}'>"
|
| 342 |
+
+ f"<div class='logo-wrap'>{icon_html}</div>"
|
| 343 |
+ f"<h3>{escape(card_cfg['title'])}</h3>"
|
| 344 |
+ f"<p>{escape(card_cfg['blurb'])}</p>"
|
| 345 |
+ f"<a class='btn' href='{escape(card_cfg.get('url', '#'))}' target='{target}' rel='noopener'>Explore</a>"
|