Update modules/ui/ui.py
Browse files- modules/ui/ui.py +105 -44
modules/ui/ui.py
CHANGED
|
@@ -60,19 +60,19 @@ st.markdown("""
|
|
| 60 |
eventos = [
|
| 61 |
{
|
| 62 |
"imagen": "assets/img/socialmedia/WebSummit_ShowCase_2025.png",
|
| 63 |
-
"titulo": "WebSummitRio 2025, Rio,
|
| 64 |
"descripcion": "AIdeaText showcase"
|
| 65 |
},
|
| 66 |
|
| 67 |
{
|
| 68 |
"imagen": "assets/img/socialmedia/image_pycon_2024.png",
|
| 69 |
-
"titulo": "PyCon 2024, Medellin, Colombia,
|
| 70 |
"descripcion": "AIdeaText showcase"
|
| 71 |
},
|
| 72 |
|
| 73 |
{
|
| 74 |
"imagen": "assets/img/socialmedia/_MG_2845.JPG",
|
| 75 |
-
"titulo": "MakerFaire 2024, Mexico City, Mexico,
|
| 76 |
"descripcion": "AIdeaText showcase"
|
| 77 |
}
|
| 78 |
]
|
|
@@ -116,7 +116,7 @@ def show_carousel():
|
|
| 116 |
img = Image.open(event["imagen"]) if isinstance(event["imagen"], str) else event["imagen"]
|
| 117 |
st.image(
|
| 118 |
img,
|
| 119 |
-
use_container_width=True,
|
| 120 |
caption=f"{event['titulo']} - {event['descripcion']}"
|
| 121 |
)
|
| 122 |
|
|
@@ -169,6 +169,10 @@ def main():
|
|
| 169 |
st.error("Los modelos NLP no están inicializados. Por favor, reinicie la aplicación.")
|
| 170 |
return
|
| 171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
lang_code = st.session_state.get('lang_code', 'en')
|
| 173 |
t = get_translations(lang_code)
|
| 174 |
|
|
@@ -305,7 +309,7 @@ def login_register_page(lang_code, t):
|
|
| 305 |
st.markdown("""
|
| 306 |
<div class="start-container">
|
| 307 |
<h3 style='margin: 0;'>
|
| 308 |
-
🚀
|
| 309 |
<span class="language-inline">
|
| 310 |
""", unsafe_allow_html=True)
|
| 311 |
|
|
@@ -313,7 +317,7 @@ def login_register_page(lang_code, t):
|
|
| 313 |
selected_lang = st.selectbox(
|
| 314 |
"",
|
| 315 |
list(languages.keys()),
|
| 316 |
-
index=list(languages.keys()).index('English'), #
|
| 317 |
label_visibility="collapsed",
|
| 318 |
key="landing_language_selector"
|
| 319 |
)
|
|
@@ -375,31 +379,31 @@ def register_form(lang_code, landing_t):
|
|
| 375 |
|
| 376 |
# Verificar si acabamos de registrar exitosamente
|
| 377 |
if 'just_registered' in st.session_state and st.session_state.just_registered:
|
| 378 |
-
st.success("✅
|
| 379 |
-
st.info("
|
| 380 |
|
| 381 |
# Botón para limpiar y empezar de nuevo
|
| 382 |
-
if st.button("
|
| 383 |
del st.session_state.just_registered
|
| 384 |
st.rerun()
|
| 385 |
return
|
| 386 |
|
| 387 |
# Solo 3 campos requeridos
|
| 388 |
-
name = st.text_input("
|
| 389 |
-
email = st.text_input("
|
| 390 |
-
institution = st.text_input("
|
| 391 |
|
| 392 |
# Botón simplificado
|
| 393 |
if st.button(landing_t['submit_application'], type="primary"):
|
| 394 |
-
logger.info(f"
|
| 395 |
|
| 396 |
# Validaciones básicas
|
| 397 |
if not name or not email or not institution:
|
| 398 |
-
st.error("
|
| 399 |
return
|
| 400 |
|
| 401 |
if not is_institutional_email(email):
|
| 402 |
-
st.error("
|
| 403 |
return
|
| 404 |
|
| 405 |
# Enviar solicitud
|
|
@@ -408,18 +412,18 @@ def register_form(lang_code, landing_t):
|
|
| 408 |
lastname="",
|
| 409 |
email=email,
|
| 410 |
institution=institution,
|
| 411 |
-
current_role="
|
| 412 |
-
desired_role="
|
| 413 |
-
reason="
|
| 414 |
)
|
| 415 |
|
| 416 |
if success:
|
| 417 |
# Marcar que acabamos de registrar
|
| 418 |
st.session_state.just_registered = True
|
| 419 |
-
logger.info(f"
|
| 420 |
|
| 421 |
# Mostrar mensaje y recargar
|
| 422 |
-
st.success("✅
|
| 423 |
st.balloons()
|
| 424 |
|
| 425 |
# Recargar después de 2 segundos
|
|
@@ -427,8 +431,8 @@ def register_form(lang_code, landing_t):
|
|
| 427 |
time.sleep(2)
|
| 428 |
st.rerun()
|
| 429 |
else:
|
| 430 |
-
st.error("❌
|
| 431 |
-
logger.error(f"Error
|
| 432 |
|
| 433 |
|
| 434 |
#############################################################
|
|
@@ -491,6 +495,43 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 491 |
margin: 15px 0;
|
| 492 |
border-radius: 0 5px 5px 0;
|
| 493 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
.logo-grid {
|
| 495 |
display: flex;
|
| 496 |
align-items: center;
|
|
@@ -504,15 +545,38 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 504 |
""", unsafe_allow_html=True)
|
| 505 |
|
| 506 |
# ============================================
|
| 507 |
-
# TAB 1: 👥 ABOUT US
|
| 508 |
# ============================================
|
| 509 |
with tab_about:
|
| 510 |
# NO repetir "About Us" - el tab ya lo dice
|
| 511 |
about_texts = {
|
| 512 |
'en': """
|
| 513 |
<div class="content-text">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
<div class="highlight-box">
|
| 515 |
-
<strong>
|
|
|
|
| 516 |
Our solution has its core business in <strong>Semantic Reasoning Graphs (SRGs)</strong>, a technological
|
| 517 |
configuration that makes the critical thinking process visible, connecting educational
|
| 518 |
training with business needs by implementing a cognitive development measurement system
|
|
@@ -521,19 +585,16 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 521 |
|
| 522 |
<div class="section-title">Validation & Stage</div>
|
| 523 |
We have been validated by the <strong>NVIDIA Inception</strong> program for emerging companies and are in
|
| 524 |
-
an advanced development stage with a <a href="https://youtu.be/_4WMufl6MTA" target="_blank">functional MVP Demo</a
|
|
|
|
| 525 |
in Latin America.
|
| 526 |
|
| 527 |
-
<div class="section-title">Mission & Vision</div>
|
| 528 |
-
<em>The real transformation happens when we stop grading what students produce and start assessing how they think. <em>
|
| 529 |
-
<strong>Mission:</strong> To transform how the world measures and develops critical thinking.<br>
|
| 530 |
-
<strong>Vision:</strong> To be the global standard for cognitive development assessment.
|
| 531 |
-
|
| 532 |
<div class="section-title">Key Differentiators</div>
|
| 533 |
• <strong>First cognitive development measurement system</strong> based on NLP<br>
|
| 534 |
• <strong>Semantic Reasoning Graphs</strong> make thinking visible<br>
|
| 535 |
• <strong>Validated by NVIDIA</strong> Inception Program<br>
|
| 536 |
-
• <strong>Scalable model</strong> for Latin American education market
|
|
|
|
| 537 |
"""
|
| 538 |
}
|
| 539 |
|
|
@@ -704,11 +765,11 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 704 |
|
| 705 |
use_case_videos = {
|
| 706 |
"📊 Semantic Analysis Example in portuguese ": "https://youtu.be/_4WMufl6MTA",
|
| 707 |
-
"💻
|
| 708 |
}
|
| 709 |
|
| 710 |
selected_title = st.selectbox(
|
| 711 |
-
"Select demonstration:"
|
| 712 |
list(use_case_videos.keys())
|
| 713 |
)
|
| 714 |
|
|
@@ -722,7 +783,7 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 722 |
light=True
|
| 723 |
)
|
| 724 |
except Exception as e:
|
| 725 |
-
st.error(f"Error loading video: {str(e)}"
|
| 726 |
|
| 727 |
# Información adicional sobre casos de uso
|
| 728 |
st.markdown("""
|
|
@@ -762,16 +823,16 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 762 |
""", unsafe_allow_html=True)
|
| 763 |
|
| 764 |
videos = {
|
| 765 |
-
"🎬 Pitch - PotencIA MX
|
| 766 |
-
"🏆 Conference - SENDA UNAM
|
| 767 |
-
"🐍
|
| 768 |
-
"👨🏫 Conference - Ser
|
| 769 |
-
"🚀 Pitch - Explora - IFE - TEC de Monterrey
|
| 770 |
-
"🎙️ Interview with Dr. Guillermo Ruíz
|
| 771 |
}
|
| 772 |
|
| 773 |
selected_title = st.selectbox(
|
| 774 |
-
"Select video:"
|
| 775 |
list(videos.keys())
|
| 776 |
)
|
| 777 |
|
|
@@ -785,7 +846,7 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 785 |
light=True
|
| 786 |
)
|
| 787 |
except Exception as e:
|
| 788 |
-
st.error(f"Error loading video: {str(e)}"
|
| 789 |
|
| 790 |
# Próximos eventos
|
| 791 |
st.markdown("""
|
|
@@ -793,7 +854,7 @@ def display_videos_and_info(lang_code, landing_t):
|
|
| 793 |
<div class="section-title">Upcoming Events</div>
|
| 794 |
|
| 795 |
<strong>Confirmed next events for 2026</strong>
|
| 796 |
-
• <strong>Stay in touch
|
| 797 |
|
| 798 |
<em>Confirmed participation in showcases and panels.</em>
|
| 799 |
</div>
|
|
@@ -820,4 +881,4 @@ __all__ = ['main', 'login_register_page', 'initialize_session_state']
|
|
| 820 |
|
| 821 |
# Bloque de ejecución condicional
|
| 822 |
if __name__ == "__main__":
|
| 823 |
-
main()
|
|
|
|
| 60 |
eventos = [
|
| 61 |
{
|
| 62 |
"imagen": "assets/img/socialmedia/WebSummit_ShowCase_2025.png",
|
| 63 |
+
"titulo": "WebSummitRio 2025, Rio, Brazil, April 27-30",
|
| 64 |
"descripcion": "AIdeaText showcase"
|
| 65 |
},
|
| 66 |
|
| 67 |
{
|
| 68 |
"imagen": "assets/img/socialmedia/image_pycon_2024.png",
|
| 69 |
+
"titulo": "PyCon 2024, Medellin, Colombia, June 7-9",
|
| 70 |
"descripcion": "AIdeaText showcase"
|
| 71 |
},
|
| 72 |
|
| 73 |
{
|
| 74 |
"imagen": "assets/img/socialmedia/_MG_2845.JPG",
|
| 75 |
+
"titulo": "MakerFaire 2024, Mexico City, Mexico, October 12-13",
|
| 76 |
"descripcion": "AIdeaText showcase"
|
| 77 |
}
|
| 78 |
]
|
|
|
|
| 116 |
img = Image.open(event["imagen"]) if isinstance(event["imagen"], str) else event["imagen"]
|
| 117 |
st.image(
|
| 118 |
img,
|
| 119 |
+
use_container_width=True,
|
| 120 |
caption=f"{event['titulo']} - {event['descripcion']}"
|
| 121 |
)
|
| 122 |
|
|
|
|
| 169 |
st.error("Los modelos NLP no están inicializados. Por favor, reinicie la aplicación.")
|
| 170 |
return
|
| 171 |
|
| 172 |
+
# [1] CONFIGURAR INGLÉS COMO IDIOMA POR DEFECTO
|
| 173 |
+
if 'lang_code' not in st.session_state:
|
| 174 |
+
st.session_state.lang_code = 'en' # Inglés por defecto
|
| 175 |
+
|
| 176 |
lang_code = st.session_state.get('lang_code', 'en')
|
| 177 |
t = get_translations(lang_code)
|
| 178 |
|
|
|
|
| 309 |
st.markdown("""
|
| 310 |
<div class="start-container">
|
| 311 |
<h3 style='margin: 0;'>
|
| 312 |
+
🚀 Start in
|
| 313 |
<span class="language-inline">
|
| 314 |
""", unsafe_allow_html=True)
|
| 315 |
|
|
|
|
| 317 |
selected_lang = st.selectbox(
|
| 318 |
"",
|
| 319 |
list(languages.keys()),
|
| 320 |
+
index=list(languages.keys()).index('English'), # Inglés seleccionado por defecto
|
| 321 |
label_visibility="collapsed",
|
| 322 |
key="landing_language_selector"
|
| 323 |
)
|
|
|
|
| 379 |
|
| 380 |
# Verificar si acabamos de registrar exitosamente
|
| 381 |
if 'just_registered' in st.session_state and st.session_state.just_registered:
|
| 382 |
+
st.success("✅ Registration successful! Your request has been sent.")
|
| 383 |
+
st.info("Please check your email for further instructions.")
|
| 384 |
|
| 385 |
# Botón para limpiar y empezar de nuevo
|
| 386 |
+
if st.button("Register another user"):
|
| 387 |
del st.session_state.just_registered
|
| 388 |
st.rerun()
|
| 389 |
return
|
| 390 |
|
| 391 |
# Solo 3 campos requeridos
|
| 392 |
+
name = st.text_input("Full name *", key="reg_name")
|
| 393 |
+
email = st.text_input("Institutional email *", key="reg_email")
|
| 394 |
+
institution = st.text_input("Educational institution *", key="reg_institution")
|
| 395 |
|
| 396 |
# Botón simplificado
|
| 397 |
if st.button(landing_t['submit_application'], type="primary"):
|
| 398 |
+
logger.info(f"Trying to send request for {email}")
|
| 399 |
|
| 400 |
# Validaciones básicas
|
| 401 |
if not name or not email or not institution:
|
| 402 |
+
st.error("Please complete all required fields (*)")
|
| 403 |
return
|
| 404 |
|
| 405 |
if not is_institutional_email(email):
|
| 406 |
+
st.error("Please use an institutional email (not gmail, hotmail, etc.)")
|
| 407 |
return
|
| 408 |
|
| 409 |
# Enviar solicitud
|
|
|
|
| 412 |
lastname="",
|
| 413 |
email=email,
|
| 414 |
institution=institution,
|
| 415 |
+
current_role="Student",
|
| 416 |
+
desired_role="Student",
|
| 417 |
+
reason="New registration"
|
| 418 |
)
|
| 419 |
|
| 420 |
if success:
|
| 421 |
# Marcar que acabamos de registrar
|
| 422 |
st.session_state.just_registered = True
|
| 423 |
+
logger.info(f"Request successfully stored for {email}")
|
| 424 |
|
| 425 |
# Mostrar mensaje y recargar
|
| 426 |
+
st.success("✅ Your request has been sent! You will receive an email with instructions.")
|
| 427 |
st.balloons()
|
| 428 |
|
| 429 |
# Recargar después de 2 segundos
|
|
|
|
| 431 |
time.sleep(2)
|
| 432 |
st.rerun()
|
| 433 |
else:
|
| 434 |
+
st.error("❌ There was an error sending your request. Please try again.")
|
| 435 |
+
logger.error(f"Error storing request for {email}")
|
| 436 |
|
| 437 |
|
| 438 |
#############################################################
|
|
|
|
| 495 |
margin: 15px 0;
|
| 496 |
border-radius: 0 5px 5px 0;
|
| 497 |
}
|
| 498 |
+
.quote-box {
|
| 499 |
+
background-color: #F0F9FF;
|
| 500 |
+
border-left: 4px solid #0EA5E9;
|
| 501 |
+
padding: 20px;
|
| 502 |
+
margin: 20px 0;
|
| 503 |
+
border-radius: 0 8px 8px 0;
|
| 504 |
+
font-style: italic;
|
| 505 |
+
font-size: 1.1em;
|
| 506 |
+
color: #0369A1;
|
| 507 |
+
}
|
| 508 |
+
.principle-box {
|
| 509 |
+
background-color: #FEF3C7;
|
| 510 |
+
border-left: 4px solid #F59E0B;
|
| 511 |
+
padding: 20px;
|
| 512 |
+
margin: 20px 0;
|
| 513 |
+
border-radius: 0 8px 8px 0;
|
| 514 |
+
font-weight: 600;
|
| 515 |
+
}
|
| 516 |
+
.mission-vision-container {
|
| 517 |
+
display: flex;
|
| 518 |
+
gap: 20px;
|
| 519 |
+
margin: 25px 0;
|
| 520 |
+
}
|
| 521 |
+
.mission-box, .vision-box {
|
| 522 |
+
flex: 1;
|
| 523 |
+
padding: 20px;
|
| 524 |
+
border-radius: 8px;
|
| 525 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
| 526 |
+
}
|
| 527 |
+
.mission-box {
|
| 528 |
+
background-color: #F0FDF4;
|
| 529 |
+
border-top: 4px solid #10B981;
|
| 530 |
+
}
|
| 531 |
+
.vision-box {
|
| 532 |
+
background-color: #F5F3FF;
|
| 533 |
+
border-top: 4px solid #8B5CF6;
|
| 534 |
+
}
|
| 535 |
.logo-grid {
|
| 536 |
display: flex;
|
| 537 |
align-items: center;
|
|
|
|
| 545 |
""", unsafe_allow_html=True)
|
| 546 |
|
| 547 |
# ============================================
|
| 548 |
+
# TAB 1: 👥 ABOUT US - REORGANIZADO
|
| 549 |
# ============================================
|
| 550 |
with tab_about:
|
| 551 |
# NO repetir "About Us" - el tab ya lo dice
|
| 552 |
about_texts = {
|
| 553 |
'en': """
|
| 554 |
<div class="content-text">
|
| 555 |
+
|
| 556 |
+
<div class="principle-box">
|
| 557 |
+
AIdeaText has this fundamental principle:<br>
|
| 558 |
+
<strong>"The real transformation happens when we stop grading what students produce and start assessing how they think."</strong>
|
| 559 |
+
</div>
|
| 560 |
+
|
| 561 |
+
<div class="mission-vision-container">
|
| 562 |
+
<div class="mission-box">
|
| 563 |
+
<h4 style='color: #10B981; margin-top: 0;'>🎯 Our Mission</h4>
|
| 564 |
+
<p><strong>To transform how the world measures and develops critical thinking.</strong></p>
|
| 565 |
+
<p>We bridge the gap between educational training and business needs by implementing
|
| 566 |
+
cognitive development measurement systems based on advanced natural language processing.</p>
|
| 567 |
+
</div>
|
| 568 |
+
|
| 569 |
+
<div class="vision-box">
|
| 570 |
+
<h4 style='color: #8B5CF6; margin-top: 0;'>🌍 Our Vision</h4>
|
| 571 |
+
<p><strong>To be the global standard for cognitive development assessment.</strong></p>
|
| 572 |
+
<p>Establishing Semantic Reasoning Graphs as the benchmark for evaluating and enhancing
|
| 573 |
+
critical thinking skills worldwide.</p>
|
| 574 |
+
</div>
|
| 575 |
+
</div>
|
| 576 |
+
|
| 577 |
<div class="highlight-box">
|
| 578 |
+
<strong>Who We Are</strong><br>
|
| 579 |
+
AIdeaText is a digital technology company for human cognitive development based in Mexico.
|
| 580 |
Our solution has its core business in <strong>Semantic Reasoning Graphs (SRGs)</strong>, a technological
|
| 581 |
configuration that makes the critical thinking process visible, connecting educational
|
| 582 |
training with business needs by implementing a cognitive development measurement system
|
|
|
|
| 585 |
|
| 586 |
<div class="section-title">Validation & Stage</div>
|
| 587 |
We have been validated by the <strong>NVIDIA Inception</strong> program for emerging companies and are in
|
| 588 |
+
an advanced development stage with a <a href="https://youtu.be/_4WMufl6MTA" target="_blank">functional MVP Demo</a>
|
| 589 |
+
and a scalable <a href="https://youtu.be/Nt7IEas_P54" target="_blank">business model</a>
|
| 590 |
in Latin America.
|
| 591 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 592 |
<div class="section-title">Key Differentiators</div>
|
| 593 |
• <strong>First cognitive development measurement system</strong> based on NLP<br>
|
| 594 |
• <strong>Semantic Reasoning Graphs</strong> make thinking visible<br>
|
| 595 |
• <strong>Validated by NVIDIA</strong> Inception Program<br>
|
| 596 |
+
• <strong>Scalable model</strong> for Latin American education market<br>
|
| 597 |
+
• <strong>Proven technology</strong> with functional MVP
|
| 598 |
"""
|
| 599 |
}
|
| 600 |
|
|
|
|
| 765 |
|
| 766 |
use_case_videos = {
|
| 767 |
"📊 Semantic Analysis Example in portuguese ": "https://youtu.be/_4WMufl6MTA",
|
| 768 |
+
"💻 First Demo - Standalone version ": "https://www.youtube.com/watch?v=nP6eXbog-ZY"
|
| 769 |
}
|
| 770 |
|
| 771 |
selected_title = st.selectbox(
|
| 772 |
+
"Select demonstration:",
|
| 773 |
list(use_case_videos.keys())
|
| 774 |
)
|
| 775 |
|
|
|
|
| 783 |
light=True
|
| 784 |
)
|
| 785 |
except Exception as e:
|
| 786 |
+
st.error(f"Error loading video: {str(e)}")
|
| 787 |
|
| 788 |
# Información adicional sobre casos de uso
|
| 789 |
st.markdown("""
|
|
|
|
| 823 |
""", unsafe_allow_html=True)
|
| 824 |
|
| 825 |
videos = {
|
| 826 |
+
"🎬 Pitch - PotencIA MX": "https://youtu.be/Nt7IEas_P54",
|
| 827 |
+
"🏆 Conference - SENDA UNAM": "https://www.youtube.com/watch?v=XFLvjST2cE0",
|
| 828 |
+
"🐍 Conference - PyCon 2024": "https://www.youtube.com/watch?v=Jn545-IKx5Q",
|
| 829 |
+
"👨🏫 Conference - Ser Maestro Foundation": "https://www.youtube.com/watch?v=imc4TI1q164",
|
| 830 |
+
"🚀 Pitch - Explora - IFE - TEC de Monterrey": "https://www.youtube.com/watch?v=Fqi4Di_Rj_s",
|
| 831 |
+
"🎙️ Interview with Dr. Guillermo Ruíz": "https://www.youtube.com/watch?v=_ch8cRja3oc"
|
| 832 |
}
|
| 833 |
|
| 834 |
selected_title = st.selectbox(
|
| 835 |
+
"Select video:",
|
| 836 |
list(videos.keys())
|
| 837 |
)
|
| 838 |
|
|
|
|
| 846 |
light=True
|
| 847 |
)
|
| 848 |
except Exception as e:
|
| 849 |
+
st.error(f"Error loading video: {str(e)}")
|
| 850 |
|
| 851 |
# Próximos eventos
|
| 852 |
st.markdown("""
|
|
|
|
| 854 |
<div class="section-title">Upcoming Events</div>
|
| 855 |
|
| 856 |
<strong>Confirmed next events for 2026</strong>
|
| 857 |
+
• <strong>Stay in touch</strong><br>
|
| 858 |
|
| 859 |
<em>Confirmed participation in showcases and panels.</em>
|
| 860 |
</div>
|
|
|
|
| 881 |
|
| 882 |
# Bloque de ejecución condicional
|
| 883 |
if __name__ == "__main__":
|
| 884 |
+
main()
|