AbhijitClemson commited on
Commit
4747d73
Β·
verified Β·
1 Parent(s): d695885

Update page_files/Home.py

Browse files
Files changed (1) hide show
  1. page_files/Home.py +722 -722
page_files/Home.py CHANGED
@@ -1,722 +1,722 @@
1
- import streamlit as st
2
- import streamlit.components.v1 as components
3
- from pathlib import Path
4
- import base64
5
- from streamlit_card import card
6
- from data_loader import get_all_sections
7
- import random
8
- from data_loader import get_all_sections
9
- import re
10
-
11
-
12
- ALL_CARDS = [
13
- ("Composites", "Material class", "material", "Composites"),
14
- ("Polymers", "Material class", "material", "Polymers"),
15
- ("Fibers", "Material class", "material", "Fibers"),
16
- ]
17
-
18
- sections = get_all_sections()
19
- for section in sections:
20
- ALL_CARDS.append((section, "Property type", "section", section))
21
-
22
- if "visible_cards" not in st.session_state:
23
- random.shuffle(ALL_CARDS)
24
- st.session_state.visible_cards = ALL_CARDS[:4]
25
-
26
- VISIBLE_CARDS = st.session_state.visible_cards
27
- prop_count = len([c for c in ALL_CARDS if c[2] == "section"])
28
-
29
- def get_card_icon(title: str, card_type: str) -> str:
30
- if card_type == "material":
31
- icons = {"composites": "🧱", "polymers": "πŸ”¬", "fibers": "🧡"}
32
- return icons.get(title.lower(), "🧱")
33
- t = title.lower()
34
- if "mechanical" in t: return "βš™οΈ"
35
- if "thermal" in t: return "πŸ”₯"
36
- if "electrical" in t: return "⚑"
37
- if "physical" in t: return "βš–οΈ"
38
- if "processing" in t: return "πŸ”§"
39
- if "optical" in t: return "πŸ”­"
40
- if "chemical" in t: return "πŸ§ͺ"
41
- if "flammab" in t: return "πŸ”΄"
42
- if "component" in t: return "🧩"
43
- if "descriptive" in t: return "πŸ“‹"
44
- return "πŸ“‹"
45
-
46
- def img_to_b64(path):
47
- try:
48
- ext = Path(path).suffix.lower().replace(".", "")
49
- mime = "png" if ext == "png" else "jpeg"
50
- with open(path, "rb") as f:
51
- data = base64.b64encode(f.read()).decode()
52
- return f"data:image/{mime};base64,{data}"
53
- except Exception:
54
- return ""
55
-
56
-
57
- home_img = img_to_b64("images/Home.png")
58
- logo_img = img_to_b64("logo.png")
59
-
60
- st.markdown("""
61
- <style>
62
- section[data-testid="stMain"] {
63
- background: #fff !important;
64
- }
65
- .stApp {
66
- background: #fff !important;
67
- }
68
- </style>
69
- """,
70
- unsafe_allow_html=True
71
- )
72
-
73
- # Global style overrides
74
- st.markdown("""
75
- <style>
76
- @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
77
-
78
- /* Hide default Streamlit chrome */
79
- .block-container { padding: 0 !important; max-width: 100% !important; background: #fff !important;}
80
- [data-testid="stToolbar"],
81
- [data-testid="stDecoration"],
82
- [data-testid="stHeader"] { display: none !important; }
83
-
84
-
85
-
86
-
87
-
88
- /* ── Search row ── */
89
- /* Only target the search bar's horizontal block, not all columns */
90
- .st-emotion-cache-y1l7l5 .st-emotion-cache-1permvm {
91
- gap: 0 !important;
92
- align-items: stretch !important;
93
- }
94
-
95
- .st-emotion-cache-16s2yzk button {
96
- background: #f2f6f4 !important;
97
- border: 1.5px solid #d0d8d4 !important;
98
- border-right: none !important;
99
- border-radius: 50px 0 0 50px !important;
100
- color: #3a5248 !important;
101
- font-family: 'DM Sans', sans-serif !important;
102
- font-size: 0.8rem !important;
103
- font-weight: 600 !important;
104
- height: 46px !important;
105
- padding: 0 16px !important;
106
- white-space: nowrap !important;
107
- box-shadow: none !important;
108
- }
109
-
110
- .st-emotion-cache-4dubyl [data-baseweb="input"] {
111
- background: #000 !important;
112
- height: 46px !important;
113
- min-height: 46px !important;
114
- border-radius: 0 !important;
115
- border-color: #d0d8d4 !important;
116
- border-left: none !important;
117
- border-right: none !important;
118
- }
119
-
120
- .st-emotion-cache-4dubyl input {
121
- background: #fff !important;
122
- height: 46px !important;
123
- padding-top: 0 !important;
124
- padding-bottom: 0 !important;
125
- font-family: 'DM Sans', sans-serif !important;
126
- font-size: 0.92rem !important;
127
- color: #000 !important;
128
- box-shadow: none !important;
129
- padding-left: 18px !important;
130
- }
131
-
132
- .st-emotion-cache-4dubyl > div {
133
-
134
- height: 46px !important;
135
- }
136
- .st-emotion-cache-4dubyl input::placeholder {
137
- color: #0f1f1a !important;
138
- opacity: 0.4 !important;
139
- }
140
- .st-emotion-cache-mpgwbc button {
141
- background: #8ACAFF !important;
142
- border: 1.5px solid #8ACAFF !important;
143
- border-left: none !important;
144
- border-radius: 0 50px 50px 0 !important;
145
- color: #0f1f1a !important;
146
- font-family: 'DM Sans', sans-serif !important;
147
- font-size: 0.88rem !important;
148
- font-weight: 600 !important;
149
- height: 46px !important;
150
- padding: 0 28px !important;
151
- box-shadow: none !important;
152
- }
153
-
154
- .st-key-view_all_btn button {
155
- background: transparent !important;
156
- border: none !important;
157
- color: #8ACAFF !important;
158
- font-family: 'DM Sans', sans-serif !important;
159
- font-size: 0.85rem !important;
160
- font-weight: 600 !important;
161
- padding: 0 !important;
162
- height: auto !important;
163
- box-shadow: none !important;
164
- cursor: pointer !important;
165
- }
166
- .st-key-view_all_btn button:hover {
167
- color: #8ACAFF !important;
168
- text-decoration: underline !important;
169
- background: transparent !important;
170
- }
171
- .footer-nav-head {
172
- font-size: 0.68rem;
173
- font-weight: 700;
174
- letter-spacing: 1.4px;
175
- text-transform: uppercase;
176
- color: #0f1f1a;
177
- margin-bottom: 14px;
178
- font-family: 'DM Sans', sans-serif;
179
- }
180
-
181
- </style>
182
- """, unsafe_allow_html=True)
183
-
184
-
185
- # Helper
186
- def go_categorized(material=None, search=None):
187
- if material:
188
- st.session_state.material_type = material
189
- if search:
190
- st.session_state.search_term = search
191
- st.switch_page("page_files/Categorized_Search.py")
192
-
193
-
194
- def go_upload():
195
- st.switch_page("page_files/Upload_Data.py")
196
-
197
-
198
- # ══════════════════════════════════════════════════════════════════════════════
199
- # 1. ANIMATION SECTION (pure HTML, no clickables needed)
200
- # ══════════════════════════════════════════════════════════════════════════════
201
- about_img_html = (
202
- f"<div class='aim-about-img'><img src='{home_img}' alt='AIM platform diagram'/></div>"
203
- if home_img else
204
- "<div class='aim-about-img-placeholder'>[ Platform diagram ]</div>"
205
- )
206
-
207
- logo_html = (
208
- f"<img src='{logo_img}' alt='AIM Logo' style='height:52px;width:52px;object-fit:contain;border-radius:14px;'/>"
209
- if logo_img else ""
210
- )
211
-
212
- components.html(f"""
213
- <!DOCTYPE html><html lang="en"><head>
214
- <meta charset="UTF-8"/>
215
- <link rel="preconnect" href="https://fonts.googleapis.com">
216
- <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
217
- <style>
218
- *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}
219
- body {{ font-family: 'DM Sans', sans-serif; background: #f7f7f5; color: #1a2e26; overflow-x: hidden; }}
220
-
221
- /* ANIMATION SECTION */
222
- .anim-section {{
223
- background: #000;
224
- padding: 80px 40px 60px;
225
- display: flex; flex-direction: column; align-items: center;
226
- }}
227
- .anim-title {{
228
- font-size: clamp(1.6rem, 3vw, 2.4rem); font-weight: 800;
229
- color: #fff; text-align: center; letter-spacing: -1px; margin-bottom: 60px;
230
- }}
231
- .anim-stage {{
232
- position: relative; width: 560px; height: 320px;
233
- display: flex; align-items: center;
234
- justify-content: space-between; padding: 0 40px;
235
- }}
236
-
237
- /* MAG GLASS */
238
- .mag-wrap {{ position: relative; width: 100px; display: flex; flex-direction: column; align-items: center; }}
239
- .mag-label {{ font-size: 0.65rem; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; color: #4a7a6a; margin-bottom: 10px; }}
240
- .mag-svg {{ width: 90px; height: 160px; overflow: visible; }}
241
- .mag-lens-flash {{ opacity: 0; transition: opacity 0.6s ease 0.5s; }}
242
- .mag-wrap.active .mag-lens-flash {{ opacity: 1; animation: lensFlash 1.8s ease-in-out infinite 0.5s; }}
243
- @keyframes lensFlash {{ 0%,100% {{ opacity:.2; r:26; }} 50% {{ opacity:.7; r:28; }} }}
244
- .mag-scan {{ opacity: 0; transition: opacity 0.3s ease 0.8s; }}
245
- .mag-wrap.active .mag-scan {{ opacity: 1; animation: scanMove 1.2s ease-in-out infinite 0.8s; }}
246
- @keyframes scanMove {{ 0% {{ transform:translateY(-14px);opacity:.8; }} 50% {{ transform:translateY(0);opacity:1; }} 100% {{ transform:translateY(14px);opacity:.8; }} }}
247
-
248
- /* BUBBLES */
249
- .bubble {{ position:absolute; border-radius:50%; background:radial-gradient(circle at 35% 35%,rgba(255,255,255,.6),rgba(80,160,255,.25)); border:1px solid rgba(100,180,255,.4); opacity:0; pointer-events:none; backdrop-filter:blur(2px); }}
250
- .b1 {{ width:18px;height:18px;left:108px;bottom:155px; }}
251
- .b2 {{ width:13px;height:13px;left:120px;bottom:175px; }}
252
- .b3 {{ width:22px;height:22px;left:96px; bottom:140px; }}
253
- .b4 {{ width:10px;height:10px;left:128px;bottom:185px; }}
254
- .b5 {{ width:16px;height:16px;left:112px;bottom:165px; }}
255
- .b6 {{ width:8px; height:8px; left:124px;bottom:195px; }}
256
- .anim-stage.active .b1 {{ animation:floatBubble1 2.6s cubic-bezier(.25,.46,.45,.94) 1.2s forwards; }}
257
- .anim-stage.active .b2 {{ animation:floatBubble2 2.2s cubic-bezier(.25,.46,.45,.94) 1.5s forwards; }}
258
- .anim-stage.active .b3 {{ animation:floatBubble3 3.0s cubic-bezier(.25,.46,.45,.94) 1.0s forwards; }}
259
- .anim-stage.active .b4 {{ animation:floatBubble4 2.0s cubic-bezier(.25,.46,.45,.94) 1.8s forwards; }}
260
- .anim-stage.active .b5 {{ animation:floatBubble5 2.8s cubic-bezier(.25,.46,.45,.94) 1.3s forwards; }}
261
- .anim-stage.active .b6 {{ animation:floatBubble6 1.8s cubic-bezier(.25,.46,.45,.94) 2.0s forwards; }}
262
- @keyframes floatBubble1 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.4); }} 15% {{ opacity:1;transform:translate(4px,-20px) scale(1); }} 60% {{ opacity:.9;transform:translate(120px,-55px) scale(.9); }} 85% {{ opacity:.5;transform:translate(280px,-30px) scale(.6); }} 100% {{ opacity:0;transform:translate(340px,-10px) scale(.2); }} }}
263
- @keyframes floatBubble2 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.3); }} 15% {{ opacity:1;transform:translate(-5px,-28px) scale(1); }} 60% {{ opacity:.9;transform:translate(110px,-70px) scale(.85); }} 85% {{ opacity:.4;transform:translate(265px,-40px) scale(.5); }} 100% {{ opacity:0;transform:translate(330px,-15px) scale(.15); }} }}
264
- @keyframes floatBubble3 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.5); }} 15% {{ opacity:1;transform:translate(6px,-15px) scale(1); }} 55% {{ opacity:.9;transform:translate(130px,-45px) scale(.95); }} 80% {{ opacity:.4;transform:translate(278px,-20px) scale(.55); }} 100% {{ opacity:0;transform:translate(345px,-5px) scale(.2); }} }}
265
- @keyframes floatBubble4 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.3); }} 20% {{ opacity:1;transform:translate(-8px,-35px) scale(1); }} 65% {{ opacity:.8;transform:translate(105px,-80px) scale(.8); }} 88% {{ opacity:.3;transform:translate(255px,-50px) scale(.4); }} 100% {{ opacity:0;transform:translate(320px,-20px) scale(.1); }} }}
266
- @keyframes floatBubble5 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.4); }} 15% {{ opacity:1;transform:translate(3px,-22px) scale(1); }} 58% {{ opacity:.9;transform:translate(115px,-60px) scale(.88); }} 83% {{ opacity:.45;transform:translate(270px,-35px) scale(.55); }} 100% {{ opacity:0;transform:translate(335px,-12px) scale(.2); }} }}
267
- @keyframes floatBubble6 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.25); }} 20% {{ opacity:1;transform:translate(-3px,-40px) scale(1); }} 62% {{ opacity:.7;transform:translate(100px,-85px) scale(.75); }} 85% {{ opacity:.25;transform:translate(250px,-55px) scale(.35); }} 100% {{ opacity:0;transform:translate(318px,-22px) scale(.1); }} }}
268
-
269
- /* DATABASE */
270
- .db-wrap {{ position:relative;width:130px;display:flex;flex-direction:column;align-items:center; }}
271
- .db-label {{ font-size:.65rem;font-weight:600;letter-spacing:1.5px;text-transform:uppercase;color:#4a7a6a;margin-bottom:10px; }}
272
- .cyl {{ opacity:.15;transition:opacity .5s ease; }}
273
- .anim-stage.active .cyl-left {{ opacity:1;transition-delay:2.0s; }}
274
- .anim-stage.active .cyl-right {{ opacity:1;transition-delay:2.2s; }}
275
- .anim-stage.active .cyl-front {{ opacity:1;transition-delay:2.4s;animation:frontPulse 2.4s ease-in-out infinite 2.8s; }}
276
- @keyframes frontPulse {{ 0%,100% {{ filter:brightness(1); }} 50% {{ filter:brightness(1.2); }} }}
277
-
278
- /* CAPTION */
279
- .anim-caption {{ margin-top:48px;display:flex;gap:40px;align-items:center; }}
280
- .anim-step {{ display:flex;align-items:center;gap:10px;opacity:0;transform:translateY(10px);transition:opacity .5s ease,transform .5s ease; }}
281
- .anim-stage.active ~ .anim-caption .anim-step:nth-child(1) {{ opacity:1;transform:none;transition-delay:.5s; }}
282
- .anim-stage.active ~ .anim-caption .anim-step:nth-child(2) {{ opacity:1;transform:none;transition-delay:1s; }}
283
- .anim-stage.active ~ .anim-caption .anim-step:nth-child(3) {{ opacity:1;transform:none;transition-delay:1.5s; }}
284
- .step-text {{ font-size:.78rem;color:#5a8a7a;font-weight:500; }}
285
- </style>
286
- </head><body>
287
-
288
- <section class="anim-section">
289
- <h2 class="anim-title">From Experiment to Database</h2>
290
- <div class="anim-stage" id="animStage">
291
- <!-- MAG GLASS -->
292
- <div class="mag-wrap" id="magWrap">
293
- <div class="mag-label">Research</div>
294
- <svg class="mag-svg" viewBox="0 0 90 160" fill="none" xmlns="http://www.w3.org/2000/svg">
295
- <defs>
296
- <radialGradient id="lensGrad" cx="40%" cy="38%" r="55%">
297
- <stop offset="0%" stop-color="#4da6ff" stop-opacity="0.25"/>
298
- <stop offset="100%" stop-color="#1a4a8a" stop-opacity="0.7"/>
299
- </radialGradient>
300
- <radialGradient id="flashGrad" cx="50%" cy="50%" r="50%">
301
- <stop offset="0%" stop-color="#8ACAFF" stop-opacity="0.9"/>
302
- <stop offset="100%" stop-color="#8ACAFF" stop-opacity="0"/>
303
- </radialGradient>
304
- </defs>
305
- <circle cx="38" cy="38" r="32" fill="none" stroke="#2a5a8a" stroke-width="3"/>
306
- <circle cx="38" cy="38" r="29" fill="url(#lensGrad)"/>
307
- <line class="mag-scan" x1="16" y1="38" x2="60" y2="38" stroke="#8ACAFF" stroke-width="1.5" stroke-linecap="round" opacity="0.8"/>
308
- <circle class="mag-lens-flash" cx="38" cy="38" r="26" fill="url(#flashGrad)"/>
309
- <circle cx="24" cy="24" r="5" fill="white" opacity="0.15"/>
310
- <circle cx="30" cy="34" r="2" fill="#8ACAFF" opacity="0.7"/>
311
- <circle cx="42" cy="30" r="2" fill="#8ACAFF" opacity="0.5"/>
312
- <circle cx="38" cy="44" r="2" fill="#8ACAFF" opacity="0.6"/>
313
- <circle cx="48" cy="38" r="1.5" fill="#8ACAFF" opacity="0.4"/>
314
- </svg>
315
- </div>
316
- <!-- BUBBLES -->
317
- <div class="bubble b1"></div><div class="bubble b2"></div>
318
- <div class="bubble b3"></div><div class="bubble b4"></div>
319
- <div class="bubble b5"></div><div class="bubble b6"></div>
320
- <!-- DATABASE -->
321
- <div class="db-wrap">
322
- <div class="db-label">Database</div>
323
- <svg width="220" height="200" viewBox="0 0 220 200" fill="none" xmlns="http://www.w3.org/2000/svg">
324
- <defs>
325
- <linearGradient id="sideBlue" x1="0" y1="0" x2="1" y2="0">
326
- <stop offset="0%" stop-color="#2a55b0"/><stop offset="50%" stop-color="#4a7fd8"/><stop offset="100%" stop-color="#2a55b0"/>
327
- </linearGradient>
328
- <linearGradient id="sideDark" x1="0" y1="0" x2="1" y2="0">
329
- <stop offset="0%" stop-color="#0d1f5c"/><stop offset="50%" stop-color="#1a3a8a"/><stop offset="100%" stop-color="#0d1f5c"/>
330
- </linearGradient>
331
- <linearGradient id="topBlue" x1="0" y1="0" x2="0" y2="1">
332
- <stop offset="0%" stop-color="#6a9fe0"/><stop offset="100%" stop-color="#3a6abf"/>
333
- </linearGradient>
334
- <linearGradient id="topDark" x1="0" y1="0" x2="0" y2="1">
335
- <stop offset="0%" stop-color="#2a4a9a"/><stop offset="100%" stop-color="#0d1f5c"/>
336
- </linearGradient>
337
- </defs>
338
- <!-- back left -->
339
- <g class="cyl cyl-left">
340
- <rect x="10" y="88" width="66" height="20" rx="1" fill="url(#sideBlue)"/>
341
- <ellipse cx="43" cy="88" rx="33" ry="10" fill="url(#topBlue)"/>
342
- <rect x="10" y="66" width="66" height="24" rx="1" fill="url(#sideBlue)"/>
343
- <ellipse cx="43" cy="66" rx="33" ry="10" fill="url(#topBlue)"/>
344
- <rect x="10" y="46" width="66" height="22" rx="1" fill="url(#sideBlue)"/>
345
- <ellipse cx="43" cy="46" rx="33" ry="10" fill="url(#topBlue)"/>
346
- <ellipse cx="43" cy="108" rx="33" ry="10" fill="#1e3a80"/>
347
- </g>
348
- <!-- back right -->
349
- <g class="cyl cyl-right">
350
- <rect x="144" y="88" width="66" height="20" rx="1" fill="url(#sideBlue)"/>
351
- <ellipse cx="177" cy="88" rx="33" ry="10" fill="url(#topBlue)"/>
352
- <rect x="144" y="66" width="66" height="24" rx="1" fill="url(#sideBlue)"/>
353
- <ellipse cx="177" cy="66" rx="33" ry="10" fill="url(#topBlue)"/>
354
- <rect x="144" y="46" width="66" height="22" rx="1" fill="url(#sideBlue)"/>
355
- <ellipse cx="177" cy="46" rx="33" ry="10" fill="url(#topBlue)"/>
356
- <ellipse cx="177" cy="108" rx="33" ry="10" fill="#1e3a80"/>
357
- </g>
358
- <!-- front center -->
359
- <g class="cyl cyl-front">
360
- <rect x="70" y="128" width="80" height="24" rx="1" fill="url(#sideDark)"/>
361
- <ellipse cx="110" cy="128" rx="40" ry="13" fill="url(#topDark)"/>
362
- <rect x="70" y="102" width="80" height="28" rx="1" fill="url(#sideDark)"/>
363
- <ellipse cx="110" cy="102" rx="40" ry="13" fill="url(#topDark)"/>
364
- <rect x="70" y="76" width="80" height="28" rx="1" fill="url(#sideDark)"/>
365
- <ellipse cx="110" cy="76" rx="40" ry="13" fill="url(#topDark)"/>
366
- <rect x="70" y="54" width="80" height="24" rx="1" fill="url(#sideDark)"/>
367
- <ellipse cx="110" cy="54" rx="40" ry="13" fill="url(#topDark)"/>
368
- <ellipse cx="110" cy="152" rx="40" ry="13" fill="#080f2a"/>
369
- </g>
370
- </svg>
371
- </div>
372
- </div>
373
- <div class="anim-caption">
374
- <div class="anim-step"><span class="step-text">Collect measurements</span></div>
375
- <div class="anim-step"><span class="step-text">Process &amp; validate</span></div>
376
- <div class="anim-step"><span class="step-text">Stored in AIM</span></div>
377
- </div>
378
- </section>
379
-
380
- <script>
381
- const stage = document.getElementById('animStage');
382
- const magWrap = document.getElementById('magWrap');
383
- const observer = new IntersectionObserver(entries => {{
384
- entries.forEach(e => {{
385
- if (e.isIntersecting) {{
386
- setTimeout(() => {{ magWrap.classList.add('active'); stage.classList.add('active'); }}, 300);
387
- }} else {{
388
- magWrap.classList.remove('active'); stage.classList.remove('active');
389
- }}
390
- }});
391
- }}, {{ threshold: 0.4 }});
392
- observer.observe(document.querySelector('.anim-section'));
393
- </script>
394
- </body></html>
395
- """, height=700, scrolling=False)
396
-
397
- # ══════════════════════════════════════════════════════════════════════════════
398
- # 2. HERO , heading + description (static HTML)
399
- # ══════════════════════════════════════════════════════════════════════════════
400
- st.markdown("""
401
- <style>
402
- .aim-hero-text {
403
- background: #fff;
404
- text-align: center;
405
- padding: 64px 40px 24px;
406
- border-bottom: none;
407
- }
408
- .aim-hero-text h1 {
409
- font-family: 'DM Sans', sans-serif;
410
- font-size: clamp(2rem, 4.5vw, 3.2rem);
411
- font-weight: 800; color: #0f1f1a;
412
- line-height: 1.1; letter-spacing: -1.5px;
413
- margin-bottom: 18px;
414
- }
415
- .aim-hero-text p {
416
- color: #5a6b65; font-size: 1rem; line-height: 1.65;
417
- max-width: 500px; margin: 0 auto 28px;
418
- }
419
- </style>
420
- <div class="aim-hero-text">
421
- <h1>Accelerate Your Composites Research</h1>
422
- <p>Access a centralized, open-source database for experimental composite material properties.
423
- Polymer, fiber, and composite datasets , all in one place.</p>
424
- </div>
425
- """, unsafe_allow_html=True)
426
-
427
-
428
-
429
- # ══════════════════════════════════════════════════════════════════════════════
430
- # 4. STATS (static HTML)
431
- # ══════════════════════════════════════════════════════════════════════════════
432
- st.markdown(f"""
433
- <style>
434
- .aim-stats {{
435
- background: #f7f7f5; border-bottom: 1px solid #e4e8e5;
436
- display: flex; justify-content: center;
437
- }}
438
- .aim-stat {{
439
- text-align: center; padding: 34px 56px;
440
- border-right: 1px solid #e4e8e5;
441
- }}
442
- .aim-stat:last-child {{ border-right: none; }}
443
- .aim-stat-num {{
444
- font-family: 'DM Sans', sans-serif;
445
- font-size: 2.1rem; font-weight: 800;
446
- color: #8ACAFF; line-height: 1; margin-bottom: 6px;
447
- }}
448
- .aim-stat-label {{
449
- font-size: 0.68rem; font-weight: 600;
450
- letter-spacing: 1.4px; color: #7a8e87; text-transform: uppercase;
451
- }}
452
- </style>
453
- <div class="aim-stats">
454
- <div class="aim-stat"><div class="aim-stat-num">3</div><div class="aim-stat-label">Material Classes</div></div>
455
- <div class="aim-stat"><div class="aim-stat-num">{prop_count}</div><div class="aim-stat-label">Properties Tracked</div></div>
456
- <div class="aim-stat"><div class="aim-stat-num">8</div><div class="aim-stat-label">Research Teams</div></div>
457
- <div class="aim-stat"><div class="aim-stat-num">2</div><div class="aim-stat-label">Universities</div></div>
458
- </div>
459
- """, unsafe_allow_html=True)
460
-
461
-
462
- # ══════════════════════════════════════════════════════════════════════════════
463
- # 5. MAJOR CATEGORIES , Streamlit columns + containers + buttons
464
- # ══════════════════════════════════════════════════════════════════════════════
465
- st.markdown("""
466
- <style>
467
- /* Card buttons - scoped to cards horizontal block only */
468
- .st-emotion-cache-r3ry0f button {
469
- background: #fff !important;
470
- border: 1.5px solid #e4e8e5 !important;
471
- border-radius: 12px !important;
472
- padding: 24px 20px !important;
473
- height: 160px !important;
474
- width: 100% !important;
475
- text-align: left !important;
476
- white-space: pre-wrap !important;
477
- line-height: 1.5 !important;
478
- box-shadow: none !important;
479
- transition: box-shadow 0.2s, border-color 0.2s !important;
480
- }
481
-
482
- .st-emotion-cache-r3ry0f button:hover {
483
- border-color: #BAE1FC !important;
484
- box-shadow: 0 0 0 3px rgba(186,225,252,0.25), 0 6px 22px rgba(0,0,0,0.07) !important;
485
- background: #fff !important;
486
- }
487
- .st-emotion-cache-r3ry0f button p:first-child {
488
- font-size: 1.1rem !important;
489
- background: #f2f4f3 !important;
490
- border-radius: 8px !important;
491
- padding: 6px 8px !important;
492
- display: inline-block !important;
493
- margin-bottom: 12px !important;
494
- line-height: 1 !important;
495
- }
496
- .st-emotion-cache-r3ry0f button p:nth-child(2) {
497
- font-family: 'DM Sans', sans-serif !important;
498
- font-size: 0.97rem !important;
499
- font-weight: 700 !important;
500
- color: #0f1f1a !important;
501
- margin-bottom: 6px !important;
502
- }
503
- .st-emotion-cache-r3ry0f button p:last-child {
504
- font-family: 'DM Sans', sans-serif !important;
505
- font-size: 0.7rem !important;
506
- font-weight: 700 !important;
507
- letter-spacing: 1.2px !important;
508
- color: #8ACAFF !important;
509
- text-transform: uppercase !important;
510
- }
511
- .cards-section-wrap h2 {
512
- font-family: 'DM Sans', sans-serif !important;
513
- font-size: 1.4rem !important;
514
- font-weight: 700 !important;
515
- color: #0f1f1a !important;
516
- margin-bottom: 6px !important;
517
- }
518
- .cards-section-wrap p {
519
- color: #5a6b65 !important;
520
- font-size: 0.9rem !important;
521
- line-height: 1.6 !important;
522
- margin-bottom: 16px !important;
523
- }
524
- </style>
525
- """, unsafe_allow_html=True)
526
-
527
-
528
-
529
- st.markdown("<div style='padding: 64px 0 0 0;'></div>", unsafe_allow_html=True)
530
-
531
- st.markdown("""
532
- <div class="cards-section-wrap">
533
- <h2>Quick Links</h2>
534
- <p>Open the pages you need most.</p>
535
- </div>
536
- """, unsafe_allow_html=True)
537
-
538
- quick_cards = [
539
- ("Extract Data", "Platform", "page_files/Upload_Data.py", "about", "πŸ“‹"),
540
- ("Contact Team", "Support", "page_files/Contact_Team.py", "contact", " πŸ›‘οΈ"),
541
- ("Search", "Data Page", "page_files/Categorized_Search.py", "search", "πŸ”Ž"),
542
- ]
543
-
544
- cols = st.columns(3, gap="large")
545
- for col, (title, tag, target_page, key_suffix, badge) in zip(cols, quick_cards):
546
- with col:
547
- if st.button(
548
- f"{badge}\n\n{title}\n\n{tag}",
549
- key=f"quick_{key_suffix}_page",
550
- use_container_width=True,
551
- ):
552
- st.switch_page(target_page)
553
-
554
- st.markdown("<div style='background:#fff; padding: 24px 0; border-bottom: 1px solid #e8e8e4;'></div>", unsafe_allow_html=True)
555
-
556
-
557
-
558
-
559
- # ══════════════════════════════════════════════════════════════════════════════
560
- # 6. ABOUT + FOOTER (static HTML)
561
- # ══════════════════════════════════════════════════════════════════════════════
562
- components.html(f"""
563
- <!DOCTYPE html><html lang="en"><head>
564
- <meta charset="UTF-8"/>
565
- <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
566
- <style>
567
- *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}
568
- body {{ font-family: 'DM Sans', sans-serif; color: #1a2e26; }}
569
-
570
- .aim-about {{ background: #fff; padding: 60px; border-bottom: 1px solid #e8e8e4; }}
571
- .aim-about h2 {{ font-size: 1.65rem; font-weight: 700; color: #0f1f1a; letter-spacing: -0.5px; margin-bottom: 8px; }}
572
- .aim-about-sub {{ color: #6a7e77; font-size: 0.9rem; margin-bottom: 30px; }}
573
- .aim-about-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }}
574
- .aim-about-text p {{ color: #3a4e47; font-size: 0.91rem; line-height: 1.75; margin-bottom: 14px; }}
575
- .aim-about-img {{ border-radius: 10px; overflow: hidden; border: 1px solid #e4e8e5; }}
576
- .aim-about-img img {{ width: 100%; display: block; }}
577
- .aim-about-img-placeholder {{
578
- background: #f0f5f3; border-radius: 10px; height: 280px;
579
- display: flex; align-items: center; justify-content: center;
580
- color: #7a9e8f; font-size: 0.85rem; border: 1.5px dashed #c8d6d0;
581
- }}
582
-
583
- .aim-footer {{ background: #fff; border-top: 1px solid #e8e8e4; padding: 50px 60px 28px; }}
584
- .aim-footer-brand {{ display: flex; align-items: center; gap: 8px; font-weight: 700; color: #0f1f1a; font-size: 0.95rem; margin-bottom: 10px; }}
585
- .aim-footer-desc {{ font-size: 0.82rem; color: #7a8e87; line-height: 1.6; }}
586
- </style>
587
- </head><body>
588
-
589
- <section class="aim-about">
590
- <h2>About the Platform</h2>
591
- <p class="aim-about-sub">Artificially Intelligent Manufacturing Paradigm (AIM) for Composites</p>
592
- <div class="aim-about-grid">
593
- <div class="aim-about-text">
594
- <p>The AIM Database tool serves as a powerful, centralized hub designed to streamline
595
- collaboration and information exchange within the composite materials research community.
596
- The platform enables researchers to contribute to a shared knowledge base by uploading
597
- experimental datasets through secure terminals.</p>
598
- <p>Users can submit specific measurements regarding mechanical properties, thermal behavior,
599
- and rheology, alongside their published journal papers , ensuring that both raw data and
600
- peer-reviewed findings are integrated into one cohesive system.</p>
601
- <p>All contributed information is securely aggregated within a central cloud architecture,
602
- allowing for efficient storage, organization, and retrieval across polymer, fiber, and
603
- composite categories.</p>
604
- </div>
605
- <div>{about_img_html}</div>
606
- </div>
607
- </section>
608
-
609
-
610
-
611
- </body></html>
612
- """, height=550, scrolling=False)
613
-
614
- st.markdown("""
615
- <style>
616
-
617
- /* Footer nav wrapper */
618
- div[data-testid="stHorizontalBlock"]:has(.footer-nav-head) {
619
- background: #fff !important;
620
- border-bottom: 1px solid #e8e8e4 !important;
621
- padding: 0 60px 28px !important;
622
- margin-top: -16px !important;
623
-
624
- }
625
- .aim-footer-brand {
626
- display: flex;
627
- align-items: center;
628
- gap: 8px;
629
- font-weight: 700;
630
- color: #0f1f1a;
631
- font-size: 0.95rem;
632
- margin-bottom: 10px;
633
- font-family: 'DM Sans', sans-serif;
634
- }
635
- .aim-footer-desc {
636
- font-size: 0.82rem;
637
- color: #7a8e87;
638
- line-height: 1.6;
639
- font-family: 'DM Sans', sans-serif;
640
- }
641
- .footer-nav-head {
642
- font-size: 0.68rem;
643
- font-weight: 700;
644
- letter-spacing: 1.4px;
645
- text-transform: uppercase;
646
- color: #0f1f1a;
647
- margin-bottom: 14px;
648
- font-family: 'DM Sans', sans-serif;
649
- }
650
- .st-emotion-cache-1ofqig9 {
651
- background: transparent !important;
652
- border: none !important;
653
- padding: 0 !important;
654
- }
655
- .st-emotion-cache-1qeq59m {
656
- background: transparent !important;
657
- border: none !important;
658
- text-decoration: none !important;
659
- padding: 0 0 9px 0 !important;
660
- display: block !important;
661
- }
662
- .st-emotion-cache-1qeq59m:hover {
663
- background: transparent !important;
664
- border: none !important;
665
- }
666
- .st-emotion-cache-1qeq59m p {
667
- color: #6a7e77 !important;
668
- font-family: 'DM Sans', sans-serif !important;
669
- font-size: 0.83rem !important;
670
- font-weight: 400 !important;
671
- margin: 0 !important;
672
- }
673
- .st-emotion-cache-1qeq59m:hover p {
674
- color: #8ACAFF !important;
675
- }
676
-
677
- /* copyright bar */
678
- .aim-footer-bottom {
679
- background: #fff;
680
- border-top: 1px solid #e8e8e4;
681
- padding: 22px 60px;
682
- font-size: 0.76rem;
683
- color: #a0b0aa;
684
- font-family: 'DM Sans', sans-serif;
685
- }
686
-
687
- </style>
688
- """, unsafe_allow_html=True)
689
-
690
- brand_col, db_col, sup_col, _ = st.columns([4, 2, 2, 2], vertical_alignment="top", width="stretch")
691
-
692
- with brand_col:
693
- st.markdown(f"""
694
- <div class="aim-footer-brand">{logo_html} AIM Composites</div>
695
- <p class="aim-footer-desc">Advancing composites research through open data and collaborative tools.
696
- A joint initiative of Clemson University and University of Delaware.</p>
697
- """, unsafe_allow_html=True)
698
-
699
- with sup_col:
700
- st.markdown('<p class="footer-nav-head">DATABASE</p>', unsafe_allow_html=True)
701
- st.page_link(
702
- "page_files/Categorized_Search.py",
703
- label="Browse Materials",
704
- )
705
- st.page_link(
706
- "page_files/Categorized_Search.py",
707
- label="Categorized Search",
708
- )
709
- st.page_link(
710
- "page_files/Upload_Data.py",
711
- label="Upload Data",
712
- )
713
-
714
- with _:
715
- st.markdown('<p class="footer-nav-head">SUPPORT</p>', unsafe_allow_html=True)
716
- st.page_link(
717
- "page_files/Contact_Team.py",
718
- label="Contact Team",
719
- )
720
-
721
-
722
-
 
