From 077fed41e630a85ce9d727dfedb69c920a375777 Mon Sep 17 00:00:00 2001 From: jrodriiguezg Date: Sat, 15 Aug 2026 15:15:28 +0200 Subject: [PATCH] Subir archivos a "/" --- README.md | 76 ++++++++++- favicon.jpg | Bin 0 -> 29849 bytes favicon.png | Bin 0 -> 5938 bytes index.html | 181 +++++++++++++++++++++++++ styles.css | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 636 insertions(+), 2 deletions(-) create mode 100644 favicon.jpg create mode 100644 favicon.png create mode 100644 index.html create mode 100644 styles.css diff --git a/README.md b/README.md index 6fc3fd0..eef49e8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,75 @@ -# jrodriiguezg.link-page +# jrodriiguezg.link — Personal Portfolio -Files for jrodriiguezg.link main page \ No newline at end of file +> Deployment & Infrastructure consultancy site. Docker, Linux, AI stacks. + +**Live:** [jrodriiguezg.link](https://jrodriiguezg.link) + +--- + +## Overview + +Personal portfolio and landing page for **Juan Raul Rodriguez Gil** — a deployment and infrastructure specialist focused on Linux, Docker, and AI pipelines. The site presents services offered, skills, projects, and contact information in a clean two-column layout. + +## Tech Stack + +| Layer | Technology | +|---|---| +| Markup | HTML5 | +| Styling | Vanilla CSS | +| Typography | Inter (Google Fonts) | +| Hosting | Self-hosted (Cloudflare Tunnel) | + +## Structure + +``` +personal_site/ +├── index.html # Main portfolio page +├── styles.css # Global stylesheet +├── favicon.png # Site favicon +├── robots.txt # SEO crawler rules +├── sitemap.xml # XML sitemap +├── lab/ # Home Lab subpage +├── gamecathode/ # GameCathode project page +├── grape/ # Grape Models project page +├── PRRPC/ # PRRPC project page +└── srag/ # SRag project page +``` + +## Services Featured + +- **Container Architecture** — Docker Compose stacks, isolated services, versioned configs +- **AI & LLM Infrastructure** — Local Ollama, RAG pipelines, ChromaDB integration +- **Linux Systems Engineering** — Server setup, hardening, administration (Debian, RHEL, Ubuntu, Fedora) +- **Infrastructure Audit** — Deep deployment review and risk assessment + +## Skills Highlighted + +| Category | Tools | +|---|---| +| Infrastructure | Docker · Docker Compose · Podman · Cloudflare · Nginx · Bash · Git · Python | +| Linux & Systems | Debian · RHEL · Ubuntu · Fedora · Windows Server | +| Networking | DNS · Reverse proxy · VPN · Cloudflare Tunnels · Firewall | +| AI & Data | Ollama · ChromaDB · LlamaIndex · OpenWebUI · AnythingLLM · Jupyter | +| Electronics | Circuit design · Soldering · Arduino · ESP32 · Raspberry Pi · KiCad | +| Business Software | Odoo ERP · Microsoft Excel · Office 365 | +| Design & Docs | Inkscape · Canva · Markdown · FreeCAD | + +## Projects + +| Project | Description | Stack | +|---|---|---| +| [LEMoE](https://lemoe.link) | AI middleware with semantic routing across expert models | Docker · Python · Ollama | +| [SRag](https://srag.jrodriiguezg.link) | Containerized RAG pipeline | ChromaDB · Docker Compose | +| [PRRPC](https://prrpc.jrodriiguezg.link) | Physical Linux status monitor via RP2040 | Linux · Wayland · Hardware | +| [Blog](https://blog.jrodriiguezg.link) | Technical writing in English and Spanish | Writing · Linux · Systems | +| Lab | Two servers, 5.6TB storage, no cloud | HP EliteDesk · Raspberry Pi 4B | + +## Contact + +- **Email:** [hola@jrodriiguezg.link](mailto:hola@jrodriiguezg.link) +- **LinkedIn:** [juanraulrodriguezgil](https://www.linkedin.com/in/juanraulrodriguezgil/) +- **GitHub:** [@jrodriiguezg](https://github.com/jrodriiguezg) + +--- + +© 2026 Juan Raul Rodriguez Gil diff --git a/favicon.jpg b/favicon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e90aa83fa77f65c34dcb60d0daf3a0674ad20e2a GIT binary patch literal 29849 zcmb5UXH-)`*EW0-2q2LrO#uNxsz5|~4@yz#0)q4+9qCBup-Gb_NG~DMK{`^T6F}(* zND+|Ur8lYX@xIr)p6Ac|t@TaHnas?YoY}MI+Slwo*Av%s0Qqx;X9@rW0ss*31zayc zCZ0WcVyyl`P2ric{C_LH0bs&^2mo&!++1ELK4#W^rN>M-_uo7I)tQ>RI{mx;KN8sQ z@y~y~1HcIH|D(yu~Ik|yt z)c@sOv|c;`^RK}?r`7+;oBgl6xs%Jk_V2+ql5gzY|NYm$^zVspS~zHHf>$E&!vMGf zF91d0@xSqdzro~)1pv__0Kkj=pE9!)0H_TCfZJ35Q+6K%0A#@c@MZ9S%Kk?tPNpuV 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 A+yDRo literal 0 HcmV?d00001 diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..9175290d5c903e3c157f7d75a11180f3fddeea5c GIT binary patch literal 5938 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 literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..336d525 --- /dev/null +++ b/index.html @@ -0,0 +1,181 @@ + + + + + + Juan Rodriguez Gil — Deployment & Infrastructure + + + + + + + + + + + + +
+
+

