fix some sections

This commit is contained in:
2026-09-09 04:00:47 -03:00
parent b3b62362e8
commit 49e90d6062
4 changed files with 571 additions and 56 deletions
+91
View File
@@ -28,6 +28,7 @@
--bg-subtle: #EDF3F5;
--bg-card: #FFFFFF;
--bg-card-alt: #f8faff;
--ripple-color:#dbdfe2;
--bg-muted: #e9ecef;
--border-color: #E2E8F0;
--border-subtle: #EEF2F6;
@@ -54,11 +55,13 @@
/* Tema Oscuro (Elegante mate, sin negros puros #000 ni neón estridente) */
[data-theme="dark"] {
--ide-primary:#40ab8e;
--bg-body: #11181D;
--bg-surface: #18222A;
--bg-surface-elevated: #202D37;
--bg-subtle: #24333E;
--bg-card: #18222A;
--ripple-color:#18222A;
--bg-card-alt: var(--bg-muted);
--card-overlay: rgba(4, 36, 29, 0.85);
--bg-muted: #24333E;
@@ -444,6 +447,65 @@ p, li, .lead-text, .text-body, .text-muted {
font-weight: 600;
text-transform: uppercase;
}
/*====================================*/
/* Estilos para efecto en Hero */
/*====================================*/
.hero-visual-graphic {
position: relative;
width: 100%;
height: 100%;
min-height: 320px;
background: transparent !important;
overflow: hidden;
border-radius: 16px;
}
#network-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
min-height: 320px;
background: transparent !important;
}
/* Canvas con fondo transparente */
#network-container canvas {
display: block !important;
width: 100% !important;
height: 100% !important;
background: transparent !important;
}
/* Degradado sutil para profundidad */
.hero-visual-graphic::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 2;
border-radius: 16px;
background: radial-gradient(
ellipse at center,
transparent 60%,
rgba(0, 0, 0, 0.05) 100%
);
}
/* Responsive */
@media (max-width: 768px) {
.hero-visual-graphic {
min-height: 240px;
}
#network-container {
min-height: 240px;
}
}
/*============================*/
/* --- 6. Botones --- */
.btn {
@@ -524,6 +586,35 @@ p, li, .lead-text, .text-body, .text-muted {
padding: 0.45rem 1rem;
font-size: 0.85rem;
}
/*==============================*/
/*efecto ruido*/
.incomodidad-section {
position: relative;
width: 100%;
padding: 100px 24px;
overflow: hidden; /* Evita que las ondas ensanchen la pantalla */
}
/* Canvas como fondo absoluto */
#ripple-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none; /* Traspasa cualquier clic al contenido */
}
/* Asegurar que el contenido esté un nivel por encima */
.incomodidad-content {
position: relative;
z-index: 1;
max-width: 900px;
margin: 0 auto;
text-align: center;
}
/*==============================*/
/* --- 7. Tarjetas y Grillas --- */
.card {
+14 -56
View File
@@ -41,62 +41,24 @@
<!-- Doble Entrada Estratégica -->
<div class="hero-doble-entrada">
<!-- Camino A: Por Desafío -->
<a href="desafios.html" class="hero-card-entry" id="hero-entry-desafios">
<div>
<!-- <div class="entry-icon">🎯</div> -->
<h3>Tengo un desafío</h3>
<p>Sé que algo no funciona o quiero mejorar un área, pero no tengo claro qué solución necesito.</p>
</div>
<div class="entry-link-text">
Explorar por Desafíos <span>→</span>
</div>
</a>
<a href="desafios.html" class="btn btn-outline btn-sm" id="hero-entry-desafios">Tengo un desafío</a>
<!-- Camino B: Por Solución Concreta -->
<a href="soluciones.html" class="hero-card-entry" id="hero-entry-soluciones">
<div>
<!-- <div class="entry-icon">⚡</div> -->
<h3>Busco una solución</h3>
<p>Busco una implementación puntual (ISO 9001, PMO, Capacitación In-Company, IA en Procesos).</p>
</div>
<div class="entry-link-text">
Ir a Soluciones Concretas <span>→</span>
</div>
</a>
<a href="soluciones.html" class="btn btn-outline btn-sm" id="hero-entry-soluciones">Busco una solución</a>
</div>
</div>
<!-- Visual Conceptual de Nodos / Infinito + Camino
<div class="hero-visual-graphic">
<div class="infinito-camino-box">
<svg viewBox="0 0 280 200" fill="none" xmlns="http://www.w3.org/2000/svg" style="width: 100%; max-width: 240px; margin-bottom: 1.25rem;">
<path d="M70 100 C20 40, 20 160, 70 100 C120 40, 160 160, 210 100 C260 40, 260 160, 210 100" stroke="var(--ide-primary)" stroke-width="6" stroke-linecap="round" stroke-dasharray="2 1" opacity="0.3"/>
<path d="M50 100 C50 65 80 65 110 100 C140 135 170 135 200 100 C230 65 260 65 260 100 C260 135 230 135 200 100 C170 65 140 65 110 100 C80 135 50 135 50 100 Z" stroke="var(--ide-accent)" stroke-width="4" fill="none" stroke-linecap="round"/>
<circle cx="50" cy="100" r="7" fill="var(--ide-primary)"/>
<circle cx="110" cy="100" r="7" fill="var(--ide-accent)"/>
<circle cx="200" cy="100" r="7" fill="var(--ide-primary-dark)"/>
<circle cx="260" cy="100" r="7" fill="var(--ide-accent-hover)"/>
<line x1="50" y1="100" x2="260" y2="100" stroke="var(--ide-gray)" stroke-width="1.5" stroke-dasharray="4 4"/>
</svg>
<div class="font-bebas" style="font-size: 1.3rem; color: var(--text-main); letter-spacing: 0.08em; margin-bottom: 0.25rem;">
CONECTIVIDAD SISTÉMICA
</div>
<p style="font-size: 0.85rem; color: var(--text-muted); line-height: 1.4;">
Procesos • Personas • Proyectos • Tecnología funcionando en conjunto.
</p>
</div>
</div> -->
<div id="hero-3d-container" style="width: 100%; height: 500px; position: relative;"></div>
</div>
</div>
</section>
<!-- BLOQUE 2: LA TENSIÓN Y EL QUIEBRE -->
<section class="section-padding">
<div class="container">
<div class="tension-box">
<section class="incomodidad-section" style="gap: 3rem;">
<canvas id="ripple-canvas"></canvas>
<div class="incomodidad-content">
<div class="tension-box" style="margin-bottom: 8rem;">
<div style="max-width: 800px; margin: 0 auto; text-align: center;">
<span class="eyebrow">LA INCOMODIDAD PRODUCTIVA</span>
<h2 style="margin-bottom: 1.5rem;">
@@ -110,12 +72,6 @@
</div>
</div>
</div>
</div>
</section>
<!-- BLOQUE 3: QUIZ INTERACTIVO -->
<section class="section-padding-sm" id="quiz-refactorizado">
<div class="container">
<div class="quiz-container" id="quiz-interactive-app">
<!-- HEADER -->
@@ -606,13 +562,12 @@
</div>
</div>
</div>
</div>
</div>
</section>
<!-- BLOQUE 4: EL ECOSISTEMA (4 CAPACIDADES) -->
<section class="section-padding">
<section class="section-padding" style="background-color: var(--bg-subtle);">
<div class="container">
<div style="display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 3rem; flex-wrap: wrap; gap: 1.5rem;">
<div>
@@ -751,10 +706,13 @@
<!-- Footer Modular (Inyectado dinámicamente por JS) -->
<footer id="site-footer"></footer>
<!-- Scripts Centralizados -->
<script src="js/main.js"></script>
<!-- Carga el SVG con JavaScript -->
<!-- Scripts particulas -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="js/particles.js"></script>
<script src="js/ripple.js"></script>
</body>
</html>
+302
View File
@@ -0,0 +1,302 @@
/**
* Plexus 3D Hero - Red de Nodos Conectados para Hero
* Integración con Three.js y soporte dinámico para Modo Claro / Oscuro vía CSS Variables.
*/
(function () {
// Asegurarse de que el DOM esté listo
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPlexus);
} else {
initPlexus();
}
function initPlexus() {
// 1. Validar dependencia de Three.js
if (typeof THREE === 'undefined') {
console.warn('[Plexus 3D] Three.js no está cargado. Asegúrate de incluir Three.js antes de este script.');
return;
}
const container = document.getElementById('hero-3d-container');
if (!container) {
// Si el contenedor no existe en esta página, no ejecutar nada
return;
}
// =========================================================================
// CONFIGURACIÓN GENERAL
// =========================================================================
const CONFIG = {
nodeCount: 100, // Cantidad de nodos (puntos)
radius: 120, // Radio general de la esfera/nube 3D
maxDistance: 60, // Distancia máxima para conectar dos nodos con línea
nodeSize: 6, // Tamaño del punto en píxeles
driftSpeed: 0.0005, // Velocidad de flotación orgánica propia
driftAmplitude: 12, // Rango máximo de oscilación (mantiene la forma)
mouseFactor: 0.35, // Grado de rotación en respuesta al cursor
lerpSpeed: 0.05, // Suavidad de la inercia del movimiento (0.01 - 0.1)
// Variables CSS que el script vigilará en tu :root / .dark
cssVars: {
nodeColor: '--ide-primary',
lineColor: '--ide-primary'
},
// Colores de respaldo en caso de que las variables no existan en el CSS
fallbacks: {
nodeColor: '#0a2540',
lineColor: '#008080'
},
lineOpacity: 0.35 // Opacidad de las líneas
};
// =========================================================================
// LECTURA REACTIVA DE VARIABLES CSS
// =========================================================================
function getCSSVariable(varName, fallback) {
const val = getComputedStyle(document.documentElement)
.getPropertyValue(varName)
.trim();
return val || fallback;
}
// =========================================================================
// CONFIGURACIÓN THREE.JS (Escena, Cámara y Renderer)
// =========================================================================
const scene = new THREE.Scene();
const width = container.clientWidth || 400;
const height = container.clientHeight || 450;
const camera = new THREE.PerspectiveCamera(50, width / height, 1, 1000);
camera.position.z = 320;
const renderer = new THREE.WebGLRenderer({
alpha: true, // Fondo 100% transparente para integrarse con el Hero
antialias: true, // Suavizado de bordes
powerPreference: 'high-performance'
});
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
container.appendChild(renderer.domElement);
// Grupo principal para aplicar rotación y control de inercia
const cloudGroup = new THREE.Group();
scene.add(cloudGroup);
// =========================================================================
// GENERACIÓN DE NODOS (Distribución Esférica Uniforme)
// =========================================================================
const nodes = [];
const nodePositions = new Float32Array(CONFIG.nodeCount * 3);
for (let i = 0; i < CONFIG.nodeCount; i++) {
// Distribución equidistante en esfera (Espiral de Fibonacci 3D)
const phi = Math.acos(-1 + (2 * i) / CONFIG.nodeCount);
const theta = Math.sqrt(CONFIG.nodeCount * Math.PI) * phi;
// Variación radial controlada para darle volumen tridimensional
const r = CONFIG.radius * (0.65 + 0.35 * Math.random());
const baseX = r * Math.cos(theta) * Math.sin(phi);
const baseY = r * Math.sin(theta) * Math.sin(phi);
const baseZ = r * Math.cos(phi);
nodes.push({
base: new THREE.Vector3(baseX, baseY, baseZ),
current: new THREE.Vector3(baseX, baseY, baseZ),
speedOffset: Math.random() * Math.PI * 2,
axisFactors: [
0.8 + Math.random() * 0.4,
0.8 + Math.random() * 0.4,
0.8 + Math.random() * 0.4
]
});
nodePositions[i * 3] = baseX;
nodePositions[i * 3 + 1] = baseY;
nodePositions[i * 3 + 2] = baseZ;
}
// Geometría y textura circular suave para los nodos
const pointsGeometry = new THREE.BufferGeometry();
pointsGeometry.setAttribute('position', new THREE.BufferAttribute(nodePositions, 3));
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(32, 32, 28, 0, Math.PI * 2);
ctx.fill();
const pointTexture = new THREE.CanvasTexture(canvas);
const pointsMaterial = new THREE.PointsMaterial({
size: CONFIG.nodeSize,
map: pointTexture,
transparent: true,
alphaTest: 0.05
});
const pointCloud = new THREE.Points(pointsGeometry, pointsMaterial);
cloudGroup.add(pointCloud);
// =========================================================================
// LÍNEAS DE CONEXIÓN DINÁMICAS
// =========================================================================
const maxPossibleLines = (CONFIG.nodeCount * (CONFIG.nodeCount - 1)) / 2;
const linePositions = new Float32Array(maxPossibleLines * 6);
const linesGeometry = new THREE.BufferGeometry();
linesGeometry.setAttribute(
'position',
new THREE.BufferAttribute(linePositions, 3).setUsage(THREE.DynamicDrawUsage)
);
const linesMaterial = new THREE.LineBasicMaterial({
transparent: true,
opacity: CONFIG.lineOpacity
});
const lines = new THREE.LineSegments(linesGeometry, linesMaterial);
cloudGroup.add(lines);
// =========================================================================
// GESTIÓN DE COLORES Y OBSERVADORES DE TEMA (DARK / LIGHT MODE)
// =========================================================================
function updateThemeColors() {
const nodeColorStr = getCSSVariable(CONFIG.cssVars.nodeColor, CONFIG.fallbacks.nodeColor);
const lineColorStr = getCSSVariable(CONFIG.cssVars.lineColor, CONFIG.fallbacks.lineColor);
pointsMaterial.color.set(nodeColorStr);
linesMaterial.color.set(lineColorStr);
}
// Aplicar colores al arrancar
updateThemeColors();
// 1. Observar cambios en atributos de <html> y <body> (classes, data-theme, etc.)
const observer = new MutationObserver(() => {
updateThemeColors();
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class', 'data-theme', 'data-mode', 'style']
});
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class', 'data-theme', 'data-mode', 'style']
});
// 2. Escuchar cambios de preferencia de color del sistema operativo
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateThemeColors);
}
// Exponer función de refresco manual por si tu toggle dispara eventos personalizados
window.refreshPlexusTheme = updateThemeColors;
// =========================================================================
// INTERACCIÓN CON EL MOUSE (PARALAJE E INCLINACIÓN 3D)
// =========================================================================
let targetRotationX = 0;
let targetRotationY = 0;
window.addEventListener('mousemove', (e) => {
const rect = container.getBoundingClientRect();
// Cálculo de posición normalizada respecto al centro de la ventana o hero
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const normX = (e.clientX - centerX) / (window.innerWidth / 2);
const normY = (e.clientY - centerY) / (window.innerHeight / 2);
targetRotationY = normX * CONFIG.mouseFactor;
targetRotationX = -normY * CONFIG.mouseFactor;
}, { passive: true });
// =========================================================================
// BUCLE DE ANIMACIÓN
// =========================================================================
let time = 0;
function animate() {
requestAnimationFrame(animate);
time += CONFIG.driftSpeed * 1000;
// 1. Oscilación armónica de los nodos (flotación sin desarme)
const currentPosArray = pointsGeometry.attributes.position.array;
for (let i = 0; i < CONFIG.nodeCount; i++) {
const node = nodes[i];
const offset = time * 0.0015 + node.speedOffset;
node.current.x = node.base.x + Math.sin(offset * node.axisFactors[0]) * CONFIG.driftAmplitude;
node.current.y = node.base.y + Math.cos(offset * node.axisFactors[1]) * CONFIG.driftAmplitude;
node.current.z = node.base.z + Math.sin(offset * node.axisFactors[2]) * CONFIG.driftAmplitude;
currentPosArray[i * 3] = node.current.x;
currentPosArray[i * 3 + 1] = node.current.y;
currentPosArray[i * 3 + 2] = node.current.z;
}
pointsGeometry.attributes.position.needsUpdate = true;
// 2. Conectar nodos próximos
let lineIndex = 0;
const lineArray = linesGeometry.attributes.position.array;
for (let i = 0; i < CONFIG.nodeCount; i++) {
for (let j = i + 1; j < CONFIG.nodeCount; j++) {
const dist = nodes[i].current.distanceTo(nodes[j].current);
if (dist < CONFIG.maxDistance) {
lineArray[lineIndex++] = nodes[i].current.x;
lineArray[lineIndex++] = nodes[i].current.y;
lineArray[lineIndex++] = nodes[i].current.z;
lineArray[lineIndex++] = nodes[j].current.x;
lineArray[lineIndex++] = nodes[j].current.y;
lineArray[lineIndex++] = nodes[j].current.z;
}
}
}
linesGeometry.setDrawRange(0, lineIndex / 3);
linesGeometry.attributes.position.needsUpdate = true;
// 3. Rotación base continua + interpolación suave hacia el mouse (Lerp)
cloudGroup.rotation.y += 0.0012;
cloudGroup.rotation.y += (targetRotationY - cloudGroup.rotation.y) * CONFIG.lerpSpeed;
cloudGroup.rotation.x += (targetRotationX - cloudGroup.rotation.x) * CONFIG.lerpSpeed;
renderer.render(scene, camera);
}
animate();
// =========================================================================
// RESPONSIVE RESIZING (Evita deformaciones y recortes)
// =========================================================================
function handleResize() {
if (!container) return;
const newWidth = container.clientWidth;
const newHeight = container.clientHeight;
if (newWidth === 0 || newHeight === 0) return;
camera.aspect = newWidth / newHeight;
camera.updateProjectionMatrix();
renderer.setSize(newWidth, newHeight);
}
// ResizeObserver para cambios precisos en el contenedor del Hero
if (window.ResizeObserver) {
const resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(container);
} else {
window.addEventListener('resize', handleResize);
}
}
})();
+164
View File
@@ -0,0 +1,164 @@
/**
* Ripple Waves Background - Versión con Distorsión de Ruido Topográfico
* Reemplaza o actualiza tu archivo ripple-bg.js
*/
(() => {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initRipple);
} else {
initRipple();
}
function initRipple() {
const canvas = document.getElementById('ripple-canvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const parent = canvas.parentElement;
// =========================================================================
// PARÁMETROS CONFIGURABLES
// =========================================================================
const CONFIG = {
spacing: 36, // Separación entre anillos
speed: 0.32, // Velocidad de expansión hacia afuera
lineWidth: 1.2, // Grosor de las líneas
maxAlpha: 0.42, // Opacidad máxima
centerOffsetX: 0.5, // Centro horizontal (50%)
centerOffsetY: 0.2, // Centro vertical (alineado con el h2)
// --- PARÁMETROS DE RUIDO / DISTORSIÓN ---
distortionStrength: 14, // Qué tanto se deforma el círculo (en px)
distortionFrequency: 3, // Cantidad de ondulaciones en el perímetro
evolutionSpeed: 0.00001, // Velocidad con la que muta la forma de las ondas
segmentsPerRing: 25, // Calidad/resolución del contorno (70 a 100)
cssVar: '--ripple-color', // Variable de tu CSS
fallbackColor: 'rgba(239, 68, 68, 0.4)'
};
let width = 0;
let height = 0;
let dpr = 1;
let maxRadius = 0;
let offset = 0;
let time = 0;
let currentColor = CONFIG.fallbackColor;
// --- Leer variable de color desde CSS ---
function updateThemeColor() {
const color = getComputedStyle(document.documentElement)
.getPropertyValue(CONFIG.cssVar)
.trim();
currentColor = color || CONFIG.fallbackColor;
}
updateThemeColor();
const observer = new MutationObserver(updateThemeColor);
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'data-theme'] });
observer.observe(document.body, { attributes: true, attributeFilter: ['class', 'data-theme'] });
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateThemeColor);
}
// --- Adaptación de resolución Retina / HiDPI ---
function resize() {
dpr = Math.min(window.devicePixelRatio || 1, 2);
width = parent.clientWidth;
height = parent.clientHeight;
canvas.width = width * dpr;
canvas.height = height * dpr;
ctx.scale(dpr, dpr);
const cx = width * CONFIG.centerOffsetX;
const cy = height * CONFIG.centerOffsetY;
const dx = Math.max(cx, width - cx);
const dy = Math.max(cy, height - cy);
maxRadius = Math.sqrt(dx * dx + dy * dy) + CONFIG.spacing * 2;
}
resize();
if (window.ResizeObserver) {
new ResizeObserver(resize).observe(parent);
} else {
window.addEventListener('resize', resize);
}
// --- Función de Pseudo-Ruido Armónico ---
// Combina ondas senoidales a distintas frecuencias para simular turbulencia/ruido suave
function getNoiseDeformation(angle, baseRadius, t) {
const f = CONFIG.distortionFrequency;
const w1 = Math.sin(angle * f + t + baseRadius * 0.015);
const w2 = Math.cos(angle * (f + 2) - t * 0.8 + baseRadius * 0.02);
const w3 = Math.sin(angle * 2 + t * 0.4);
// Crece ligeramente a medida que el anillo viaja hacia afuera
const growthFactor = 0.5 + (baseRadius / maxRadius) * 0.5;
return (w1 * 0.55 + w2 * 0.35 + w3 * 0.1) * CONFIG.distortionStrength * growthFactor;
}
// --- Loop de Animación ---
function render() {
ctx.clearRect(0, 0, width, height);
const cx = width * CONFIG.centerOffsetX;
const cy = height * CONFIG.centerOffsetY;
offset = (offset + CONFIG.speed) % CONFIG.spacing;
time += CONFIG.evolutionSpeed * 1000;
ctx.save();
ctx.strokeStyle = currentColor;
ctx.lineWidth = CONFIG.lineWidth;
const angleStep = (Math.PI * 2) / CONFIG.segmentsPerRing;
// Dibujar cada anillo deformado
for (let r = offset; r < maxRadius; r += CONFIG.spacing) {
if (r <= 2) continue;
const progress = r / maxRadius;
// Desvanecimiento suave en centro y bordes
let alpha = 1;
if (progress < 0.12) {
alpha = progress / 0.12;
} else {
alpha = Math.cos(((progress - 0.12) / 0.88) * (Math.PI / 2));
}
ctx.globalAlpha = Math.max(0, Math.min(1, alpha * CONFIG.maxAlpha));
ctx.beginPath();
// Trazar el contorno deformado punto a punto
for (let i = 0; i <= CONFIG.segmentsPerRing; i++) {
const angle = i * angleStep;
const deformation = getNoiseDeformation(angle, r, time);
const currentRadius = Math.max(0, r + deformation);
const px = cx + Math.cos(angle) * currentRadius;
const py = cy + Math.sin(angle) * currentRadius;
if (i === 0) {
ctx.moveTo(px, py);
} else {
ctx.lineTo(px, py);
}
}
ctx.closePath();
ctx.stroke();
}
ctx.restore();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
})();