ecosistema finish
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
Binary file not shown.
+120
-1
@@ -23,6 +23,7 @@
|
||||
--ide-digital: #2c69ab;
|
||||
--teal-dark: #1B5E6E;
|
||||
--cta-hover:#40ab8e;
|
||||
--bg-subtle: #ecf9fd;
|
||||
|
||||
/* Tema Claro (Por defecto) */
|
||||
--bg-body: #F8FAFB;
|
||||
@@ -46,6 +47,9 @@
|
||||
--shadow-sm: 0 1px 3px rgba(10, 72, 94, 0.05);
|
||||
--shadow-md: 0 4px 16px rgba(10, 72, 94, 0.08);
|
||||
--shadow-lg: 0 10px 30px rgba(10, 72, 94, 0.12);
|
||||
--shadow-canva: rgba(10, 72, 94, 0.05);
|
||||
|
||||
--ide-primary-line: rgba(9,75,99, 0.4);
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 12px;
|
||||
@@ -63,6 +67,7 @@
|
||||
--bg-surface: #18222A;
|
||||
--bg-surface-elevated: #202D37;
|
||||
--bg-subtle: #24333E;
|
||||
--ide-primary-dark: #1D4672;
|
||||
--bg-card: #18222A;
|
||||
--ripple-color:#253441;
|
||||
--bg-card-alt: var(--bg-muted);
|
||||
@@ -79,6 +84,7 @@
|
||||
--ide-digital: #2c69ab;
|
||||
--teal-dark: #40ab8e;
|
||||
--cta-hover:#0d5a91;
|
||||
--bg-subtle: #03151c;;
|
||||
|
||||
|
||||
--text-primary:#4fb389;
|
||||
@@ -87,9 +93,12 @@
|
||||
--text-muted: #8E9FAA;
|
||||
--text-inverse: #0E2833;
|
||||
|
||||
--ide-primary-line: rgba(79,179,137, 0.4);
|
||||
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 20px rgba(0, 0, 0, 0.4);
|
||||
--shadow-lg: 0 10px 35px rgba(0, 0, 0, 0.55);
|
||||
--shadow-canva: rgba(92, 103, 107, 0.432);
|
||||
}
|
||||
|
||||
/* --- 2. Reseteo y Base --- */
|
||||
@@ -189,7 +198,9 @@ p, li, .lead-text, .text-body, .text-muted {
|
||||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||||
}
|
||||
|
||||
|
||||
.bg-subtle-section{
|
||||
background-color: var(--bg-subtle);
|
||||
}
|
||||
.lead-text {
|
||||
font-size: 1.15rem;
|
||||
color: var(--text-muted);
|
||||
@@ -580,6 +591,20 @@ p, li, .lead-text, .text-body, .text-muted {
|
||||
color: var(--ide-accent);
|
||||
background-color: var(--ide-accent-soft);
|
||||
}
|
||||
.btn-primary-cta {
|
||||
background-color: var(--ide-primary-dark);
|
||||
color: #FFFFFF;
|
||||
border-color: var(--ide-primary);
|
||||
}
|
||||
|
||||
.btn-primary-cta:hover {
|
||||
background-color: var(--ide-primary-dark);
|
||||
border-color: var(--ide-primary-dark);
|
||||
color: #FFFFFF;
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-outline-cta {
|
||||
background-color: transparent;
|
||||
color: var(--text-inverse);
|
||||
@@ -1437,7 +1462,101 @@ p, li, .lead-text, .text-body, .text-muted {
|
||||
border-color: var(--ide-primary);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
/* --- Tarjetas de Ecosistema --- */
|
||||
|
||||
.card-excellence:hover {
|
||||
border-color: var(--ide-excellence);
|
||||
box-shadow: 0 0 30px rgba(79,179,137, 0.5);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.card-projects:hover {
|
||||
border-color: var(--ide-projects);
|
||||
box-shadow: 0 0 30px rgba(237,141,1, 0.5);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.card-academy:hover {
|
||||
border-color: var(--ide-academy);
|
||||
box-shadow: 0 0 30px rgba(168,60,65, 0.5);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.card-digital:hover {
|
||||
border-color: var(--ide-digital);
|
||||
box-shadow: 0 0 30px rgba(44,105,171, 0.5);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
/* --- Formato NetSchema --- */
|
||||
.netschema-section {
|
||||
width: 100%;
|
||||
padding: 40px 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--bg-subtle);
|
||||
}
|
||||
|
||||
/* Envoltura con aspecto horizontal responsivo */
|
||||
.netschema-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 1240px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.netschema-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
/* El canvas se escala proporcionalmente según el ancho del contenedor */
|
||||
canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 1240 / 520;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
LEYENDA / TOOLTIP FLOTANTE
|
||||
========================================================================== */
|
||||
.network-tooltip {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
background: rgba(22, 24, 38, 0.94);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
color: #ffffff;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
|
||||
transform: translate(-50%, -125%);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
z-index: 20;
|
||||
max-width: 240px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.network-tooltip.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translate(-50%, -115%);
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
margin-bottom: 3px;
|
||||
color: #ffffff;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.tooltip-desc {
|
||||
font-size: 12px;
|
||||
color: #cfd4dc;
|
||||
line-height: 1.35;
|
||||
}
|
||||
/* --- 17. Footer --- */
|
||||
#site-footer {
|
||||
position: relative;
|
||||
|
||||
+3
-3
@@ -49,9 +49,9 @@
|
||||
|
||||
<!-- Contenido -->
|
||||
<div class="container container-narrow text-center" style="position: relative; z-index: 2;">
|
||||
<span class="eyebrow" style="color: var(--text-inverse)">PUNTO DE PARTIDA</span>
|
||||
<h1 style="margin-bottom: 1.25rem; color:var(--text-inverse);">¿Qué está pasando en tu organización?</h1>
|
||||
<p class="lead-text" style="color:var(--text-inverse)">
|
||||
<span class="eyebrow" style="color: var(--text-main)">PUNTO DE PARTIDA</span>
|
||||
<h1 style="margin-bottom: 1.25rem; color:var(--text-main);">¿Qué está pasando en tu organización?</h1>
|
||||
<p class="lead-text" style="color:var(--text-main)">
|
||||
No necesitás tener la solución definida para empezar a conversar. Si reconocés alguno de estos síntomas o metas en tu contexto, hacé clic para explorar cómo podemos abordarlo.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
+75
-65
@@ -7,7 +7,8 @@
|
||||
<meta name="description" content="Conocé cómo se articulan IDE Excellence, IDE Project, IDE Academy e IDE Digital para generar valor integral y sostenible en las organizaciones.">
|
||||
<meta property="og:title" content="El Ecosistema IDE | Cuatro Capacidades Conectadas">
|
||||
<meta property="og:description" content="No se trata de sumar soluciones. Se trata de conectar las capacidades necesarias para una transformación real.">
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -17,66 +18,85 @@
|
||||
<main>
|
||||
|
||||
<!-- Header Ecosistema -->
|
||||
<section class="section-padding" style="background-color: var(--bg-surface); border-bottom: 1px solid var(--border-color);">
|
||||
<section class="section-padding hero-video-section" style="background-color: var(--bg-surface); border-bottom: 1px solid var(--border-color); position: relative; overflow: hidden; min-height: 400px;">
|
||||
|
||||
<!-- Video de fondo -->
|
||||
<video autoplay muted loop playsinline style="
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
opacity: 0.15;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
">
|
||||
<source src="/assets/video/Video header Ide3.mp4" type="video/mp4">
|
||||
Tu navegador no soporta videos.
|
||||
</video>
|
||||
|
||||
<!-- Overlay oscuro opcional para mejor contraste -->
|
||||
<div style="
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(19, 167, 138, 0.5);
|
||||
z-index: 1;
|
||||
"></div>
|
||||
|
||||
<!-- Contenido -->
|
||||
<div class="container container-narrow text-center" style="position: relative; z-index: 2;">
|
||||
<div class="container container-narrow text-center">
|
||||
<span class="eyebrow">LA PROPUESTA DE VALOR</span>
|
||||
<h1 style="margin-bottom: 1.25rem;">Una mirada. <br> Múltiples capacidades. <br> Un mismo propósito.</h1>
|
||||
<p class="lead-text" style="margin: 0 auto 2rem auto;">
|
||||
<span class="eyebrow" style="color: var(--text-main);">LA PROPUESTA DE VALOR</span>
|
||||
<h1 style="margin-bottom: 1.25rem;color:var(--text-main);">Una mirada. <br> Múltiples capacidades. <br> Un mismo propósito.</h1>
|
||||
<p class="lead-text" style="margin: 0 auto 2rem auto;color:var(--text-main);">
|
||||
No se trata de sumar soluciones aisladas. Se trata de conectar las capacidades necesarias para generar una transformación real que perdure en el conjunto de la organización.
|
||||
</p>
|
||||
|
||||
<!-- Principio Destacado en Caja -->
|
||||
<div style="background-color: var(--ide-accent-soft); border-left: 4px solid var(--ide-accent); padding: 1.25rem 1.5rem; text-align: left; border-radius: var(--radius-sm); max-width: 760px; margin: 0 auto;">
|
||||
<div style="background-color: var(--ide-accent); border-left: 4px solid var(--text-main); padding: 1.25rem 1.5rem; text-align: left; border-radius: var(--radius-sm); max-width: 760px; margin: 0 auto;">
|
||||
<p style="font-size: 0.98rem; color: var(--text-main); font-weight: 600; margin: 0;">
|
||||
<em>“No todos los desafíos necesitan todo el Ecosistema. Pero todos pueden beneficiarse de una mirada capaz de comprender el conjunto.”</em>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Gráfico Visual de Conectividad del Ecosistema -->
|
||||
<section class="section-padding-sm bg-subtle-section">
|
||||
<div class="container text-center">
|
||||
<div style="max-width: 860px; margin: 0 auto; background-color: var(--bg-surface); border: 1px solid var(--border-color); border-radius: var(--radius-lg); padding: 2.5rem 1.5rem;">
|
||||
<!-- SECCIÓN WEB DEDICADA PARA EL ESQUEMA -->
|
||||
<section class="netschema-section">
|
||||
<div class="netschema-wrapper">
|
||||
<div style="text-align: center; margin-bottom: 3rem;">
|
||||
<span class="eyebrow">ARQUITECTURA DE INTEGRACIÓN</span>
|
||||
<h2 style="font-size: 1.8rem; margin-bottom: 1.5rem;">El Modelo de Red Sistémica</h2>
|
||||
<h2>El Modelo de Red Sistémica</h2>
|
||||
</div>
|
||||
<!-- Canvas de alta resolución -->
|
||||
<canvas id="networkCanvas" width="1200" height="520"></canvas>
|
||||
|
||||
<div class="grid-4" style="margin-top: 2rem; text-align: center;">
|
||||
<div style="padding: 1rem; border: 1px dashed var(--border-color); border-radius: var(--radius-md);">
|
||||
<!-- <div style="font-size: 1.75rem; color: var(--ide-primary); margin-bottom: 0.25rem;">📊</div> -->
|
||||
<strong style="color: var(--ide-primary);">EXCELLENCE</strong>
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;">Procesos & Calidad</p>
|
||||
</div>
|
||||
<div style="padding: 1rem; border: 1px dashed var(--border-color); border-radius: var(--radius-md);">
|
||||
<!-- <div style="font-size: 1.75rem; color: var(--ide-accent); margin-bottom: 0.25rem;">🎯</div> -->
|
||||
<strong style="color: var(--ide-accent);">PROJECT</strong>
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;">Dirección & PMO</p>
|
||||
</div>
|
||||
<div style="padding: 1rem; border: 1px dashed var(--border-color); border-radius: var(--radius-md);">
|
||||
<!-- <div style="font-size: 1.75rem; color: #D97706; margin-bottom: 0.25rem;">🎓</div> -->
|
||||
<strong style="color: #ed8d01;">ACADEMY</strong>
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;">Capacidades & Personas</p>
|
||||
</div>
|
||||
<div style="padding: 1rem; border: 1px dashed var(--border-color); border-radius: var(--radius-md);">
|
||||
<!-- <div style="font-size: 1.75rem; color: #6366F1; margin-bottom: 0.25rem;">⚡</div> -->
|
||||
<strong style="color: #2c69ab;">DIGITAL</strong>
|
||||
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem;">IA & Automatización</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Tooltip / Leyenda emergente en hover -->
|
||||
<div id="tooltip" class="network-tooltip hidden">
|
||||
<div id="tooltip-title" class="tooltip-title"></div>
|
||||
<div id="tooltip-desc" class="tooltip-desc"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Profundización de las 4 Capacidades -->
|
||||
<section class="section-padding">
|
||||
<div class="container">
|
||||
|
||||
<div style="text-align: center; margin-bottom: 3rem;">
|
||||
<span class="eyebrow">MÓDULOS</span>
|
||||
<h2>Conocé el detalle de Nuestro Ecosistema</h2>
|
||||
</div>
|
||||
<!-- 1. IDE Excellence -->
|
||||
<div id="excellence" class="card" style="margin-bottom: 2.5rem; padding: 2.5rem;">
|
||||
<div id="excellence" class="card card-excellence" style="margin-bottom: 2.5rem; padding: 2.5rem;">
|
||||
<div class="grid-2" style="gap: 2.5rem; align-items: center;">
|
||||
<div>
|
||||
<span class="ecosystem-badge badge-excellence">GESTIÓN Y PROCESOS</span>
|
||||
<h2 style="color: var(--ide-primary); font-size: 2rem; margin-bottom: 1rem;">IDE Excellence</h2>
|
||||
<p style="font-size: 1.05rem; color: var(--text-body); line-height: 1.65; margin-bottom: 1rem;">
|
||||
<strong>Para fortalecer la gestión:</strong> Diseñamos y optimizamos la arquitectura de procesos organizacionales, sistemas de gestión de calidad y cumplimiento normativo internacional.
|
||||
</p>
|
||||
@@ -88,26 +108,19 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<div class="img-placeholder" style="aspect-ratio: 4/3;">
|
||||
<!-- <div class="img-placeholder-icon">📋</div> -->
|
||||
<div class="img-placeholder-label">Espacio Gráfico IDE Excellence</div>
|
||||
</div>
|
||||
<img src="assets/img/excellence.png" alt="IDE Excellence" style="width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-md);">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. IDE Project -->
|
||||
<div id="project" class="card" style="margin-bottom: 2.5rem; padding: 2.5rem;">
|
||||
<div id="project" class="card card-projects" style="margin-bottom: 2.5rem; padding: 2.5rem;">
|
||||
<div class="grid-2" style="gap: 2.5rem; align-items: center;">
|
||||
<div>
|
||||
<div class="img-placeholder" style="aspect-ratio: 4/3;">
|
||||
<!-- <div class="img-placeholder-icon">🎯</div> -->
|
||||
<div class="img-placeholder-label">Espacio Gráfico IDE Project</div>
|
||||
</div>
|
||||
<img src="assets/img/projects.png" alt="IDE Project" style="width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-md);">
|
||||
</div>
|
||||
<div>
|
||||
<span class="ecosystem-badge badge-project">DIRECCIÓN Y RESULTADOS</span>
|
||||
<h2 style="color: var(--ide-accent); font-size: 2rem; margin-bottom: 1rem;">IDE Project</h2>
|
||||
<p style="font-size: 1.05rem; color: var(--text-body); line-height: 1.65; margin-bottom: 1rem;">
|
||||
<strong>Para convertir desafíos en proyectos y proyectos en resultados:</strong> Estructuramos la metodología y el control para que las iniciativas estratégicas se cumplan en tiempo, costo y alcance.
|
||||
</p>
|
||||
@@ -122,13 +135,12 @@
|
||||
</div>
|
||||
|
||||
<!-- 3. IDE Academy -->
|
||||
<div id="academy" class="card" style="margin-bottom: 2.5rem; padding: 2.5rem;">
|
||||
<div id="academy" class="card card-academy" style="margin-bottom: 2.5rem; padding: 2.5rem;">
|
||||
<div class="grid-2" style="gap: 2.5rem; align-items: center;">
|
||||
<div>
|
||||
<span class="ecosystem-badge badge-academy">DESARROLLO HUMANO</span>
|
||||
<h2 style="color: #ed8d01; font-size: 2rem; margin-bottom: 1rem;">IDE Academy</h2>
|
||||
<p style="font-size: 1.05rem; color: var(--text-body); line-height: 1.65; margin-bottom: 1rem;">
|
||||
<strong>Para desarrollar las capacidades que hacen posible la transformación:</strong> Formación ejecutiva e In-Company diseñada para que los equipos comprendan, apliquen y sostengan los cambios.
|
||||
<strong>Para desarrollar las capacidades que hacen posible la transformación:</strong>Formación ejecutiva e In-Company diseñada para que los equipos comprendan, apliquen y sostengan los cambios.
|
||||
</p>
|
||||
<ul style="list-style: none; display: flex; flex-direction: column; gap: 0.6rem; font-size: 0.92rem; color: var(--text-muted); margin-bottom: 1.25rem;">
|
||||
<li>• Capacitación In-Company sobre casos y procesos reales.</li>
|
||||
@@ -138,26 +150,19 @@
|
||||
<a href="ide-academy.html" class="btn btn-outline btn-sm">Ver Catálogo de Cursos →</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="img-placeholder" style="aspect-ratio: 4/3;">
|
||||
<!-- <div class="img-placeholder-icon">🎓</div> -->
|
||||
<div class="img-placeholder-label">Espacio Gráfico IDE Academy</div>
|
||||
</div>
|
||||
<img src="assets/img/academy.png" alt="IDE Academy" style="width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-md);">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4. IDE Digital -->
|
||||
<div id="digital" class="card" style="padding: 2.5rem;">
|
||||
<div id="digital" class="card card-digital" style="padding: 2.5rem;">
|
||||
<div class="grid-2" style="gap: 2.5rem; align-items: center;">
|
||||
<div>
|
||||
<div class="img-placeholder" style="aspect-ratio: 4/3;">
|
||||
<!-- <div class="img-placeholder-icon">⚡</div> -->
|
||||
<div class="img-placeholder-label">Espacio Gráfico IDE Digital</div>
|
||||
</div>
|
||||
<img src="assets/img/digital.png" alt="IDE Digital" style="width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-md);">
|
||||
</div>
|
||||
<div>
|
||||
<span class="ecosystem-badge badge-digital">TECNOLOGÍA CON SENTIDO</span>
|
||||
<h2 style="color: #2c69ab; font-size: 2rem; margin-bottom: 1rem;">IDE Digital</h2>
|
||||
<p style="font-size: 1.05rem; color: var(--text-body); line-height: 1.65; margin-bottom: 1rem;">
|
||||
<strong>Para integrar tecnología, datos e IA con propósito:</strong> No sumamos herramientas por moda; automatizamos procesos clave y potenciamos la toma de decisiones con analítica de datos.
|
||||
</p>
|
||||
@@ -174,6 +179,8 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<!-- Ejemplos Concretos de Integración -->
|
||||
<section class="section-padding bg-subtle-section">
|
||||
<div class="container">
|
||||
@@ -213,22 +220,25 @@
|
||||
</section>
|
||||
|
||||
<!-- CTA Final -->
|
||||
<section class="section-padding">
|
||||
<div class="container text-center">
|
||||
<h2 style="font-size: 2rem; margin-bottom: 1rem;">¿Qué combinación de capacidades necesita tu organización?</h2>
|
||||
<p class="lead-text" style="margin: 0 auto 2rem auto;">
|
||||
<section class="section-padding" style="background-color: var(--teal-dark); border-top: 1px solid var(--border-color);">
|
||||
<div class="container container-narrow text-center">
|
||||
<h2 style="color: var(--text-inverse); margin-bottom: 1rem;">¿Qué combinación de capacidades necesita tu organización?</h2>
|
||||
<p class="lead-text-cta" style="margin-bottom: 1.5rem;">
|
||||
Conversemos para diseñar una propuesta a medida de tu realidad.
|
||||
</p>
|
||||
<div style="display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap;">
|
||||
<a href="contacto.html" class="btn btn-primary btn-lg">HABLEMOS</a>
|
||||
<a href="desafios.html" class="btn btn-outline btn-lg">Explorar por Desafíos</a>
|
||||
<a href="contacto.html" class="btn btn-primary-cta btn-sm">HABLEMOS</a>
|
||||
<a href="desafios.html" class="btn btn-outline-cta btn-sm">Explorar por Desafíos</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer id="site-footer"></footer>
|
||||
<script src="js/main.js"></script>
|
||||
<script src="js/netschema.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@
|
||||
<meta name="description" content="Desarrollamos las capacidades que hacen posible la transformación organizacional. Cursos ejecutivos y formación In-Company en Calidad, Proyectos e IA.">
|
||||
<meta property="og:title" content="IDE Academy | Formación Ejecutiva e In-Company">
|
||||
<meta property="og:description" content="Programas aplicados de gestión de calidad ISO, PMO y metodologías ágiles, y adopción de IA en procesos.">
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@
|
||||
<meta name="description" content="Artículos, análisis y perspectivas sobre pensamiento sistémico, calidad, normas ISO, gestión de proyectos y transformación digital.">
|
||||
<meta property="og:title" content="Insights & Blog | IDE Consultores">
|
||||
<meta property="og:description" content="Evidencia, metodología y reflexiones prácticas para líderes organizacionales.">
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
// Mapa: propiedad de CONFIG -> nombre de variable CSS
|
||||
const CSS_VARS = {
|
||||
greenNodesColor: "--ide-primary",
|
||||
greenStrokeColor: "--ide-primary-light",
|
||||
linkLineColor: "--ide-primary-line",
|
||||
linkGreenColor: "--ide-primary-line",
|
||||
linkActiveColor: "--ide-primary-light",
|
||||
purpleCoreLineColor: "--ide-primary-line",
|
||||
shadowCanva: "--shadow-canva",
|
||||
textMain: "--text-main",
|
||||
textInverse: "--text-inverse"
|
||||
};
|
||||
|
||||
// Mapa: id de nodo violeta -> variable CSS
|
||||
const PURPLE_NODE_VARS = {
|
||||
p_top: "--ide-excellence",
|
||||
p_bottom: "--ide-digital",
|
||||
p_left: "--ide-academy",
|
||||
p_right: "--ide-projects"
|
||||
};
|
||||
|
||||
// Variable CSS común para todos los amarillos
|
||||
const YELLOW_NODE_VAR = "--ide-primary-dark";
|
||||
|
||||
/* ==========================================================================
|
||||
UTILIDAD: LEER VARIABLES CSS
|
||||
========================================================================== */
|
||||
|
||||
function readCSSVariable(variableName, fallback = "") {
|
||||
const value = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(variableName)
|
||||
.trim();
|
||||
return value || fallback;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
CONFIGURACIÓN GENERAL
|
||||
========================================================================== */
|
||||
|
||||
const CONFIG = {
|
||||
// 1. Color global configurable para TODOS los nodos verdes
|
||||
greenNodesColor: readCSSVariable(CSS_VARS.greenNodesColor),
|
||||
greenStrokeColor: readCSSVariable(CSS_VARS.greenStrokeColor),
|
||||
|
||||
// 2. Estilos de líneas punteadas y dinámicas
|
||||
linkLineColor: readCSSVariable(CSS_VARS.linkLineColor),
|
||||
linkGreenColor: readCSSVariable(CSS_VARS.linkGreenColor),
|
||||
linkActiveColor: readCSSVariable(CSS_VARS.linkActiveColor),
|
||||
purpleCoreLineColor: readCSSVariable(CSS_VARS.purpleCoreLineColor),
|
||||
shadowCanva: readCSSVariable(CSS_VARS.shadowCanva),
|
||||
textMain: readCSSVariable(CSS_VARS.textMain),
|
||||
textInverse: readCSSVariable(CSS_VARS.textInverse),
|
||||
|
||||
// 3. Nodos VIOLETAS centrales (posiciones, color individual, leyenda)
|
||||
purpleNodes: [
|
||||
{
|
||||
id: "p_top",
|
||||
x: 615, y: 185,
|
||||
radius: 42,
|
||||
label: "Excellence",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_top),
|
||||
desc: "Servidor central de control y despacho de tareas."
|
||||
},
|
||||
{
|
||||
id: "p_bottom",
|
||||
x: 615, y: 335,
|
||||
radius: 42,
|
||||
label: "Digital",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_bottom),
|
||||
desc: "Base de datos y redundancia estructural sur."
|
||||
},
|
||||
{
|
||||
id: "p_left",
|
||||
x: 485, y: 260,
|
||||
radius: 42,
|
||||
label: "Academy",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_left),
|
||||
desc: "Pasarela occidental hacia submódulos de entrada."
|
||||
},
|
||||
{
|
||||
id: "p_right",
|
||||
x: 745, y: 260,
|
||||
radius: 42,
|
||||
label: "Projects",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_right),
|
||||
desc: "Concentrador oriental hacia subredes de distribución."
|
||||
}
|
||||
],
|
||||
|
||||
// 4. Nodos AMARILLOS: Cada uno conectado a 2 VIOLETAS
|
||||
yellowNodes: [
|
||||
// --- Lado Izquierdo ---
|
||||
{ id: "yl_1", x: 420, y: 130, r: 26, label: "5S", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Capacitación e Impmentación", purpleTargets: ["p_top", "p_left"], side: "left" },
|
||||
{ id: "yl_2", x: 350, y: 165, r: 24, label: "ISO 9001", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Certificación", purpleTargets: ["p_top", "p_left"], side: "left" },
|
||||
{ id: "yl_3", x: 355, y: 245, r: 25, label: "KAIZEN", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Modificación de Procesos", purpleTargets: ["p_left", "p_top"], side: "left" },
|
||||
{ id: "yl_4", x: 335, y: 335, r: 26, label: "BPM", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Automatización de Procesos de Venta", purpleTargets: ["p_left", "p_bottom"], side: "left" },
|
||||
{ id: "yl_5", x: 395, y: 415, r: 28, label: "KPI", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Implementación y Monitoreo", purpleTargets: ["p_bottom", "p_left"], side: "left" },
|
||||
{ id: "yl_6", x: 255, y: 350, r: 25, label: "PDCA", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Capacitación y Mejora de Taller", purpleTargets: ["p_left", "p_bottom"], side: "left" },
|
||||
|
||||
// --- Lado Derecho ---
|
||||
{ id: "yr_1", x: 800, y: 135, r: 25, label: "BPR", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Capacitación e Impmentación", purpleTargets: ["p_top", "p_right"], side: "right" },
|
||||
{ id: "yr_2", x: 875, y: 155, r: 25, label: "ISO45001", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Certificación", purpleTargets: ["p_top", "p_right"], side: "right" },
|
||||
{ id: "yr_3", x: 950, y: 195, r: 25, label: "LEAN", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Modificación de Procesos", purpleTargets: ["p_right", "p_top"], side: "right" },
|
||||
{ id: "yr_4", x: 875, y: 235, r: 25, label: "ISO14001", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Automatización de Procesos de Venta", purpleTargets: ["p_right", "p_top"], side: "right" },
|
||||
{ id: "yr_5", x: 890, y: 300, r: 25, label: "PMO", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Mejoras en procedimientos de Atención de situaciones críticas", purpleTargets: ["p_right", "p_bottom"], side: "right" },
|
||||
{ id: "yr_6", x: 965, y: 345, r: 25, label: "SIGMA", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Capacitación y Mejora de Taller", purpleTargets: ["p_right", "p_bottom"], side: "right" },
|
||||
{ id: "yr_7", x: 890, y: 375, r: 25, label: "IA", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Automatización de Toma de Decisiones", purpleTargets: ["p_bottom", "p_right"], side: "right" },
|
||||
{ id: "yr_8", x: 830, y: 420, r: 26, label: "RPA", color: readCSSVariable(YELLOW_NODE_VAR, "#24333E"), desc: "Implementación y Monitoreo", purpleTargets: ["p_bottom", "p_right"], side: "right" }
|
||||
]
|
||||
};
|
||||
|
||||
/* ==========================================================================
|
||||
MOTOR INTERACTIVO Y RENDERIZADO
|
||||
========================================================================== */
|
||||
|
||||
const canvas = document.getElementById("networkCanvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const tooltip = document.getElementById("tooltip");
|
||||
const tooltipTitle = document.getElementById("tooltip-title");
|
||||
const tooltipDesc = document.getElementById("tooltip-desc");
|
||||
|
||||
let nodes = [];
|
||||
let links = [];
|
||||
let hoveredNode = null;
|
||||
let dashOffset = 0;
|
||||
|
||||
// Construcción de la estructura
|
||||
function initNetwork() {
|
||||
nodes = [];
|
||||
links = [];
|
||||
const linkSet = new Set(); // Previene enlaces duplicados entre mismos nodos
|
||||
|
||||
function addLink(fromId, toId, type, curved = false, curvature = 0) {
|
||||
const keyA = `${fromId}->${toId}`;
|
||||
const keyB = `${toId}->${fromId}`;
|
||||
if (linkSet.has(keyA) || linkSet.has(keyB)) return;
|
||||
linkSet.add(keyA);
|
||||
links.push({ from: fromId, to: toId, type, curved, curvature });
|
||||
}
|
||||
|
||||
// 1. Agregar Nodos Violetas
|
||||
CONFIG.purpleNodes.forEach(p => {
|
||||
nodes.push({
|
||||
...p,
|
||||
type: "purple",
|
||||
baseX: p.x,
|
||||
baseY: p.y,
|
||||
speedOffset: Math.random() * 10
|
||||
});
|
||||
});
|
||||
|
||||
// Conectar los violetas entre sí (malla central)
|
||||
const purples = nodes.filter(n => n.type === "purple");
|
||||
for (let i = 0; i < purples.length; i++) {
|
||||
for (let j = i + 1; j < purples.length; j++) {
|
||||
addLink(purples[i].id, purples[j].id, "purple-purple", false);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Agregar Nodos Amarillos y crear DOBLE conexión curva hacia los violetas
|
||||
CONFIG.yellowNodes.forEach((y) => {
|
||||
nodes.push({
|
||||
id: y.id,
|
||||
x: y.x,
|
||||
y: y.y,
|
||||
baseX: y.x,
|
||||
baseY: y.y,
|
||||
radius: y.r,
|
||||
label: y.label,
|
||||
color: y.color,
|
||||
desc: y.desc,
|
||||
side: y.side,
|
||||
type: "yellow",
|
||||
speedOffset: Math.random() * 10
|
||||
});
|
||||
|
||||
if (Array.isArray(y.purpleTargets)) {
|
||||
y.purpleTargets.forEach((targetPurpleId, index) => {
|
||||
addLink(
|
||||
targetPurpleId,
|
||||
y.id,
|
||||
"purple-yellow",
|
||||
true,
|
||||
index === 0 ? 0.16 : -0.16
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 3. Crear Nodos Verdes Terciarios
|
||||
const greenPositions = [
|
||||
// Lado Izquierdo
|
||||
{ id: "gl_1", x: 190, y: 95, side: "left" },
|
||||
{ id: "gl_2", x: 260, y: 120, side: "left" },
|
||||
{ id: "gl_3", x: 140, y: 155, side: "left" },
|
||||
{ id: "gl_4", x: 75, y: 150, side: "left" },
|
||||
{ id: "gl_5", x: 130, y: 225, side: "left" },
|
||||
{ id: "gl_6", x: 65, y: 230, side: "left" },
|
||||
{ id: "gl_7", x: 195, y: 235, side: "left" },
|
||||
{ id: "gl_8", x: 60, y: 290, side: "left" },
|
||||
{ id: "gl_9", x: 165, y: 305, side: "left" },
|
||||
{ id: "gl_10", x: 75, y: 375, side: "left" },
|
||||
{ id: "gl_11", x: 140, y: 380, side: "left" },
|
||||
{ id: "gl_12", x: 145, y: 440, side: "left" },
|
||||
{ id: "gl_13", x: 235, y: 425, side: "left" },
|
||||
|
||||
// Lado Derecho
|
||||
{ id: "gr_1", x: 1040, y: 90, side: "right" },
|
||||
{ id: "gr_2", x: 960, y: 110, side: "right" },
|
||||
{ id: "gr_3", x: 1025, y: 150, side: "right" },
|
||||
{ id: "gr_4", x: 1095, y: 155, side: "right" },
|
||||
{ id: "gr_5", x: 1160, y: 150, side: "right" },
|
||||
{ id: "gr_6", x: 1030, y: 220, side: "right" },
|
||||
{ id: "gr_7", x: 1105, y: 215, side: "right" },
|
||||
{ id: "gr_8", x: 1170, y: 225, side: "right" },
|
||||
{ id: "gr_9", x: 1060, y: 285, side: "right" },
|
||||
{ id: "gr_10", x: 1120, y: 305, side: "right" },
|
||||
{ id: "gr_11", x: 1175, y: 290, side: "right" },
|
||||
{ id: "gr_12", x: 1025, y: 335, side: "right" },
|
||||
{ id: "gr_13", x: 1095, y: 350, side: "right" },
|
||||
{ id: "gr_14", x: 1035, y: 400, side: "right" },
|
||||
{ id: "gr_15", x: 1085, y: 435, side: "right" },
|
||||
{ id: "gr_16", x: 1165, y: 385, side: "right" },
|
||||
{ id: "gr_17", x: 970, y: 425, side: "right" }
|
||||
];
|
||||
|
||||
greenPositions.forEach(g => {
|
||||
nodes.push({
|
||||
id: g.id,
|
||||
x: g.x,
|
||||
y: g.y,
|
||||
baseX: g.x,
|
||||
baseY: g.y,
|
||||
radius: 14,
|
||||
type: "green",
|
||||
side: g.side,
|
||||
speedOffset: Math.random() * 10
|
||||
});
|
||||
});
|
||||
|
||||
// 4. CONEXIÓN AMARILLOS <-> VERDES (cobertura total en ambas direcciones)
|
||||
const yellows = nodes.filter(n => n.type === "yellow");
|
||||
const greens = nodes.filter(n => n.type === "green");
|
||||
|
||||
// PASO A: cada amarillo con al menos 1 verde
|
||||
yellows.forEach((yNode, idx) => {
|
||||
const candidateGreens = greens.filter(g => g.side === yNode.side);
|
||||
candidateGreens.sort((a, b) => {
|
||||
const distA = Math.hypot(a.baseX - yNode.baseX, a.baseY - yNode.baseY);
|
||||
const distB = Math.hypot(b.baseX - yNode.baseX, b.baseY - yNode.baseY);
|
||||
return distA - distB;
|
||||
});
|
||||
|
||||
if (candidateGreens.length > 0) {
|
||||
addLink(
|
||||
yNode.id,
|
||||
candidateGreens[0].id,
|
||||
"yellow-green",
|
||||
true,
|
||||
idx % 2 === 0 ? 0.20 : -0.20
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// PASO B: cada verde con al menos 1 amarillo
|
||||
greens.forEach((gNode, idx) => {
|
||||
const alreadyConnected = links.some(
|
||||
l => l.type === "yellow-green" && (l.from === gNode.id || l.to === gNode.id)
|
||||
);
|
||||
|
||||
const candidateYellows = yellows.filter(y => y.side === gNode.side);
|
||||
candidateYellows.sort((a, b) => {
|
||||
const distA = Math.hypot(a.baseX - gNode.baseX, a.baseY - gNode.baseY);
|
||||
const distB = Math.hypot(b.baseX - gNode.baseX, b.baseY - gNode.baseY);
|
||||
return distA - distB;
|
||||
});
|
||||
|
||||
if (!alreadyConnected && candidateYellows.length > 0) {
|
||||
addLink(
|
||||
candidateYellows[0].id,
|
||||
gNode.id,
|
||||
"yellow-green",
|
||||
true,
|
||||
idx % 2 === 0 ? -0.22 : 0.22
|
||||
);
|
||||
} else if (candidateYellows.length > 1 && idx % 3 === 0) {
|
||||
addLink(
|
||||
candidateYellows[1].id,
|
||||
gNode.id,
|
||||
"yellow-green",
|
||||
true,
|
||||
idx % 2 === 0 ? 0.26 : -0.26
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bucle de actualización (flotación en zona segura)
|
||||
function update(time) {
|
||||
const maxOffset = 4.5;
|
||||
|
||||
nodes.forEach(node => {
|
||||
if (hoveredNode && hoveredNode.id === node.id) {
|
||||
node.x = node.baseX;
|
||||
node.y = node.baseY;
|
||||
return;
|
||||
}
|
||||
|
||||
const t = (time * 0.0016) + node.speedOffset;
|
||||
node.x = node.baseX + Math.sin(t) * maxOffset;
|
||||
node.y = node.baseY + Math.cos(t * 0.85) * (maxOffset * 0.75);
|
||||
});
|
||||
|
||||
dashOffset -= 0.35;
|
||||
}
|
||||
|
||||
// Renderizado de líneas curvadas
|
||||
function drawConnection(source, target, link, isHighlight) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(source.x, source.y);
|
||||
|
||||
if (link.curved) {
|
||||
const midX = (source.x + target.x) / 2;
|
||||
const midY = (source.y + target.y) / 2;
|
||||
const dx = target.x - source.x;
|
||||
const dy = target.y - source.y;
|
||||
const dist = Math.hypot(dx, dy);
|
||||
|
||||
const nx = -dy / dist;
|
||||
const ny = dx / dist;
|
||||
|
||||
const arcHeight = dist * link.curvature;
|
||||
const cpX = midX + nx * arcHeight;
|
||||
const cpY = midY + ny * arcHeight;
|
||||
|
||||
ctx.quadraticCurveTo(cpX, cpY, target.x, target.y);
|
||||
} else {
|
||||
ctx.lineTo(target.x, target.y);
|
||||
}
|
||||
|
||||
if (link.type === "purple-purple") {
|
||||
ctx.setLineDash([4, 4]);
|
||||
ctx.lineDashOffset = dashOffset * 0.5;
|
||||
ctx.strokeStyle = isHighlight ? CONFIG.linkActiveColor : CONFIG.purpleCoreLineColor;
|
||||
ctx.lineWidth = isHighlight ? 2.6 : 1.8;
|
||||
} else if (link.type === "yellow-green") {
|
||||
ctx.setLineDash([3, 4]);
|
||||
ctx.lineDashOffset = dashOffset;
|
||||
ctx.strokeStyle = isHighlight ? CONFIG.linkActiveColor : CONFIG.linkGreenColor;
|
||||
ctx.lineWidth = isHighlight ? 2.0 : 0.95;
|
||||
} else {
|
||||
ctx.setLineDash([3, 4]);
|
||||
ctx.lineDashOffset = dashOffset;
|
||||
ctx.strokeStyle = isHighlight ? CONFIG.linkActiveColor : CONFIG.linkLineColor;
|
||||
ctx.lineWidth = isHighlight ? 2.0 : 1.05;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// Renderizado del Canvas
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
const nodeMap = new Map(nodes.map(n => [n.id, n]));
|
||||
|
||||
// 1. Dibujar líneas
|
||||
links.forEach(link => {
|
||||
const s = nodeMap.get(link.from);
|
||||
const t = nodeMap.get(link.to);
|
||||
if (!s || !t) return;
|
||||
|
||||
const isHighlight = hoveredNode && (hoveredNode.id === s.id || hoveredNode.id === t.id);
|
||||
drawConnection(s, t, link, isHighlight);
|
||||
});
|
||||
|
||||
// 2. Dibujar nodos
|
||||
nodes.forEach(node => {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
|
||||
|
||||
if (node.type === "purple") {
|
||||
ctx.fillStyle = node.color;
|
||||
ctx.strokeStyle = node.color;
|
||||
ctx.lineWidth = 1.8;
|
||||
} else if (node.type === "yellow") {
|
||||
ctx.fillStyle = node.color;
|
||||
ctx.strokeStyle = node.color;
|
||||
ctx.lineWidth = 1.3;
|
||||
} else {
|
||||
ctx.fillStyle = CONFIG.greenNodesColor;
|
||||
ctx.strokeStyle = CONFIG.greenStrokeColor;
|
||||
ctx.lineWidth = 1.1;
|
||||
}
|
||||
|
||||
if (hoveredNode && hoveredNode.id === node.id) {
|
||||
ctx.shadowColor = CONFIG.shadowCanva;
|
||||
ctx.shadowBlur = 14;
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
|
||||
// 3. Texto en nodos violetas y amarillos
|
||||
if (node.label) {
|
||||
ctx.save();
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
|
||||
if (node.type === "purple") {
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.font = "bold 12.5px system-ui, sans-serif";
|
||||
} else if (node.type === "yellow") {
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.font = "600 10.5px system-ui, sans-serif";
|
||||
}
|
||||
|
||||
ctx.fillText(node.label, node.x, node.y);
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Loop 60 FPS
|
||||
function animate(time) {
|
||||
update(time);
|
||||
draw();
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// Coordenadas con soporte responsivo
|
||||
function getCanvasCoordinates(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
|
||||
return {
|
||||
x: (e.clientX - rect.left) * scaleX,
|
||||
y: (e.clientY - rect.top) * scaleY,
|
||||
cssX: e.clientX - rect.left,
|
||||
cssY: e.clientY - rect.top
|
||||
};
|
||||
}
|
||||
|
||||
// Eventos de Mouse
|
||||
canvas.addEventListener("mousemove", (e) => {
|
||||
const { x, y, cssX, cssY } = getCanvasCoordinates(e);
|
||||
|
||||
const targets = nodes.filter(n => n.type === "purple" || n.type === "yellow");
|
||||
let found = null;
|
||||
|
||||
for (let node of targets) {
|
||||
if (Math.hypot(node.x - x, node.y - y) <= node.radius) {
|
||||
found = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hoveredNode = found;
|
||||
|
||||
if (hoveredNode) {
|
||||
canvas.style.cursor = "pointer";
|
||||
tooltipTitle.textContent = hoveredNode.label;
|
||||
tooltipDesc.textContent = hoveredNode.desc;
|
||||
|
||||
tooltip.style.left = `${cssX}px`;
|
||||
tooltip.style.top = `${cssY - (hoveredNode.radius * (canvas.getBoundingClientRect().height / canvas.height))}px`;
|
||||
tooltip.classList.remove("hidden");
|
||||
} else {
|
||||
canvas.style.cursor = "default";
|
||||
tooltip.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener("mouseleave", () => {
|
||||
hoveredNode = null;
|
||||
tooltip.classList.add("hidden");
|
||||
});
|
||||
|
||||
/* ==========================================================================
|
||||
SOPORTE DE TEMA CLARO / OSCURO
|
||||
--------------------------------------------------------------------------
|
||||
- Re-lee las variables CSS y actualiza CONFIG + nodos vivos.
|
||||
- Observa cambios de `data-theme` o `class` en <html> y <body>.
|
||||
========================================================================== */
|
||||
|
||||
function refreshColorsFromCSS() {
|
||||
// 1. Colores globales
|
||||
CONFIG.greenNodesColor = readCSSVariable(CSS_VARS.greenNodesColor, CONFIG.greenNodesColor);
|
||||
CONFIG.greenStrokeColor = readCSSVariable(CSS_VARS.greenStrokeColor, CONFIG.greenStrokeColor);
|
||||
CONFIG.linkLineColor = readCSSVariable(CSS_VARS.linkLineColor, CONFIG.linkLineColor);
|
||||
CONFIG.linkGreenColor = readCSSVariable(CSS_VARS.linkGreenColor, CONFIG.linkGreenColor);
|
||||
CONFIG.linkActiveColor = readCSSVariable(CSS_VARS.linkActiveColor, CONFIG.linkActiveColor);
|
||||
CONFIG.purpleCoreLineColor = readCSSVariable(CSS_VARS.purpleCoreLineColor, CONFIG.purpleCoreLineColor);
|
||||
|
||||
// 2. Colores de violetas en CONFIG
|
||||
CONFIG.purpleNodes.forEach(p => {
|
||||
const cssVar = PURPLE_NODE_VARS[p.id];
|
||||
if (cssVar) p.color = readCSSVariable(cssVar, p.color);
|
||||
});
|
||||
|
||||
// 3. Colores de amarillos en CONFIG
|
||||
CONFIG.yellowNodes.forEach(y => {
|
||||
y.color = readCSSVariable(YELLOW_NODE_VAR, y.color);
|
||||
});
|
||||
|
||||
// 4. Propagar a los nodos ya instanciados
|
||||
nodes.forEach(node => {
|
||||
if (node.type === "purple" && PURPLE_NODE_VARS[node.id]) {
|
||||
node.color = readCSSVariable(PURPLE_NODE_VARS[node.id], node.color);
|
||||
} else if (node.type === "yellow") {
|
||||
node.color = readCSSVariable(YELLOW_NODE_VAR, node.color);
|
||||
}
|
||||
// Los verdes leen CONFIG.greenNodesColor directo en draw(), no requieren update propio
|
||||
});
|
||||
}
|
||||
|
||||
function watchThemeChanges() {
|
||||
const observer = new MutationObserver(() => {
|
||||
refreshColorsFromCSS();
|
||||
});
|
||||
|
||||
// Observa <html> (habitual en data-theme) y <body> (por si acaso)
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "class"]
|
||||
});
|
||||
|
||||
if (document.body) {
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "class"]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Inicialización
|
||||
initNetwork();
|
||||
watchThemeChanges();
|
||||
requestAnimationFrame(animate);
|
||||
@@ -0,0 +1,537 @@
|
||||
// Mapa: propiedad de CONFIG -> nombre de variable CSS
|
||||
const CSS_VARS = {
|
||||
greenNodesColor: "--ide-primary",
|
||||
greenStrokeColor: "--ide-primary-light",
|
||||
linkLineColor: "--canvas-link-line-color",
|
||||
linkGreenColor: "--canvas-link-green-color",
|
||||
linkActiveColor: "--canvas-link-active-color",
|
||||
purpleCoreLineColor: "--canvas-purple-core-line-color"
|
||||
};
|
||||
|
||||
// Mapa: id de nodo violeta -> variable CSS
|
||||
const PURPLE_NODE_VARS = {
|
||||
p_top: "--canvas-purple-p-top-color",
|
||||
p_bottom: "--canvas-purple-p-bottom-color",
|
||||
p_left: "--canvas-purple-p-left-color",
|
||||
p_right: "--canvas-purple-p-right-color"
|
||||
};
|
||||
|
||||
// Variable CSS común para todos los amarillos
|
||||
const YELLOW_NODE_VAR = "--canvas-yellow-node-color";
|
||||
|
||||
/* ==========================================================================
|
||||
UTILIDAD: LEER VARIABLES CSS
|
||||
========================================================================== */
|
||||
|
||||
function readCSSVariable(variableName, fallback = "") {
|
||||
const value = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(variableName)
|
||||
.trim();
|
||||
return value || fallback;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
CONFIGURACIÓN GENERAL
|
||||
========================================================================== */
|
||||
|
||||
const CONFIG = {
|
||||
// 1. Color global configurable para TODOS los nodos verdes
|
||||
greenNodesColor: readCSSVariable(CSS_VARS.greenNodesColor, "#9b1717"),
|
||||
greenStrokeColor: readCSSVariable(CSS_VARS.greenStrokeColor, "#630b5f"),
|
||||
|
||||
// 2. Estilos de líneas punteadas y dinámicas
|
||||
linkLineColor: readCSSVariable(CSS_VARS.linkLineColor, "rgba(95, 110, 240, 0.40)"),
|
||||
linkGreenColor: readCSSVariable(CSS_VARS.linkGreenColor, "rgba(235, 60, 60, 0.42)"),
|
||||
linkActiveColor: readCSSVariable(CSS_VARS.linkActiveColor, "rgba(25, 35, 205, 0.95)"),
|
||||
purpleCoreLineColor: readCSSVariable(CSS_VARS.purpleCoreLineColor, "rgba(35, 35, 35, 0.70)"),
|
||||
|
||||
// 3. Nodos VIOLETAS centrales (posiciones, color individual, leyenda)
|
||||
purpleNodes: [
|
||||
{
|
||||
id: "p_top",
|
||||
x: 615, y: 185,
|
||||
radius: 42,
|
||||
label: "Núcleo N",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_top, "#8a00c2"),
|
||||
desc: "Servidor central de control y despacho de tareas."
|
||||
},
|
||||
{
|
||||
id: "p_bottom",
|
||||
x: 615, y: 335,
|
||||
radius: 42,
|
||||
label: "Núcleo S",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_bottom, "#9900d8"),
|
||||
desc: "Base de datos y redundancia estructural sur."
|
||||
},
|
||||
{
|
||||
id: "p_left",
|
||||
x: 485, y: 260,
|
||||
radius: 42,
|
||||
label: "Núcleo O",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_left, "#7e00b3"),
|
||||
desc: "Pasarela occidental hacia submódulos de entrada."
|
||||
},
|
||||
{
|
||||
id: "p_right",
|
||||
x: 745, y: 260,
|
||||
radius: 42,
|
||||
label: "Núcleo E",
|
||||
color: readCSSVariable(PURPLE_NODE_VARS.p_right, "#9400cf"),
|
||||
desc: "Concentrador oriental hacia subredes de distribución."
|
||||
}
|
||||
],
|
||||
|
||||
// 4. Nodos AMARILLOS: Cada uno conectado a 2 VIOLETAS
|
||||
yellowNodes: [
|
||||
// --- Lado Izquierdo ---
|
||||
{ id: "yl_1", x: 420, y: 130, r: 26, label: "IZQ-1", color: readCSSVariable(YELLOW_NODE_VAR, "#ffff00"), desc: "Módulo intermedio A-1", purpleTargets: ["p_top", "p_left"], side: "left" },
|
||||
{ id: "yl_2", x: 350, y: 165, r: 24, label: "IZQ-2", color: readCSSVariable(YELLOW_NODE_VAR, "#fff01f"), desc: "Módulo intermedio A-2", purpleTargets: ["p_top", "p_left"], side: "left" },
|
||||
{ id: "yl_3", x: 355, y: 245, r: 25, label: "IZQ-3", color: readCSSVariable(YELLOW_NODE_VAR, "#ffee00"), desc: "Balanceador izquierdo", purpleTargets: ["p_left", "p_top"], side: "left" },
|
||||
{ id: "yl_4", x: 335, y: 335, r: 26, label: "IZQ-4", color: readCSSVariable(YELLOW_NODE_VAR, "#ffff00"), desc: "Filtro de tráfico A-4", purpleTargets: ["p_left", "p_bottom"], side: "left" },
|
||||
{ id: "yl_5", x: 395, y: 415, r: 28, label: "IZQ-5", color: readCSSVariable(YELLOW_NODE_VAR, "#fff33b"), desc: "Procesador de paquetes", purpleTargets: ["p_bottom", "p_left"], side: "left" },
|
||||
{ id: "yl_6", x: 255, y: 350, r: 25, label: "IZQ-6", color: readCSSVariable(YELLOW_NODE_VAR, "#ffee00"), desc: "Enrutador secundario", purpleTargets: ["p_left", "p_bottom"], side: "left" },
|
||||
|
||||
// --- Lado Derecho ---
|
||||
{ id: "yr_1", x: 800, y: 135, r: 25, label: "DER-1", color: readCSSVariable(YELLOW_NODE_VAR, "#ffff00"), desc: "Concentrador de salida 1", purpleTargets: ["p_top", "p_right"], side: "right" },
|
||||
{ id: "yr_2", x: 875, y: 155, r: 25, label: "DER-2", color: readCSSVariable(YELLOW_NODE_VAR, "#ffee00"), desc: "Concentrador de salida 2", purpleTargets: ["p_top", "p_right"], side: "right" },
|
||||
{ id: "yr_3", x: 950, y: 195, r: 25, label: "DER-3", color: readCSSVariable(YELLOW_NODE_VAR, "#ffff00"), desc: "Distribuidor rápido 3", purpleTargets: ["p_right", "p_top"], side: "right" },
|
||||
{ id: "yr_4", x: 875, y: 235, r: 25, label: "DER-4", color: readCSSVariable(YELLOW_NODE_VAR, "#fff33b"), desc: "Replicador este 4", purpleTargets: ["p_right", "p_top"], side: "right" },
|
||||
{ id: "yr_5", x: 890, y: 300, r: 25, label: "DER-5", color: readCSSVariable(YELLOW_NODE_VAR, "#ffee00"), desc: "Gestor de colas 5", purpleTargets: ["p_right", "p_bottom"], side: "right" },
|
||||
{ id: "yr_6", x: 965, y: 345, r: 25, label: "DER-6", color: readCSSVariable(YELLOW_NODE_VAR, "#ffff00"), desc: "Canal asíncrono 6", purpleTargets: ["p_right", "p_bottom"], side: "right" },
|
||||
{ id: "yr_7", x: 890, y: 375, r: 25, label: "DER-7", color: readCSSVariable(YELLOW_NODE_VAR, "#fff01f"), desc: "Salida perimetral 7", purpleTargets: ["p_bottom", "p_right"], side: "right" },
|
||||
{ id: "yr_8", x: 830, y: 420, r: 26, label: "DER-8", color: readCSSVariable(YELLOW_NODE_VAR, "#ffee00"), desc: "Canal de descarga 8", purpleTargets: ["p_bottom", "p_right"], side: "right" }
|
||||
]
|
||||
};
|
||||
|
||||
/* ==========================================================================
|
||||
MOTOR INTERACTIVO Y RENDERIZADO
|
||||
========================================================================== */
|
||||
|
||||
const canvas = document.getElementById("networkCanvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const tooltip = document.getElementById("tooltip");
|
||||
const tooltipTitle = document.getElementById("tooltip-title");
|
||||
const tooltipDesc = document.getElementById("tooltip-desc");
|
||||
|
||||
let nodes = [];
|
||||
let links = [];
|
||||
let hoveredNode = null;
|
||||
let dashOffset = 0;
|
||||
|
||||
// Construcción de la estructura
|
||||
function initNetwork() {
|
||||
nodes = [];
|
||||
links = [];
|
||||
const linkSet = new Set(); // Previene enlaces duplicados entre mismos nodos
|
||||
|
||||
function addLink(fromId, toId, type, curved = false, curvature = 0) {
|
||||
const keyA = `${fromId}->${toId}`;
|
||||
const keyB = `${toId}->${fromId}`;
|
||||
if (linkSet.has(keyA) || linkSet.has(keyB)) return;
|
||||
linkSet.add(keyA);
|
||||
links.push({ from: fromId, to: toId, type, curved, curvature });
|
||||
}
|
||||
|
||||
// 1. Agregar Nodos Violetas
|
||||
CONFIG.purpleNodes.forEach(p => {
|
||||
nodes.push({
|
||||
...p,
|
||||
type: "purple",
|
||||
baseX: p.x,
|
||||
baseY: p.y,
|
||||
speedOffset: Math.random() * 10
|
||||
});
|
||||
});
|
||||
|
||||
// Conectar los violetas entre sí (malla central)
|
||||
const purples = nodes.filter(n => n.type === "purple");
|
||||
for (let i = 0; i < purples.length; i++) {
|
||||
for (let j = i + 1; j < purples.length; j++) {
|
||||
addLink(purples[i].id, purples[j].id, "purple-purple", false);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Agregar Nodos Amarillos y crear DOBLE conexión curva hacia los violetas
|
||||
CONFIG.yellowNodes.forEach((y) => {
|
||||
nodes.push({
|
||||
id: y.id,
|
||||
x: y.x,
|
||||
y: y.y,
|
||||
baseX: y.x,
|
||||
baseY: y.y,
|
||||
radius: y.r,
|
||||
label: y.label,
|
||||
color: y.color,
|
||||
desc: y.desc,
|
||||
side: y.side,
|
||||
type: "yellow",
|
||||
speedOffset: Math.random() * 10
|
||||
});
|
||||
|
||||
if (Array.isArray(y.purpleTargets)) {
|
||||
y.purpleTargets.forEach((targetPurpleId, index) => {
|
||||
addLink(
|
||||
targetPurpleId,
|
||||
y.id,
|
||||
"purple-yellow",
|
||||
true,
|
||||
index === 0 ? 0.16 : -0.16
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 3. Crear Nodos Verdes Terciarios
|
||||
const greenPositions = [
|
||||
// Lado Izquierdo
|
||||
{ id: "gl_1", x: 190, y: 95, side: "left" },
|
||||
{ id: "gl_2", x: 260, y: 120, side: "left" },
|
||||
{ id: "gl_3", x: 140, y: 155, side: "left" },
|
||||
{ id: "gl_4", x: 75, y: 150, side: "left" },
|
||||
{ id: "gl_5", x: 130, y: 225, side: "left" },
|
||||
{ id: "gl_6", x: 65, y: 230, side: "left" },
|
||||
{ id: "gl_7", x: 195, y: 235, side: "left" },
|
||||
{ id: "gl_8", x: 60, y: 290, side: "left" },
|
||||
{ id: "gl_9", x: 165, y: 305, side: "left" },
|
||||
{ id: "gl_10", x: 75, y: 375, side: "left" },
|
||||
{ id: "gl_11", x: 140, y: 380, side: "left" },
|
||||
{ id: "gl_12", x: 145, y: 440, side: "left" },
|
||||
{ id: "gl_13", x: 235, y: 425, side: "left" },
|
||||
|
||||
// Lado Derecho
|
||||
{ id: "gr_1", x: 1040, y: 90, side: "right" },
|
||||
{ id: "gr_2", x: 960, y: 110, side: "right" },
|
||||
{ id: "gr_3", x: 1025, y: 150, side: "right" },
|
||||
{ id: "gr_4", x: 1095, y: 155, side: "right" },
|
||||
{ id: "gr_5", x: 1160, y: 150, side: "right" },
|
||||
{ id: "gr_6", x: 1030, y: 220, side: "right" },
|
||||
{ id: "gr_7", x: 1105, y: 215, side: "right" },
|
||||
{ id: "gr_8", x: 1170, y: 225, side: "right" },
|
||||
{ id: "gr_9", x: 1060, y: 285, side: "right" },
|
||||
{ id: "gr_10", x: 1120, y: 305, side: "right" },
|
||||
{ id: "gr_11", x: 1175, y: 290, side: "right" },
|
||||
{ id: "gr_12", x: 1025, y: 335, side: "right" },
|
||||
{ id: "gr_13", x: 1095, y: 350, side: "right" },
|
||||
{ id: "gr_14", x: 1035, y: 400, side: "right" },
|
||||
{ id: "gr_15", x: 1085, y: 435, side: "right" },
|
||||
{ id: "gr_16", x: 1165, y: 385, side: "right" },
|
||||
{ id: "gr_17", x: 970, y: 425, side: "right" }
|
||||
];
|
||||
|
||||
greenPositions.forEach(g => {
|
||||
nodes.push({
|
||||
id: g.id,
|
||||
x: g.x,
|
||||
y: g.y,
|
||||
baseX: g.x,
|
||||
baseY: g.y,
|
||||
radius: 14,
|
||||
type: "green",
|
||||
side: g.side,
|
||||
speedOffset: Math.random() * 10
|
||||
});
|
||||
});
|
||||
|
||||
// 4. CONEXIÓN AMARILLOS <-> VERDES (cobertura total en ambas direcciones)
|
||||
const yellows = nodes.filter(n => n.type === "yellow");
|
||||
const greens = nodes.filter(n => n.type === "green");
|
||||
|
||||
// PASO A: cada amarillo con al menos 1 verde
|
||||
yellows.forEach((yNode, idx) => {
|
||||
const candidateGreens = greens.filter(g => g.side === yNode.side);
|
||||
candidateGreens.sort((a, b) => {
|
||||
const distA = Math.hypot(a.baseX - yNode.baseX, a.baseY - yNode.baseY);
|
||||
const distB = Math.hypot(b.baseX - yNode.baseX, b.baseY - yNode.baseY);
|
||||
return distA - distB;
|
||||
});
|
||||
|
||||
if (candidateGreens.length > 0) {
|
||||
addLink(
|
||||
yNode.id,
|
||||
candidateGreens[0].id,
|
||||
"yellow-green",
|
||||
true,
|
||||
idx % 2 === 0 ? 0.20 : -0.20
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// PASO B: cada verde con al menos 1 amarillo
|
||||
greens.forEach((gNode, idx) => {
|
||||
const alreadyConnected = links.some(
|
||||
l => l.type === "yellow-green" && (l.from === gNode.id || l.to === gNode.id)
|
||||
);
|
||||
|
||||
const candidateYellows = yellows.filter(y => y.side === gNode.side);
|
||||
candidateYellows.sort((a, b) => {
|
||||
const distA = Math.hypot(a.baseX - gNode.baseX, a.baseY - gNode.baseY);
|
||||
const distB = Math.hypot(b.baseX - gNode.baseX, b.baseY - gNode.baseY);
|
||||
return distA - distB;
|
||||
});
|
||||
|
||||
if (!alreadyConnected && candidateYellows.length > 0) {
|
||||
addLink(
|
||||
candidateYellows[0].id,
|
||||
gNode.id,
|
||||
"yellow-green",
|
||||
true,
|
||||
idx % 2 === 0 ? -0.22 : 0.22
|
||||
);
|
||||
} else if (candidateYellows.length > 1 && idx % 3 === 0) {
|
||||
addLink(
|
||||
candidateYellows[1].id,
|
||||
gNode.id,
|
||||
"yellow-green",
|
||||
true,
|
||||
idx % 2 === 0 ? 0.26 : -0.26
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bucle de actualización (flotación en zona segura)
|
||||
function update(time) {
|
||||
const maxOffset = 4.5;
|
||||
|
||||
nodes.forEach(node => {
|
||||
if (hoveredNode && hoveredNode.id === node.id) {
|
||||
node.x = node.baseX;
|
||||
node.y = node.baseY;
|
||||
return;
|
||||
}
|
||||
|
||||
const t = (time * 0.0016) + node.speedOffset;
|
||||
node.x = node.baseX + Math.sin(t) * maxOffset;
|
||||
node.y = node.baseY + Math.cos(t * 0.85) * (maxOffset * 0.75);
|
||||
});
|
||||
|
||||
dashOffset -= 0.35;
|
||||
}
|
||||
|
||||
// Renderizado de líneas curvadas
|
||||
function drawConnection(source, target, link, isHighlight) {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(source.x, source.y);
|
||||
|
||||
if (link.curved) {
|
||||
const midX = (source.x + target.x) / 2;
|
||||
const midY = (source.y + target.y) / 2;
|
||||
const dx = target.x - source.x;
|
||||
const dy = target.y - source.y;
|
||||
const dist = Math.hypot(dx, dy);
|
||||
|
||||
const nx = -dy / dist;
|
||||
const ny = dx / dist;
|
||||
|
||||
const arcHeight = dist * link.curvature;
|
||||
const cpX = midX + nx * arcHeight;
|
||||
const cpY = midY + ny * arcHeight;
|
||||
|
||||
ctx.quadraticCurveTo(cpX, cpY, target.x, target.y);
|
||||
} else {
|
||||
ctx.lineTo(target.x, target.y);
|
||||
}
|
||||
|
||||
if (link.type === "purple-purple") {
|
||||
ctx.setLineDash([4, 4]);
|
||||
ctx.lineDashOffset = dashOffset * 0.5;
|
||||
ctx.strokeStyle = isHighlight ? "#000000" : CONFIG.purpleCoreLineColor;
|
||||
ctx.lineWidth = isHighlight ? 2.6 : 1.8;
|
||||
} else if (link.type === "yellow-green") {
|
||||
ctx.setLineDash([3, 4]);
|
||||
ctx.lineDashOffset = dashOffset;
|
||||
ctx.strokeStyle = isHighlight ? CONFIG.linkActiveColor : CONFIG.linkGreenColor;
|
||||
ctx.lineWidth = isHighlight ? 2.0 : 0.95;
|
||||
} else {
|
||||
ctx.setLineDash([3, 4]);
|
||||
ctx.lineDashOffset = dashOffset;
|
||||
ctx.strokeStyle = isHighlight ? CONFIG.linkActiveColor : CONFIG.linkLineColor;
|
||||
ctx.lineWidth = isHighlight ? 2.0 : 1.05;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
// Renderizado del Canvas
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
const nodeMap = new Map(nodes.map(n => [n.id, n]));
|
||||
|
||||
// 1. Dibujar líneas
|
||||
links.forEach(link => {
|
||||
const s = nodeMap.get(link.from);
|
||||
const t = nodeMap.get(link.to);
|
||||
if (!s || !t) return;
|
||||
|
||||
const isHighlight = hoveredNode && (hoveredNode.id === s.id || hoveredNode.id === t.id);
|
||||
drawConnection(s, t, link, isHighlight);
|
||||
});
|
||||
|
||||
// 2. Dibujar nodos
|
||||
nodes.forEach(node => {
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
|
||||
|
||||
if (node.type === "purple") {
|
||||
ctx.fillStyle = node.color;
|
||||
ctx.strokeStyle = "#210036";
|
||||
ctx.lineWidth = 1.8;
|
||||
} else if (node.type === "yellow") {
|
||||
ctx.fillStyle = node.color;
|
||||
ctx.strokeStyle = "#383500";
|
||||
ctx.lineWidth = 1.3;
|
||||
} else {
|
||||
ctx.fillStyle = CONFIG.greenNodesColor;
|
||||
ctx.strokeStyle = CONFIG.greenStrokeColor;
|
||||
ctx.lineWidth = 1.1;
|
||||
}
|
||||
|
||||
if (hoveredNode && hoveredNode.id === node.id) {
|
||||
ctx.shadowColor = "rgba(0, 0, 0, 0.35)";
|
||||
ctx.shadowBlur = 14;
|
||||
}
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
|
||||
// 3. Texto en nodos violetas y amarillos
|
||||
if (node.label) {
|
||||
ctx.save();
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
|
||||
if (node.type === "purple") {
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.font = "bold 12.5px system-ui, sans-serif";
|
||||
} else if (node.type === "yellow") {
|
||||
ctx.fillStyle = "#1e1e1e";
|
||||
ctx.font = "600 10.5px system-ui, sans-serif";
|
||||
}
|
||||
|
||||
ctx.fillText(node.label, node.x, node.y);
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Loop 60 FPS
|
||||
function animate(time) {
|
||||
update(time);
|
||||
draw();
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// Coordenadas con soporte responsivo
|
||||
function getCanvasCoordinates(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
|
||||
return {
|
||||
x: (e.clientX - rect.left) * scaleX,
|
||||
y: (e.clientY - rect.top) * scaleY,
|
||||
cssX: e.clientX - rect.left,
|
||||
cssY: e.clientY - rect.top
|
||||
};
|
||||
}
|
||||
|
||||
// Eventos de Mouse
|
||||
canvas.addEventListener("mousemove", (e) => {
|
||||
const { x, y, cssX, cssY } = getCanvasCoordinates(e);
|
||||
|
||||
const targets = nodes.filter(n => n.type === "purple" || n.type === "yellow");
|
||||
let found = null;
|
||||
|
||||
for (let node of targets) {
|
||||
if (Math.hypot(node.x - x, node.y - y) <= node.radius) {
|
||||
found = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hoveredNode = found;
|
||||
|
||||
if (hoveredNode) {
|
||||
canvas.style.cursor = "pointer";
|
||||
tooltipTitle.textContent = hoveredNode.label;
|
||||
tooltipDesc.textContent = hoveredNode.desc;
|
||||
|
||||
tooltip.style.left = `${cssX}px`;
|
||||
tooltip.style.top = `${cssY - (hoveredNode.radius * (canvas.getBoundingClientRect().height / canvas.height))}px`;
|
||||
tooltip.classList.remove("hidden");
|
||||
} else {
|
||||
canvas.style.cursor = "default";
|
||||
tooltip.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener("mouseleave", () => {
|
||||
hoveredNode = null;
|
||||
tooltip.classList.add("hidden");
|
||||
});
|
||||
|
||||
/* ==========================================================================
|
||||
SOPORTE DE TEMA CLARO / OSCURO
|
||||
--------------------------------------------------------------------------
|
||||
- Re-lee las variables CSS y actualiza CONFIG + nodos vivos.
|
||||
- Observa cambios de `data-theme` o `class` en <html> y <body>.
|
||||
========================================================================== */
|
||||
|
||||
function refreshColorsFromCSS() {
|
||||
// 1. Colores globales
|
||||
CONFIG.greenNodesColor = readCSSVariable(CSS_VARS.greenNodesColor, CONFIG.greenNodesColor);
|
||||
CONFIG.greenStrokeColor = readCSSVariable(CSS_VARS.greenStrokeColor, CONFIG.greenStrokeColor);
|
||||
CONFIG.linkLineColor = readCSSVariable(CSS_VARS.linkLineColor, CONFIG.linkLineColor);
|
||||
CONFIG.linkGreenColor = readCSSVariable(CSS_VARS.linkGreenColor, CONFIG.linkGreenColor);
|
||||
CONFIG.linkActiveColor = readCSSVariable(CSS_VARS.linkActiveColor, CONFIG.linkActiveColor);
|
||||
CONFIG.purpleCoreLineColor = readCSSVariable(CSS_VARS.purpleCoreLineColor, CONFIG.purpleCoreLineColor);
|
||||
|
||||
// 2. Colores de violetas en CONFIG
|
||||
CONFIG.purpleNodes.forEach(p => {
|
||||
const cssVar = PURPLE_NODE_VARS[p.id];
|
||||
if (cssVar) p.color = readCSSVariable(cssVar, p.color);
|
||||
});
|
||||
|
||||
// 3. Colores de amarillos en CONFIG
|
||||
CONFIG.yellowNodes.forEach(y => {
|
||||
y.color = readCSSVariable(YELLOW_NODE_VAR, y.color);
|
||||
});
|
||||
|
||||
// 4. Propagar a los nodos ya instanciados
|
||||
nodes.forEach(node => {
|
||||
if (node.type === "purple" && PURPLE_NODE_VARS[node.id]) {
|
||||
node.color = readCSSVariable(PURPLE_NODE_VARS[node.id], node.color);
|
||||
} else if (node.type === "yellow") {
|
||||
node.color = readCSSVariable(YELLOW_NODE_VAR, node.color);
|
||||
}
|
||||
// Los verdes leen CONFIG.greenNodesColor directo en draw(), no requieren update propio
|
||||
});
|
||||
}
|
||||
|
||||
function watchThemeChanges() {
|
||||
const observer = new MutationObserver(() => {
|
||||
refreshColorsFromCSS();
|
||||
});
|
||||
|
||||
// Observa <html> (habitual en data-theme) y <body> (por si acaso)
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "class"]
|
||||
});
|
||||
|
||||
if (document.body) {
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme", "class"]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Inicialización
|
||||
initNetwork();
|
||||
watchThemeChanges();
|
||||
requestAnimationFrame(animate);
|
||||
+3
-3
@@ -49,9 +49,9 @@
|
||||
|
||||
<!-- Contenido -->
|
||||
<div class="container container-narrow text-center" style="position: relative; z-index: 2;">
|
||||
<span class="eyebrow" style="color: var(--text-inverse)">RESPUESTAS ESPECÍFICAS</span>
|
||||
<h1 style="margin-bottom: 1.25rem; color:var(--text-inverse);">Soluciones concretas a necesidades definidas</h1>
|
||||
<p class="lead-text" style="color:var(--text-inverse)">
|
||||
<span class="eyebrow" style="color: var(--text-main)">RESPUESTAS ESPECÍFICAS</span>
|
||||
<h1 style="margin-bottom: 1.25rem; color:var(--text-main);">Soluciones concretas a necesidades definidas</h1>
|
||||
<p class="lead-text" style="color:var(--text-main)">
|
||||
Si ya sabés exactamente qué servicio estás buscando, aquí encontrarás el detalle técnico de nuestras intervenciones, alcances y valor esperado.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@
|
||||
<meta name="description" content="Conocé el origen, propósito, equipo senior y metodología que sustentan el Ecosistema IDE Consultores.">
|
||||
<meta property="og:title" content="Somos IDE | Identidad, Propósito y Equipo">
|
||||
<meta property="og:description" content="Un equipo multidisciplinario con sólida trayectoria en gestión de calidad, proyectos, formación y tecnología.">
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
<meta name="description" content="Buscamos profesionales apasionados por el pensamiento sistémico, la gestión de calidad, la dirección de proyectos y la tecnología.">
|
||||
<meta property="og:title" content="Trabaja con Nosotros | IDE Consultores">
|
||||
<meta property="og:description" content="Formá parte de un equipo multidisciplinario que impulsa la transformación organizacional real.">
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.png">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user