feat: implement proper two-page CV layout matching original React design

**Page 1 Changes:**
- Left sidebar with first half of skills categories
- Main content: Personal info, Education, Skills summary, Experience
- No footer on page 1

**Page 2 Changes:**
- Main content: Awards, Courses, Languages, References, Other
- Right sidebar with second half of skills categories
- Footer with address, phone, email (dark gray #303030 background)
- Same header as page 1

**Technical Implementation:**
- Split skills between left/right sidebars using midpoint calculation in Go handler
- CSS Grid layout: Page 1 (left sidebar + main), Page 2 (main + right sidebar)
- Footer styled to match React CV exactly (centered, horizontal layout)
- Print CSS with proper page breaks for A4 layout
- Mobile responsive: stacks to single column, hides page 2 header
- All links in References section are clickable

**Data Model:**
- Moved Languages, Courses, References, Other from sidebar to page 2 main content
- Skills split evenly between SkillsLeft and SkillsRight template variables

Matches pixel-perfect design from original React CV screenshot.
This commit is contained in:
juanatsap
2025-11-04 19:07:34 +00:00
parent 7003694e4d
commit e2c4eafda2
3 changed files with 585 additions and 152 deletions
+76 -13
View File
@@ -1,21 +1,29 @@
package handlers
import (
"fmt"
"log"
"net/http"
"time"
"github.com/juanatsap/cv-site/internal/models"
"github.com/juanatsap/cv-site/internal/pdf"
"github.com/juanatsap/cv-site/internal/templates"
)
// CVHandler handles CV-related requests
type CVHandler struct {
templates *templates.Manager
templates *templates.Manager
pdfGenerator *pdf.Generator
serverAddr string
}
// NewCVHandler creates a new CV handler
func NewCVHandler(tmpl *templates.Manager) *CVHandler {
func NewCVHandler(tmpl *templates.Manager, serverAddr string) *CVHandler {
return &CVHandler{
templates: tmpl,
templates: tmpl,
pdfGenerator: pdf.NewGenerator(30 * time.Second),
serverAddr: serverAddr,
}
}
@@ -40,10 +48,15 @@ func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
return
}
// Split skills between left and right sidebars
skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)
// Prepare template data
data := map[string]interface{}{
"CV": cv,
"Lang": lang,
"CV": cv,
"Lang": lang,
"SkillsLeft": skillsLeft,
"SkillsRight": skillsRight,
}
// Render template
@@ -81,10 +94,15 @@ func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
return
}
// Split skills between left and right sidebars
skillsLeft, skillsRight := splitSkills(cv.Skills.Technical)
// Prepare template data
data := map[string]interface{}{
"CV": cv,
"Lang": lang,
"CV": cv,
"Lang": lang,
"SkillsLeft": skillsLeft,
"SkillsRight": skillsRight,
}
// Render template
@@ -101,9 +119,7 @@ func (h *CVHandler) CVContent(w http.ResponseWriter, r *http.Request) {
}
}
// ExportPDF handles PDF export requests
// For now, redirects to print-friendly version
// In production, integrate with chromedp or similar for actual PDF generation
// ExportPDF handles PDF export requests using chromedp
func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
// Get language from query parameter
lang := r.URL.Query().Get("lang")
@@ -111,7 +127,54 @@ func (h *CVHandler) ExportPDF(w http.ResponseWriter, r *http.Request) {
lang = "en"
}
// Redirect to print-friendly version
// The browser's print dialog will handle PDF generation
http.Redirect(w, r, "/?lang="+lang+"&print=true", http.StatusSeeOther)
// Validate language
if lang != "en" && lang != "es" {
HandleError(w, r, BadRequestError("Unsupported language. Use 'en' or 'es'"))
return
}
// Construct URL to generate PDF from
// Use localhost instead of the actual server address to avoid network overhead
url := fmt.Sprintf("http://%s/?lang=%s", h.serverAddr, lang)
log.Printf("Generating PDF from URL: %s", url)
// Generate PDF
pdfData, err := h.pdfGenerator.GenerateFromURL(r.Context(), url)
if err != nil {
log.Printf("PDF generation failed: %v", err)
HandleError(w, r, InternalError(err))
return
}
// Set response headers
filename := fmt.Sprintf("CV-Juan-Andres-Moreno-Rubio-%s.pdf", lang)
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(pdfData)))
// Write PDF data
if _, err := w.Write(pdfData); err != nil {
log.Printf("Failed to write PDF response: %v", err)
return
}
log.Printf("Successfully generated PDF: %s (%d bytes)", filename, len(pdfData))
}
// splitSkills splits skill categories between left (page 1) and right (page 2) sidebars
// The split is done at the midpoint to evenly distribute skills
func splitSkills(skills []models.SkillCategory) (left, right []models.SkillCategory) {
if len(skills) == 0 {
return nil, nil
}
// Calculate midpoint
mid := len(skills) / 2
// Split at midpoint
left = skills[:mid]
right = skills[mid:]
return left, right
}
+301
View File
@@ -728,3 +728,304 @@ a:focus {
max-width: none;
}
}
/* ===============================================
TWO-PAGE LAYOUT STYLES
=============================================== */
/* Page Container - Each CV page */
.cv-page {
background: var(--paper-white);
max-width: 1200px;
margin: 2rem auto;
box-shadow: 2px 2px 9px rgba(0,0,0,0.5);
border: 1px solid #333;
transform: scale(0.95);
transform-origin: top center;
transition: transform 0.3s ease;
}
/* Page Content Grid */
.page-content {
display: grid;
}
/* Page 1: Left sidebar + Main content */
.page-1 .page-content {
grid-template-columns: 300px 1fr;
}
/* Page 2: Main content + Right sidebar */
.page-2 .page-content {
grid-template-columns: 1fr 300px;
}
/* Sidebar positioning */
.cv-sidebar-left {
grid-column: 1;
grid-row: 1;
}
.cv-sidebar-right {
grid-column: 2;
grid-row: 1;
}
/* Main content positioning */
.page-1 .cv-main {
grid-column: 2;
grid-row: 1;
}
.page-2 .cv-main {
grid-column: 1;
grid-row: 1;
}
/* ===============================================
FOOTER STYLES
=============================================== */
.cv-footer {
background: #303030;
color: #ccc;
padding: 20px 0;
margin: 0;
grid-column: 1 / -1; /* Span all columns */
}
.footer-content {
list-style: none;
text-align: center;
margin: 0;
padding: 0;
}
.footer-content li {
display: inline-block;
margin: 0;
}
.footer-content li > div {
display: inline-block;
margin: 0 20px;
text-align: left;
}
.footer-label {
width: 200px;
font-size: 1.7em;
}
.footer-value {
width: 450px;
font-size: 1em;
}
.footer-value b {
font-weight: normal;
font-size: 1.7em;
}
.footer-separator {
position: relative;
left: -4%;
font-size: 0.6em;
}
.footer-separator i {
opacity: 0.3;
}
.cv-footer a {
color: inherit;
}
.cv-footer a:hover {
color: #0275d8;
text-decoration: none;
font-weight: bold;
}
/* ===============================================
PRINT STYLES - TWO-PAGE LAYOUT
=============================================== */
@media print {
body {
background: white;
margin: 0;
padding: 0;
}
.action-bar {
display: none !important;
}
.cv-page {
box-shadow: none;
border: none;
margin: 0 auto;
transform: scale(1);
max-width: 100%;
page-break-after: always;
page-break-inside: avoid;
}
.cv-page.page-2 {
page-break-after: auto;
}
.page-content {
page-break-inside: avoid;
}
.cv-section {
page-break-inside: avoid;
}
/* Ensure footer only on page 2 */
.page-1 .cv-footer {
display: none !important;
}
.cv-footer {
page-break-inside: avoid;
background: #ddd !important;
color: #333 !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
a {
text-decoration: none;
font-weight: 800;
color: inherit;
}
/* Set up proper A4 page dimensions */
@page {
size: A4 portrait;
margin: 0.5in;
}
}
/* ===============================================
SECTION STYLES FOR PAGE 2
=============================================== */
.award-item,
.course-item,
.language-item,
.reference-item,
.other-content {
margin-bottom: 1em;
}
.award-item strong,
.course-item strong,
.language-item strong {
font-weight: 600;
color: var(--text-dark);
}
.award-item small,
.course-item small {
color: #666;
font-size: 0.875em;
}
.award-desc,
.course-desc {
margin-top: 0.5em;
color: var(--text-gray);
font-size: 0.95em;
}
.reference-item {
margin-bottom: 0.5em;
}
.reference-item a {
font-weight: 500;
}
.ref-type {
color: #999;
margin-left: 0.5em;
font-size: 0.875em;
}
/* ===============================================
MOBILE RESPONSIVE - TWO-PAGE LAYOUT
=============================================== */
@media (max-width: 900px) {
.cv-page {
margin: 1rem;
transform: scale(1);
}
/* Stack layout on mobile */
.page-1 .page-content,
.page-2 .page-content {
grid-template-columns: 1fr;
}
.cv-sidebar-left,
.cv-sidebar-right {
grid-column: 1;
}
.page-1 .cv-main,
.page-2 .cv-main {
grid-column: 1;
}
/* Hide header on page 2 for mobile to merge pages */
.page-2 .cv-title-badges-header {
display: none;
}
/* Adjust footer for mobile */
.footer-content li > div {
display: block;
margin: 0;
text-align: center;
width: 100%;
}
.footer-label {
font-size: 1em;
margin-top: 15px;
color: #777;
}
.footer-separator {
display: none;
}
.footer-value {
font-size: 1.5em;
margin-bottom: 0;
padding: 0;
}
}
/* ===============================================
TABLET RESPONSIVE
=============================================== */
@media (max-width: 768px) and (min-width: 577px) {
.page-content {
gap: 1rem;
}
.footer-label {
font-size: 1.2em;
}
.footer-value {
font-size: 1em;
}
}
+208 -139
View File
@@ -1,149 +1,218 @@
<!-- Professional Title Badges - Full Width Top Bar -->
<div class="cv-title-badges-header">
<span class="title-badge">{{if eq .Lang "es"}}ANALISTA PROGRAMADOR{{else}}ANALYST PROGRAMMER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">NODEJS + REACTJS {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">WEB {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">GO {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">PHP {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
</div>
<!-- Left Sidebar - Skills -->
<aside class="cv-sidebar">
<!-- Skills Section - Dynamically render all categories -->
{{range .CV.Skills.Technical}}
<section class="sidebar-section">
<h3 class="sidebar-title">{{.Category}}</h3>
<div class="sidebar-content">
{{range .Items}}<div class="skill-item">{{.}}</div>{{end}}
</div>
</section>
{{end}}
<!-- Languages Section -->
<section class="sidebar-section">
<h3 class="sidebar-title">{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</h3>
<div class="sidebar-content">
{{range .CV.Languages}}
<div class="language-item">
<strong>{{.Language}}</strong>: {{.Proficiency}}
{{if .Detail}}<br><small style="color: #666;">{{.Detail}}</small>{{end}}
</div>
{{end}}
</div>
</section>
<!-- Courses Section -->
{{if .CV.Courses}}
<section class="sidebar-section">
<h3 class="sidebar-title">{{if eq .Lang "es"}}Cursos Realizados{{else}}Training Courses{{end}}</h3>
<div class="sidebar-content">
{{range .CV.Courses}}
<div class="course-item">
<strong>{{.Title}}</strong><br>
<small>{{.Institution}} - {{.Location}}</small><br>
<small>{{.Date}} ({{.Duration}})</small>
{{if .Description}}<p class="course-desc">{{.Description}}</p>{{end}}
</div>
{{end}}
</div>
</section>
{{end}}
<!-- References Section -->
{{if .CV.References}}
<section class="sidebar-section">
<h3 class="sidebar-title">{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</h3>
<div class="sidebar-content">
{{range .CV.References}}
<div class="reference-item">
<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>
<small class="ref-type">({{.Type}})</small>
</div>
{{end}}
</div>
</section>
{{end}}
<!-- Other Section (Driver's License) -->
{{if .CV.Other.DriverLicense}}
<section class="sidebar-section">
<h3 class="sidebar-title">{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</h3>
<div class="sidebar-content">
{{if eq .Lang "es"}}Carnet de conducir {{.CV.Other.DriverLicense}}{{else}}Driver's License {{.CV.Other.DriverLicense}}{{end}}
</div>
</section>
{{end}}
</aside>
<!-- Main Content Area -->
<main class="cv-main">
<!-- Header with Name and Photo -->
<div class="cv-header">
<div class="cv-header-content">
<div class="cv-header-left">
<h1 class="cv-name">{{.CV.Personal.Name}}</h1>
<p class="cv-experience-years">{{if eq .Lang "es"}}20 años de experiencia{{else}}20 years of experience{{end}}</p>
<!-- Intro/Excerpt Text - No section heading, just the text -->
<div class="intro-text">{{.CV.Summary}}</div>
</div>
<div class="cv-photo">
<img src="/static/images/profile/photo.jpg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
</div>
</div>
<!-- PAGE 1 -->
<div class="cv-page page-1">
<!-- Professional Title Badges - Full Width Top Bar -->
<div class="cv-title-badges-header">
<span class="title-badge">{{if eq .Lang "es"}}ANALISTA PROGRAMADOR{{else}}ANALYST PROGRAMMER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">NODEJS + REACTJS {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">WEB {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">GO {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">PHP {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
</div>
<!-- Education -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</h3>
{{range .CV.Education}}
<div class="education-item">
<strong>{{.Degree}}</strong> ({{.StartDate}}-{{.EndDate}}) {{if eq $.Lang "es"}}obtenido de{{else}}obtained from the{{end}} <strong>{{.Institution}}</strong> ({{.Location}})
</div>
{{end}}
</section>
<!-- Skills Summary -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</h3>
<p class="summary-text">
{{if eq .Lang "es"}}
Amplio conocimiento en entornos web, tanto J2EE como PHP. Experto en tecnologías front-end, aunque con considerable experiencia en sistemas back-end. Receptivo al aprendizaje de nuevas tecnologías, y con una gran dosis de creatividad. Capacidad de analizar problemas y aportar soluciones específicas adaptadas a cada tipo de cliente. Me gusta trabajar tanto solo como en grupos.
{{else}}
Extensive knowledge in web environments, both J2EE and PHP. Expert in front-end technologies, although with considerable experience in back-end systems. Receptive to learning new technologies, and with a large dose of creativity. Ability to analyze problems and provide specific solutions tailored to each client type. I like to work both alone and in groups.
<!-- Page 1 Content Grid: Left Sidebar + Main Content -->
<div class="page-content">
<!-- Left Sidebar - Skills (first half) -->
<aside class="cv-sidebar cv-sidebar-left">
{{range $index, $category := .SkillsLeft}}
<section class="sidebar-section">
<h3 class="sidebar-title">{{$category.Category}}</h3>
<div class="sidebar-content">
{{range $category.Items}}<div class="skill-item">{{.}}</div>{{end}}
</div>
</section>
{{end}}
</p>
</section>
</aside>
<!-- Experience -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</h3>
{{range .CV.Experience}}
<div class="experience-item">
<div class="experience-header">
<div class="experience-title-line">
<h4 class="position">{{.Position}} / {{if eq $.Lang "es"}}Analista Programador{{else}}Analyst Programmer{{end}}</h4>
<span class="experience-period">{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</span>
<!-- Main Content Area - Page 1 -->
<main class="cv-main">
<!-- Header with Name and Photo -->
<div class="cv-header">
<div class="cv-header-content">
<div class="cv-header-left">
<h1 class="cv-name">{{.CV.Personal.Name}}</h1>
<p class="cv-experience-years">{{if eq .Lang "es"}}20 años de experiencia{{else}}20 years of experience{{end}}</p>
<!-- Intro/Excerpt Text - No section heading, just the text -->
<div class="intro-text">{{.CV.Summary}}</div>
</div>
<div class="cv-photo">
<img src="/static/images/profile/photo.jpg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
</div>
</div>
</div>
{{if .ShortDescription}}
<p class="short-desc">{{.ShortDescription}}</p>
<!-- Education -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</h3>
{{range .CV.Education}}
<div class="education-item">
<strong>{{.Degree}}</strong> ({{.StartDate}}-{{.EndDate}}) {{if eq $.Lang "es"}}obtenido de{{else}}obtained from the{{end}} <strong>{{.Institution}}</strong> ({{.Location}})
</div>
{{end}}
</section>
<!-- Skills Summary -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</h3>
<p class="summary-text">
{{if eq .Lang "es"}}
Amplio conocimiento en entornos web, tanto J2EE como PHP. Experto en tecnologías front-end, aunque con considerable experiencia en sistemas back-end. Receptivo al aprendizaje de nuevas tecnologías, y con una gran dosis de creatividad. Capacidad de analizar problemas y aportar soluciones específicas adaptadas a cada tipo de cliente. Me gusta trabajar tanto solo como en grupos.
{{else}}
Extensive knowledge in web environments, both J2EE and PHP. Expert in front-end technologies, although with considerable experience in back-end systems. Receptive to learning new technologies, and with a large dose of creativity. Ability to analyze problems and provide specific solutions tailored to each client type. I like to work both alone and in groups.
{{end}}
</p>
</section>
<!-- Experience -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</h3>
{{range .CV.Experience}}
<div class="experience-item">
<div class="experience-header">
<div class="experience-title-line">
<h4 class="position">{{.Position}} / {{if eq $.Lang "es"}}Analista Programador{{else}}Analyst Programmer{{end}}</h4>
<span class="experience-period">{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</span>
</div>
</div>
{{if .ShortDescription}}
<p class="short-desc">{{.ShortDescription}}</p>
{{end}}
<div class="long-only">
<ul class="responsibilities">
{{range .Responsibilities}}
<li>{{.}}</li>
{{end}}
</ul>
</div>
</div>
{{end}}
</section>
</main>
</div>
</div>
<!-- PAGE 2 -->
<div class="cv-page page-2">
<!-- Professional Title Badges - Same as Page 1 -->
<div class="cv-title-badges-header">
<span class="title-badge">{{if eq .Lang "es"}}ANALISTA PROGRAMADOR{{else}}ANALYST PROGRAMMER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">NODEJS + REACTJS {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">WEB {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">GO {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
<span class="badge-separator">|</span>
<span class="title-badge">PHP {{if eq .Lang "es"}}DESARROLLADOR{{else}}DEVELOPER{{end}}</span>
</div>
<!-- Page 2 Content Grid: Main Content + Right Sidebar -->
<div class="page-content">
<!-- Main Content Area - Page 2 -->
<main class="cv-main">
<!-- Awards Section -->
{{if .CV.Awards}}
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}</h3>
{{range .CV.Awards}}
<div class="award-item">
<strong>{{.Title}}</strong><br>
<small>{{.Issuer}} - {{.Date}}</small>
{{if .Description}}<p class="award-desc">{{.Description}}</p>{{end}}
</div>
{{end}}
</section>
{{end}}
<div class="long-only">
<ul class="responsibilities">
{{range .Responsibilities}}
<li>{{.}}</li>
{{end}}
</ul>
</div>
</div>
{{end}}
</section>
<!-- Courses Section -->
{{if .CV.Courses}}
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}</h3>
{{range .CV.Courses}}
<div class="course-item">
<strong>{{.Title}}</strong><br>
<small>{{.Institution}} - {{.Location}}</small><br>
<small>{{.Date}} ({{.Duration}})</small>
{{if .Description}}<p class="course-desc">{{.Description}}</p>{{end}}
</div>
{{end}}
</section>
{{end}}
</main>
<!-- Languages Section -->
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</h3>
{{range .CV.Languages}}
<div class="language-item">
<strong>{{.Language}}</strong>: {{.Proficiency}}
{{if .Detail}}<br><small style="color: #666;">{{.Detail}}</small>{{end}}
</div>
{{end}}
</section>
<!-- References Section -->
{{if .CV.References}}
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</h3>
{{range .CV.References}}
<div class="reference-item">
<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>
<small class="ref-type">({{.Type}})</small>
</div>
{{end}}
</section>
{{end}}
<!-- Other Section (Driver's License) -->
{{if .CV.Other.DriverLicense}}
<section class="cv-section">
<h3 class="section-title">{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</h3>
<div class="other-content">
{{if eq .Lang "es"}}Carnet de conducir {{.CV.Other.DriverLicense}}{{else}}Driver's License {{.CV.Other.DriverLicense}}{{end}}
</div>
</section>
{{end}}
</main>
<!-- Right Sidebar - Skills (second half) -->
<aside class="cv-sidebar cv-sidebar-right">
{{range $index, $category := .SkillsRight}}
<section class="sidebar-section">
<h3 class="sidebar-title">{{$category.Category}}</h3>
<div class="sidebar-content">
{{range $category.Items}}<div class="skill-item">{{.}}</div>{{end}}
</div>
</section>
{{end}}
</aside>
</div>
<!-- Footer - Only on Page 2 -->
<footer class="cv-footer">
<ul class="footer-content">
<li>
<div class="footer-label">address_</div>
<div class="footer-separator"><i class="fa fa-circle"></i></div>
<div class="footer-value">Carrer Meer, N° 51 4° 2ª, 08003 Barcelona</div>
</li>
<li>
<div class="footer-label">phone#</div>
<div class="footer-separator"><i class="fa fa-circle"></i></div>
<div class="footer-value">+34 676875420</div>
</li>
<li>
<div class="footer-label">email@</div>
<div class="footer-separator"><i class="fa fa-circle"></i></div>
<div class="footer-value">
<a href="mailto:txeo.msx@gmail.com" target="_blank" rel="noopener noreferrer">txeo.msx@gmail.com</a>
</div>
</li>
</ul>
</footer>
</div>