1
+ import streamlit as st
2
+ import streamlit.components.v1 as components
3
+ from pathlib import Path
4
+ import base64
5
+ from streamlit_card import card
6
+ from data_loader import get_all_sections
7
+ import random
8
+ from data_loader import get_all_sections
9
+ import re
10
+
11
+
12
+ ALL_CARDS = [
13
+ ("Composites", "Material class", "material", "Composites"),
14
+ ("Polymers", "Material class", "material", "Polymers"),
15
+ ("Fibers", "Material class", "material", "Fibers"),
16
+ ]
17
+
18
+ sections = get_all_sections()
19
+ for section in sections:
20
+ ALL_CARDS.append((section, "Property type", "section", section))
21
+
22
+ if "visible_cards" not in st.session_state:
23
+ random.shuffle(ALL_CARDS)
24
+ st.session_state.visible_cards = ALL_CARDS[:4]
25
+
26
+ VISIBLE_CARDS = st.session_state.visible_cards
27
+ prop_count = len([c for c in ALL_CARDS if c[2] == "section"])
28
+
29
+ def get_card_icon(title: str, card_type: str) -> str:
30
+ if card_type == "material":
31
+ icons = {"composites": "🧱", "polymers": "πŸ”¬", "fibers": "🧡"}
32
+ return icons.get(title.lower(), "🧱")
33
+ t = title.lower()
34
+ if "mechanical" in t: return "βš™οΈ"
35
+ if "thermal" in t: return "πŸ”₯"
36
+ if "electrical" in t: return "⚑"
37
+ if "physical" in t: return "βš–οΈ"
38
+ if "processing" in t: return "πŸ”§"
39
+ if "optical" in t: return "πŸ”­"
40
+ if "chemical" in t: return "πŸ§ͺ"
41
+ if "flammab" in t: return "πŸ”΄"
42
+ if "component" in t: return "🧩"
43
+ if "descriptive" in t: return "πŸ“‹"
44
+ return "πŸ“‹"
45
+
46
+ def img_to_b64(path):
47
+ try:
48
+ ext = Path(path).suffix.lower().replace(".", "")
49
+ mime = "png" if ext == "png" else "jpeg"
50
+ with open(path, "rb") as f:
51
+ data = base64.b64encode(f.read()).decode()
52
+ return f"data:image/{mime};base64,{data}"
53
+ except Exception:
54
+ return ""
55
+
56
+
57
+ home_img = img_to_b64("images/Home.png")
58
+ logo_img = img_to_b64("logo.png")
59
+
60
+ st.markdown("""
61
+ <style>
62
+ section[data-testid="stMain"] {
63
+ background: #fff !important;
64
+ }
65
+ .stApp {
66
+ background: #fff !important;
67
+ }
68
+ </style>
69
+ """,
70
+ unsafe_allow_html=True
71
+ )
72
+
73
+ # Global style overrides
74
+ st.markdown("""
75
+ <style>
76
+ @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
77
+
78
+ /* Hide default Streamlit chrome */
79
+ .block-container { padding: 0 !important; max-width: 100% !important; background: #fff !important;}
80
+ [data-testid="stToolbar"],
81
+ [data-testid="stDecoration"],
82
+ [data-testid="stHeader"] { display: none !important; }
83
+
84
+
85
+
86
+
87
+
88
+ /* ── Search row ── */
89
+ /* Only target the search bar's horizontal block, not all columns */
90
+ .st-emotion-cache-y1l7l5 .st-emotion-cache-1permvm {
91
+ gap: 0 !important;
92
+ align-items: stretch !important;
93
+ }
94
+
95
+ .st-emotion-cache-16s2yzk button {
96
+ background: #f2f6f4 !important;
97
+ border: 1.5px solid #d0d8d4 !important;
98
+ border-right: none !important;
99
+ border-radius: 50px 0 0 50px !important;
100
+ color: #3a5248 !important;
101
+ font-family: 'DM Sans', sans-serif !important;
102
+ font-size: 0.8rem !important;
103
+ font-weight: 600 !important;
104
+ height: 46px !important;
105
+ padding: 0 16px !important;
106
+ white-space: nowrap !important;
107
+ box-shadow: none !important;
108
+ }
109
+
110
+ .st-emotion-cache-4dubyl [data-baseweb="input"] {
111
+ background: #000 !important;
112
+ height: 46px !important;
113
+ min-height: 46px !important;
114
+ border-radius: 0 !important;
115
+ border-color: #d0d8d4 !important;
116
+ border-left: none !important;
117
+ border-right: none !important;
118
+ }
119
+
120
+ .st-emotion-cache-4dubyl input {
121
+ background: #fff !important;
122
+ height: 46px !important;
123
+ padding-top: 0 !important;
124
+ padding-bottom: 0 !important;
125
+ font-family: 'DM Sans', sans-serif !important;
126
+ font-size: 0.92rem !important;
127
+ color: #000 !important;
128
+ box-shadow: none !important;
129
+ padding-left: 18px !important;
130
+ }
131
+
132
+ .st-emotion-cache-4dubyl > div {
133
+
134
+ height: 46px !important;
135
+ }
136
+ .st-emotion-cache-4dubyl input::placeholder {
137
+ color: #0f1f1a !important;
138
+ opacity: 0.4 !important;
139
+ }
140
+ .st-emotion-cache-mpgwbc button {
141
+ background: #8ACAFF !important;
142
+ border: 1.5px solid #8ACAFF !important;
143
+ border-left: none !important;
144
+ border-radius: 0 50px 50px 0 !important;
145
+ color: #0f1f1a !important;
146
+ font-family: 'DM Sans', sans-serif !important;
147
+ font-size: 0.88rem !important;
148
+ font-weight: 600 !important;
149
+ height: 46px !important;
150
+ padding: 0 28px !important;
151
+ box-shadow: none !important;
152
+ }
153
+
154
+ .st-key-view_all_btn button {
155
+ background: transparent !important;
156
+ border: none !important;
157
+ color: #8ACAFF !important;
158
+ font-family: 'DM Sans', sans-serif !important;
159
+ font-size: 0.85rem !important;
160
+ font-weight: 600 !important;
161
+ padding: 0 !important;
162
+ height: auto !important;
163
+ box-shadow: none !important;
164
+ cursor: pointer !important;
165
+ }
166
+ .st-key-view_all_btn button:hover {
167
+ color: #8ACAFF !important;
168
+ text-decoration: underline !important;
169
+ background: transparent !important;
170
+ }
171
+ .footer-nav-head {
172
+ font-size: 0.68rem;
173
+ font-weight: 700;
174
+ letter-spacing: 1.4px;
175
+ text-transform: uppercase;
176
+ color: #0f1f1a;
177
+ margin-bottom: 14px;
178
+ font-family: 'DM Sans', sans-serif;
179
+ }
180
+
181
+ </style>
182
+ """, unsafe_allow_html=True)
183
+
184
+
185
+ # Helper
186
+ def go_categorized(material=None, search=None):
187
+ if material:
188
+ st.session_state.material_type = material
189
+ if search:
190
+ st.session_state.search_term = search
191
+ st.switch_page("page_files/Categorized_Search.py")
192
+
193
+
194
+ def go_upload():
195
+ st.switch_page("page_files/Upload_Data.py")
196
+
197
+
198
+ # ══════════════════════════════════════════════════════════════════════════════
199
+ # 1. ANIMATION SECTION (pure HTML, no clickables needed)
200
+ # ══════════════════════════════════════════════════════════════════════════════
201
+ about_img_html = (
202
+ f"<div class='aim-about-img'><img src='{home_img}' alt='AIM platform diagram'/></div>"
203
+ if home_img else
204
+ "<div class='aim-about-img-placeholder'>[ Platform diagram ]</div>"
205
+ )
206
+
207
+ logo_html = (
208
+ f"<img src='{logo_img}' alt='AIM Logo' style='height:52px;width:52px;object-fit:contain;border-radius:14px;'/>"
209
+ if logo_img else ""
210
+ )
211
+
212
+ components.html(f"""
213
+ <!DOCTYPE html><html lang="en"><head>
214
+ <meta charset="UTF-8"/>
215
+ <link rel="preconnect" href="https://fonts.googleapis.com">
216
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
217
+ <style>
218
+ *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}
219
+ body {{ font-family: 'DM Sans', sans-serif; background: #f7f7f5; color: #1a2e26; overflow-x: hidden; }}
220
+
221
+ /* ANIMATION SECTION */
222
+ .anim-section {{
223
+ background: #000;
224
+ padding: 80px 40px 60px;
225
+ display: flex; flex-direction: column; align-items: center;
226
+ }}
227
+ .anim-title {{
228
+ font-size: clamp(1.6rem, 3vw, 2.4rem); font-weight: 800;
229
+ color: #fff; text-align: center; letter-spacing: -1px; margin-bottom: 60px;
230
+ }}
231
+ .anim-stage {{
232
+ position: relative; width: 560px; height: 320px;
233
+ display: flex; align-items: center;
234
+ justify-content: space-between; padding: 0 40px;
235
+ }}
236
+
237
+ /* MAG GLASS */
238
+ .mag-wrap {{ position: relative; width: 100px; display: flex; flex-direction: column; align-items: center; }}
239
+ .mag-label {{ font-size: 0.65rem; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; color: #4a7a6a; margin-bottom: 10px; }}
240
+ .mag-svg {{ width: 90px; height: 160px; overflow: visible; }}
241
+ .mag-lens-flash {{ opacity: 0; transition: opacity 0.6s ease 0.5s; }}
242
+ .mag-wrap.active .mag-lens-flash {{ opacity: 1; animation: lensFlash 1.8s ease-in-out infinite 0.5s; }}
243
+ @keyframes lensFlash {{ 0%,100% {{ opacity:.2; r:26; }} 50% {{ opacity:.7; r:28; }} }}
244
+ .mag-scan {{ opacity: 0; transition: opacity 0.3s ease 0.8s; }}
245
+ .mag-wrap.active .mag-scan {{ opacity: 1; animation: scanMove 1.2s ease-in-out infinite 0.8s; }}
246
+ @keyframes scanMove {{ 0% {{ transform:translateY(-14px);opacity:.8; }} 50% {{ transform:translateY(0);opacity:1; }} 100% {{ transform:translateY(14px);opacity:.8; }} }}
247
+
248
+ /* BUBBLES */
249
+ .bubble {{ position:absolute; border-radius:50%; background:radial-gradient(circle at 35% 35%,rgba(255,255,255,.6),rgba(80,160,255,.25)); border:1px solid rgba(100,180,255,.4); opacity:0; pointer-events:none; backdrop-filter:blur(2px); }}
250
+ .b1 {{ width:18px;height:18px;left:108px;bottom:155px; }}
251
+ .b2 {{ width:13px;height:13px;left:120px;bottom:175px; }}
252
+ .b3 {{ width:22px;height:22px;left:96px; bottom:140px; }}
253
+ .b4 {{ width:10px;height:10px;left:128px;bottom:185px; }}
254
+ .b5 {{ width:16px;height:16px;left:112px;bottom:165px; }}
255
+ .b6 {{ width:8px; height:8px; left:124px;bottom:195px; }}
256
+ .anim-stage.active .b1 {{ animation:floatBubble1 2.6s cubic-bezier(.25,.46,.45,.94) 1.2s forwards; }}
257
+ .anim-stage.active .b2 {{ animation:floatBubble2 2.2s cubic-bezier(.25,.46,.45,.94) 1.5s forwards; }}
258
+ .anim-stage.active .b3 {{ animation:floatBubble3 3.0s cubic-bezier(.25,.46,.45,.94) 1.0s forwards; }}
259
+ .anim-stage.active .b4 {{ animation:floatBubble4 2.0s cubic-bezier(.25,.46,.45,.94) 1.8s forwards; }}
260
+ .anim-stage.active .b5 {{ animation:floatBubble5 2.8s cubic-bezier(.25,.46,.45,.94) 1.3s forwards; }}
261
+ .anim-stage.active .b6 {{ animation:floatBubble6 1.8s cubic-bezier(.25,.46,.45,.94) 2.0s forwards; }}
262
+ @keyframes floatBubble1 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.4); }} 15% {{ opacity:1;transform:translate(4px,-20px) scale(1); }} 60% {{ opacity:.9;transform:translate(120px,-55px) scale(.9); }} 85% {{ opacity:.5;transform:translate(280px,-30px) scale(.6); }} 100% {{ opacity:0;transform:translate(340px,-10px) scale(.2); }} }}
263
+ @keyframes floatBubble2 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.3); }} 15% {{ opacity:1;transform:translate(-5px,-28px) scale(1); }} 60% {{ opacity:.9;transform:translate(110px,-70px) scale(.85); }} 85% {{ opacity:.4;transform:translate(265px,-40px) scale(.5); }} 100% {{ opacity:0;transform:translate(330px,-15px) scale(.15); }} }}
264
+ @keyframes floatBubble3 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.5); }} 15% {{ opacity:1;transform:translate(6px,-15px) scale(1); }} 55% {{ opacity:.9;transform:translate(130px,-45px) scale(.95); }} 80% {{ opacity:.4;transform:translate(278px,-20px) scale(.55); }} 100% {{ opacity:0;transform:translate(345px,-5px) scale(.2); }} }}
265
+ @keyframes floatBubble4 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.3); }} 20% {{ opacity:1;transform:translate(-8px,-35px) scale(1); }} 65% {{ opacity:.8;transform:translate(105px,-80px) scale(.8); }} 88% {{ opacity:.3;transform:translate(255px,-50px) scale(.4); }} 100% {{ opacity:0;transform:translate(320px,-20px) scale(.1); }} }}
266
+ @keyframes floatBubble5 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.4); }} 15% {{ opacity:1;transform:translate(3px,-22px) scale(1); }} 58% {{ opacity:.9;transform:translate(115px,-60px) scale(.88); }} 83% {{ opacity:.45;transform:translate(270px,-35px) scale(.55); }} 100% {{ opacity:0;transform:translate(335px,-12px) scale(.2); }} }}
267
+ @keyframes floatBubble6 {{ 0% {{ opacity:0;transform:translate(0,0) scale(.25); }} 20% {{ opacity:1;transform:translate(-3px,-40px) scale(1); }} 62% {{ opacity:.7;transform:translate(100px,-85px) scale(.75); }} 85% {{ opacity:.25;transform:translate(250px,-55px) scale(.35); }} 100% {{ opacity:0;transform:translate(318px,-22px) scale(.1); }} }}
268
+
269
+ /* DATABASE */
270
+ .db-wrap {{ position:relative;width:130px;display:flex;flex-direction:column;align-items:center; }}
271
+ .db-label {{ font-size:.65rem;font-weight:600;letter-spacing:1.5px;text-transform:uppercase;color:#4a7a6a;margin-bottom:10px; }}
272
+ .cyl {{ opacity:.15;transition:opacity .5s ease; }}
273
+ .anim-stage.active .cyl-left {{ opacity:1;transition-delay:2.0s; }}
274
+ .anim-stage.active .cyl-right {{ opacity:1;transition-delay:2.2s; }}
275
+ .anim-stage.active .cyl-front {{ opacity:1;transition-delay:2.4s;animation:frontPulse 2.4s ease-in-out infinite 2.8s; }}
276
+ @keyframes frontPulse {{ 0%,100% {{ filter:brightness(1); }} 50% {{ filter:brightness(1.2); }} }}
277
+
278
+ /* CAPTION */
279
+ .anim-caption {{ margin-top:48px;display:flex;gap:40px;align-items:center; }}
280
+ .anim-step {{ display:flex;align-items:center;gap:10px;opacity:0;transform:translateY(10px);transition:opacity .5s ease,transform .5s ease; }}
281
+ .anim-stage.active ~ .anim-caption .anim-step:nth-child(1) {{ opacity:1;transform:none;transition-delay:.5s; }}
282
+ .anim-stage.active ~ .anim-caption .anim-step:nth-child(2) {{ opacity:1;transform:none;transition-delay:1s; }}
283
+ .anim-stage.active ~ .anim-caption .anim-step:nth-child(3) {{ opacity:1;transform:none;transition-delay:1.5s; }}
284
+ .step-text {{ font-size:.78rem;color:#5a8a7a;font-weight:500; }}
285
+ </style>
286
+ </head><body>
287
+
288
+ <section class="anim-section">
289
+ <h2 class="anim-title">From Experiment to Database</h2>
290
+ <div class="anim-stage" id="animStage">
291
+ <!-- MAG GLASS -->
292
+ <div class="mag-wrap" id="magWrap">
293
+ <div class="mag-label">Research</div>
294
+ <svg class="mag-svg" viewBox="0 0 90 160" fill="none" xmlns="http://www.w3.org/2000/svg">
295
+ <defs>
296
+ <radialGradient id="lensGrad" cx="40%" cy="38%" r="55%">
297
+ <stop offset="0%" stop-color="#4da6ff" stop-opacity="0.25"/>
298
+ <stop offset="100%" stop-color="#1a4a8a" stop-opacity="0.7"/>
299
+ </radialGradient>
300
+ <radialGradient id="flashGrad" cx="50%" cy="50%" r="50%">
301
+ <stop offset="0%" stop-color="#8ACAFF" stop-opacity="0.9"/>
302
+ <stop offset="100%" stop-color="#8ACAFF" stop-opacity="0"/>
303
+ </radialGradient>
304
+ </defs>
305
+ <circle cx="38" cy="38" r="32" fill="none" stroke="#2a5a8a" stroke-width="3"/>
306
+ <circle cx="38" cy="38" r="29" fill="url(#lensGrad)"/>
307
+ <line class="mag-scan" x1="16" y1="38" x2="60" y2="38" stroke="#8ACAFF" stroke-width="1.5" stroke-linecap="round" opacity="0.8"/>
308
+ <circle class="mag-lens-flash" cx="38" cy="38" r="26" fill="url(#flashGrad)"/>
309
+ <circle cx="24" cy="24" r="5" fill="white" opacity="0.15"/>
310
+ <circle cx="30" cy="34" r="2" fill="#8ACAFF" opacity="0.7"/>
311
+ <circle cx="42" cy="30" r="2" fill="#8ACAFF" opacity="0.5"/>
312
+ <circle cx="38" cy="44" r="2" fill="#8ACAFF" opacity="0.6"/>
313
+ <circle cx="48" cy="38" r="1.5" fill="#8ACAFF" opacity="0.4"/>
314
+ </svg>
315
+ </div>
316
+ <!-- BUBBLES -->
317
+ <div class="bubble b1"></div><div class="bubble b2"></div>
318
+ <div class="bubble b3"></div><div class="bubble b4"></div>
319
+ <div class="bubble b5"></div><div class="bubble b6"></div>
320
+ <!-- DATABASE -->
321
+ <div class="db-wrap">
322
+ <div class="db-label">Database</div>
323
+ <svg width="220" height="200" viewBox="0 0 220 200" fill="none" xmlns="http://www.w3.org/2000/svg">
324
+ <defs>
325
+ <linearGradient id="sideBlue" x1="0" y1="0" x2="1" y2="0">
326
+ <stop offset="0%" stop-color="#2a55b0"/><stop offset="50%" stop-color="#4a7fd8"/><stop offset="100%" stop-color="#2a55b0"/>
327
+ </linearGradient>
328
+ <linearGradient id="sideDark" x1="0" y1="0" x2="1" y2="0">
329
+ <stop offset="0%" stop-color="#0d1f5c"/><stop offset="50%" stop-color="#1a3a8a"/><stop offset="100%" stop-color="#0d1f5c"/>
330
+ </linearGradient>
331
+ <linearGradient id="topBlue" x1="0" y1="0" x2="0" y2="1">
332
+ <stop offset="0%" stop-color="#6a9fe0"/><stop offset="100%" stop-color="#3a6abf"/>
333
+ </linearGradient>
334
+ <linearGradient id="topDark" x1="0" y1="0" x2="0" y2="1">
335
+ <stop offset="0%" stop-color="#2a4a9a"/><stop offset="100%" stop-color="#0d1f5c"/>
336
+ </linearGradient>
337
+ </defs>
338
+ <!-- back left -->
339
+ <g class="cyl cyl-left">
340
+ <rect x="10" y="88" width="66" height="20" rx="1" fill="url(#sideBlue)"/>
341
+ <ellipse cx="43" cy="88" rx="33" ry="10" fill="url(#topBlue)"/>
342
+ <rect x="10" y="66" width="66" height="24" rx="1" fill="url(#sideBlue)"/>
343
+ <ellipse cx="43" cy="66" rx="33" ry="10" fill="url(#topBlue)"/>
344
+ <rect x="10" y="46" width="66" height="22" rx="1" fill="url(#sideBlue)"/>
345
+ <ellipse cx="43" cy="46" rx="33" ry="10" fill="url(#topBlue)"/>
346
+ <ellipse cx="43" cy="108" rx="33" ry="10" fill="#1e3a80"/>
347
+ </g>
348
+ <!-- back right -->
349
+ <g class="cyl cyl-right">
350
+ <rect x="144" y="88" width="66" height="20" rx="1" fill="url(#sideBlue)"/>
351
+ <ellipse cx="177" cy="88" rx="33" ry="10" fill="url(#topBlue)"/>
352
+ <rect x="144" y="66" width="66" height="24" rx="1" fill="url(#sideBlue)"/>
353
+ <ellipse cx="177" cy="66" rx="33" ry="10" fill="url(#topBlue)"/>
354
+ <rect x="144" y="46" width="66" height="22" rx="1" fill="url(#sideBlue)"/>
355
+ <ellipse cx="177" cy="46" rx="33" ry="10" fill="url(#topBlue)"/>
356
+ <ellipse cx="177" cy="108" rx="33" ry="10" fill="#1e3a80"/>
357
+ </g>
358
+ <!-- front center -->
359
+ <g class="cyl cyl-front">
360
+ <rect x="70" y="128" width="80" height="24" rx="1" fill="url(#sideDark)"/>
361
+ <ellipse cx="110" cy="128" rx="40" ry="13" fill="url(#topDark)"/>
362
+ <rect x="70" y="102" width="80" height="28" rx="1" fill="url(#sideDark)"/>
363
+ <ellipse cx="110" cy="102" rx="40" ry="13" fill="url(#topDark)"/>
364
+ <rect x="70" y="76" width="80" height="28" rx="1" fill="url(#sideDark)"/>
365
+ <ellipse cx="110" cy="76" rx="40" ry="13" fill="url(#topDark)"/>
366
+ <rect x="70" y="54" width="80" height="24" rx="1" fill="url(#sideDark)"/>
367
+ <ellipse cx="110" cy="54" rx="40" ry="13" fill="url(#topDark)"/>
368
+ <ellipse cx="110" cy="152" rx="40" ry="13" fill="#080f2a"/>
369
+ </g>
370
+ </svg>
371
+ </div>
372
+ </div>
373
+ <div class="anim-caption">
374
+ <div class="anim-step"><span class="step-text">Collect measurements</span></div>
375
+ <div class="anim-step"><span class="step-text">Process &amp; validate</span></div>
376
+ <div class="anim-step"><span class="step-text">Stored in AIM</span></div>
377
+ </div>
378
+ </section>
379
+
380
+ <script>
381
+ const stage = document.getElementById('animStage');
382
+ const magWrap = document.getElementById('magWrap');
383
+ const observer = new IntersectionObserver(entries => {{
384
+ entries.forEach(e => {{
385
+ if (e.isIntersecting) {{
386
+ setTimeout(() => {{ magWrap.classList.add('active'); stage.classList.add('active'); }}, 300);
387
+ }} else {{
388
+ magWrap.classList.remove('active'); stage.classList.remove('active');
389
+ }}
390
+ }});
391
+ }}, {{ threshold: 0.4 }});
392
+ observer.observe(document.querySelector('.anim-section'));
393
+ </script>
394
+ </body></html>
395
+ """, height=700, scrolling=False)
396
+
397
+ # ══════════════════════════════════════════════════════════════════════════════
398
+ # 2. HERO , heading + description (static HTML)
399
+ # ══════════════════════════════════════════════════════════════════════════════
400
+ st.markdown("""
401
+ <style>
402
+ .aim-hero-text {
403
+ background: #fff;
404
+ text-align: center;
405
+ padding: 64px 40px 24px;
406
+ border-bottom: none;
407
+ }
408
+ .aim-hero-text h1 {
409
+ font-family: 'DM Sans', sans-serif;
410
+ font-size: clamp(2rem, 4.5vw, 3.2rem);
411
+ font-weight: 800; color: #0f1f1a;
412
+ line-height: 1.1; letter-spacing: -1.5px;
413
+ margin-bottom: 18px;
414
+ }
415
+ .aim-hero-text p {
416
+ color: #5a6b65; font-size: 1rem; line-height: 1.65;
417
+ max-width: 500px; margin: 0 auto 28px;
418
+ }
419
+ </style>
420
+ <div class="aim-hero-text">
421
+ <h1>Accelerate Your Composites Research</h1>
422
+ <p>Access a centralized, open-source database for experimental composite material properties.
423
+ Polymer, fiber, and composite datasets , all in one place.</p>
424
+ </div>
425
+ """, unsafe_allow_html=True)
426
+
427
+
428
+
429
+ # ══════════════════════════════════════════════════════════════════════════════
430
+ # 4. STATS (static HTML)
431
+ # ══════════════════════════════════════════════════════════════════════════════
432
+
433
+
434
+ # ══════════════════════════════════════════════════════════════════════════════
435
+ # 5. MAJOR CATEGORIES , Streamlit columns + containers + buttons
436
+ # ══════════════════════════════════════════════════════════════════════════════
437
+ st.markdown("""
438
+ <style>
439
+ /* Card buttons - scoped to cards horizontal block only */
440
+ .st-emotion-cache-r3ry0f button {
441
+ background: #fff !important;
442
+ border: 1.5px solid #e4e8e5 !important;
443
+ border-radius: 12px !important;
444
+ padding: 24px 20px !important;
445
+ height: 160px !important;
446
+ width: 100% !important;
447
+ text-align: left !important;
448
+ white-space: pre-wrap !important;
449
+ line-height: 1.5 !important;
450
+ box-shadow: none !important;
451
+ transition: box-shadow 0.2s, border-color 0.2s !important;
452
+ }
453
+
454
+ .st-emotion-cache-r3ry0f button:hover {
455
+ border-color: #BAE1FC !important;
456
+ box-shadow: 0 0 0 3px rgba(186,225,252,0.25), 0 6px 22px rgba(0,0,0,0.07) !important;
457
+ background: #fff !important;
458
+ }
459
+ .st-emotion-cache-r3ry0f button p:first-child {
460
+ font-size: 1.1rem !important;
461
+ background: #f2f4f3 !important;
462
+ border-radius: 8px !important;
463
+ padding: 6px 8px !important;
464
+ display: inline-block !important;
465
+ margin-bottom: 12px !important;
466
+ line-height: 1 !important;
467
+ }
468
+ .st-emotion-cache-r3ry0f button p:nth-child(2) {
469
+ font-family: 'DM Sans', sans-serif !important;
470
+ font-size: 0.97rem !important;
471
+ font-weight: 700 !important;
472
+ color: #0f1f1a !important;
473
+ margin-bottom: 6px !important;
474
+ }
475
+ .st-emotion-cache-r3ry0f button p:last-child {
476
+ font-family: 'DM Sans', sans-serif !important;
477
+ font-size: 0.7rem !important;
478
+ font-weight: 700 !important;
479
+ letter-spacing: 1.2px !important;
480
+ color: #8ACAFF !important;
481
+ text-transform: uppercase !important;
482
+ }
483
+ .cards-section-wrap h2 {
484
+ font-family: 'DM Sans', sans-serif !important;
485
+ font-size: 1.4rem !important;
486
+ font-weight: 700 !important;
487
+ color: #0f1f1a !important;
488
+ margin-bottom: 6px !important;
489
+ }
490
+ .cards-section-wrap p {
491
+ color: #5a6b65 !important;
492
+ font-size: 0.9rem !important;
493
+ line-height: 1.6 !important;
494
+ margin-bottom: 16px !important;
495
+ }
496
+ </style>
497
+ """, unsafe_allow_html=True)
498
+
499
+
500
+
501
+ st.markdown("<div style='padding: 64px 0 0 0;'></div>", unsafe_allow_html=True)
502
+
503
+ st.markdown("""
504
+ <div class="cards-section-wrap">
505
+ <h2>Quick Links</h2>
506
+ <p>Open the pages you need most.</p>
507
+ </div>
508
+ """, unsafe_allow_html=True)
509
+
510
+ quick_cards = [
511
+ ("Extract Data", "Platform", "page_files/Upload_Data.py", "about", "πŸ“‹"),
512
+ ("Contact Team", "Support", "page_files/Contact_Team.py", "contact", " πŸ›‘οΈ"),
513
+ ("Search", "Data Page", "page_files/Categorized_Search.py", "search", "πŸ”Ž"),
514
+ ]
515
+
516
+ cols = st.columns(3, gap="large")
517
+ for col, (title, tag, target_page, key_suffix, badge) in zip(cols, quick_cards):
518
+ with col:
519
+ if st.button(
520
+ f"{badge}\n\n{title}\n\n{tag}",
521
+ key=f"quick_{key_suffix}_page",
522
+ use_container_width=True,
523
+ ):
524
+ st.switch_page(target_page)
525
+
526
+ st.markdown("<div style='background:#fff; padding: 24px 0; border-bottom: 1px solid #e8e8e4;'></div>", unsafe_allow_html=True)
527
+
528
+
529
+
530
+ st.markdown(f"""
531
+ <style>
532
+ .aim-stats {{
533
+ background: #f7f7f5; border-bottom: 1px solid #e4e8e5;
534
+ display: flex; justify-content: center;
535
+ }}
536
+ .aim-stat {{
537
+ text-align: center; padding: 34px 56px;
538
+ border-right: 1px solid #e4e8e5;
539
+ }}
540
+ .aim-stat:last-child {{ border-right: none; }}
541
+ .aim-stat-num {{
542
+ font-family: 'DM Sans', sans-serif;
543
+ font-size: 2.1rem; font-weight: 800;
544
+ color: #8ACAFF; line-height: 1; margin-bottom: 6px;
545
+ }}
546
+ .aim-stat-label {{
547
+ font-size: 0.68rem; font-weight: 600;
548
+ letter-spacing: 1.4px; color: #7a8e87; text-transform: uppercase;
549
+ }}
550
+ </style>
551
+ <div class="aim-stats">
552
+ <div class="aim-stat"><div class="aim-stat-num">3</div><div class="aim-stat-label">Material Classes</div></div>
553
+ <div class="aim-stat"><div class="aim-stat-num">{prop_count}</div><div class="aim-stat-label">Properties Tracked</div></div>
554
+ <div class="aim-stat"><div class="aim-stat-num">8</div><div class="aim-stat-label">Research Teams</div></div>
555
+ <div class="aim-stat"><div class="aim-stat-num">2</div><div class="aim-stat-label">Universities</div></div>
556
+ </div>
557
+ """, unsafe_allow_html=True)
558
+
559
+ # ══════════════════════════════════════════════════════════════════════════════
560
+ # 6. ABOUT + FOOTER (static HTML)
561
+ # ══════════════════════════════════════════════════════════════════════════════
562
+ components.html(f"""
563
+ <!DOCTYPE html><html lang="en"><head>
564
+ <meta charset="UTF-8"/>
565
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
566
+ <style>
567
+ *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}
568
+ body {{ font-family: 'DM Sans', sans-serif; color: #1a2e26; }}
569
+
570
+ .aim-about {{ background: #fff; padding: 60px; border-bottom: 1px solid #e8e8e4; }}
571
+ .aim-about h2 {{ font-size: 1.65rem; font-weight: 700; color: #0f1f1a; letter-spacing: -0.5px; margin-bottom: 8px; }}
572
+ .aim-about-sub {{ color: #6a7e77; font-size: 0.9rem; margin-bottom: 30px; }}
573
+ .aim-about-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }}
574
+ .aim-about-text p {{ color: #3a4e47; font-size: 0.91rem; line-height: 1.75; margin-bottom: 14px; }}
575
+ .aim-about-img {{ border-radius: 10px; overflow: hidden; border: 1px solid #e4e8e5; }}
576
+ .aim-about-img img {{ width: 100%; display: block; }}
577
+ .aim-about-img-placeholder {{
578
+ background: #f0f5f3; border-radius: 10px; height: 280px;
579
+ display: flex; align-items: center; justify-content: center;
580
+ color: #7a9e8f; font-size: 0.85rem; border: 1.5px dashed #c8d6d0;
581
+ }}
582
+
583
+ .aim-footer {{ background: #fff; border-top: 1px solid #e8e8e4; padding: 50px 60px 28px; }}
584
+ .aim-footer-brand {{ display: flex; align-items: center; gap: 8px; font-weight: 700; color: #0f1f1a; font-size: 0.95rem; margin-bottom: 10px; }}
585
+ .aim-footer-desc {{ font-size: 0.82rem; color: #7a8e87; line-height: 1.6; }}
586
+ </style>
587
+ </head><body>
588
+
589
+ <section class="aim-about">
590
+ <h2>About the Platform</h2>
591
+ <p class="aim-about-sub">Artificially Intelligent Manufacturing Paradigm (AIM) for Composites</p>
592
+ <div class="aim-about-grid">
593
+ <div class="aim-about-text">
594
+ <p>The AIM Database tool serves as a powerful, centralized hub designed to streamline
595
+ collaboration and information exchange within the composite materials research community.
596
+ The platform enables researchers to contribute to a shared knowledge base by uploading
597
+ experimental datasets through secure terminals.</p>
598
+ <p>Users can submit specific measurements regarding mechanical properties, thermal behavior,
599
+ and rheology, alongside their published journal papers , ensuring that both raw data and
600
+ peer-reviewed findings are integrated into one cohesive system.</p>
601
+ <p>All contributed information is securely aggregated within a central cloud architecture,
602
+ allowing for efficient storage, organization, and retrieval across polymer, fiber, and
603
+ composite categories.</p>
604
+ </div>
605
+ <div>{about_img_html}</div>
606
+ </div>
607
+ </section>
608
+
609
+
610
+
611
+ </body></html>
612
+ """, height=550, scrolling=False)
613
+
614
+ st.markdown("""
615
+ <style>
616
+
617
+ /* Footer nav wrapper */
618
+ div[data-testid="stHorizontalBlock"]:has(.footer-nav-head) {
619
+ background: #fff !important;
620
+ border-bottom: 1px solid #e8e8e4 !important;
621
+ padding: 0 60px 28px !important;
622
+ margin-top: -16px !important;
623
+
624
+ }
625
+ .aim-footer-brand {
626
+ display: flex;
627
+ align-items: center;
628
+ gap: 8px;
629
+ font-weight: 700;
630
+ color: #0f1f1a;
631
+ font-size: 0.95rem;
632
+ margin-bottom: 10px;
633
+ font-family: 'DM Sans', sans-serif;
634
+ }
635
+ .aim-footer-desc {
636
+ font-size: 0.82rem;
637
+ color: #7a8e87;
638
+ line-height: 1.6;
639
+ font-family: 'DM Sans', sans-serif;
640
+ }
641
+ .footer-nav-head {
642
+ font-size: 0.68rem;
643
+ font-weight: 700;
644
+ letter-spacing: 1.4px;
645
+ text-transform: uppercase;
646
+ color: #0f1f1a;
647
+ margin-bottom: 14px;
648
+ font-family: 'DM Sans', sans-serif;
649
+ }
650
+ .st-emotion-cache-1ofqig9 {
651
+ background: transparent !important;
652
+ border: none !important;
653
+ padding: 0 !important;
654
+ }
655
+ .st-emotion-cache-1qeq59m {
656
+ background: transparent !important;
657
+ border: none !important;
658
+ text-decoration: none !important;
659
+ padding: 0 0 9px 0 !important;
660
+ display: block !important;
661
+ }
662
+ .st-emotion-cache-1qeq59m:hover {
663
+ background: transparent !important;
664
+ border: none !important;
665
+ }
666
+ .st-emotion-cache-1qeq59m p {
667
+ color: #6a7e77 !important;
668
+ font-family: 'DM Sans', sans-serif !important;
669
+ font-size: 0.83rem !important;
670
+ font-weight: 400 !important;
671
+ margin: 0 !important;
672
+ }
673
+ .st-emotion-cache-1qeq59m:hover p {
674
+ color: #8ACAFF !important;
675
+ }
676
+
677
+ /* copyright bar */
678
+ .aim-footer-bottom {
679
+ background: #fff;
680
+ border-top: 1px solid #e8e8e4;
681
+ padding: 22px 60px;
682
+ font-size: 0.76rem;
683
+ color: #a0b0aa;
684
+ font-family: 'DM Sans', sans-serif;
685
+ }
686
+
687
+ </style>
688
+ """, unsafe_allow_html=True)
689
+
690
+ brand_col, db_col, sup_col, _ = st.columns([4, 2, 2, 2], vertical_alignment="top", width="stretch")
691
+
692
+ with brand_col:
693
+ st.markdown(f"""
694
+ <div class="aim-footer-brand">{logo_html} AIM Composites</div>
695
+ <p class="aim-footer-desc">Advancing composites research through open data and collaborative tools.
696
+ A joint initiative of Clemson University and University of Delaware.</p>
697
+ """, unsafe_allow_html=True)
698
+
699
+ with sup_col:
700
+ st.markdown('<p class="footer-nav-head">DATABASE</p>', unsafe_allow_html=True)
701
+ st.page_link(
702
+ "page_files/Categorized_Search.py",
703
+ label="Browse Materials",
704
+ )
705
+ st.page_link(
706
+ "page_files/Categorized_Search.py",
707
+ label="Categorized Search",
708
+ )
709
+ st.page_link(
710
+ "page_files/Upload_Data.py",
711
+ label="Upload Data",
712
+ )
713
+
714
+ with _:
715
+ st.markdown('<p class="footer-nav-head">SUPPORT</p>', unsafe_allow_html=True)
716
+ st.page_link(
717
+ "page_files/Contact_Team.py",
718
+ label="Contact Team",
719
+ )
720
+
721
+
722
+