+ Infrastructure
+ that just works. +

+
+
+

+ I design and deploy Linux and Docker-based stacks for teams that can't afford surprises in production. From a single service to a full AI pipeline. +

+ Talk to me → +
+
+ + +
+ + +
+ +
+

Services

+
+
+

Container Architecture

+

Translating complex application dependencies into clean, reproducible Docker Compose stacks. Every service isolated, every config versioned.

+
+
+

AI & LLM Infrastructure

+

Private deployment of language models and RAG pipelines. Local Ollama setups, ChromaDB integration, and intelligent request routing.

+
+
+

Linux Systems Engineering

+

Server setup, hardening, and long-term administration. Debian, RHEL, Ubuntu, Fedora.

+
+
+

Infrastructure Audit

+

A deep review of your current deployment — finding the things that will hurt you before they do.

+
+
+
+ +
+

Tools & Skills

+
+
+

Infrastructure

+

Docker · Docker Compose · Podman · Cloudflare · Nginx · Bash · Git · Python

+
+
+

Linux & Systems

+

Debian · RHEL · Ubuntu · Fedora · Windows Server · Windows administration

+
+
+

Networking

+

DNS · Reverse proxy · VPN · LAN/WAN · Cloudflare Tunnels · Firewall configuration

+
+
+

AI & Data

+

Ollama · ChromaDB · LlamaIndex · OpenWebUI · AnythingLLM · Jupyter · Python

+
+
+

Electronics & Hardware

+

Circuit design · Soldering · Arduino · ESP32 · Raspberry Pi · KiCad · FreeCAD

+
+
+

Business Software

+

Odoo ERP · Microsoft Excel · Office 365

+
+
+

Design & Documentation

+

Inkscape · Canva · Markdown · FreeCAD

+
+
+
+ + + Home Lab +

Two Linux servers running 15+ self-hosted services. See the hardware and what's deployed on it.

