-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
114 lines (105 loc) · 7.43 KB
/
Copy pathapp.js
File metadata and controls
114 lines (105 loc) · 7.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/* ============================================================
Gerónimo Ordóñez Navarro — shared render + logic.
Content object C lives in content.js (load it BEFORE this).
Every render is guarded so this file drives index.html and
about.html. Plain JS, no framework. No em dashes anywhere.
============================================================ */
let currentLang='es';
const $=id=>document.getElementById(id);
/* ---------- project card ---------- */
function pcardHTML(p,extra){
const live=/producci|production/i.test(p.badge)?' is-live':'';
return `<div class="pcard${live}">
<div class="pcard-head">
<h3>${p.title}${p.subtitle?`<span class="psub">${p.subtitle}</span>`:''}</h3>
<span class="badge">${p.badge}</span>
</div>
<p class="problem">${p.problem}</p>
<ul class="pcard-highlights">${p.highlights.map(h=>`<li>${h}</li>`).join('')}</ul>
<div class="stack-tags">${p.stack.map(s=>`<span class="stack-tag">${s}</span>`).join('')}${p.techBadge?`<span class="tech-badge"><span class="mark"></span>${p.techBadge}</span>`:''}</div>
${p.roadmap?`<div class="roadmap-box"><span class="rm-label">${extra.rl}</span><ul>${p.roadmap.map(r=>`<li>${r}</li>`).join('')}</ul></div>`:''}
</div>`;
}
function renderIndex(lang){
const d=C[lang];
if($('timeline'))$('timeline').innerHTML=d.jobs.map(j=>`
<div class="tl-item${j.current?' current':''}">
<div class="tl-title">${j.title}</div>
<div class="tl-company">${j.company}</div>
<div class="tl-dates">${j.dates}</div>
<ul class="tl-bullets">${j.bullets.map(b=>`<li>${b}</li>`).join('')}</ul>
</div>`).join('');
if($('skills-panel'))$('skills-panel').innerHTML=d.skillGroups.map(g=>`
<div class="sp-heading">${g.label}</div>
<div class="chip-grid">${g.items.map(i=>`<span class="chip${g.accents.includes(i)?' accent':''}">${i}</span>`).join('')}</div>`).join('');
const rl=lang==='es'?'En construcción sobre este núcleo':'Building on this core';
if($('industrial-list'))$('industrial-list').innerHTML=d.industrial.map(p=>pcardHTML(p,{rl})).join('');
if($('academic-list'))$('academic-list').innerHTML=d.academic.map(p=>pcardHTML(p,{rl})).join('');
if($('neuro-list'))$('neuro-list').innerHTML=d.neuro.map(p=>pcardHTML(p,{rl})).join('');
if($('education-list'))$('education-list').innerHTML=d.education.map(e=>`<div class="list-item"><div class="primary">${e.primary}</div><div class="secondary">${e.secondary}</div></div>`).join('');
if($('languages-list'))$('languages-list').innerHTML=d.languages.map(l=>`<div class="list-item"><div class="primary">${l.primary}</div><div class="secondary">${l.secondary}</div></div>`).join('');
}
function renderAbout(lang){
const d=C[lang];
if($('about-prose'))$('about-prose').innerHTML=d.aboutLong.map(s=>`
<h3><span class="h-no">${s.no}</span>${s.h}</h3>
${s.paras.map((p,i)=>`<p${i===0?' class="drop"':''}>${p}</p>`).join('')}`).join('');
if($('principles-block')){
$('principles-block').innerHTML=`<h3><span class="h-no">·</span>${d.principlesLabel}</h3>
<ul class="principles">${d.principles.map(p=>`<li><span class="p-num">${p.n}</span><strong>${p.strong}</strong> ${p.text}</li>`).join('')}</ul>`;
}
if($('side-now'))$('side-now').innerHTML=`<h4>${d.nowLabel}</h4><div class="now">${d.nowText}</div>`;
if($('side-facts'))$('side-facts').innerHTML=`<h4>${d.factsLabel}</h4><ul>${d.facts.map(f=>`<li><span class="lab">${f.lab}</span><span class="val">${f.val}</span></li>`).join('')}</ul>`;
if($('side-focus'))$('side-focus').innerHTML=`<h4>${d.focusLabel}</h4><ul>${d.focus.map(f=>`<li>${f}</li>`).join('')}</ul>`;
if($('edu-list-a'))$('edu-list-a').innerHTML=d.education.map(e=>`<div class="list-item"><div class="primary">${e.primary}</div><div class="secondary">${e.secondary}</div></div>`).join('');
if($('lang-list-a'))$('lang-list-a').innerHTML=d.languages.map(l=>`<div class="list-item"><div class="primary">${l.primary}</div><div class="secondary">${l.secondary}</div></div>`).join('');
}
function setLang(lang){
currentLang=lang;document.documentElement.lang=lang;
const be=$('btn-es'),bn=$('btn-en');
if(be)be.classList.toggle('active',lang==='es');
if(bn)bn.classList.toggle('active',lang==='en');
document.querySelectorAll('[data-i18n]').forEach(el=>{
const v=C[lang][el.getAttribute('data-i18n')];
if(typeof v==='string')el.textContent=v;
});
renderIndex(lang);
renderAbout(lang);
try{localStorage.setItem('gon-lang',lang);}catch(e){}
}
(function boot(){
let saved='es';
try{saved=localStorage.getItem('gon-lang')||'es';}catch(e){}
setLang(saved==='en'?'en':'es');
})();
/* ============================================================
Spiking neural network — live background simulation (hero).
Only runs where #nn-canvas exists. Neutral grey palette at
low opacity so it reads as texture, not decoration.
============================================================ */
(function(){
const canvas=document.getElementById('nn-canvas');
if(!canvas)return;
if(window.matchMedia&&window.matchMedia('(prefers-reduced-motion:reduce)').matches)return;
const ctx=canvas.getContext('2d');
let W,H,dpr;const NODES=[],EDGES=[],SPIKES=[];const NC=55,ED=180;
function resize(){dpr=window.devicePixelRatio||1;W=canvas.parentElement.offsetWidth;H=canvas.parentElement.offsetHeight;canvas.width=W*dpr;canvas.height=H*dpr;canvas.style.width=W+'px';canvas.style.height=H+'px';ctx.setTransform(dpr,0,0,dpr,0,0)}
function init(){
NODES.length=0;EDGES.length=0;SPIKES.length=0;
for(let i=0;i<NC;i++)NODES.push({x:Math.random()*W,y:Math.random()*H,r:2+Math.random()*2.5,vx:(Math.random()-.5)*.12,vy:(Math.random()-.5)*.12,firing:0});
for(let i=0;i<NODES.length;i++)for(let j=i+1;j<NODES.length;j++){const dx=NODES[i].x-NODES[j].x,dy=NODES[i].y-NODES[j].y,d=Math.sqrt(dx*dx+dy*dy);if(d<ED)EDGES.push({a:i,b:j,d})}
}
window.addEventListener('resize',()=>{resize();init()});
resize();init();
let lastSpike=0;
function frame(t){
ctx.clearRect(0,0,W,H);
NODES.forEach(n=>{n.x+=n.vx;n.y+=n.vy;if(n.x<0||n.x>W)n.vx*=-1;if(n.y<0||n.y>H)n.vy*=-1;n.firing=Math.max(0,n.firing-.015)});
EDGES.forEach(e=>{const a=NODES[e.a],b=NODES[e.b],bright=Math.max(a.firing,b.firing);ctx.strokeStyle=`rgba(120,120,130,${.15+bright*.35})`;ctx.lineWidth=.5+bright*1;ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.lineTo(b.x,b.y);ctx.stroke()});
if(t-lastSpike>600+Math.random()*800){lastSpike=t;const si=Math.floor(Math.random()*NODES.length);NODES[si].firing=1;const vis=new Set([si]),q=[si];let d=0;while(q.length&&d<6){const nx=[];q.forEach(ni=>{EDGES.forEach(e=>{let nb=-1;if(e.a===ni&&!vis.has(e.b))nb=e.b;if(e.b===ni&&!vis.has(e.a))nb=e.a;if(nb>=0){vis.add(nb);nx.push(nb);SPIKES.push({from:ni,to:nb,progress:0,speed:.012+Math.random()*.008})}})});q.length=0;q.push(...nx);d++}}
for(let i=SPIKES.length-1;i>=0;i--){const s=SPIKES[i];s.progress+=s.speed;if(s.progress>=1){NODES[s.to].firing=1;SPIKES.splice(i,1);continue}const a=NODES[s.from],b=NODES[s.to],x=a.x+(b.x-a.x)*s.progress,y=a.y+(b.y-a.y)*s.progress;ctx.fillStyle=`rgba(100,110,120,${.5+.3*Math.sin(s.progress*Math.PI)})`;ctx.beginPath();ctx.arc(x,y,3,0,Math.PI*2);ctx.fill()}
NODES.forEach(n=>{const g=n.firing;if(g>.01){ctx.fillStyle=`rgba(100,110,120,${g*.2})`;ctx.beginPath();ctx.arc(n.x,n.y,n.r+g*5,0,Math.PI*2);ctx.fill()}ctx.fillStyle=`rgba(100,110,120,${.25+g*.3})`;ctx.beginPath();ctx.arc(n.x,n.y,n.r,0,Math.PI*2);ctx.fill()});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();