2025-10-20 08:54:21 +01:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="{{if eq .Lang "es"}}es{{else}}en{{end}}">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
2025-10-31 11:06:38 +00:00
|
|
|
<!-- Primary Meta Tags -->
|
|
|
|
|
<title>{{.CV.Personal.Name}} - {{if eq .Lang "es"}}Curriculum Vitae{{else}}Curriculum Vitae{{end}}</title>
|
|
|
|
|
<meta name="title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
|
|
|
|
|
<meta name="description" content="{{.CV.Personal.Title}} | {{if eq .Lang "es"}}18 años de experiencia en desarrollo web, SAP CDC, React, Node.js, Go, HTMX y desarrollo asistido por IA{{else}}18 years of experience in web development, SAP CDC, React, Node.js, Go, HTMX and AI-assisted development{{end}}">
|
|
|
|
|
<meta name="keywords" content="{{if eq .Lang "es"}}CV, Curriculum Vitae, {{.CV.Personal.Name}}, Desarrollador FullStack, SAP CDC, React, Node.js, Go, HTMX, IA, Desarrollo Web, Consultor Técnico{{else}}CV, Resume, {{.CV.Personal.Name}}, FullStack Developer, SAP CDC, React, Node.js, Go, HTMX, AI, Web Development, Technical Consultant{{end}}">
|
|
|
|
|
<meta name="author" content="{{.CV.Personal.Name}}">
|
|
|
|
|
<meta name="robots" content="index, follow">
|
2025-11-11 13:53:14 +00:00
|
|
|
<link rel="canonical" href="{{.CanonicalURL}}">
|
|
|
|
|
|
|
|
|
|
<!-- Hreflang tags for international SEO -->
|
|
|
|
|
<link rel="alternate" hreflang="en" href="{{.AlternateEN}}">
|
|
|
|
|
<link rel="alternate" hreflang="es" href="{{.AlternateES}}">
|
|
|
|
|
<link rel="alternate" hreflang="x-default" href="https://juan.andres.morenorub.io/?lang=en">
|
2025-10-31 11:06:38 +00:00
|
|
|
|
|
|
|
|
<!-- Open Graph / Facebook -->
|
|
|
|
|
<meta property="og:type" content="profile">
|
|
|
|
|
<meta property="og:url" content="{{.CV.Personal.Website}}">
|
|
|
|
|
<meta property="og:title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
|
|
|
|
|
<meta property="og:description" content="{{.CV.Personal.Title}} | {{if eq .Lang "es"}}Consultor Técnico Senior con 18 años de experiencia{{else}}Senior Technical Consultant with 18 years of experience{{end}}">
|
|
|
|
|
<meta property="og:image" content="{{.CV.Personal.Website}}/static/images/profile.jpg">
|
|
|
|
|
<meta property="og:locale" content="{{if eq .Lang "es"}}es_ES{{else}}en_US{{end}}">
|
|
|
|
|
<meta property="og:site_name" content="{{.CV.Personal.Name}}">
|
|
|
|
|
<meta property="profile:first_name" content="Juan Andrés">
|
|
|
|
|
<meta property="profile:last_name" content="Moreno Rubio">
|
|
|
|
|
<meta property="profile:username" content="txeo">
|
|
|
|
|
|
|
|
|
|
<!-- Social Media Card (Generic) -->
|
|
|
|
|
<meta name="twitter:card" content="summary">
|
|
|
|
|
<meta name="twitter:title" content="{{.CV.Personal.Name}} - {{if eq .Lang "es"}}CV Profesional{{else}}Professional CV{{end}}">
|
|
|
|
|
<meta name="twitter:description" content="{{.CV.Personal.Title}}">
|
|
|
|
|
<meta name="twitter:image" content="{{.CV.Personal.Website}}/static/images/profile.jpg">
|
|
|
|
|
|
|
|
|
|
<!-- HTMX Configuration -->
|
|
|
|
|
<meta name="htmx-config" content='{"timeout":5000,"defaultSwapStyle":"innerHTML","defaultSwapDelay":0,"defaultSettleDelay":20}'>
|
|
|
|
|
|
|
|
|
|
<!-- HTMX with SRI (Subresource Integrity) -->
|
|
|
|
|
<script src="https://unpkg.com/htmx.org@1.9.10"
|
|
|
|
|
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
|
|
|
|
|
crossorigin="anonymous"></script>
|
2025-10-20 08:54:21 +01:00
|
|
|
|
2025-11-17 13:00:03 +00:00
|
|
|
<!-- CV Core Functions - Regular JavaScript (global reusable functions) -->
|
|
|
|
|
<script src="/static/js/cv-functions.js"></script>
|
|
|
|
|
|
2025-11-14 21:38:09 +00:00
|
|
|
<!-- Hyperscript Functions - Must load BEFORE hyperscript library -->
|
2025-11-17 16:28:52 +00:00
|
|
|
<!-- ✅ NO def limit with latest hyperscript - organized by category -->
|
|
|
|
|
<script type="text/hyperscript" src="/static/hyperscript/utils._hs"></script>
|
|
|
|
|
<script type="text/hyperscript" src="/static/hyperscript/toggles._hs"></script>
|
|
|
|
|
<script type="text/hyperscript" src="/static/hyperscript/hover-sync._hs"></script>
|
2025-11-14 21:38:09 +00:00
|
|
|
|
2025-11-12 22:54:46 +00:00
|
|
|
<!-- Hyperscript - Declarative event handling for enhanced interactivity -->
|
2025-11-17 13:00:03 +00:00
|
|
|
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>
|
2025-11-12 22:54:46 +00:00
|
|
|
|
2025-11-07 11:49:47 +00:00
|
|
|
<!-- Iconify - Load synchronously for immediate rendering -->
|
|
|
|
|
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
|
2025-11-06 10:36:00 +00:00
|
|
|
|
2025-10-20 08:54:21 +01:00
|
|
|
<!-- CSS -->
|
|
|
|
|
<link rel="stylesheet" href="/static/css/main.css">
|
2025-11-05 12:15:43 +00:00
|
|
|
<link rel="stylesheet" href="/static/css/logo-toggle.css">
|
2025-10-20 08:54:21 +01:00
|
|
|
<link rel="stylesheet" href="/static/css/print.css" media="print">
|
|
|
|
|
|
2025-10-31 11:06:38 +00:00
|
|
|
<!-- Fonts with Preload -->
|
2025-10-20 08:54:21 +01:00
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
|
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
2025-10-31 11:06:38 +00:00
|
|
|
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
|
2025-10-20 08:54:21 +01:00
|
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
2025-10-31 11:06:38 +00:00
|
|
|
|
|
|
|
|
<!-- Structured Data (JSON-LD) -->
|
|
|
|
|
<script type="application/ld+json">
|
|
|
|
|
{
|
|
|
|
|
"@context": "https://schema.org",
|
|
|
|
|
"@type": "Person",
|
|
|
|
|
"name": "{{.CV.Personal.Name}}",
|
|
|
|
|
"jobTitle": "{{.CV.Personal.Title}}",
|
|
|
|
|
"url": "{{.CV.Personal.Website}}",
|
|
|
|
|
"email": "{{.CV.Personal.Email}}",
|
|
|
|
|
"telephone": "{{.CV.Personal.Phone}}",
|
|
|
|
|
"address": {
|
|
|
|
|
"@type": "PostalAddress",
|
|
|
|
|
"addressLocality": "{{.CV.Personal.Location}}"
|
|
|
|
|
},
|
|
|
|
|
"sameAs": [
|
|
|
|
|
"{{.CV.Personal.LinkedIn}}",
|
|
|
|
|
"{{.CV.Personal.GitHub}}",
|
2025-11-08 10:23:31 +00:00
|
|
|
"{{.CV.Personal.Domestika}}"
|
2025-10-31 11:06:38 +00:00
|
|
|
],
|
|
|
|
|
"alumniOf": {
|
|
|
|
|
"@type": "EducationalOrganization",
|
|
|
|
|
"name": "Universidad de Extremadura"
|
|
|
|
|
},
|
|
|
|
|
"knowsAbout": [
|
|
|
|
|
"Web Development",
|
|
|
|
|
"SAP Customer Data Cloud",
|
|
|
|
|
"React",
|
|
|
|
|
"Node.js",
|
|
|
|
|
"Go",
|
|
|
|
|
"HTMX",
|
|
|
|
|
"AI-Assisted Development",
|
|
|
|
|
"Full Stack Development"
|
|
|
|
|
],
|
|
|
|
|
"worksFor": {
|
|
|
|
|
"@type": "Organization",
|
|
|
|
|
"name": "Olympic Broadcasting Services"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
2025-10-20 08:54:21 +01:00
|
|
|
</head>
|
2025-11-12 23:07:44 +00:00
|
|
|
<body {{if .ThemeClean}}class="theme-clean"{{end}}
|
2025-11-15 15:59:54 +00:00
|
|
|
_="on load call initScrollBehavior()
|
|
|
|
|
on scroll from window call handleScroll()
|
|
|
|
|
on keydown
|
2025-11-17 08:34:50 +00:00
|
|
|
set tagName to event.target.tagName
|
|
|
|
|
set isInputField to (tagName is 'INPUT' or tagName is 'TEXTAREA')
|
|
|
|
|
|
|
|
|
|
-- Show shortcuts modal with '?'
|
|
|
|
|
if event.key is '?' and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
|
|
|
|
|
halt the event
|
|
|
|
|
set modal to #shortcuts-modal
|
|
|
|
|
if modal then call modal.showModal() end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Toggle CV length with 'L'
|
2025-11-17 13:00:03 +00:00
|
|
|
if (event.key is 'l' or event.key is 'L') and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
|
2025-11-17 08:34:50 +00:00
|
|
|
halt the event
|
2025-11-17 13:00:03 +00:00
|
|
|
set lengthToggle to (#lengthToggle or #lengthToggleMenu)
|
|
|
|
|
if lengthToggle then set lengthToggle's checked to (not lengthToggle's checked) then send change to lengthToggle end
|
2025-11-17 08:34:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Toggle icons with 'I'
|
2025-11-17 13:00:03 +00:00
|
|
|
if (event.key is 'i' or event.key is 'I') and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
|
2025-11-17 08:34:50 +00:00
|
|
|
halt the event
|
2025-11-17 13:00:03 +00:00
|
|
|
set iconToggle to (#iconToggle or #iconToggleMenu)
|
|
|
|
|
if iconToggle then set iconToggle's checked to (not iconToggle's checked) then send change to iconToggle end
|
2025-11-17 08:34:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Toggle theme with 'V'
|
2025-11-17 13:00:03 +00:00
|
|
|
if (event.key is 'v' or event.key is 'V') and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
|
2025-11-17 08:34:50 +00:00
|
|
|
halt the event
|
2025-11-17 13:00:03 +00:00
|
|
|
set themeToggle to (#themeToggle or #themeToggleMenu)
|
|
|
|
|
if themeToggle then set themeToggle's checked to (not themeToggle's checked) then send change to themeToggle end
|
2025-11-15 15:59:54 +00:00
|
|
|
end
|
|
|
|
|
end">
|
2025-11-12 19:54:56 +00:00
|
|
|
<!-- Top anchor for back-to-top link -->
|
|
|
|
|
<div id="top"></div>
|
|
|
|
|
|
2025-11-12 18:26:18 +00:00
|
|
|
{{template "action-bar" .}}
|
|
|
|
|
{{template "hamburger-menu" .}}
|
2025-11-07 19:11:21 +00:00
|
|
|
|
2025-11-12 15:24:09 +00:00
|
|
|
<!-- Zoom Wrapper (for zoom functionality) -->
|
|
|
|
|
<div id="zoom-wrapper" class="zoom-wrapper">
|
|
|
|
|
<!-- CV Content Container -->
|
|
|
|
|
<div class="cv-container">
|
2025-11-12 18:55:06 +00:00
|
|
|
{{template "cv-content.html" .}}
|
2025-11-09 19:30:05 +00:00
|
|
|
</div>
|
2025-11-10 16:03:29 +00:00
|
|
|
|
2025-11-12 18:26:18 +00:00
|
|
|
{{template "page-footer" .}}
|
|
|
|
|
</div> <!-- End zoom-wrapper -->
|
2025-11-12 11:00:29 +00:00
|
|
|
|
2025-11-12 18:26:18 +00:00
|
|
|
{{template "error-toast" .}}
|
|
|
|
|
{{template "back-to-top" .}}
|
2025-11-17 08:34:50 +00:00
|
|
|
{{template "info-button" .}}
|
|
|
|
|
{{template "download-button" .}}
|
|
|
|
|
{{template "print-friendly-button" .}}
|
|
|
|
|
{{template "zoom-toggle-button" .}}
|
|
|
|
|
{{template "shortcuts-button" .}}
|
2025-11-12 18:26:18 +00:00
|
|
|
{{template "info-modal" .}}
|
2025-11-15 15:59:54 +00:00
|
|
|
{{template "shortcuts-modal" .}}
|
2025-11-12 18:26:18 +00:00
|
|
|
{{template "pdf-modal" .}}
|
|
|
|
|
{{template "zoom-control" .}}
|
2025-11-12 11:00:29 +00:00
|
|
|
|
2025-11-11 21:43:12 +00:00
|
|
|
<!-- External JavaScript - CSP Compliant -->
|
|
|
|
|
<script src="/static/js/main.js"></script>
|
2025-11-09 15:02:31 +00:00
|
|
|
|
2025-11-11 21:43:12 +00:00
|
|
|
<!-- Matomo Analytics - Nonce-based CSP -->
|
|
|
|
|
<script nonce="{{.CSPNonce}}">
|
2025-11-09 15:02:31 +00:00
|
|
|
var _paq = window._paq = window._paq || [];
|
|
|
|
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
|
|
|
|
_paq.push(['trackPageView']);
|
|
|
|
|
_paq.push(['enableLinkTracking']);
|
|
|
|
|
(function() {
|
|
|
|
|
var u="https://matomo.drolo.club/";
|
|
|
|
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
|
|
|
|
_paq.push(['setSiteId', '4']);
|
|
|
|
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
|
|
|
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
|
|
|
|
})();
|
2025-10-27 22:32:32 +00:00
|
|
|
</script>
|
2025-11-09 15:02:31 +00:00
|
|
|
<!-- End Matomo Code -->
|
2025-10-20 08:54:21 +01:00
|
|
|
</body>
|
|
|
|
|
</html>
|