+ View the lab → +
+ +
+ + + + +
+ + + + + \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..32db233 --- /dev/null +++ b/styles.css @@ -0,0 +1,381 @@ +:root { + --bg: #111111; + --surface: #181818; + --text: #e2e2e2; + --muted: #666; + --border: #242424; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { scroll-behavior: smooth; } + +body { + font-family: 'Inter', sans-serif; + background: var(--bg); + color: var(--text); + font-size: 1rem; + line-height: 1.7; + -webkit-font-smoothing: antialiased; +} + +/* ─── NAV ─── */ + +nav { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.8rem 4rem; + border-bottom: 1px solid var(--border); +} + +.nav-name { + font-size: 0.95rem; + font-weight: 500; + color: var(--text); + text-decoration: none; +} + +.nav-links { + display: flex; + gap: 2.5rem; +} + +.nav-links a { + font-size: 0.85rem; + color: var(--muted); + text-decoration: none; + transition: color 0.2s; +} + +.nav-links a:hover { color: var(--text); } + +/* ─── DESKTOP LAYOUT ─── */ + +/* Hero: full width, 2 columns */ +header { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: end; + padding: 6rem 4rem 5rem; + border-bottom: 1px solid var(--border); +} + +.hero-left { } + +.hero-right { + padding-bottom: 0.4rem; +} + +h1 { + font-size: clamp(2.4rem, 4vw, 3.8rem); + font-weight: 600; + line-height: 1.1; + letter-spacing: -0.03em; + margin-bottom: 0; +} + +h1 em { + font-style: italic; + font-weight: 300; +} + +.tagline { + font-size: 1rem; + color: var(--muted); + font-weight: 300; + line-height: 1.8; + margin-bottom: 2rem; +} + +.cta { + display: inline-block; + font-size: 0.9rem; + font-weight: 500; + color: var(--bg); + background: var(--text); + text-decoration: none; + padding: 0.65rem 1.4rem; + border-radius: 4px; + transition: opacity 0.2s; +} + +.cta:hover { opacity: 0.8; } + +/* ─── MAIN CONTENT AREA ─── */ + +.content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0; + padding: 0 4rem; +} + +/* Left column */ +.col-left { + padding: 4rem 4rem 4rem 0; + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; + gap: 4rem; +} + +/* Right column */ +.col-right { + padding: 4rem 0 4rem 4rem; + display: flex; + flex-direction: column; + gap: 4rem; +} + +/* ─── SECTION TITLES ─── */ + +h2 { + font-size: 0.72rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); + margin-bottom: 2rem; +} + +/* ─── SERVICES ─── */ + +.service-list { + display: flex; + flex-direction: column; + gap: 1.8rem; +} + +.service-title { + font-size: 0.95rem; + font-weight: 500; + margin-bottom: 0.3rem; +} + +.service-desc { + font-size: 0.88rem; + color: var(--muted); + font-weight: 300; + line-height: 1.7; +} + +/* ─── PROJECTS ─── */ + +.projects { + display: flex; + flex-direction: column; + gap: 0; +} + +.project { + display: block; + padding: 1.4rem 0; + text-decoration: none; + color: inherit; + border-top: 1px solid var(--border); + transition: padding-left 0.2s; +} + +.project:last-child { + border-bottom: 1px solid var(--border); +} + +.project:hover { padding-left: 0.5rem; } + +.project-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.4rem; +} + +.project-name { + font-size: 0.95rem; + font-weight: 500; +} + +.project-arrow { + font-size: 0.8rem; + color: var(--muted); +} + +.project p { + font-size: 0.88rem; + color: var(--muted); + font-weight: 300; + margin-bottom: 0.8rem; + line-height: 1.6; +} + +.project-stack { + font-size: 0.75rem; + color: var(--muted); + opacity: 0.6; +} + +/* ─── TOOLS / SKILLS ─── */ + +.skills-grid { + display: flex; + flex-direction: column; + gap: 1.4rem; +} + +.skill-group-title { + font-size: 0.78rem; + font-weight: 500; + color: var(--text); + margin-bottom: 0.2rem; +} + +.skill-group p:last-child { + font-size: 0.85rem; + color: var(--muted); + font-weight: 300; + line-height: 1.7; +} + +/* ─── LAB CALLOUT ─── */ + +.lab-callout { + display: flex; + flex-direction: column; + gap: 0.6rem; + padding: 1.5rem; + border: 1px solid var(--border); + border-radius: 4px; + text-decoration: none; + color: inherit; + transition: border-color 0.2s, background 0.2s; +} + +.lab-callout:hover { + background: #181818; + border-color: #333; +} + +.lab-callout-label { + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); +} + +.lab-callout p { + font-size: 0.88rem; + color: var(--muted); + font-weight: 300; + line-height: 1.6; +} + +.lab-callout-link { + font-size: 0.85rem; + font-weight: 500; + color: var(--text); +} + +/* ─── CONTACT ─── */ + +.contact p { + font-size: 0.95rem; + color: var(--muted); + font-weight: 300; + margin-bottom: 1.5rem; + line-height: 1.8; +} + +.email-link { + display: inline-block; + font-size: 1rem; + font-weight: 500; + color: var(--text); + text-decoration: none; + border-bottom: 1px solid var(--border); + padding-bottom: 2px; + margin-bottom: 1.5rem; + transition: border-color 0.2s; +} + +.email-link:hover { border-color: var(--muted); } + +.social-links { + display: flex; + gap: 1.5rem; +} + +.social-links a { + font-size: 0.85rem; + color: var(--muted); + text-decoration: none; + transition: color 0.2s; +} + +.social-links a:hover { color: var(--text); } + +/* ─── FOOTER ─── */ + +footer { + padding: 1.5rem 4rem; + border-top: 1px solid var(--border); + font-size: 0.8rem; + color: var(--muted); + display: flex; + justify-content: space-between; +} + +/* ─── TABLET ─── */ + +@media (max-width: 900px) { + nav { padding: 1.5rem 2rem; } + + header { + grid-template-columns: 1fr; + gap: 2rem; + padding: 4rem 2rem; + } + + .content { + grid-template-columns: 1fr; + padding: 0 2rem; + } + + .col-left { + padding: 3rem 0; + border-right: none; + border-bottom: 1px solid var(--border); + } + + .col-right { padding: 3rem 0; } + + footer { padding: 1.5rem 2rem; } +} + +/* ─── MOBILE ─── */ + +@media (max-width: 520px) { + nav { + padding: 1.2rem 1.2rem; + } + + .nav-links { gap: 1.2rem; } + + header { padding: 3rem 1.2rem; } + + h1 { font-size: 2.2rem; } + + .content { padding: 0 1.2rem; } + + footer { + padding: 1.5rem 1.2rem; + flex-direction: column; + gap: 0.3rem; + } + + .social-links { flex-wrap: wrap; gap: 1rem; } +} \ No newline at end of file